Spring笔记(三):Aop详解

一、Aop原理

(一)动态代理

1、详见:java进阶(七):详解JAVA代理

2、主要是Proxy 与 InvocationHandle r接口


(二)Cglib 实现

1、主要是 Enhancer 和 MethodInterceptor 接口

2、实现代码如下:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:18px;">    /** 
  2.      * 利用 cglib 实现 
  3.      * @param targrt 
  4.      * @return 
  5.      */  
  6.     public static Object getCgProxy(final Object targrt,final Advice advice){  
  7.           
  8.         //利用cglib 中的Enhancer  
  9.         Enhancer enhancer = new Enhancer();  
  10.           
  11.         //把目标类设置为代理的父类(继承目标类,覆盖其所有非final的方法)  
  12.         enhancer.setSuperclass(targrt.getClass());  
  13.           
  14.         //设置回调  
  15.         enhancer.setCallback(new MethodInterceptor() {//实现MethodInterceptor 接口  
  16.               
  17.             @Override  
  18.             public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {  
  19.                 Object object = null;  
  20.                 try{  
  21.                     advice.before(method);//前置  
  22.                     object = methodProxy.invoke(targrt, args);  
  23.                     advice.after(method);//后置  
  24.                 }catch (Exception e) {  
  25.                     advice.afterThrow(method);//例外  
  26.                 }finally{  
  27.                     advice.afterFinally(method);//最终  
  28.                 }  
  29.   
  30.                 return object;  
  31.             }  
  32.         });  
  33.           
  34.         return enhancer.create();  
  35.     }  
  36. </span>  

(三)环境及概念

1、需要的包:spring的包,还需要 aspectjweaver.jar,aopalliance.jar ,asm.jar 和cglib.jar 。

2、Aop的实现方式:Spring 接口方式,schema配置方式和注解的三种方式

3、概念

1)切面(aspect):用来切插业务方法的类。

2)连接点(joinpoint):是切面类和业务类的连接点,其实就是封装了业务方法的一些基本属性,作为通知的参数来解析。

3)通知(advice):在切面类中,声明对业务方法做额外处理的方法。

4)切入点(pointcut):业务类中指定的方法,作为切面切入的点。其实就是指定某个方法作为切面切的地方。

5)目标对象(target object):被代理对象。

6)AOP代理(aop proxy):代理对象。

7)前置通知(before advice):在切入点之前执行。

8)后置通知(after returning advice):在切入点执行完成后,执行通知。

9)环绕通知(around advice):包围切入点,调用方法前后完成自定义行为。

10、异常通知(after throwing advice):在切入点抛出异常后,执行通知。


二、Spring接口的aop实现

(一)详见:Spring AOP (上)

(二)分析

1、实现依赖比较麻烦,spring定义了一堆通知的接口,只要是实现即可。如前置通知接口 MethodBeforeAdvice

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:18px;">package main.java.com.spring.aop.apj;  
  2.   
  3. import java.lang.reflect.Method;  
  4.   
  5. import org.springframework.aop.MethodBeforeAdvice;  
  6.   
  7. /** 
  8.  * Spring接口的前置通知 
  9.  * BaseBeforeAdvice 
  10.  * @title 
  11.  * @desc 
  12.  * @author SAM-SHO 
  13.  * @Jan 17, 2015 
  14.  */  
  15. public class BaseBeforeAdvice implements MethodBeforeAdvice {  
  16.   
  17.     /** 
  18.      * method : 切入的方法 <br> 
  19.      * args :切入方法的参数 <br> 
  20.      * target :目标对象 
  21.      */  
  22.     @Override  
  23.     public void before(Method method, Object[] args, Object target) throws Throwable {  
  24.         System.out.println("===========进入beforeAdvice()============ \n");  
  25.   
  26.         System.out.print("准备在" + target + "对象上用");  
  27.         System.out.print(method + "方法进行对 '");  
  28.         System.out.print(args[0] + "'进行删除!\n\n");  
  29.   
  30.         System.out.println("要进入切入点方法了 \n");  
  31.     }  
  32.   
  33. }</span>  


