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; 24 .準備通知--增強的方法myAdviceimport 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() { 11System.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 } 395 .編寫配置對象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