一.概述
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
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
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