Java EE學習筆記(三)

Spring AOP

一、Spring AOP簡介

1)、AOP的全稱是Aspect-Oriented Programming,即面向切面編程(也稱面向方面編程)。它是面向對象編程(OOP)的一種補充,目前已成爲一種比較成熟的編程方式。java

2)、AOP採起橫向抽取機制,將分散在各個方法中的重複代碼提取出來,而後在程序編譯或運行時,再將這些提取出來的代碼應用到須要執行的地方。這種採用橫向抽取機制的方式,採用傳統的OOP思想顯然是沒法辦到的,由於OOP只能實現父子關係的縱向的重用(縱向繼承體系重複性代碼)。雖然AOP是一種新的編程思想,但卻不是OOP的替代品,它只是OOP的延伸和補充。 經典應用:事務管理。實現原理:AOP底層將採用代理機制進行實現。spring

3)、類與切面的關係:express

AOP的使用,使開發人員在編寫業務邏輯時能夠專心於核心業務,而不用過多的關注於其餘業務邏輯的實現,這不但提升了開發效率,並且加強了代碼的可維護性編程

4)、目前最流行的AOP框架有2個:Spring AOPAspectJ(一個基於Java語言的AOP框架)數組

5)、AOP術語:Aspect、Joinpoint、Pointcut、Advice、Target Object、Proxy和Weaving。app

a)、Aspect(切面,切入點pointcut和通知advice的結合):該類要被Spring容器識別爲切面,須要在配置文件中經過<bean>元素指定框架

b)、Joinpoint(鏈接點,即那些可能被攔截到的方法):其實是對象的一個操做,例如方法的調用或異常的拋出。在Spring AOP中,鏈接點就是指方法的調用ide

c)、Pointcut(切入點,已被加強的鏈接點,是鏈接點的子集,例如:addUser():一般在程序中,切入點指的是類或者方法名,如某個通知要應用到全部以add開頭的方法中,那麼全部知足這一規則的方法都是切入點。函數

d)、Advice(通知/加強處理,加強代碼,例如:before()、after()等):AOP框架在特定的切入點執行加強處理,即在定義好的切入點處所要執行的程序代碼。能夠將其理解爲切面類中的方法,它是切面的具體實現性能

e)、Target Object(目標對象,須要被代理的類,例如:UserService):是指全部被通知的對象,也稱爲被加強對象。若是AOP框架採用的是動態的AOP實現,那麼該對象就是一個被代理對象

f)、Proxy(代理):將通知advice應用到目標對象target以後,被動態建立的對象

g)、Weaving(織入):將切面代碼(加強advice應用到)插入到目標對象target上,從而生成代理對象的過程

二、動態代理

1)、JDK動態代理(實現接口的代理方式)

a)、JDK動態代理是經過java.lang.reflect.Proxy 類來實現的,咱們能夠調用Proxy類的newProxyInstance()方法來建立代理對象。對於使用業務接口的類,Spring默認會使用JDK動態代理來實現AOP

b)、src->com.itheima.jdk

①UserDao.java

1 package com.itheima.jdk;
2 
3 public interface UserDao {
4     public void addUser(); // 添加方法
5     public void deleteUser(); // 刪除方法
6 }

②目標類:UserDaoImpl.java(接口的實現類、即切入點pointcut

 1 package com.itheima.jdk;
 2 
 3 public class UserDaoImpl implements UserDao {
 4     // 本案例中會將實現類UserDaoImpl做爲     目標類     ,對其中的方法進行加強處理
 5     
 6     public void addUser() {
 7         System.out.println("添加用戶");
 8     }
 9 
10     public void deleteUser() {
11         System.out.println("刪除用戶");
12     }
13 }

c)、src->com.itheima.aspect

③切面類(這裏是通知類):MyAspect.java(存放多個通知advice)

 1 package com.itheima.aspect;
 2 
 3 // 切面類:能夠存在多個通知Advice(即加強的方法)
 4 public class MyAspect {
 5     // 切面類中的這2個方法就是通知
 6     public void check_Permissions() { 
 7         System.out.println("模擬檢查權限..."); 
 8     } 
 9     public void log() {
10         System.out.println("模擬記錄日誌...");
11     }
12 }