2、需要 指定切点:实现接口 MethodBeforeAdvice 

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:18px;">/** 
  2.  * 定义一个切点,指定对应方法匹配。来供切面来针对方法进行处理<br> 
  3.  *  
  4.  * 继承NameMatchMethodPointcut类,来用方法名匹配 
  5.  *  
  6.  * @author  
  7.  *  
  8.  */  
  9. public class Pointcut extends NameMatchMethodPointcut {  
  10.   
  11.     private static final long serialVersionUID = 3990456017285944475L;  
  12.   
  13.     @SuppressWarnings("rawtypes")  
  14.     @Override  
  15.     public boolean matches(Method method, Class targetClass) {  
  16.         // 设置单个方法匹配  
  17.         this.setMappedName("delete");  
  18.         // 设置多个方法匹配  
  19.         String[] methods = { "delete""modify" };  
  20.           
  21.         //也可以用“ * ” 来做匹配符号  
  22.         // this.setMappedName("get*");  
  23.           
  24.         this.setMappedNames(methods);  
  25.   
  26.         return super.matches(method, targetClass);  
  27.     }  
  28.   
  29. }</span>  

3、需要配置。很麻烦。


三、spring利用aspectj 实现aop

(一)源码

1、业务类(target)

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:18px;">package main.java.com.spring.aop.service;  
  2.   
  3. /** 
  4.  * 业务接口 
  5.  * PersonService 
  6.  * @title 
  7.  * @desc 
  8.  * @author SAM-SHO 
  9.  * @Jan 17, 2015 
  10.  */  
  11. public interface PersonService {  
  12.     /** 
  13.      * 保存用户实体 
  14.      * @param name 
  15.      */  
  16.     public void save(String name);  
  17.       
  18.     /** 
  19.      * 删除用户实体 
  20.      * @param id 
  21.      */  
  22.     public void delete(Integer id);  
  23.       
  24.     /** 
  25.      * 查询编号为id的使用用户姓名 
  26.      * @param id 
  27.      */  
  28.     public String queryPersonName(Integer id);  
  29.       
  30.     /** 
  31.      * 更新实体 
  32.      * @param name 
  33.      * @param id 
  34.      */  
  35.     public void update(String name ,Integer id);  
  36. }  
  37. </span>  

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:18px;">package main.java.com.spring.aop.service.impl;  
  2.   
  3. import main.java.com.spring.aop.service.PersonService;  
  4.   
  5. /** 
  6.  * 业务实现类 
  7.  * PersionServiceImpl 
  8.  * @title 
  9.  * @desc 
  10.  * @author SAM-SHO 
  11.  * @Jan 17, 2015 
  12.  */  
  13. public class PersonServiceImpl implements PersonService {  
  14.   
  15.       
  16.     @Override  
  17.     public void save(String name) {  
  18.         System.out.println("我是save()方法");  
  19.   
  20.     }  
  21.   
  22.     @Override  
  23.     public void delete(Integer id) {  
  24.         System.out.println("我是 delete() 方法");  
  25.   
  26.     }  
  27.   
  28.     @Override  
  29.     public String queryPersonName(Integer id) {  
  30.         System.out.println("我是 query() 方法");  
  31.         return "XXX";  
  32.     }  
  33.   
  34.     @Override  
  35.     public void update(String name, Integer id) {  
  36.         System.out.println("我是 update() 方法");  
  37.   
  38.     }  
  39.   
  40. }  
  41. </span>  

