spring心得8--AOP各种通知案例讲解.doc

来源:互联网 发布:监控软件行为 编辑:程序博客网 时间:2024/05/17 03:50

   上一篇博客中已经简单介绍了,这里通过案例详细说明一下aop中各种通知的用法。

   先列出后面介绍的JDK动态代理通知(主要区别于cglib代理而言,下面会具体介绍)、静态切入点、正则切入点等都依赖使用的抽象主题(一个接口,可以是多个)、是、真实主题(改接口的实现类)

  接口:SayService.java

package www.csdn.spring.proxy.advice;public interface SayService {public void say(String content);public void sayHi();public void sayHello();public void byebye();}


实现类:SayServiceImpl.java

package www.csdn.spring.proxy.advice;public interface SayService {public void say(String content);public void sayHi();public void sayHello();public void byebye();}   实现类:SayServiceImpl.javapackage www.csdn.spring.proxy.advice;public class SayServiceImpl implements SayService{@Overridepublic void say(String content) {System.out.println("say:"+content);//int i=1/0;}@Overridepublic void sayHi() {System.out.println("===sayHi()方法执行了===");}@Overridepublic void sayHello() {System.out.println("===sayHello()方法执行了===");}@Overridepublic void byebye() {System.out.println("===byebye()方法执行了===");}}


 

1.各种通知的使用,JDK动态代理

   前置通知、后置通知、环绕通知、异常通知、引入通知的案例分析,下面注释都有详细解释,这里不再赘述。

   spring-advice.xml  spring配置文件

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xsi:schemaLocation="http://www.springframework.org/schema/beans           http://www.springframework.org/schema/beans/spring-beans.xsd">  <!-- 创建前置通知 -->  <bean id="beforeAdvice" class="www.csdn.spring.proxy.advice.BeforeAdvice"/>  <!-- 创建后置通知 -->  <bean id="afterAdvice" class="www.csdn.spring.proxy.advice.AfterAdvice"/>  <!-- 创建环绕通知 -->  <bean id="aroundAdvice" class="www.csdn.spring.proxy.advice.AroundAdvice"/>  <!-- 创建异常通知 -->  <bean id="throwAdvice" class="www.csdn.spring.proxy.advice.ThrowAdvice"/>  <!-- 引入通知 -->   <bean id="auditableAdvice" class="www.csdn.spring.proxy.advice.AuditableImpl"/>    <!-- 真实主题   目标对象 -->  <bean id="sayServiceImpl" class="www.csdn.spring.proxy.advice.SayServiceImpl"/>      <!-- 配置代理操作 -->  <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">       <!--抽象主题   实现接口 -->    <property name="proxyInterfaces">      <array>        <value>www.csdn.spring.proxy.advice.SayService</value>        <value>www.csdn.spring.proxy.advice.Auditable</value>      </array>    </property>        <!-- 目标对象 -->    <property name="target">      <ref bean="sayServiceImpl"/>    </property>        <!-- 织入的通知的名称 -->    <property name="interceptorNames">      <array>        <value>beforeAdvice</value>        <value>afterAdvice</value>         <value>aroundAdvice</value>         <value>throwAdvice</value>         <value>auditableAdvice</value>       </array>    </property>  </bean></beans>


 

下面是这几个通知具体反映到的类,最关键的是这个几个通知代理类所要继承的接口,不可写错,更不可不写,否则会出错,这些错误将在后期本人一篇spring错误总结的博客中涉及到,敬请期待。

  前者通知类:BeforeAdvice.java

package www.csdn.spring.proxy.advice;import java.lang.reflect.Method;import org.springframework.aop.MethodBeforeAdvice;public class BeforeAdvice implements MethodBeforeAdvice {@Overridepublic void before(Method method, Object[] args, Object target)throws Throwable {System.out.println("在目标方法执行之前执行........................");// 执行完之后执行exit方法,退出程序//System.exit(0);// 暴露的参数的含义System.out.println("目标方法:" + method.getName());if (args != null && args.length > 0) {for (Object arg : args) {System.out.println("传递的参数值:" + arg);}}System.out.println("目标对象:" + target.getClass());}}后置通知类:AfterAdvice.javapackage www.csdn.spring.proxy.advice;import java.lang.reflect.Method;import org.springframework.aop.AfterReturningAdvice;public class AfterAdvice implements AfterReturningAdvice{@Overridepublic void afterReturning(Object returnValue, Method method,Object[] args, Object target) throws Throwable {System.out.println("在目标方法执行之后执行..............................");}}环绕通知类:AroundAdvice.javapackage www.csdn.spring.proxy.advice;import org.aopalliance.intercept.MethodInterceptor;import org.aopalliance.intercept.MethodInvocation;public class AroundAdvice implements MethodInterceptor {public void beforeMethod() {System.out.println("around---------------------目标方法之前执行");}public void afterMethod() {System.out.println("around---------------------目标方法之后执行");}@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {Object returnValue = null;beforeMethod();returnValue = invocation.proceed();afterMethod();return returnValue;}}异常通知类:ThrowsAdvice.javapackage www.csdn.spring.proxy.advice;import java.lang.reflect.Method;import org.springframework.aop.ThrowsAdvice;public class ThrowAdvice implements ThrowsAdvice{public void afterThrowing(Method m,Object[] os,Objecttarget,Throwable throwable){System.out.println("出现异常了:"+throwable.getLocalizedMessage());}}


 

引用通知需要注意是,因为这里spring使用的是jdk动态代理,所以使用引用通知的时候一定先创建一个引用通知的抽象接口,再创建一个引用通知类。

   抽象主题  Auditable.java

package www.csdn.spring.proxy.advice;import java.util.Date;public interface Auditable {public void setDate(Date date);public Date getDate();}  引用通知类 AuditableService.javapackage www.csdn.spring.proxy.advice;import java.util.Date;import org.springframework.aop.support.DelegatingIntroductionInterceptor;public class AuditableImpl extends DelegatingIntroductionInterceptor implements Auditable {private Date date;@Overridepublic void setDate(Date date) {this.date = date;}@Overridepublic Date getDate() {return date;}}测试类  AdviceTest.javapackage www.csdn.spring.proxy.advice;import java.util.Date;import org.junit.Test;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class AdviceTest {@Testpublic void testAdvice() {ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-advice.xml");// 不使用代理的时候// context.getBean("sayServiceImpl",SayServiceImpl.class).say("嗨!杨凯!");// 使用代理的时候SayService sayService = context.getBean("proxyFactoryBean", SayService.class);sayService.say("嗨!杨凯!");//引入通知测试Auditable auditable = (Auditable) sayService;auditable.setDate(new Date());System.out.println(auditable.getDate());}}


 

2.各种通知的使用,cglib代理

   spring中的JDK动态代理和cglib代理区别就是:1)在配置文件中的区别,即在配置文件中抽象主键,接口对象配置不同,具体不同详见下面红色部分;主要通过property属性的proxyTargetClass值设置。2)即JDK动态代理和cglib代理的不同,前者基于接口代理,后者基于类代理,所以这里就有了真实主题的不同,即实现类的不同,cglib的真实主题没有实现任何接口。

spring-advices.xml spring配置文件

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xsi:schemaLocation="http://www.springframework.org/schema/beans           http://www.springframework.org/schema/beans/spring-beans.xsd">  <!-- 创建前置通知 -->  <bean id="beforeAdvice" class="www.csdn.spring.proxy.advice.BeforeAdvice"/>  <!-- 创建后置通知 -->  <bean id="afterAdvice" class="www.csdn.spring.proxy.advice.AfterAdvice"/>  <!-- 创建环绕通知 -->  <bean id="aroundAdvice" class="www.csdn.spring.proxy.advice.AroundAdvice"/>  <!-- 创建异常通知 -->  <bean id="throwAdvice" class="www.csdn.spring.proxy.advice.ThrowAdvice"/>    <!-- 真实主题   目标对象 -->  <bean id="sayServiceImpls" class="www.csdn.spring.proxy.advice.SayServiceImpls"/>      <!-- 配置代理操作 -->  <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">       <!--抽象主题   实现接口 -->    <!-- 写法一:直接使用代理类,不再去指明是哪个代理类 ;这个时候value的值不管是true或false都其作用-->    <property name="proxyTargetClass" value="true"/>              <!-- 指明代理类,这时候value必须是true的时候才起作用 ;    使用false会报错:Bean named 'proxyFactoryBean' must be of type [www.csdn.spring.advice.SayServiceImpls],     but was actually of type [$Proxy4]    <property name="proxyInterfaces">      <array>        <value>www.csdn.spring.proxy.advice.SayService</value>      </array>    </property>    <property name="proxyTargetClass">      <value>true</value>    </property>    -->        <!-- 目标对象 -->    <property name="target">      <ref bean="sayServiceImpls"/>    </property>        <!-- 织入的通知的名称 -->    <property name="interceptorNames">      <array>        <value>beforeAdvice</value>        <value>afterAdvice</value>         <value>aroundAdvice</value>         <value>throwAdvice</value>       </array>    </property>  </bean></beans>


真实主题:SayServiceImpls.java

package www.csdn.spring.proxy.advice;public class SayServiceImpls{public void say(String content) {System.out.println("say:"+content);}}测试类  AdviceTests.javapackage www.csdn.spring.proxy.advice;import org.junit.Test;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class AdviceTests {@Testpublic void testAdvice() {ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-advices.xml");// 不使用代理的时候// context.getBean("sayServiceImpls",SayServiceImpls.class).say("嗨!你好!");// 使用代理的时候context.getBean("proxyFactoryBean", SayServiceImpls.class).say("嗨!你好!");}}


 

3.静态切入点

   为什么会有静态切入点,上一篇看博客已经介绍过,为了使你编写的通知,即代理在指定切入点起作用,也就是所谓的是aop的通知有实用价值。这里通过案例详细分析静态切入点。

spring-staticAdvisor.xml spring配置文件

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xsi:schemaLocation="http://www.springframework.org/schema/beans           http://www.springframework.org/schema/beans/spring-beans.xsd">  <!-- 创建前置通知 -->  <bean id="beforeAdvice" class="www.csdn.spring.proxy.advice.BeforeAdvice"/>  <!-- 创建后置通知 -->  <bean id="afterAdvice" class="www.csdn.spring.proxy.advice.AfterAdvice"/>  <!-- 创建环绕通知 -->  <bean id="aroundAdvice" class="www.csdn.spring.proxy.advice.AroundAdvice"/>  <!-- 创建异常通知 -->  <bean id="throwAdvice" class="www.csdn.spring.proxy.advice.ThrowAdvice"/>      <!-- 静态切入点 -->  <bean id="nameMatchMethodPointcutAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">    <!-- 织入通知,比如环绕通知 -->    <property name="advice">      <ref bean="aroundAdvice"/>    </property>    <!-- 指明切入点 -->    <property name="mappedName">      <value>say</value>    </property>  </bean>    <!-- 真实主题   目标对象 -->  <bean id="sayServiceImpl" class="www.csdn.spring.proxy.advice.SayServiceImpl"/>      <!-- 配置代理操作 -->  <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">       <!--抽象主题   实现接口 -->    <property name="proxyInterfaces">      <array>        <value>www.csdn.spring.proxy.advice.SayService</value>      </array>    </property>        <!-- 目标对象 -->    <property name="target">      <ref bean="sayServiceImpl"/>    </property>        <!-- 织入的通知的名称 -->    <property name="interceptorNames">      <array>        <value>nameMatchMethodPointcutAdvisor</value>      </array>    </property>  </bean></beans>


 

测试类  AdvisorTest.javapackage www.csdn.spring.proxy.advice;import org.junit.Test;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class AdvisorTest {@Testpublic void testAdvice() {ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-s*.xml");// 使用代理的时候SayService sayService = context.getBean("proxyFactoryBean", SayService.class);sayService.say("嗨!杨凯,你好!");sayService.sayHi();sayService.sayHello();sayService.byebye();}}


 

4.正则切入点

   闲话少说,与静态切入点不同的就是正则切入点匹配正则表达式,重点代码见下面红色部分.

spring-regAdvisor.xml spring配置文件

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xsi:schemaLocation="http://www.springframework.org/schema/beans           http://www.springframework.org/schema/beans/spring-beans.xsd">  <!-- 创建环绕通知 -->  <bean id="aroundAdvice" class="www.csdn.spring.proxy.advice.AroundAdvice"/>       <!-- 静态切入点 -->  <bean id="regexpMethodPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">    <!-- 织入通知,比如环绕通知 -->    <property name="advice">      <ref bean="aroundAdvice"/>    </property>    <!-- 指明切入点 -->    <property name="patterns">    <!-- .是通配符的意思;第一个.*代表匹配任何包名和类名 ;第二个.*代表匹配以say开头的任意方法-->      <array>      <value>.*sayH.</value>      <value>.*bye.*</value>      <value>www.*\.SayService\.sayHell.</value>      </array>    </property>  </bean>    <!-- 真实主题   目标对象 -->  <bean id="sayServiceImpl" class="www.csdn.spring.proxy.advice.SayServiceImpl"/>      <!-- 配置代理操作 -->  <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">       <!--抽象主题   实现接口 -->    <property name="proxyInterfaces">      <array>        <value>www.csdn.spring.proxy.advice.SayService</value>      </array>    </property>        <!-- 目标对象 -->    <property name="target">      <ref bean="sayServiceImpl"/>    </property>        <!-- 织入的通知的名称 -->    <property name="interceptorNames">      <array>        <value>regexpMethodPointcutAdvisor</value>      </array>    </property>  </bean></beans>


 

测试类 AdvisorTest.java

package www.csdn.spring.proxy.advice;import org.junit.Test;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class AdvisorTest {@Testpublic void testAdvice() {ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-r*.xml");// 使用代理的时候SayService sayService = context.getBean("proxyFactoryBean", SayService.class);sayService.say("嗨!杨凯,你好!");sayService.sayHi();sayService.sayHello();sayService.byebye();}}


 

原创粉丝点击