spring笔记3-AOP

Posted 胡广勤

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring笔记3-AOP相关的知识,希望对你有一定的参考价值。

一.概述
AOP:(Aspect Oriented Programming)即:面向切面编程。把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对我们的已有方法进行增强。

二.术语
Joinpoint(连接点):可以被代理增强的方法,即被spring拦截到的点,spring中点即方法,因为spring只支持方法类型的连接点;
Pointcut(切入点):需要或已经被增强的Joinpoint;
Advice(通知):拦截到Joinpoint之后要做的事情即为通知,即要增强的方法;
     通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
Target(目标对象):代理的目标对象,或被增强的对象,或Pointcut所在对象;
Proxy(代理对象):对目标对象进行增强后产生的对象;一个类被AOP织入增强后,就产生一个结果代理类。
Weaver(织入):把增强应用到目标对象来创建新的代理对象的过程。将通知应用到切点的过程,称为Weaver;spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
Aspect(切面):切点+通知
Introduction(引介):引介是一种特殊的通知,在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field。

三.案例演示:xml--抽取UserServiceImpl中的公共代码
1.导包-4+2/aspects/aop/aopalliance/aspectj.weaver/test
2.建立工程结构:
     huguangqin.com.cnblogs.advice
     huguangqin.com.cnblogs.service
     huguangqin.com.cnblogs.service.serviceImpl
     huguangqin.com.cnblogs.test
3.准备目标对象--要增强的方法所在类UserServiceImpl
    

  1 package huguangqin.com.cnblogs.service.serviceImpl;
  2     import huguangqin.com.cnblogs.service.UserService;
  3      public class UserServiceImpl implements UserService {
  4          @Override
  5          public void save() {
  6              System.out.println("客户保存了");
  7          }
  8 
  9         @Override
 10          public void delete() {
 11              System.out.println("客户删除了");
 12          }
 13 
 14         @Override
 15          public void find() {
 16              System.out.println("客户找到了");
 17          }
 18 
 19         @Override
 20          public void update() {
 21              System.out.println("客户更新了");
 22          }
 23      }
 24 
4 .准备通知--增强的方法myAdvice
    
  1 package huguangqin.com.cnblogs.advice;
  2      import org.aspectj.lang.ProceedingJoinPoint;
  3      //通知类
  4     public class MyAdvice {
  5          // 前置
  6         public void before() {
  7              System.out.println("我是前置通知!");
  8          }
  9 
 10         // 环绕通知
 11         /*
 12          * spring框架为我们提供了一个接口:ProceedingJoinPoint,它可以作为环绕通知的方法参数
 13          * 在环绕通知执行时,spring框架会为我们提供该接口的实现类对象,我们直接使用就行。
 14          * 该接口中有一个方法proceed(),此方法就相当于method.invoke()
 15           */
 16          public Object around(ProceedingJoinPoint pjp) throws Throwable {
 17              System.out.println("我是环绕通知,前半部分!");
 18              // 执行目标方法
 19             Object proceed = pjp.proceed();
 20              System.out.println("我是环绕通知,后半部分!");
 21              return proceed;
 22          }
 23 
 24         // 后置 => 出现异常就不执行的后置通知
 25         public void afterReturning() {
 26              System.out.println("我是后置通知,出现异常就不执行的后置通知!");
 27          }
 28 
 29         // 最终通知 => 无论是否出现异常都执行的后置通知
 30         public void after() {
 31              System.out.println("我是最终通知,出现异常仍然执行的后置通知!");
 32          }
 33 
 34         // 异常 => 出现异常才执行的通知
 35         public void afterThrowing() {
 36              System.out.println("我是异常通知,出现异常才执行的通知!");
 37          }
 38      }
 39 
5 .编写配置对象applicationContext.xml(位置在src下)
   
  1  <?xml version="1.0" encoding="UTF-8"?>
  2 
  3     <beans
  4      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5      xmlns="http://www.springframework.org/schema/beans"
  6      xmlns:context="http://www.springframework.org/schema/context"
  7      xmlns:aop="http://www.springframework.org/schema/aop"
  8      xsi:schemaLocation="http://www.springframework.org/schema/beans
  9                          http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
 10                          http://www.springframework.org/schema/context
 11                          http://www.springframework.org/schema/context/spring-context-4.2.xsd
 12                          http://www.springframework.org/schema/aop
 13                          http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">
 14 
 15      <!--配置目标对象  -->
 16      <bean name="userService" class="huguangqin.com.cnblogs.service.serviceImpl.UserServiceImpl"></bean>
 17 
 18     <!--配置通知对象  -->
 19      <bean name="myAdvice" class="huguangqin.com.cnblogs.advice.MyAdvice"></bean>
 20 
 21     <!--将通知织入目标  -->
 22      <!-- 切点表达式
 23         public void huguangqin.com.cnblogs.service.serviceImpl.UserServiceImpl.save()
 24          * huguangqin.com.cnblogs.service.serviceImpl.*ServiceImpl.*(..):
 25              第一个*代表:返回值任意;
 26              第二个*代表:包内所有以ServiceImpl结尾的类;
 27              第三个*代表:类内所有方法
 28             括号内..代表:任意参数
 29      -->
 30      <aop:config>
 31          <!--配置切点  -->
 32          <aop:pointcut expression="execution(* huguangqin.com.cnblogs.service.serviceImpl.*ServiceImpl.*(..))" id="mypc"></aop:pointcut>
 33          <!--配置切面  -->
 34          <aop:aspect ref="myAdvice">
 35              <!--前置通知  -->
 36              <aop:before method="before" pointcut-ref="mypc" />
 37              <!--环绕通知  -->
 38              <aop:around method="around" pointcut-ref="mypc" />
 39              <!--最终通知 :出现异常也执行 -->
 40              <aop:after method="after" pointcut-ref="mypc" />
 41              <!--异常通知  -->
 42              <aop:after-throwing method="afterThrowing" pointcut-ref="mypc" />
 43              <!--后置通知:出现异常不执行  -->
 44              <aop:after-returning method="afterReturning" pointcut-ref="mypc" />
 45          </aop:aspect>
 46      </aop:config>
 47      </beans>
 48 

