美文网首页
Spring AOP之类型选择与代理机制

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

作者: 夏与清风 | 来源:发表于2019-07-24 23:09 被阅读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开发大全》

    相关文章

      网友评论

          本文标题:Spring AOP之类型选择与代理机制

          本文链接:https://www.haomeiwen.com/subject/fjzglctx.html