Spring中的AOP

    技术2022-05-11  66

    一、概述

    (一)基本概念

    1 、什么是AOP

        面向方面编程。所谓方面即是指日志、权限、异常处理、事务处理等。

    2 AOP3个关键概念

       1 )切入点( Pointcut ): Pointcut Join Point 的集合, Join Point 就是需要注入 Adivce 的位置,也就是需要插入日志输出代码、事务处理代码等“方面”( Aspect ,也就是 AOP 中的 A )代码的地方。

        比如我现在要写一个存钱的方法: saving ()

        通常情况下我就得在这个 saving ()方法前后写些事务代码

        如:

     

              logger.log(Level.INFO,”start”);

    Saving();

                             logger.log(Level.INFO,”end”);

          

             对于事务代码而言, saving ()方法的前后就都是 Join Point 了。在 Spring 中它对应 config.xml 中设定的方法,这个方法就是类( class )中需要进行某方面处理的方法( method )。

             2 )通知( Advice ): 就是指 Join Point 对应的代码(方法)。比如日志输出这个方面,指的就是日志输出的代码或方法了。在 Spring 中,它对应类( class )。

            3 Advisor Poincut Advice 的配置器,它包括 Pointcut Advice ,是将 Advice 注入程序中 Pointcut 位置的代码。在 Sping 中,它对应 config.xml 中的配置段 <bean id=logAdvisor class=”org.springframework.aop.support.RegexpMethodPointcutAdvisor”>

      <bean id=”logAdvisor” class=”org.springframework.aop.support.RegexpMetho

    dPointcutAdvisor”>

        //advice 属性对应的当然就是输出日志的类,也就是 对应的那个bean

        <property name=”advice”>

             <ref bean=”log”/>

        </property>

       //patterns 属性指出指定要代理的方法,使用的是正则表达式

        <property name=”patterns”>

             <value>.*doAuditing.*</value>

        </property>

    </bean>

     

    (二)框架图

     

    创建代理的两种方法

    ProxyFactoryBean

    动态代理

     

    Spring4 Advice

    Interception Around

    Before

    After Returning

    Throw

     

    两种代理方式

    Java 动态代理

    CGLIB 代理

     

    (三)何时使用什么

      1 、创建代理的两种方法中首选动态代理。

    1 种:针对某个类进行配置。可以指定某个类中所有方法都调用方面,也可以指定某个类中的某个方法,此时由于用到正则表达式,于是需要引入 jakarta-oro- 2.0.8 .jar 包。

    2 种:针对某个方法进行配置。

      2 Spring4Advice

          1 种:在需要调用方面的方法前后都调用处理方面的代码

          2 种:在需要调用方面的方法之前调用处理方面的代码

    3 种:在需要调用方面的方法之后都调用处理方面的代码

    4 种:在需要调用方面的方法发生异常时调用处理方面的代码

    3 、两种代理方式首选第1种。

              1 种:面向接口,必须先定义接口,这是好的习惯,应该提倡

              2 种:当没有接口的时候,可以使用这种方法。需引入 cglib-nodep-2.1_3,jar 包。

    二、详细

    (一)、创建AOP代理的两种方法:

    1 、用ProxyFactoryBean创建AOP代理

       (需要指明代理目标类)

    1 )代理目标类的所有方法

    <?xml version="1.0" encoding="UTF-8"?>

    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"

     "http://www.springframework.org/dtd/spring-beans.dtd">

    <beans>

    // 下面 interceptorNames 属性(property)中的value值就是这个beanid,其主要对应的是写入日志的那个类,也就是Spring AOP概念中的Advice(通知)。

    <bean id="log" class="com.gc.action.LogAround"/>

    // 要输出日志的那个类(因为这种方法必须要指明代理目标类)

    <bean id="timeBook" class="com.gc.action. timeBook "/>

    // 下面开始定义代理类,也就是ProxyFactoryBean,这是Spring自带的类,这也是Spring AOP中的Advisor

    <bean id=”logProxy” class=”org.springframework.aop.framework.ProxyFactor

    yBean ”>

     // 第一个属性,是指明要代理的类的接口,因为这个例子中使用的是Java动态代理机制来实现AOP的,因此必须指明接口

        <property name=”proxyInterfaces”>

            <value>com.gc.impl.TimeBookInterface</value>

        </property>

    // 这个属性是指明代理目标(target)类,也就是 定义的那个类

        <property name=”target”>

            <ref bean=”timeBook”/>

        </property>

    [U1]   // 这个属性是用来指明插入哪个Advice,此处使用list,应该表示这个类不只是可以调用这一个log

        <property name=”interceptorNames”>

             <list>

                  <value>log</value> // 这个值(log)对应 中定义的那个idlogbean

             </list>

        </property>

    [U2]   </bean>

    </beans>

    2 )代理目标类的指定方法

    <?xml version="1.0" encoding="UTF-8"?>

    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"

     "http://www.springframework.org/dtd/spring-beans.dtd">

    <beans>

    <bean id="log" class="com.gc.action.LogAround"/>

    <bean id="timeBook" class="com.gc.action. timeBook "/>

    // 在上一段配置文件中添加了下面这个bean,用来指明要输出日志的指定方法(上一个例子是所有方法都输出日志)

    <bean id=”logAdvisor” class=”org.springframework.aop.support.RegexpMetho

    dPointcutAdvisor”>

        //advice 属性对应的当然就是输出日志的类,也就是 对应的那个bean

        <property name=”advice”>

             <ref bean=”log”/>

        </property>

       //patterns 属性指出指定要代理的方法,使用的是正则表达式

        <property name=”patterns”>

             <value>.*doAuditing.*</value>

        </property>

    </bean>

    <bean id=”logProxy” class=”org.springframework.aop.framework.ProxyFactor

    yBean ”>

        <property name=”proxyInterfaces”>

            <value>com.gc.impl.TimeBookInterface</value>

        </property>

        <property name=”target”>

            <ref bean=”timeBook”/>

        </property>

        <property name=”interceptorNames”>

             <list>

                  <value>log</value>

             </list>

        </property>

    </bean>

    </beans>

     

    2 、用DefaultAdvisorAutoProxyCreator创建自动代理

       (好处:不用指明代理目标类,如果一个大项目中有很多类也不必一个一个设置 AOP 代理)

    <?xml version="1.0" encoding="UTF-8"?>

    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"

     "http://www.springframework.org/dtd/spring-beans.dtd">

    <beans>

    <bean id="log" class="com.gc.action.Log Aop "/>

    <bean id="timeBook" class="com.gc.action. timeBook "/>

    // 使用DefaultAdvisorAutoProxyCreator(红色代码)替代ProxyFactoryBean(绿色代码),因为绿色代码的作用是为具体的类(即所谓代理目标类)设置advice

    <bean id=”autoProxyCreator” class=”org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator”>

    <bean id=”logAdvisor [U3]   ” class=”org.springframework.aop.support.RegexpMetho

    dPointcutAdvisor”>

         <property name=”advice”>

             <ref bean=”log”/>

        </property>

         <property name=”patterns”>

             <value>.*doAuditing.*</value> [U4]  

        </property>

    </bean>

    /*<bean id=”logProxy” class=”org.springframework.aop.framework.ProxyFactor

    yBean”>

        <property name=”proxyInterfaces”>

            <value>com.gc.impl.TimeBookInterface</value>

        </property>

        <property name=”target”>

            <ref bean=”timeBook”/>

        </property>

        <property name=”interceptorNames”>

             <list>

                  <value>log</value>

             </list>

        </property>

    </bean>*/

    </beans>

     

    3 、总结

    实际上, DefaultAdvisorAutoProxyCreator ProxyFactoryBean 就是两种代理类,前者是自动的将 Advisor 和目标类联系起来,后者是通过指定的方式,将目标类和 Advisor 组合起来。

    advisor ,对应的就是 org.springframework.aop.support.RegexpMethodPointcutAdvisor ,通过正则表达式来匹配类中的方法(设定 Pointcut )。

    (二)Spring四种通知(Advice)形式

    1 Interception Around通知

           1 )负责输出日志的类

    import org.aopalliance.intercept.MethodInterceptor;

    import org.aopalliance.intercept.MethodInvocation;

    import org.apache.log4j.Level;

    import org.apache.log4j.Logger;

     

    public class LogAround implements MethodInterceptor{

          private Logger logger = Logger.getLogger(this.getClass().getName());

       

        public Objectinvoke(MethodInvocation methodInvocation) throws Throwable {

               logger.log(Level.INFO, methodInvocation.getArguments()[0] + " 开始审核数据...."); 

            try {

              Object result = methodInvocation.proceed();

              return result;

            }

            finally {

                     logger.log(Level.INFO, methodInvocation.getArguments()[0] + " 审核数据结束....");

            }       

       }

    }     

      2 )配置文件

    <?xml version="1.0" encoding="UTF-8"?>

    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"

     "http://www.springframework.org/dtd/spring-beans.dtd">

    <beans>

    <bean id="log" class="com.gc.action.Log Aop "/>

    <bean id="timeBook" class="com.gc.action. timeBook "/>

    <bean id=”logProxy” class=”org.springframework.aop.framework.ProxyFactor

    yBean”>

        <property name=”proxyInterfaces”>

            <value>com.gc.impl.TimeBookInterface</value>

        </property>

        <property name=”target”>

            <ref bean=”timeBook”/>

        </property>

        <property name=”interceptorNames”>

             <list>

                  <value>log</value>

             </list>

        </property>

    </bean>

    </beans>

     

     

    2 Before通知

    1 )负责输出日志的类

    import java.lang.reflect.Method;

     

    import org.apache.log4j.Level;

    import org.apache.log4j.Logger;

    import org.springframework.aop.MethodBeforeAdvice;

     

    public class LogBefore implements MethodBeforeAdvice {

           private Logger logger = Logger.getLogger(this.getClass().getName());

       

        public void before(Method method, Object[] args, Object target) throws Throwable {

                  logger.log(Level.INFO, args[0] + " 开始审核数据 ....");

       }

    }

    2 )配置文件

         与第 1 种方法相同。

    3 After Returning通知

    1 )负责输出日志的类

    import java.lang.reflect.Method;

     

    import org.apache.log4j.Level;

    import org.apache.log4j.Logger;

    import org.springframework.aop.AfterReturningAdvice;

     

    public class LogAfterReturning implements AfterReturningAdvice {

           private Logger logger = Logger.getLogger(this.getClass().getName());

       

        public void afterReturning(Method method, Object[] args, Object target) throws Throwable {

                  logger.log(Level.INFO, args[0] + " 开始审核数据 ....");

       }

    }

    2 )配置文件

         与第 1 种方法相同。

    4 Throw通知

    1 )负责输出日志的类

    import java.lang.reflect.Method;

     

    import org.apache.log4j.Level;

    import org.apache.log4j.Logger;

    import org.springframework.aop.ThrowsAdvice;

     

    public class LogThrow implements ThrowsAdvice {

           private Logger logger = Logger.getLogger(this.getClass().getName());

       

        public void afterThrowing(Method method, Object[] args, Object target,Throwable subclass) throws Throwable {

                  logger.log(Level.INFO, args[0] + " 开始审核数据 ....");

       }

    }

    2 )配置文件

         与第 1 种方法相同。

    5 、测试程序

       1 )使用自动代理

    public class TestHelloWorld {

           public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {

                  ApplicationContext actx=new FileSystemXmlApplicationContext("config.xml");

                  TimeBookInterface timeBookProxy = (TimeBookInterface)actx.getBean("timeBook");

                  timeBookProxy.doAuditing(" 张三 ");

       2 )不使用自动代理

    public class TestHelloWorld {

           public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {

                  ApplicationContext actx=new FileSystemXmlApplicationContext("config.xml");

                  TimeBookInterface timeBookProxy = (TimeBookInterface)actx.getBean("logProxy");

                  timeBookProxy.doAuditing(" 张三 ");

    使用自动代理,则直接调用该类的名字( timeBook ),否则调用相应的代理 bean logProxy )。因为第 1 种方法中根本就没有对应的代理 bean ,只有一个 Spring 的自动代理类

     

    (三)Spring两种代理方式

    1 Java动态代理

    1 )配制文件(不使用自动代理)

    <?xml version="1.0" encoding="UTF-8"?>

    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"

     "http://www.springframework.org/dtd/spring-beans.dtd">

    <beans>

    <bean id="log" class="com.gc.action.Log Aop "/>

    <bean id="timeBook" class="com.gc.action. timeBook "/>

    <bean id=”logProxy” class=”org.springframework.aop.framework.ProxyFactor

    yBean”>

        <property name=”proxyInterfaces”>

            <value>com.gc.impl.TimeBookInterface</value>

        </property>

        <property name=”target”>

            <ref bean=”timeBook”/>

        </property>

        <property name=”interceptorNames”>

             <list>

                  <value>log</value>

             </list>

        </property>

    </bean>

    </beans>

     

    2 )测试程序

    public class TestHelloWorld {

           public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {

                  ApplicationContext actx=new FileSystemXmlApplicationContext("config.xml");

                  TimeBookInterface timeBookProxy = (TimeBookInterface)actx.getBean("logProxy");

                  timeBookProxy.doAuditing(" 张三 ");

     

    2 CGLIB代理

    1 )配制文件(不使用自动代理)

    <?xml version="1.0" encoding="UTF-8"?>

    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"

     "http://www.springframework.org/dtd/spring-beans.dtd">

    <beans>

    <bean id="log" class="com.gc.action.Log Aop "/>

    <bean id="timeBook" class="com.gc.action. timeBook "/>

    <bean id=”logProxy” class=”org.springframework.aop.framework.ProxyFactor

    yBean”>

    // 增加如下属性,就表示使用的是CGLIB代理(对目标类直接代理)

        <property name=”proxyTargetClass”>

            <value>true</value>

        </property>

       /* 然后去掉下面的属性,也就是说此种方法不需要面向接口,或不需要指出接口

    <property name=”proxyInterfaces”>

            <value>com.gc.impl.TimeBookInterface</value>

        </property>*/

        <property name=”target”>

            <ref bean=”timeBook”/>

        </property>

        <property name=”interceptorNames”>

             <list>

                  <value>log</value>

             </list>

        </property>

    </bean>

    </beans>

     

    2 )测试程序

    public class TestHelloWorld {

           public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {

                  ApplicationContext actx=new FileSystemXmlApplicationContext("config.xml");

                  TimeBookInterface timeBookProxy = (TimeBookInterface)actx.getBean("logProxy");

                  timeBookProxy.doAuditing(" 张三 ");

     

     

     

    最新回复(0)