6.测试
    
  1 package huguangqin.com.cnblogs.test;
  2      import javax.annotation.Resource;
  3      import org.junit.Test;
  4      import org.junit.runner.RunWith;
  5      import org.springframework.test.context.ContextConfiguration;
  6      import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  7      import huguangqin.com.cnblogs.service.UserService;
  8 
  9     @RunWith(SpringJUnit4ClassRunner.class)
 10      @ContextConfiguration("classpath:applicationContext.xml")
 11      public class Demo {
 12          @Resource(name = "userService")
 13          private UserService us;
 14 
 15         @Test
 16          public void save() {
 17              us.save();
 18          }
 19      }
 20 

四.案例演示:注解模式
     与xml模式相比,需修改applicationContext.xml和MyAdvice类
     1.applicationContext.xml
   

  1  <?xml version="1.0" encoding="UTF-8"?>
  2 
  3     <beans
  4      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5      xmlns="http://www.springframework.org/schema/beans"
  6      xmlns:context="http://www.springframework.org/schema/context"
  7      xmlns:aop="http://www.springframework.org/schema/aop"
  8      xsi:schemaLocation="http://www.springframework.org/schema/beans
  9                          http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
 10                          http://www.springframework.org/schema/context
 11                          http://www.springframework.org/schema/context/spring-context-4.2.xsd
 12                          http://www.springframework.org/schema/aop
 13                          http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">
 14 
 15      <!--配置目标对象  -->
 16      <bean name="userService" class="huguangqin.com.cnblogs.service.serviceImpl.UserServiceImpl"></bean>
 17 
 18     <!--配置通知对象  -->
 19      <bean name="myAdvice" class="huguangqin.com.cnblogs.advice.MyAdvice"></bean>
 20 
 21     <!--将通知织入目标 ,打开注解配置开关 -->
 22      <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
 23      </beans>
 24 

    2.MyAdvice
   

  1  package huguangqin.com.cnblogs.advice;
  2 
  3     import org.aspectj.lang.ProceedingJoinPoint;
  4      import org.aspectj.lang.annotation.After;
  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 
 11     //通知类
 12     @Aspect
 13      public class MyAdvice {
 14 
 15         // 前置
 16         @Before("execution(* huguangqin.com.cnblogs.service.serviceImpl.*ServiceImpl.*(..))")
 17          public void before() {
 18              System.out.println("我是前置通知!");
 19          }
 20 
 21         // 环绕通知
 22         /*
 23           * spring框架为我们提供了一个接口:ProceedingJoinPoint,它可以作为环绕通知的方法参数
 24          * 在环绕通知执行时,spring框架会为我们提供该接口的实现类对象,我们直接使用就行。
 25          * 该接口中有一个方法proceed(),此方法就相当于method.invoke()
 26           */
 27          @Around("execution(* huguangqin.com.cnblogs.service.serviceImpl.*ServiceImpl.*(..))")
 28          public Object around(ProceedingJoinPoint pjp) throws Throwable {
 29              System.out.println("我是环绕通知,前半部分!");
 30              // 执行目标方法
 31             Object proceed = pjp.proceed();
 32              System.out.println("我是环绕通知,后半部分!");
 33              return proceed;
 34          }
 35 
 36         // 后置 => 出现异常就不执行的后置通知
 37         @AfterReturning("execution(* huguangqin.com.cnblogs.service.serviceImpl.*ServiceImpl.*(..))")
 38          public void afterReturning() {
 39              System.out.println("我是后置通知,出现异常就不执行的后置通知!");
 40          }
 41 
 42         // 最终通知 => 无论是否出现异常都执行的后置通知
 43         @After("execution(* huguangqin.com.cnblogs.service.serviceImpl.*ServiceImpl.*(..))")
 44          public void after() {
 45              System.out.println("我是最终通知,出现异常仍然执行的后置通知!");
 46          }
 47 
 48         // 异常 => 出现异常才执行的通知
 49         @AfterThrowing("execution(* huguangqin.com.cnblogs.service.serviceImpl.*ServiceImpl.*(..))")
 50          public void afterThrowing() {
 51              System.out.println("我是异常通知,出现异常才执行的通知!");
 52          }
 53      }
 54 