d)、src->com.itheima.jdk

④代理類:JdkProxy.java(編寫工廠生成代理)

 1 package com.itheima.jdk;
 2 import java.lang.reflect.InvocationHandler;
 3 import java.lang.reflect.Method;
 4 import java.lang.reflect.Proxy;
 5 import com.itheima.aspect.MyAspect;
 6 /**
 7  * JDK代理類
 8  */
 9 public class JdkProxy implements InvocationHandler{ // 須要實現InvocationHandler接口,並編寫代理方法
10     
11     // 一、聲明目標類接口對象,經過下面來建立對目標類的代理
12     private UserDao userDao;
13     
14     // 二、建立代理方法
15     public  Object createProxy(UserDao userDao) {
16         this.userDao = userDao;
17         
18         // 1)、類加載器:當前類名.class.getClassLoader();
19         //         目標類實例.getClass().getClassLoader();
20         ClassLoader classLoader = JdkProxy.class.getClassLoader();
21         
22         // 2)、被代理對象實現的全部接口
23         Class[] clazz = userDao.getClass().getInterfaces();
24         
25         // 3)、使用代理類,進行加強,返回的是代理後的對象
26         return  Proxy.newProxyInstance(classLoader,clazz,this);
27         /* 參數1:classLoader(類加載器):動態代理類,運行時建立,任何類都須要類加載器將其加載到內存
28          * 參數2:interfaces:代理類須要實現的全部接口
29          *         方式1:目標類實例.getClass().getInterfaces(); 注意:只能得到本身的接口,不能得到父類接口
30          *         方式2:new Class[]{UserDao.class}
31          * 參數3:InvocationHandler 處理類(接口),必須進行實現類,通常採用匿名內部類
32          */
33     }
34     
35     /*
36      * 全部動態代理類的方法調用,都會交由invoke()方法去處理,執行一次方法,則調用一次invoke()方法 37      * proxy: 被代理後的對象 this
38      * method: 將要被執行的方法信息(反射) 
39      *       執行方法名:method.getName();
40      *       執行方法:method.invoke(目標類接口實例,實際參數);
41      * args: 執行方法時須要的參數
42      */
43     @Override   // 實現了接口中的invoke()方法
44     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
45         // 一、聲明切面類對象(即通知)
46         MyAspect myAspect = new MyAspect();
47         
48         // 二、前加強(要執行advice,那麼就要取建立對應的通知類,這裏是MyAspect切面類)
49         myAspect.check_Permissions();
50         
51         // 三、在目標類上調用方法,並傳入參數, method.invoke(目標類接口實例, 實際參數);
52         Object obj = method.invoke(userDao, args);
53         
54         // 四、後加強
55         myAspect.log();
56         
57         return obj; // 返回代理對象 58     }
59 }

⑤測試類:JdkTest.java

 1 package com.itheima.jdk;
 2 public class JdkTest{
 3     public static void main(String[] args) {
 4         // 一、建立代理對象來組合通知和切入點足
 5         JdkProxy jdkProxy = new JdkProxy();
 6         
 7          // 二、建立目標類對象
 8         UserDao userDao= new UserDaoImpl();
 9         
10         // 三、從代理對象中獲取加強後的目標對象,而後每調用目標類的一個方法將執行(invoke())before()+調用的方法+after(),即首尾通知(切入點處加強代碼)
11         UserDao userDao1 = (UserDao) jdkProxy.createProxy(userDao);
12         
13         // 四、執行方法
14         userDao1.addUser();
15         userDao1.deleteUser();
16     }
17 }

⑥運行結果:

2)、CGLIB代理

a)、若是想代理沒有實現接口的類(只有實現類),那麼可使用CGLIB代理使用動態代理的對象必須實現一個或多個接口

b)、CGLIB(Code Generation Library)是一個高性能開源的代碼生成包,它採用很是底層字節碼技術,對指定的目標類生成一個子類並對子類進行加強

c)、src->com.itheima.cglib

①UserDao.java

 1 package com.itheima.cglib;
 2 //目標類:UserDao 
 3 public class UserDao {
 4     public void addUser() {
 5         System.out.println("添加用戶");
 6     }
 7     public void deleteUser() {
 8         System.out.println("刪除用戶");
 9     }
10 }

