Spring中的AOP

    技术2022-05-11  95

    Spring 中的 AOP 创建时间: 2007年1月27日星期六 一、概述 (一)基本概念 1、什么是AOP?     面向方面编程。所谓方面即是指日志、权限、异常处理、事务处理等。 2、AOP的3个关键概念    (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、Spring4种Advice:       第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值就是这个bean的id,其主要对应的是写入日志的那个类,也就是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)对应 中定义的那个id为log的bean          </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.LogAop"/> <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 Object invoke(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.LogAop"/> <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.LogAop"/> <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.LogAop"/> <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("张三");    
     [U1]Join Point  [U2]Advice  [U3]这个名字自己可以随便起  [U4]如果想对类的所有方法都有效,就要写成.*.*  

    最新回复(0)