四.动态代理回顾
1.原生动态代理--必需要一个接口,否则不能使用
a.编写接口
   

  1  package huguangqin.com.cnblogs.serive;
  2 
  3     public interface Service {
  4          void find();
  5      }
  6 

b.编写接口的实现类
   

  1  package huguangqin.com.cnblogs.seriveImpl;
  2 
  3     import huguangqin.com.cnblogs.serive.Service;
  4 
  5     public class ServiceImpl implements Service {
  6 
  7         @Override
  8          public void find() {
  9              System.out.println("找到了~~");
 10          }
 11 
 12     }
 13 

c.创建该动态代理类
    

  1 package huguangqin.com.cnblogs.proxy;
  2 
  3     import java.lang.reflect.InvocationHandler;
  4     import java.lang.reflect.Method;
  5      import java.lang.reflect.Proxy;
  6 
  7     import huguangqin.com.cnblogs.serive.Service;
  8      import huguangqin.com.cnblogs.seriveImpl.ServiceImpl;
  9 
 10     public class MyProxy implements InvocationHandler {
 11 
 12         // 目标接口--我需要知道代理哪个接口
 13         private Service target;
 14 
 15         // 代理类的有参构造--利用给我的接口生成 这个接口代理类
 16         public MyProxy(Service target) {
 17              super();
 18              this.target = target;
 19          }
 20 
 21         // 获取代理对象--输出该接口的代理代象
 22         public Service getServiceProxy() {
 23              return (Service) Proxy.newProxyInstance(ServiceImpl.class.getClassLoader(), ServiceImpl.class.getInterfaces(),
 24                      this);
 25          }
 26 
 27         @Override
 28          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 29              System.out.println("start transaction");
 30              Object invoke = method.invoke(target, args);
 31             System.out.println("end transaction");
 32              return invoke;
 33          }
 34 
 35     }
 36 

d.创建测试类
    

  1 package huguangqin.com.cnblogs.proxy;
  2 
  3     import org.junit.Test;
  4 
  5     import huguangqin.com.cnblogs.serive.Service;
  6      import huguangqin.com.cnblogs.seriveImpl.ServiceImpl;
  7 
  8     public class Demo {
  9 
 10         @Test
 11          public void demo() {
 12              // 被代理对象
 13             Service ser = new ServiceImpl();
 14              // 代理工厂
 15             MyProxy mp = new MyProxy(ser);
 16              // 获得代理类
 17             Service proxy = mp.getServiceProxy();
 18              proxy.find();
 19          }
 20      }
 21 


2.CGLIB创建动态代理
c.创建动态代理类
    

  1 package huguangqin.com.cnblogs.proxy;
  2 
  3     import java.lang.reflect.Method;
  4 
  5     import org.springframework.cglib.proxy.Enhancer;
  6      import org.springframework.cglib.proxy.MethodInterceptor;
  7      import org.springframework.cglib.proxy.MethodProxy;
  8 
  9     import huguangqin.com.cnblogs.serive.Service;
 10      import huguangqin.com.cnblogs.seriveImpl.ServiceImpl;
 11 
 12     //cglib
 13      public class MyProxy implements MethodInterceptor {
 14 
 15         public Service getMyProxy() {
 16              Enhancer en = new Enhancer();
 17              // 指定父类
 18             en.setSuperclass(ServiceImpl.class);
 19              // 设置需要增强的代码
 20             en.setCallback(this);
 21              // 创建代理对象
 22             return (Service) en.create();
 23 
 24         }
 25 
 26         @Override
 27          // proxy: 代理对象
 28         // arg1: 目标方法对象
 29         // arg2: 目标方法参数
 30         // methodProxy: 代理方法对象
 31         public Object intercept(Object proxy, Method arg1, Object[] arg2, MethodProxy methodProxy) throws Throwable {
 32              System.out.println("start");
 33             // 调用原业务方法
 34             Object invokeSuper = methodProxy.invokeSuper(proxy, arg2);
 35              System.out.println("end");
 36              return invokeSuper;
 37          }
 38 
 39     }
 40 

d.测试类
   

  1  package huguangqin.com.cnblogs.test;
  2 
  3     import org.junit.Test;
  4 
  5     import huguangqin.com.cnblogs.proxy.MyProxy;
  6      import huguangqin.com.cnblogs.serive.Service;
  7 
  8     public class Demo {
  9 
 10         @Test
 11          public void demo() {
 12              // 代理工厂
 13             MyProxy mp = new MyProxy();
 14              // 获得代理类
 15             Service proxy = mp.getMyProxy();
 16              proxy.find();
 17          }
 18      }
 19 





































以上是关于spring笔记3-AOP的主要内容,如果未能解决你的问题,请参考以下文章

JavaEE--Spring_AOP

Spring 3.0 AOP AOP 术语

Java开发Spring之AOP详解(xml--注解->方法增强事务管理(声明事务的实现))

Java自学!spring-kafka监听器不好使

Spring之003: AOP开发

Spring AOP快速入门详解