②代理類:CglibProxy.java

 1 package com.itheima.cglib;
 2 import java.lang.reflect.Method;
 3 import org.springframework.cglib.proxy.Enhancer;
 4 import org.springframework.cglib.proxy.MethodInterceptor;
 5 import org.springframework.cglib.proxy.MethodProxy;
 6 import com.itheima.aspect.MyAspect;
 7 
 8 // 代理類CglibProxy(字節碼加強框架cglib, 在運行時,建立目標類的子類,從而對目標類進行加強)
 9 public class CglibProxy implements MethodInterceptor{ // 須要實現MethodInterceptor接口,並實現接口中的intercept方法
10     // 代理方法
11     public  Object createProxy(Object target) {
12         // 一、建立一個動態類對象,它是CGLIB的核心類
13         Enhancer enhancer = new Enhancer();
14         
15         // 二、肯定須要加強的(目標)類,設置其父類CglibProxy(肯定父類)
16         enhancer.setSuperclass(target.getClass());
17         
18         // 三、添加回調函數,this就是代理類
19         enhancer.setCallback(this);
20         
21         // 四、返回建立的代理類
22         return enhancer.create();
23     }
24     /**
25      * intercept() 等效於jdk中的invoke()
26      * proxy CGlib根據指定父類生成的代理對象
27      * method 攔截的方法
28      * args 攔截方法的參數數組
29      * methodProxy 方法的代理對象,用於執行父類的方法 
30      */
31     
32     @Override
33     public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
34          // 一、建立切面類對象
35         MyAspect myAspect = new MyAspect();
36         
37         // 二、前加強
38         myAspect.check_Permissions();
39         
40         // 三、目標方法執行(執行代理類的父類的invoke()方法)
41         Object obj = methodProxy.invokeSuper(proxy, args);
42         
43         // 四、後加強
44         myAspect.log();    
45         return obj;
46     }
47 }

③測試類:CglibTest.java

 1 package com.itheima.cglib;
 2 // 測試類
 3 public class CglibTest {
 4     public static void main(String[] args) {
 5         // 一、建立代理對象
 6         CglibProxy cglibProxy = new CglibProxy();
 7         
 8         // 二、建立目標對象
 9         UserDao userDao = new UserDao();
10         
11         // 三、獲取加強後的目標對象
12         UserDao userDao1 = (UserDao)cglibProxy.createProxy(userDao);
13         
14         // 四、執行方法
15         userDao1.addUser();
16         userDao1.deleteUser();
17     }
18 }

④運行結果:

 三、基於代理類的AOP實現

1)、ProxyFactoryBean是FactoryBean接口的實現類,FactoryBean負責實例化一個Bean,而ProxyFactoryBean負責爲其餘Bean建立代理實例。在Spring中,使用ProxyFactoryBean是建立AOP代理的最基本方式。

2)、Spring的通知類型:Spring按照通知在目標類方法的鏈接點位置,能夠分爲5種類型,具體以下:

org.springframework.aop.MethodBeforeAdvice(前置通知)
在目標方法執行前實施加強,能夠應用於權限管理等功能。
org.springframework.aop.AfterReturningAdvice(後置通知)
在目標方法執行後實施加強,能夠應用於關閉流、上傳文件、刪除臨時文件等功能。
org.aopalliance.intercept.MethodInterceptor(環繞通知)
在目標方法執行先後實施加強,能夠應用於日誌、事務管理等功能。
org.springframework.aop.ThrowsAdvice(異常拋出通知)
在方法拋出異常後實施加強,能夠應用於處理異常記錄日誌等功能。
org.springframework.aop.IntroductionInterceptor(引介通知)
在目標類中添加一些新的方法和屬性,能夠應用於修改老版本程序。

3)、ProxyFactoryBean類中的經常使用可配置屬性以下:

4)、src->com.itheima.factorybean