2、切面类(aspect:类内部定义各种通知advice)

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:18px;">package main.java.com.spring.aop.apj;  
  2.   
  3. import org.aspectj.lang.JoinPoint;  
  4. import org.aspectj.lang.ProceedingJoinPoint;  
  5.   
  6. /** 
  7.  *  
  8.  * AspectAdvice 
  9.  * @title 切面类 
  10.  * @desc 
  11.  * @author SAM-SHO 
  12.  * @Jan 11, 2015 
  13.  */  
  14. public class AspectAdvice {  
  15.   
  16.     /** 
  17.      * 前置通知 
  18.      *  
  19.      * @param jp 
  20.      */  
  21.     public void doBefore(JoinPoint jp) {  
  22.         System.out.println("===========进入before advice============ \n");  
  23.   
  24.         System.out.println("准备在" + jp.getTarget().getClass() + "对象上用");  
  25.         System.out.println(jp.getSignature().getName() + "方法进行对 '");  
  26.         System.out.println(jp.getArgs()[0] + "'进行删除!\n\n");  
  27.   
  28.         System.out.println("要进入切入点方法了 \n");  
  29.     }  
  30.   
  31.     /** 
  32.      * 后置通知 
  33.      *  
  34.      * @param jp 
  35.      *            连接点 
  36.      * @param result 
  37.      *            返回值 
  38.      */  
  39.     public void doAfter(JoinPoint jp, String result) {  
  40.         System.out.println("==========进入after advice=========== \n");  
  41.         System.out.println("切入点方法执行完了 \n");  
  42.   
  43.         System.out.print(jp.getArgs()[0] + "在");  
  44.         System.out.print(jp.getTarget().getClass() + "对象上被");  
  45.         System.out.print(jp.getSignature().getName() + "方法删除了");  
  46.         System.out.print("只留下:" + result + "\n\n");  
  47.     }  
  48.   
  49.     /** 
  50.      * 环绕通知 
  51.      *  
  52.      * @param pjp 
  53.      *            连接点 
  54.      */  
  55.     public void doAround(ProceedingJoinPoint pjp) throws Throwable {  
  56.         System.out.println("===========进入around环绕方法!=========== \n");  
  57.   
  58.         // 调用目标方法之前执行的动作  
  59.         System.out.println("调用方法之前: 执行!\n");  
  60.   
  61.         // 调用方法的参数  
  62.         Object[] args = pjp.getArgs();  
  63.         // 调用的方法名  
  64.         String method = pjp.getSignature().getName();  
  65.         // 获取目标对象  
  66.         Object target = pjp.getTarget();  
  67.         // 执行完方法的返回值:调用proceed()方法,就会触发切入点方法执行  
  68.         Object result = pjp.proceed();  
  69.   
  70.         System.out.println("调用方法结束:之后执行!\n");  
  71.           
  72.         System.out.println("输出:" + args[0] + ";" + method + ";" + target + ";" + result + "\n");  
  73.           
  74.     }  
  75.   
  76.     /** 
  77.      * 异常通知 
  78.      *  
  79.      * @param jp 
  80.      * @param e 
  81.      */  
  82.     public void doThrow(JoinPoint jp, Throwable e) {  
  83.         System.out.println("删除出错啦");  
  84.     }  
  85.   
  86. }</span>  


3、配置。

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:18px;">    <!-- 声明一个业务类的Bean -->  
  2.     <bean id="personService" class="main.java.com.spring.aop.service.impl.PersonServiceImpl" />   
  3.   
  4.     <!-- 声明通知类 -->  
  5.     <bean id="aspectAdvice" class="main.java.com.spring.aop.apj.AspectAdvice" />  
  6.   
  7.     <aop:config>  
  8.         <aop:aspect id="businessAspect" ref="aspectAdvice">  
  9.             <!--配置指定切入的对象  -->  
  10.             <aop:pointcut id="point_cut" expression="execution(* main.java.com.spring.aop.service..*.*(..))" />  
  11.             <!--只匹配add方法作为切入点  -->  
  12.             <!-- <aop:pointcut id="except_add" expression="execution(* aop.schema.*.add(..))" /> -->  
  13.             <!--前置通知  -->  
  14.             <aop:before method="doBefore" pointcut-ref="point_cut" />  
  15.             <!--后置通知 returning指定返回参数  -->  
  16.             <!-- <aop:after-returning method="doAfter" pointcut-ref="point_cut" returning="result" /> -->  
  17.             <!--环绕  -->  
  18.             <!-- <aop:around method="doAround" pointcut-ref="point_cut"/> -->  
  19.             <!--throw中  -->  
  20.             <!-- <aop:after-throwing method="doThrow" pointcut-ref="point_cut" throwing="e"/> -->  
  21.         </aop:aspect>  
  22.     </aop:config>  
  23. </span>  


4、测试。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:18px;">    @Test  
  2.     public void sopTest(){  
  3.         ApplicationContext cxt = new ClassPathXmlApplicationContext("applicationAopContext.xml");  
  4.         PersonService personService = (PersonService) cxt.getBean("personService");  
  5.         personService.save("shaoxiaobao...");  
  6. //      tAspectBusiness.delete("zhaoxioaniu");  
  7.     }   
  8. </span>  



四、注解实现aop

(一)实现配置

1、开打自动扫描,注解实现Bean的管理与注入。(目标与切面都交由spring管理)

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:18px;">    <!-- 开打自动扫描 -->  
  2.     <context:component-scan base-package="main.java.com.spring" /></span>  

2、开发Aop的注解

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:18px;">    <!-- 打开aop 注解 -->  
  2.     <aop:aspectj-autoproxy /></span>  

