1. 程式人生 > >spring筆記3-AOP

spring筆記3-AOP

父類 ride 支持 ack beans 方法參數 cep 結構 express

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