①切面類:MyAspect.java(須要實現接口)

 1 package com.itheima.factorybean;
 2 import org.aopalliance.intercept.MethodInterceptor;
 3 import org.aopalliance.intercept.MethodInvocation;
 4 // 切面類MyAspect,要肯定通知,須要實現不一樣接口,接口就是規範,從而肯定方法名稱。(環繞通知)
 5 public class MyAspect implements MethodInterceptor { // 實現該接口,而且實現接口中的invoke()方法
 6     
 7     @Override
 8     public Object invoke(MethodInvocation mi) throws Throwable {
 9         
10         check_Permissions();
11         // 手動執行目標方法
12         Object obj = mi.proceed();
13         log();
14         return obj;
15     }
16     public void check_Permissions(){ // 如下2個是加強的方法,也就是通知
17         System.out.println("模擬檢查權限...");
18     }
19     public void log(){
20         System.out.println("模擬記錄日誌...");
21     }
22 }

②配置文件:applicationContext.xml

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 
 5 http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
 6 
 7     <!-- 一、 建立目標類 -->
 8     <bean id="userDaoID" class="com.itheima.jdk.UserDaoImpl" />
 9     
10     <!-- 二、 建立切面類 --> 
11     <bean id="myAspectID" class="com.itheima.factorybean.MyAspect" />
12     
13     <!-- 三、目標:使用Spring代理工廠定義一個名稱爲userDaoProxyID的代理對象
14         使用工廠bean FactoryBean,底層調用getObject() 返回特殊的bean
15         ProxyFactoryBean 用於建立代理工廠bean,生成特殊代理對象
16      -->
17     <bean id="userDaoProxyID" class="org.springframework.aop.framework.ProxyFactoryBean">
18     
19         <!-- 3.1 、指定代理實現的接口
20             proxyInterfaces:肯定接口
21         -->
22         <property name="proxyInterfaces" value="com.itheima.jdk.UserDao" />
23         
24         <!-- 3.2 、指定目標對象 
25             target:肯定目標類
26         -->
27         <property name="target" ref="userDaoID" />
28         
29         <!-- 3.3 、指定切面,織入環繞通知
30             interceptorNames:通知切面類的名稱(類型是String[]數組),若是設置一個值,採用一個值value=""
31          -->
32         <property name="interceptorNames" value="myAspectID" />
33         
34         <!-- 3.4 指定代理方式,true則使用cglib,false(默認)則使用jdk動態代理 -->
35         <property name="proxyTargetClass" value="true" />
36     </bean>
37 </beans>

③測試類:ProxyFactoryBeanTest.java

 1 package com.itheima.factorybean;
 2 import org.springframework.context.ApplicationContext;
 3 import org.springframework.context.support.ClassPathXmlApplicationContext;
 4 import com.itheima.jdk.UserDao;
 5 // 測試類
 6 public class ProxyFactoryBeanTest {
 7     public static void main(String args[]) {
 8        String xmlPath = "com/itheima/factorybean/applicationContext.xml";
 9        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
10        // 從Spring容器得到內容
11        UserDao userDao = (UserDao) applicationContext.getBean("userDaoProxy");
12        // 執行方法
13        userDao.addUser();
14        userDao.deleteUser();
15     }
16 }

④運行結果:

四、AspectJ開發

1)、使用AspectJ實現AOP有兩種方式:一種是基於XML的聲明式AspectJ,另外一種是基於註解的聲明式AspectJ

2)、基於XML的聲明式AspectJ

a)、基於XML的聲明式AspectJ是指經過XML文件來定義切面、切入點及通知,全部的切面、切入點和通知都必須定義在<aop:config>元素內。

b)、<aop:config>元素及其子元素以下:

 

c)、XML文件中經常使用元素的配置方式以下:

 1 <!--定義切面Bean -->
 2     <bean id="myAspect" class="com.itheima.aspectj.xml.MyAspect" /> 
 3         <aop:config>
 4             <!-- 配置切面 -->
 5             <aop:aspect  id="aspect" ref="myAspect"> 
 6             <!-- 配置切入點 -->
 7             <aop:pointcut expression="execution(* com.itheima.jdk.*.*(..))「 id="myPointCut" />
 8             <aop:before method="myBefore" pointcut-ref="myPointCut" />
 9             <aop:after-returning method="myAfterReturning「 pointcut-ref="myPointCut" returning="returnVal" />