(二)注解实现切面类。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:18px;">package main.java.com.spring.aop.apj;  
  2.   
  3. import org.aspectj.lang.JoinPoint;  
  4. import org.aspectj.lang.ProceedingJoinPoint;  
  5. import org.aspectj.lang.annotation.AfterReturning;  
  6. import org.aspectj.lang.annotation.AfterThrowing;  
  7. import org.aspectj.lang.annotation.Around;  
  8. import org.aspectj.lang.annotation.Aspect;  
  9. import org.aspectj.lang.annotation.Before;  
  10. import org.aspectj.lang.annotation.Pointcut;  
  11. import org.springframework.stereotype.Component;  
  12.   
  13. @Component//让spring管理bean  
  14. @Aspect//定义切面类  
  15. public class AnnotationAspectAdvice {  
  16.     /* 
  17.      * 指定切入点匹配表达式,注意它是以方法的形式进行声明的。 
  18.      * 分析: 
  19.      *  execution 是方法的织入语言 
  20.      *  第一个 * :返回任意类型 
  21.      *  main.java.com.spring.aop.service: 包。 
  22.      *  ..:service包以及其子包。 
  23.      *  第二个 * :service包以及其子包下的任意类。 
  24.      *  第三个 * :service包以及其子包下的任意类的任意方法。 
  25.      *  (..) :方法的参数为任意。 
  26.      *  总结:对 main.java.com.spring.aop.service包以及其子包下的任意类的任意方法作切入 
  27.      */  
  28.     @Pointcut("execution(* main.java.com.spring.aop.service..*.*(..))")  
  29.     public void anyMethod() {  
  30.     }  
  31.   
  32.     /** 
  33.      * 前置通知 
  34.      *  
  35.      * @param jp 
  36.      */  
  37.     @Before(value = "execution(* main.java.com.spring.aop.service..*.*(..))")  
  38.     public void doBefore(JoinPoint jp) {  
  39.         System.out.println("===========进入before advice============ \n");  
  40.   
  41.         System.out.print("准备在" + jp.getTarget().getClass() + "对象上用");  
  42.         System.out.print(jp.getSignature().getName() + "方法进行对 '");  
  43.         System.out.print(jp.getArgs()[0] + "'进行删除!\n\n");  
  44.   
  45.         System.out.println("要进入切入点方法了 \n");  
  46.     }  
  47.   
  48.     /** 
  49.      * 后置通知 
  50.      *  
  51.      * @param jp 
  52.      *            连接点 
  53.      * @param result 
  54.      *            返回值 
  55.      */  
  56.     @AfterReturning(value = "anyMethod()", returning = "result")  
  57.     public void doAfter(JoinPoint jp, String result) {  
  58.         System.out.println("==========进入after advice=========== \n");  
  59.         System.out.println("切入点方法执行完了 \n");  
  60.   
  61.         System.out.print(jp.getArgs()[0] + "在");  
  62.         System.out.print(jp.getTarget().getClass() + "对象上被");  
  63.         System.out.print(jp.getSignature().getName() + "方法删除了");  
  64.         System.out.print("只留下:" + result + "\n\n");  
  65.     }  
  66.   
  67.     /** 
  68.      * 环绕通知 
  69.      *  
  70.      * @param pjp 
  71.      *            连接点 
  72.      */  
  73.     @Around(value = "execution(* main.java.com.spring.aop.service..*.*(..))")  
  74.     public void doAround(ProceedingJoinPoint pjp) throws Throwable {  
  75.         System.out.println("===========进入around环绕方法!=========== \n");  
  76.   
  77.         // 调用目标方法之前执行的动作  
  78.         System.out.println("调用方法之前: 执行!\n");  
  79.   
  80.         // 调用方法的参数  
  81.         Object[] args = pjp.getArgs();  
  82.         // 调用的方法名  
  83.         String method = pjp.getSignature().getName();  
  84.         // 获取目标对象  
  85.         Object target = pjp.getTarget();  
  86.         // 执行完方法的返回值:调用proceed()方法,就会触发切入点方法执行  
  87.         Object result = pjp.proceed();  
  88.   
  89.         System.out.println("输出:" + args[0] + ";" + method + ";" + target + ";" + result + "\n");  
  90.         System.out.println("调用方法结束:之后执行!\n");  
  91.     }  
  92.   
  93.     /** 
  94.      * 异常通知 
  95.      *  
  96.      * @param jp 
  97.      * @param e 
  98.      */  
  99.     @AfterThrowing(value = "execution(* main.java.com.spring.aop.service.*.*(..))", throwing = "e")  
  100.     public void doThrow(JoinPoint jp, Throwable e) {  
  101.         System.out.println("删除出错啦");  
  102.     }  
  103.   
  104.   
  105. }  
  106. </span>  

1、JoinPoint :切入点,可以得到切入代理对象的信息

1)getArgs():获取方法参数。

2)getTarget():得到目标对象。

3)getSignature():得到方法。


2、ProceedingJoinPoint:环绕方法的使用。

1)proceed():执行目标对象的方法。


3、Throwable:异常获取。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值