本文主要是介绍Spring 揭秘之Spring AOP一世(2)织入实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
文章目录
- Spring AOP一世
- Spring AOP的织入
- 与ProxyFactory打交道
- 基于接口的代理
- 基于类的代理
- Introduction的织入
- ProxyFactory的本质
- 容器中的织入器——ProxyFactoryBean
- ProxyFactoryBean的本质
- ProxyFactoryBean的使用
- 加快织入的自动化进程
- 自动代理得以实现的原理
- 可用的AutoProxyCreator
- 扩展AutoProxyCreator
- TargetSource
- 可用的TargetSource
- 自定义TargetSource
- 小结
Spring AOP一世
Spring AOP的织入
如今,所有基本概念都已经到位,只差将其拼装在一起,也就是织入。还记得我们曾学习过的AOP和OOP在系统实现当中的关系吗?我们需要将AOP织入到OOP中,以便让我们的准备好的一起,行动起来;这个角色就是织入器啦;
Spring AOP采用ProxyFactory类作为织入器;我们就从最基本的织入器开始吧!
所谓织入,就是在Joinpoint(来自OOP业务系统)处加入Advice(来自AOP)所表示的逻辑;
其中Joinpoint由Pointcut进行匹配,于是我们只需要将Pointcut、Advice和业务系统对象交给织入器,然后Magic Happens!织入过程就完成啦;
与ProxyFactory打交道
Pointcut和Advice由Advisor组合;所以我们只要将Advisor和业务对象交给ProxyFactory,然后获得增强版的业务对象(也就是所谓的代理对象)即可;
除了传入Advisor,也可以传入Advice,ProxyFactory将根据传入的Advice创建相应的Advisor,当然,使用的Pointcut为Pointcut.TRUE;
我们可以通过更多的属性设置来控制ProxyFactory的织入行为。Spring通过代理模式实现AOP的时候,采用动态代理和CGLIB两种机制,分别对实现某些接口和未实现接口的目标类生成代理对象;这里所谓的控制,就是控制Spring AOP采用哪种方式进行织入;
基于接口的代理
默认情况下,只要目标对象实现了相应接口,就会对目标对象进行基于接口的代理;我们也可以通过ProxyFactory的setInterfaces()方法指定具体的接口;
需要注意的是,我们可以将代理对象强转为接口类型(也就是通过接口引用代理对象),但是不能将代理对象强转为被代理类,即便它们都实现了相同接口,这也很好理解嘛;
基于类的代理
如果目标对象没有实现任何接口,默认情况下ProxyFactory会对目标类进行基于类的代理,即使用CGLIB;
需要注意的是,即便目标对象也实现了某个接口,我们也可以强制ProxyFactory使用基于类的代理;
总的来说,如果下列条件满足其一,就会使用基于类的代理:
- 目标对象没有实现任何接口,此时直接使用基于类的代理;
- proxyTargetClass属性为true;
- optimize属性为true;
Introduction的织入
Introduction可以为已经存在的对象类型添加新的行为,只能应用于对象级别的拦截,而不是方法级别的拦截;在进行Introduction的织入过程中,不需要指定Pointcut(用于匹配方法),只需指定目标接口类型;
需要注意的是,对Introduction进行织入,新添加的接口类型必须通过setInterfaces指定;原来的目标对象可以自由选择织入方式;
ProxyFactory的本质
知其表而不知其里,充其量也只能算是一个画匠,而不是画师;只懂得如何使用API,而不知道这些API为何如此设计,终将无法迈出从“画匠”到“画师”的那一步;
认清其本质不仅让我们清楚其如何实现,帮助我们获取系统设计的宝贵经验,还可以让我们更加灵活地使用它;
ProxyFactory的根是AopProxy;Spring AOP使用AopProxy对不同的代理实现机制进行了适度的抽象;目前,Spring提供基于JDK的动态代理和CGLIB这两种机制的AopProxy实现:
[外链图片转存失败(img-gVxGmJ4P-1562428071220)(]?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NseDMzMjA2MTI1NDA=,size_16,color_FFFFFF,t_70)
AopProxyFactory通过抽象工厂模式根据传入的AdvisedSupport实例提供的信息,决定创建什么类型的AopProxy:如果isOptimize或者isProxyTargetClass为true,或者目标对象没有实现任何接口,那么就用cglib生成代理对象;否则使用动态代理;
AdvisedSupport实际上就是一个生成代理对象所需信息的载体:
ProxyConfig类记录生成代理对象的控制信息;Advised记录生成代理对象所需要的必要信息,如目标类、Advise、Advisor;
其中,ProxyConfig有以下属性:
- proxyTargetClass:默认为false,即使用基于接口的代理;
- optimize:该属性为true的时候,proxyFactory将使用CGLIB进行代理;默认为false;实际上,它还指示是否对代理对象进行进一步优化措施;
- opaque:控制代理对象是否可以被强转为Advised;默认为false;
- exposeProxy:是否将当前代理对象绑定到ThreadLocal,如果目标对象需要访问代理对象,则可以通过AopContext.currentProxy()获得;默认为false;
- frozen:如果为true,那么代理对象生成的信息配置完成,那么就不能更改;
到这里,再上一图:
前面我们提到过,ProxyFactory是基本的织入器,那么接下来看看织入器体系以及其他场景下的织入器:
容器中的织入器——ProxyFactoryBean
使用ProxyFactory,我们可以独立使用Spring AOP;所谓独立就是说独立于Spring IoC容器;但是Spring IoC、Spring AOP作为“质量三角”之二,为什么不把它们组合在一起呢?
实际上,合则两利;因为AOP王国里的一切终究是要寄生到OOP王国里的,而Spring IoC 容器对业务对象之间的解耦本领,使得我们构建OOP王国变得简单,那么将Spring AOP同Spring IoC相结合也是“大势所趋”;
Spring IoC 容器内部使用ProxyFactoryBean作为织入器,使用方面同ProxyFactory没有什么大的区别;我们在看清了ProxyFactory的本质之后,自然也要对ProxyFactoryBean一探究竟;
ProxyFactoryBean的本质
ProxyFactoryBean=Proxy+FactoryBean;前者表示其功能为产生Proxy,这是织入器的核心功能;后者表示其本质为FactoryBean;所谓FactoryBean是说,如果容器中的某个对象持有FactoryBean对象引用,那么其获得的不是FactoryBean本身,而是其getObject()方法返回的对象;
FactoryBean创建对象的时候,需要指出其scope:Singleleton或者Prototype;如果是Singleleton的话,FactoryBean就会缓存返回值;
ProxyFactoryBean的使用
同ProxyFactory一样,大部分可以设置的项目都是相同的,因为它们都继承自同一个父类;但是也有一些个性设置:
- proxyInterfaces:指定多个接口类型;如果目标对象实现了某个或者多个接口,那么即便我们不显式指定接口,该类也会自动检测,因为有一个属性为autodetectInterfaces,默认值为true;
- interceptorNames:指定一组Advisor、Advice或者Interceptor;如果没有通过相应的设置目标对象的方法,明确为ProxyFactoryBean设定目标对象,那么最后一个位置可以放置目标对象;可以使用通配符让ProxyFactoryBean在容器中搜索符合条件的目标对象;
- Singleton:属于FactoryBean的属性;
目前为止,AOP中的一切内容都可以在容器中使用啦;从Pointcut到Advice、Advisor;我们可以将生成的代理对象直接注入到需要它的地方;此时,需要注意的是,我们应该依赖代理对象而不是目标对象,否则将不会产生任何拦截效果;
加快织入的自动化进程
在IoC容器中使用ProxyFactoryBean进行织入固然不错,但是如果要对每个目标对象都给出他们各自对应的ProxyFactoryBean配置,太过于麻烦;所以Spring AOP给出了自动代理机制以解决配置工作量较大的问题;
自动代理得以实现的原理
Spring AOP的自动代理依托于ApplicationContext之上,更为准确的说是IoC容器的BeanPostProcessor之上;还记得BeanPostProcessor吗?知识传送门;我们只需要在对象初始化的时候,为其生成代理对象并返回,即可实现织入过程;
所以,所谓织入,其实就是为目标对象生成代理对象的过程;该过程需要织入的内容以及寻找目标对象的方法;
可用的AutoProxyCreator
Spring AOP提供了BeanNameAutoProxyCreator和DefaultAdvisorAutoProxyCreator这两个常用类;
- BeanNameAutoProxyCreator:指定一组目标对象的beanName,将一组拦截器应用到这些目标对象之上;在指定BeanName的时候,可使用*通配符;
- DefaultAdvisorAutoProxyCreator:如果说BeanNameAutoProxyCreator属于半自动配置的话(毕竟还需要手动指定beanName和拦截器),那么DefaultAdvisorAutoProxyCreator就属于全自动配置了;只需要在容器(ApplicationContext)中声明一个该类的实例,然后只需要描述各种Advisor,容器启动后就会完成织入;
扩展AutoProxyCreator
只需要继承AbstractAutoProxyCreator或者AbstractAdvisorAutoProxyCreator即可,不需要什么都重新开始;
所有的AutoProxyCreator都是InstantiationAwareBeanPostProcessor,这种类型的BeanPostProcessor略有不同;当容器中检测到有该类型的BeanPostProcessor时,会直接通过该类中的逻辑构造对象实例并返回,并不会走正常的对象实例化流程;
所以呢,子类只需要继承合适的父类,然后提供规则匹配一类的逻辑即可;当然,如有必要,进行覆盖即可;
TargetSource
Spring AOP内部会对目标对象做统一的封装——TargetSource。也就是TargetSource代表了调用链终点的目标对象;
它的特性之一就是每次的方法调用都会触发TargetSource的getTarget()方法,getTarget()方法会返回具体的目标对象;
可用的TargetSource
- SingletonTargetSource,最为常用的TargetSource实现类;在使用ProxyFactoryBean的setTarget()方法设置完目标对象后,内部处理实际上就是用SingletonTargetSource进行包装;每次方法调用时,都返回同一个目标对象,符合Singleton的语义;
- PrototypeTargetSource,符合Prototype的语义,即每次方法调用时都返回新创建的目标对象;此时,目标对象的bean定义声明的Scope必须为prototype;
- HotSwappableTargetSource:比较实用的TargetSource实现。它允许我们在系统运行时,根据某种条件动态替换目标对象类的具体实现;通过其swap方法,我们可以使用新的目标对象替换原来的目标对象;
- CommonsPoolTargetSource:为目标对象创建一个对象池,然后每次请求对象时,便从对象池中返回;
- ThreadLocalTargetSource:将目标对象同线程相关联;不同线程返回不同的目标对象;
自定义TargetSource
直接扩展TargetSource接口即可;
public interface TargetSource extends TargetClassAware {/*** Return the type of targets returned by this {@link TargetSource}.* <p>Can return {@code null}, although certain usages of a {@code TargetSource}* might just work with a predetermined target class.* @return the type of targets returned by this {@link TargetSource}*/@Override@NullableClass<?> getTargetClass();/*** Will all calls to {@link #getTarget()} return the same object?* <p>In that case, there will be no need to invoke {@link #releaseTarget(Object)},* and the AOP framework can cache the return value of {@link #getTarget()}.* @return {@code true} if the target is immutable* @see #getTarget*/boolean isStatic();/*** Return a target instance. Invoked immediately before the* AOP framework calls the "target" of an AOP method invocation.* @return the target object which contains the joinpoint,* or {@code null} if there is no actual target instance* @throws Exception if the target object can't be resolved*/@NullableObject getTarget() throws Exception;/*** Release the given target object obtained from the* {@link #getTarget()} method, if any.* @param target object obtained from a call to {@link #getTarget()}* @throws Exception if the object can't be released*/void releaseTarget(Object target) throws Exception;}
小结
这一节中,我们了解了Spring AOP对AOP的各种概念的实现;它们是整个框架的基础;体现了Spring框架的稳定性和灵活性;
接下来,我们将看看Spring AOP一世的升级版——Spring AOP 二世;
这篇关于Spring 揭秘之Spring AOP一世(2)织入实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!