10             <aop:around method="myAround" pointcut-ref="myPointCut" />
11             <aop:after-throwing method="myAfterThrowing「 pointcut-ref="myPointCut" throwing="e" />
12             <aop:after method="myAfter" pointcut-ref="myPointCut" />
13         </aop:aspect>
14      </aop:config>

d)、配置切面:在Spring的配置文件中,配置切面使用的是<aop:aspect>元素,該元素會將一個已定義好的Spring Bean轉換成切面Bean,因此要在配置文件中先定義一個普通的Spring Bean(如myAspect)。配置<aop:aspect>元素時,一般會指定id和ref兩個屬性:

e)、配置切入點:當<aop:pointcut>元素做爲<aop:config>元素的子元素定義時,表示該切入點是全局切入點,它可被多個切面所共享;當<aop:pointcut>元素做爲<aop:aspect>元素的子元素時,表示該切入點只對當前切面有效。在定義<aop:pointcut>元素時,一般會指定id和expression兩個屬性:

f)、切入點表達式:execution(* com.itheima.jdk.*.*(..)) 是定義的切入點表達式,該切入點表達式的意思是匹配com.itheima.jdk包中任意類的任意方法的執行

其中execution()是表達式的主體,第1個*表示的是返回類型,使用*表明全部類型;com.itheima.jdk表示的是須要攔截的包名,後面第2個*表示的是類名,使用*表明全部的類;第3個*表示的是方法名,使用*表示全部方法;後面(..)表示方法的參數,其中的".."表示任意參數。注意:第1個*與包名之間有一個空格

g)、切入點表達式的基本格式:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)

帶有問號(?)的部分表示可配置項,而其餘部分屬於必須配置項

h)、配置通知:使用<aop:aspect>的子元素能夠配置5種經常使用通知,這5個子元素不支持使用子元素,但在使用時能夠指定一些屬性,其經常使用屬性及其描述以下:

i)、src->com.itheima.aspectj.xml

①切面類:MyAspect.java

 1 package com.itheima.aspectj.xml;
 2 import org.aspectj.lang.JoinPoint;
 3 import org.aspectj.lang.ProceedingJoinPoint;
 4 /**
 5  *切面類,在此類中編寫通知
 6  */
 7 public class MyAspect {
 8     // 前置通知
 9     public void myBefore(JoinPoint joinPoint) { //使用JoinPoint接口及其子接口ProceedingJoinPoint
10         System.out.print("前置通知 :模擬執行權限檢查...,");
11         System.out.print("目標類是:"+joinPoint.getTarget() ); // 得到目標對象的類名
12         System.out.println(",被織入加強處理的目標方法爲:"+joinPoint.getSignature().getName()); // 得到目標方法
13     }
14     
15     // 後置通知
16     public void myAfterReturning(JoinPoint joinPoint) {
17         System.out.print("後置通知:模擬記錄日誌...," );
18         System.out.println("被織入加強處理的目標方法爲:" + joinPoint.getSignature().getName());
19     }
20     /**
21      * 環繞通知
22      * ProceedingJoinPoint 是JoinPoint子接口,表示能夠執行目標方法
23      * 1.必須是Object類型的返回值
24      * 2.必須接收一個參數,類型爲ProceedingJoinPoint
25      * 3.必須throws Throwable
26      */
27     public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
28         // 開始
29         System.out.println("環繞開始:執行目標方法以前,模擬開啓事務...");
30         // 必須手動執行當前目標方法
31         Object obj = proceedingJoinPoint.proceed();
32         // 結束
33         System.out.println("環繞結束:執行目標方法以後,模擬關閉事務...");
34         return obj;
35     }
36     
37     // 異常通知 
38     public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {
39         System.out.println("異常通知:" + "出錯了" + e.getMessage());
40     }
41     
42     // 最終通知
43     public void myAfter() {
44         System.out.println("最終通知:模擬方法結束後的釋放資源...");
45     }
46 }

②配置文件:applicationContext.xml

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 4    xmlns:aop="http://www.springframework.org/schema/aop"
 5 xsi:schemaLocation="http://www.springframework.org/schema/beans 
 6                     http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
 7                     http://www.springframework.org/schema/aop 
 8                     http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
 9     <!-- 1 、建立目標類 -->
