一、AOP类型选择
AOP框架有多种选择,如完整的AspectJ框架、springAOP框架,同样风格方式也分基于XML的风格和基于@AspectJ注解的风格。如何抉择使用哪种,需要根据应用需求、团队及开发工具等方面的因素综合考虑。
1、AspectJ与springAOP
它们的目的都是为了统一处理横切业务,不同点是:
springAOP并不提供完整的AOP功能,它更注重与IOC容器的结合,来解决横切业务问题,而AspectJ在功能完善度方面更具优势。
AspectJ的AOP实现方式是依赖于特殊编译器(ajc编译器),而springAOP采用动态代理技术来构建内部机制(动态织入),AspectJ采用静态织入方式。springAOP只是使用了与AspectJ5同样的注解,底层是靠动态代理技术实现,并不依赖于AspectJ的编译器。
2、基于@AspectJ注解风格与基于XML风格
使用springAOP可以选择使用基于@AspectJ注解的风格和基于XML的风格,各有优劣。
基于XML的风格
优点:它由POJO支持,优点是从配置中可以清楚看到系统中存在哪些方面。
缺点:
1)它没有完全封装在一个地方,当使用XML风格时,需求点被分解为支持bean类的声明和配置文件中的XML。当使用@AspectJ风格时,所有的信息都被封装在一个模块中(即aspect)。
2)XML风格只支持singleton的实例化模型,不能组合XML中声明命名切入点。示例如下:
@AspectJ风格:
@Pointcut("execution(* get*())")
public void getProperty(){}
@Pointcut("execution(* com.xxx.User+ *(..))")
public void operUser(){}
@Pointcut("getProperty() && operUser()")
public void getUserProperty(){}
XML风格:
<aop:pointcut id="getProperty" expression="execution(* get*())" />
<aop:pointcut id="operUser" expression="execution(* com.xxx.User+ *(..))" />
基于@AspectJ风格
@AspectJ风格支持额外的实例化模型和丰富的切入点组合,它具有将aspect保持为模块化单元的优点,可以很容易的迁移到AspectJ框架中。
二、代理机制
springAOP支持使用JDK动态代理或CGLIB为目标对象创建代理。默认时,如果要被代理的目标对象实现了至少一个接口,则将使用JDK动态代理,并且所有目标类型实现的接口都将被代理。如果目标对象没有实现任何接口,将使用CGLIB代理。
也可以强制使用CGLIB代理,但有以下限制条件:
1)final方法不能被通知,因为它们不能被覆盖。
2)spring3.2之后,不需要将CGLIB添加到项目类路径中,它已经包含在了spring-core的jar中。因此基于CGLIB的代理和JDK动态代理具有相同的工作方式。
3)spring4.0之后,代理对象的构造函数将不会被调用两次,因为CGLIB代理实例将通过Objenesis创建。只有当JVM不允许时,才可能会看到来自springAOP支持的双重调用。
使用CGLIB代理,需要将<aop:config>的proxy-target-class属性设为true。
<aop:config proxy-target-class="true">...</aop:config>
若在使用@AspectJ自动代理支持时强制使用CGLIB,需要将<aop:aspectj-autoproxy>元素的proxy-target-class属性设为true。
<aop:aspectj-autoproxy proxy-target-class="true" />
关于代理机制的示例:
public class PojoA implements Pojo{
public void foo() {
this.bar();//通过在this引用上直接调用
}
public void bar() {...}
}
public class Main{
public static void main(String... args) {
Pojo p = new PojoA();
p.foo();//通过在pojo引用上直接调用
}
}
调用过程如下:
不使用代理,直接调用在对象上 使用代理,在代理对象上调用public class Main{
public static void main(String... args) {
ProxyFactory f = new ProxyFactory(new PojoA());
f.addInterface(Pojo.class);
f.addAdvice(new RetryAdvice());
Pojo p = (Pojo)f.getProxy();
p.foo();//调用在代理上的方法
}
}
上述代码中有一个队代理的引用,该对象引用的方法调用将是代理上的调用,因此代理能够委托给与该特定方法调用相关的所有拦截器(advice)。一旦调用最终到达目标对象,此时 PojoA引用将调用它自己可能产生的任何方法,而非代理上的方法。这是由于自我调用,不会让与方法调用相关的advice获得执行的机会导致的。改进方式如下:
public class Main{
public static void main(String... args) {
ProxyFactory f = new ProxyFactory(new PojoA());
f.addInterface(Pojo.class);
f.addAdvice(new RetryAdvice());
f.setExposeProxy(true);//暴露AOP代理对象,解决自我调用问题
Pojo p = (Pojo)f.getProxy();
p.foo();//在代理上调用
}
}
三、创建@AspectJ代理
除了使用<aop:config>和<aop:aspectj-autoproxy>配置,还可以通过编程方法来创建通知目标对象的代理。
org.springframework.aop.aspectj.annotation.AspectJProxyFactory类可以用于为一个或多个@AspectJ切面所通知的目标对象创建代理。使用方法如下:
AspectJProxyFactory factory =new AspectJProxyFactory(targetObject);//创建生产代理的工厂
factory.addAspect(SecurityManager.class);//添加一个切面,类必须是@AspectJ切面
factory.addAspect(usageTracker);//添加现有的方法实例,提供对象的类型必须是@AspectJ切面
MyInterfaceType proxy = factory.getProxy();//获取代理对象
--参考文献《Srping5开发大全》
网友评论