Spring AOP之类型选择与代理机制

2019-07-24  本文已影响0人  夏与清风

一、AOP类型选择

AOP框架有多种选择,如完整的AspectJ框架、springAOP框架,同样风格方式也分基于XML的风格和基于@AspectJ注解的风格。如何抉择使用哪种,需要根据应用需求、团队及开发工具等方面的因素综合考虑。

1、AspectJ与springAOP

它们的目的都是为了统一处理横切业务,不同点是:

\bullet springAOP并不提供完整的AOP功能,它更注重与IOC容器的结合,来解决横切业务问题,而AspectJ在功能完善度方面更具优势。

\bullet 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开发大全》

上一篇 下一篇

猜你喜欢

热点阅读