10     <bean id="userDaoID" class="com.itheima.jdk.UserDaoImpl" />
11     
12     <!-- 2 、建立切面類 -->
13     <bean id="myAspectID" class="com.itheima.aspectj.xml.MyAspect" />
14     
15     <!-- 3 、aop編程,proxy-target-class = "true"聲明使用cglib動態代理 -->
16     <aop:config >
17         
18         <!-- 配置切面 ,切面在myAspectID-->
19         <aop:aspect ref="myAspectID">
20 
21           <!-- 3.1 、配置切入點,通知最後加強哪些方法 (從目標對象中得到具體方法)-->
22           <aop:pointcut expression="execution(* com.itheima.jdk.*.*(..))" id="myPointCutID" />
23           
24             <!-- 3.2 、關聯通知Advice和切入點pointCut -->
25             
26             <!-- 3.2.1 、前置通知 ,pointcut-ref切入點引用-->
27             <aop:before method="myBefore" pointcut-ref="myPointCutID" />
28             
29             <!-- 3.2.2 後置通知,在方法返回以後執行,就能夠得到返回值
30              returning屬性:用於設置後置通知的第二個參數的名稱,類型是Object 
31              -->
32             <aop:after-returning method="myAfterReturning"
33                 pointcut-ref="myPointCutID" returning="returnVal" />
34     
35             <!-- 3.2.3 環繞通知 -->
36             <aop:around method="myAround" pointcut-ref="myPointCutID" />
37             
38             <!-- 3.2.4 拋出通知:用於處理程序發生異常-->
39             <!-- * 注意:若是程序沒有異常,將不會執行加強 -->
40             <!-- * throwing屬性:用於設置通知第二個參數的名稱,類型Throwable -->
41             <aop:after-throwing method="myAfterThrowing"
42                 pointcut-ref="myPointCutID" throwing="e" />
43                 
44             <!-- 3.2.5 最終通知:不管程序發生任何事情,都將執行,至關於try-catch-finally塊 -->
45             <aop:after method="myAfter" pointcut-ref="myPointCutID" />
46             
47         </aop:aspect>
48     </aop:config>
49 </beans>

注意:後置通知只有在目標方法成功執行後纔會被織入,而最終通知不論目標方法如何結束(包括成功執行和異常停止2種狀況),它都會被織入。

③測試類:TestXmlAspectj.java

 1 package com.itheima.aspectj.xml;
 2 import org.springframework.context.ApplicationContext;
 3 import org.springframework.context.support.ClassPathXmlApplicationContext;
 4 import com.itheima.jdk.UserDao;
 5 // 測試類
 6 public class TestXmlAspectj {
 7     public static void main(String args[]) {
 8         String xmlPath = "com/itheima/aspectj/xml/applicationContext.xml";
 9         ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
10         // 1 、從spring容器得到內容
11         UserDao userDao = (UserDao) applicationContext.getBean("userDaoID");
12         // 二、 執行方法
13         userDao.addUser();
14     }
15 }

④運行結果:

3)、基於註解的聲明式AspectJ

a)、AspectJ框架爲AOP的實現提供了一套註解,用以取代Spring配置文件中爲實現AOP功能所配置的臃腫代碼。AspectJ的註解及其描述以下所示:

b)、src->com.itheima.aspectj.annotation

①MyAspect.java

 1 package com.itheima.aspectj.annotation;
 2 import org.aspectj.lang.JoinPoint;
 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 import org.aspectj.lang.annotation.Pointcut;
11 import org.springframework.stereotype.Component;
12 /**
13  * 切面類,在此類中編寫通知
14  */
15 @Aspect // 註解定義了切面類
16 @Component // 因爲Aspect類在Spring中是做爲組件使用的,因此還要添加組件才能生效
17 public class MyAspect {
18     
19     // 定義切入點表達式,聲明公共切入點
20     @Pointcut("execution(* com.itheima.jdk.*.*(..))")
21     
22     // 使用一個返回值爲void、方法體爲空的方法來命名切入點
23     private void myPointCut(){}
24     
25     /**
26      *     前置通知,替換
27      * <aop:before method="myBefore" pointcut="execution(* com.itheima.jdk.*.*(..))" />
28      */
29     @Before("myPointCut()")
30     public void myBefore(JoinPoint joinPoint) {
31         System.out.print("前置通知 :模擬執行權限檢查...,");
32         System.out.print("目標類是:"+joinPoint.getTarget() );
33         System.out.println(",被織入加強處理的目標方法爲:" +joinPoint.getSignature().getName());
34     }
35     
36     // 後置通知
37     @AfterReturning(value="myPointCut()") // 注意有返回值,要有value
38     public void myAfterReturning(JoinPoint joinPoint) {
39         System.out.print("後置通知:模擬記錄日誌...," );
40         System.out.println("被織入加強處理的目標方法爲:" + joinPoint.getSignature().getName());
41     }
42     
43     // 環繞通知    
44     @Around("myPointCut()")
45     public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
46         // 開始
47         System.out.println("環繞開始:執行目標方法以前,模擬開啓事務...");
48         // 手動執行當前目標方法
49         Object obj = proceedingJoinPoint.proceed();
50         // 結束
51         System.out.println("環繞結束:執行目標方法以後,模擬關閉事務...");
52         return obj;
53     }
54     
55     // 異常通知
56     @AfterThrowing(value="myPointCut()",throwing="e")
57     public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {
58         System.out.println("異常通知:" + "出錯了" + e.getMessage());
59     }
60     
61     // 最終通知
62     @After("myPointCut()")
63     public void myAfter() {
64         System.out.println("最終通知:模擬方法結束後的釋放資源...");
65     }
66 }

②applicationContext.xml

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 4   xmlns:aop="http://www.springframework.org/schema/aop"
 5   xmlns:context="http://www.springframework.org/schema/context"
 6   xsi:schemaLocation="http://www.springframework.org/schema/beans 
 7   http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
 8   http://www.springframework.org/schema/aop 
 9   http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
10   http://www.springframework.org/schema/context 
11   http://www.springframework.org/schema/context/spring-context-4.3.xsd">
12       
13       <!-- 指定須要掃描的包,使註解生效 -->
14       <context:component-scan base-package="com.itheima" />
15       
16       <!-- 啓動基於註解的聲明式AspectJ支持,肯定aop註解生效 -->
17       <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
18 </beans>

③添加註解(DAO層):/chapter03/src/com/itheima/jdk/UserDaoImpl.java

 1 package com.itheima.jdk;
 2 
 3 import org.springframework.stereotype.Repository;
 4 
 5 @Repository("userDaoID") 
 6 //這裏是aop基於註解的聲明式AspectJ用到的:@Repository註解將UserDaoImpl(數據訪問層DAO層)的類標識爲Spring中的Bean,其寫法至關於配置文件中
 7 //<bean id = "userDaoID" class = "com.itheima.annotation.UserDaoImpl" />的編寫
 8 
 9 public class UserDaoImpl implements UserDao {
10     // 本案例中會將實現類UserDaoImpl做爲     目標類     ,對其中的方法進行加強處理
11     
12     public void addUser() {
13         System.out.println("添加用戶");
14     }
15 
16     public void deleteUser() {
17         System.out.println("刪除用戶");
18     }
19 }

④TestAnnotationAspectj.java

 1 package com.itheima.aspectj.annotation;
 2 import org.springframework.context.ApplicationContext;
 3 import org.springframework.context.support.ClassPathXmlApplicationContext;
 4 import com.itheima.jdk.UserDao;
 5 // 測試類
 6 public class TestAnnotationAspectj {
 7     public static void main(String args[]) {
 8         String xmlPath = "com/itheima/aspectj/annotation/applicationContext.xml";
 9         ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
10         // 一、從spring容器得到內容
11         UserDao userDao = (UserDao) applicationContext.getBean("userDaoID");
12         // 二、 執行方法
13         userDao.addUser();
14     }
15 }

⑤運行結果:(與上面的執行結果同樣,只是在目標方法先後通知的執行順序發生了變化)

相關文章
相關標籤/搜索