目錄
三,Spring核心AOP
1,介紹
2,代理&靜态代理
2.1 原有方式:核心業務和服務方法都編寫在一起
2.2 基于類的靜态代理
2.3 基于接口的靜态代理
2.4 提取出切面代碼,作為AOP接口
2.5 總結
3,動态代理
3.1 基于JDK代理
結構化
3.2 基于CGLIB代理
結構化
3.3 使用細節對比
4,SpringAOP介紹
4.1 AOP介紹
4.2 AspectJ 對 AOP 的實作
5,SpringAOP的實作——注解方式
5.1 測試項目的基本結構
5.2 前置通知@Before
5.3 連接配接點JoinPoint
5.4 後置通知@AfterReturning
5.5 異常通知@AfterThrowing
5.6 最終通知@After
5.7 環繞通知@Around
5.8 切入點表達式@Pointcut
6,SpringAOP的實作——XML方式
三,Spring核心AOP
1,介紹
AOP為Aspect Oriented Programming的縮寫,意思為面向切面程式設計,是通過預編譯方式和運作期動态代理實作程式功能的統一維護的一種技術。
AOP的作用:不修改源碼的情況下,程式運作期間對方法進行功能增強
好處:
- 減少代碼的重複,提高開發效率,便于維護。
- 專注核心業務的開發(比如業務開發過程中往往需要考慮事務、日志等的處理)。
核心業務和服務性代碼(日志、事務、權限等)混合在一起。開發時各自做自己擅長的事情,運作的時候将服務性代碼織入到核心業務中。
通過spring工廠自動實作将服務性代碼以切面的方式加入到核心業務代碼中。
2,代理&靜态代理
代理模式給某一個對象提供一個代理對象,并由代理對象控制對原對象的引用。通俗的來講代理模式就是我們生活中常見的中介。
2.1 原有方式:核心業務和服務方法都編寫在一起
public class TeamService {
public void add(){
try {
System.out.println("開始事務");
System.out.println("TeamService---- add----");// 核心業務
System.out.println("送出事務");
} catch (Exception e) {
e.printStackTrace();
System.out.println("復原事務");
}
}
}
2.2 基于類的靜态代理
可以将服務性代碼分離出來。但是代理類隻能代理一個類。
被代理類
public class TeamService {
public void add(){
System.out.println("TeamService---- add----");// 核心業務
}
}
代理類
/**
* 基于類的靜态代理:
* 要求繼承被代理的類
* 弊端:這個代理類每次隻能代理一個類(和被代理類綁定)
*/
public class ProxyTeamService extends TeamService {
public void add(){
try {
System.out.println("開始事務");
super.add();//核心業務就是由被代理對象完成 ;其他服務功能由代理類完成
System.out.println("送出事務");
}catch (Exception e){
System.out.println("復原事務");
}
}
}
2.3 基于接口的靜态代理
為核心業務(儲存add)建立一個接口,通過接口暴露被代理的方法。
要求:代理類和被代理類都實作了同一個接口。
優勢:
- 代理類與被代理類解耦。相比基于類的靜态代理,一個代理類隻要傳入不同的參數,就可以代理不同的類;
- 由于代理類與被代理類松耦合,是以可以友善的實作多級代理;
接口
/**
* 接口定義核心方法
*/
public interface IService {
void add();
}
被代理類
// 被代理類1
public class TeamService implements IService{
@Override
public void add(){
System.out.println("TeamService---- add----");// 核心業務
}
}
// 被代理類2
public class UserService implements IService{
@Override
public void add() {
System.out.println("UserService---- add-----");
}
}
代理類
/**
* 基于接口的靜态代理:
* 代理類和被代理類實作同一個接口
*/
public class ProxyTranService implements IService {
private IService service;//被代理的對象
public ProxyTranService(IService service) {
this.service = service;
}
@Override
public void add() {
try {
System.out.println("開始事務");
service.add();//核心業務就是由被代理對象完成 ;其他服務功能由代理類完成
System.out.println("送出事務");
}catch (Exception e){
System.out.println("復原事務");
}
}
}
public class ProxyLogService implements IService {
private IService service;//被代理對象
public ProxyLogService(IService service) {
this.service = service;
}
@Override
public void add() {
try {
System.out.println("開始日志");
service.add();//核心業務就是由被代理對象完成 ;其他服務功能由代理類完成
System.out.println("結束日志");
}catch (Exception e){
System.out.println("異常日志");
}
}
}
測試類
public static void main(String[] args) {
TeamService teamService=new TeamService();//被代理對象
UserService userService=new UserService();//被代理對象
// 代理類與被代理類解耦。一個代理類,傳入不同的參數,即可代理不同的類
ProxyTranService tranService=new ProxyTranService(userService);//事務代理對象--一級代理
// tranService.add();//代理對象幹活
ProxyLogService logService=new ProxyLogService(tranService);//日志的代理對象--二級代理
logService.add();
}
2.4 提取出切面代碼,作為AOP接口
共有4個位置可以将切面代碼編織進入核心業務代碼中。
代理将被代理對象和切面整合起來,形成完整的業務邏輯。
切面接口
/**
* 切面:服務代碼,切入到核心代碼中,切入到哪裡,給了四個位置
*/
public interface AOP {
void before();
void after();
void exception();
void myFinally();
}
事務切面
public class TranAOP implements AOP {
@Override
public void before() {
System.out.println("事務----before");
}
@Override
public void after() {
System.out.println("事務----after");
}
@Override
public void exception() {
System.out.println("事務----exception");
}
@Override
public void myFinally() {
System.out.println("事務----myFinally");
}
}
日志切面
public class LogAop implements AOP{
@Override
public void before() {
System.out.println("日志----before");
}
@Override
public void after() {
System.out.println("日志----after");
}
@Override
public void exception() {
System.out.println("日志----exception");
}
@Override
public void myFinally() {
System.out.println("日志----myFinally");
}
}
代理類
public class ProxyAOPService implements IService {
private IService service;//被代理對象
private AOP aop;//要加入切面
public ProxyAOPService(IService service, AOP aop) {
this.service = service;
this.aop = aop;
}
@Override
public void add() {
try {
aop.before();
service.add();//被代理對象幹活
aop.after();
}catch (Exception e){
aop.exception();
}finally {
aop.myFinally();
}
}
}
測試類
@Test
public void test02(){
IService teamService=new TeamService();//被代理對象--核心内容
AOP logAop=new LogAop();//切面-服務内容
AOP tranAop=new TranAOP();
IService service=new ProxyAOPService(teamService,logAop); //代理對象--一級代理
IService service2=new ProxyAOPService(service,tranAop);//代理對象--二級代理
service2.add();
}
2.5 總結
靜态代理:
- 優點:可以做到在不修改目标對象的功能前提下,對目标對象功能擴充。
- 缺點: 因為代理對象,需要與目标對象實作一樣的接口。是以會有很多代理類,類太多。 一旦接口增加方法,目标對象與代理對象都要維護。
3,動态代理
動态代理是指代理類對象在程式運作時由JVM根據反射機制動态生成的。動态代理不需要定義代理類。動态代理其實就是jdk運作期間,動态建立class位元組碼并加載到JVM。
- 靜态代理:要求代理類一定存在;
- 動态代理:程式運作的時候,根據要被代理的對象動态生成代理類;
類型:1、基于JDK的動态代理,2、基于CGLIB的動态代理;
3.1 基于JDK代理
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
- ClassLoader :類加載器,因為動态代理類,借助别人的類加載器。一般使用被代理對象的類加載器。
- Class<?>[] interfaces:接口類對象的集合,針對接口的代理,針對哪個接口做代理,一般使用的就是被代理對象的接口。
- InvocationHandler:句柄,回調函數,編寫代理的規則代碼
public Object invoke(Object arg0, Method arg1, Object[] arg2)
- Object arg0:代理對象
- Method arg1:被代理的方法
- Object[] arg2:被代理方法被執行的時候的參數的數組
接口
/**
* 接口定義核心方法
*/
public interface IService {
void add();
}
被代理類
// 被代理類1
public class TeamService implements IService{
@Override
public void add(){
System.out.println("TeamService---- add----");// 核心業務
}
}
// 被代理類2
public class UserService implements IService{
@Override
public void add() {
System.out.println("UserService---- add-----");
}
}
使用動态代理
public class MyJDKProxy {
public static void main(String[] args) {
//目标對象--被代理對象
TeamService teamService=new TeamService();
//傳回代理對象 調用JDK中Proxy類中的靜态方法newProxyInstance擷取動态代理類的執行個體
IService proxyService= (IService) Proxy.newProxyInstance(
teamService.getClass().getClassLoader(),
teamService.getClass().getInterfaces(),
new InvocationHandler() {//回調函數 編寫代理規則
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
System.out.println("開始事務");
Object invoke = method.invoke(teamService, args);//核心業務
System.out.println("送出事務");
return invoke;
}catch (Exception e){
System.out.println("復原事務");
e.printStackTrace();
throw e;
}finally {
System.out.println("finally---------");
}
}
}
);
//代理對象幹活
proxyService.add();
System.out.println(teamService.getClass());
System.out.println(proxyService.getClass()+"--------");
}
}
可以看出上面并沒有顯式的建立代理類,代理類的建立工作交由jdk執行。
此外由于沒有代理類的定義,是以代理類也不需要實作接口,是以接口變動時,隻需要修改核心業務即可,動态代理對象的生成基本沒有變動。
結構化
方式1
建立ProxyHandler類,實作InvocationHandler接口,重寫invoke方法,不需要在newProxyInstance中重寫invoke方法,看起來更簡潔一些。
ProxyHandler類
public class ProxyHandler implements InvocationHandler {
private IService service;//目标對象
private AOP aop;//切面
public ProxyHandler(IService service, AOP aop) {
this.service = service;
this.aop = aop;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
aop.before();
Object invoke = method.invoke(service, args);//核心業務
aop.after();
return invoke;
}catch (Exception e){
aop.exception();
e.printStackTrace();
throw e;
}finally {
aop.myFinally();
}
}
}
測試方法
public static void main2(String[] args) {
//目标對象--被代理對象
TeamService teamService=new TeamService();
//切面
AOP tranAop=new TranAOP();
//傳回代理對象 基于JDK的動态代理
IService proxyService= (IService) Proxy.newProxyInstance(
teamService.getClass().getClassLoader(),
teamService.getClass().getInterfaces(),
new ProxyHandler(teamService,tranAop)
);
//代理對象幹活
proxyService.add();
System.out.println(teamService.getClass());
System.out.println(proxyService.getClass()+"------");
}
方式2
通過ProxyFactory類擷取動态代理的執行個體
ProxyFactory類
public class ProxyFactory {
private IService service;//目标對象
private AOP aop;//切面
public ProxyFactory(IService service, AOP aop) {
this.service = service;
this.aop = aop;
}
/**
* 擷取動态代理的示例
* @return
*/
public Object getProxyInstance() {
return Proxy.newProxyInstance(
service.getClass().getClassLoader(),
service.getClass().getInterfaces(),
new InvocationHandler() {//回調函數 編寫代理規則
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
aop.before();
Object invoke = method.invoke(service, args);//核心業務
aop.after();
return invoke;
}catch (Exception e){
aop.exception();
e.printStackTrace();
throw e;
}finally {
aop.myFinally();
}
}
}
);
}
}
測試方法
public class MyJDKProxy {
public static void main(String[] args) {
//目标對象--被代理對象
TeamService teamService=new TeamService();
//切面
AOP tranAop=new TranAOP();
AOP logAop=new LogAop();
//擷取代理對象
IService service= (IService) new ProxyFactory(teamService,tranAop).getProxyInstance();
IService service1= (IService) new ProxyFactory(service,logAop).getProxyInstance();
service1.add();//核心業務+服務代碼混合在一起的完整的業務員方法
}
}
代理對象不需要實作接口,但是目标對象(被代理類)一定要實作接口;否則不能用JDK動态代理。如果想要功能擴充,但目标對象沒有實作接口,怎樣功能擴充?
采用子類的方式實作代理CGLIB。
3.2 基于CGLIB代理
CGLIB是一個強大的、高性能的代碼生成庫。其被廣泛應用于AOP架構(Spring、dynaop)中,用以提供方法攔截操作。Hibernate作為一個比較受歡迎的ORM架構,同樣使用CGLIB來代理單端(多對一和一對一)關聯(延遲提取集合使用的另一種機制)。CGLIB作為一個開源項目,其代碼托管在github,位址為:https://github.com/cglib/cglib
參考@danchu【CGLIB(Code Generation Library)詳解】
Cglib代理,也叫做子類代理。在記憶體中建構一個子類對象進而實作對目标對象功能的擴充。
- JDK的動态代理有一個限制,就是使用動态代理的對象必須實作一個或多個接口。如果想代理沒有實作接口的類,就可以使用CGLIB實作。
- CGLIB是一個強大的高性能的代碼生成包,它可以在運作期擴充Java類與實作Java接口。它廣泛的被許多AOP的架構使用,例如Spring AOP和dynaop,為他們提供方法的interception。
- CGLIB包的底層是通過使用一個小而快的位元組碼處理架構ASM,來轉換位元組碼并生成新的類。不鼓勵直接使用ASM,因為它要求你必須對JVM内部結構包括class檔案的格式和指令集都很熟悉。
核心業務
public class NBAService {
public int add(String name,int id){
System.out.println("NBAService---- add----");
return id;
}
}
測試類
public static void main(String[] args) {
//目标對象:沒有接口
NBAService nbaService=new NBAService();
//建立代理對象:選擇cglib動态代理
NBAService proxyService= (NBAService) Enhancer.create(nbaService.getClass(),
new MethodInterceptor() {//回調函數編寫代理規則
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
try {
System.out.println("開始事務");
Object invoke = methodProxy.invokeSuper(o, objects);//核心
System.out.println("送出事務");
return invoke;
} catch (Exception e){
System.out.println("事務復原");
throw e;
} finally {
System.out.println("finally------------");
}
}
});
//代理對象幹活
int res=proxyService.add("huren",1001);
System.out.println(res);
}
結構化
代理對象工廠(建立代理對象)
public class CglibProxyFactory {
//目标對象
private NBAService nbaService;//沒有實作接口的
//切面
private AOP aop;//切面
/**
* 建立代理對象
* @param nbaService
* @param aop
* @return
*/
public Object getProxyInstance(NBAService nbaService,AOP aop){
return Enhancer.create(nbaService.getClass(),
new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
try {
aop.before();
Object o1 = methodProxy.invokeSuper(o, objects);
aop.after();
return o1;
} catch (Exception e){
aop.exception();
throw e;
} finally {
System.out.println("finally-----------");
}
}
});
}
}
3.3 使用細節對比
1,建立代理對象的方法
基于JDK
// 傳回指定接口的代理執行個體,該接口将方法調用分派給指定的調用處理程式。
Proxy.newProxyInstance(ClassLoader loader, 類<?>[] interfaces, InvocationHandler h);
Proxy.newProxyInstance(service.getClass().getClassLoader(),
service.getClass().getInterfaces(),
new InvocationHandler(){...}
);
基于CGLIB
Enhancer.create(service.getClass(), new MethodInterceptor(){...});
2,回調函數實作
基于JDK(回調函數InvocationHandler中重寫invoke方法)
invoke(Object proxy, Method method, Object[] args)
new InvocationHandler() {//回調函數 編寫代理規則
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
aop.before();
Object invoke = method.invoke(service, args);//核心業務
aop.after();
return invoke;
}catch (Exception e){
aop.exception();
e.printStackTrace();
throw e;
}finally {
aop.myFinally();
}
}
}
基于CGLIB(回調函數MethodInterceptor中重寫intercept方法)
intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
try {
aop.before();
Object o1 = methodProxy.invokeSuper(o, objects);
aop.after();
return o1;
}catch (Exception e){
aop.exception();
throw e;
} finally {
System.out.println("finally-----------");
}
}
}
3,核心業務處理
基于JDK
Object invoke = method.invoke(service, args);//核心業務
基于CGLIB
Object o1 = methodProxy.invokeSuper(o, objects);
4,SpringAOP介紹
Spring的AOP實作底層就是對上面的動态代理的代碼(Proxy.newProxyInstance、Enhancer.create)進行了封裝,封裝後我們隻需要對需要關注的部分進行代碼編寫,并通過配置的方式完成指定目标的方法增強。
4.1 AOP介紹
切面的三個關鍵因素:
- 1、切面的功能--切面能幹啥
- 2、切面的執行位置--使用Pointcut表示切面執行的位置
- 3、切面的執行時間--使用Advice表示時間,在目标方法之前還是之後執行。
先來介紹AOP的相關術語:
Target(目标對象):
要被增強的對象,一般是業務邏輯類的對象。比如service
Proxy(代理):
一個類被 AOP 織入增強後,就産生一個結果代理類。
Aspect(切面):
表示增強的功能,就是一些代碼完成的某個功能,非業務功能。是切入點和通知的結合。比如事務、日志等功能;
Joinpoint(連接配接點):
所謂連接配接點是指那些被攔截到的點。在Spring中,這些點指的是方法(一般是類中的業務方),因為Spring隻支援方法類型的連接配接點。
Pointcut(切入點):
切入點指聲明的一個或多個連接配接點的集合。通過切入點指定一組方法。
被标記為 final 的方法是不能作為連接配接點與切入點的。因為最終的是不能被修改的,不能被增強的。
Advice(通知/增強):
所謂通知是指攔截到 Joinpoint 之後所要做的事情就是通知。
通知定義了增強代碼切入到目标代碼的時間點,是目标方法執行之前執行,還是之後執行等。通知類型不同,切入時間不同。
通知的類型:前置通知,後置通知,異常通知,最終通知,環繞通知。
切入點定義切入的位置,通知定義切入的時間
Weaving(織入).:
是指把增強應用到目标對象來建立新的代理對象的過程。 spring 采用動态代理織入,而 AspectJ 采用編譯期織入和類裝載期織入。
4.2 AspectJ 對 AOP 的實作
對于 AOP 這種程式設計思想,很多架構都進行了實作。Spring 就是其中之一,可以完成面向切面程式設計。AspectJ 也實作了 AOP 的功能,且其實作方式更為簡捷而且還支援注解式開發。是以,Spring 又将AspectJ 的對于 AOP 的實作也引入到了自己的架構中。
在 Spring 中使用 AOP 開發時,一般使用 AspectJ 的實作方式
AspectJ 是一個優秀面向切面的架構,它擴充了 Java 語言,提供了強大的切面實作。
1,AspectJ的通知類型
AspectJ 中常用的通知有5種類型:前置通知、後置通知、環繞通知、異常通知、最終通知
2,AspectJ的切入點表達式
AspectJ 定義了專門的表達式用于指定切入點。 表達式的原型如下:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
- modifiers-pattern:通路權限類型
- ret-type-pattern: 傳回值類型
- declaring-type-pattern: 包名類名
- name-pattern(param-pattern) :方法名(參數類型和參數個數)
- throws-pattern :抛出異常類型
- ?表示可選的部分
以上表達式共 4 個部分。
execution(通路權限 方法傳回值 方法聲明(參數) 異常類型)
切入點表達式要比對的對象就是目标方法的方法名。是以,execution 表達式中就是方法的簽名。
PS:表達式中黑色文字表示可省略部分,各部分間用空格分開。在其中可以使用以下符号:
3,示例
execution(* com.kkb.service.*.*(..))
指定切入點為:定義在 service 包裡的任意類的任意方法。
execution(* com.kkb.service..*.*(..))
指定切入點為:定義在 service 包或者子包裡的任意類的任意方法。“..”出現在類名中時,後面必須跟 “*”,表示包、子包下的所有類。
execution(* com.kkb.service.IUserService+.*(..))
指定切入點為:IUserService 若為接口,則為接口中的任意方法及其所有實作類中的任意方法;若為類,則為該類及其子類中的任意方法。
5,SpringAOP的實作——注解方式
開發階段:關注核心業務和AOP代碼
運作階段:spring架構會在運作的時候将核心業務和AOP代碼通過動态代理的方式編織在一起
代理方式的選擇:是否實作了接口:有接口就選擇JDK動态代理;沒有就選擇CGLIB動态代理。
5.1 測試項目的基本結構
1,建立項目引入依賴
<dependencies>
<!--spring 核心依賴-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
<!--測試依賴-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!--編譯插件-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
2,建立spring配置檔案spring.xml引入限制
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--在beans标簽中 引入AOP和context限制-->
3,建立核心業務類
核心業務接口
public interface IService {
void add(int id,String name);
boolean update(int num);
}
核心業務類
/**
* 核心業務類
*/
public class TeamService implements IService{
@Override
public void add(int id, String name) {
//int num=10/0;
System.out.println("TeamService---- add----");
}
@Override
public boolean update(int num) {
System.out.println("TeamService---- update----");
if(num>666){
return true;
}
return false;
}
}
4,定義切面類
/**
* 切面類
*/
public class MyAspect {
public void before() {
System.out.println("前置通知:在目标方法執行之前被調用的通知");
}
public void after() {
System.out.println("後置通知:在目标方法執行之後被調用的通知");
}
public void exception() {
System.out.println("異常通知:在目标方法執行出現異常被調用的通知");
}
public void myFinally() {
System.out.println("最終通知:無論是否出現異常都是最後被調用的通知");
}
}
5,定義測試方法
public class TeamServiceTest {
@Test
public void test01() {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
TeamService teamService = (TeamService) ac.getBean("teamService");
teamService.add(1001, "永凍隊");
System.out.println("-------------");
boolean update = teamService.update(1002);
System.out.println("掃描結果:" + update);
}
}
運作
原因:沒有實作依賴注入
6,通過注解+掃描包的方式實作依賴注入
<context:component-scan base-package="com.xxy.service"/>
運作
核心業務沒問題,但是沒有将切面編織在一起
7,切面對象的建立也交給spring容器處理,并且通過注解标記為切面類,同樣也需要通過添加掃描包的方式實作依賴注入
<context:component-scan base-package="com.xxy.service,com.xxy.aop"/>
運作
仍然沒有将切面内容與核心業務編織在一起,因為還沒有定義切面方法切入業務的位置
5.2 前置通知@Before
8,通過注解+切入點表達式指定切入點
運作,發現仍然沒有(這裡就不貼圖了),原因:沒有開啟AspectJ注解的自動代理
9,開啟AspectJ注解的自動代理
運作測試
5.3 連接配接點JoinPoint
10,通過連接配接點JoinPoint,獲得攔截到的方法名稱及參數清單
5.4 後置通知@AfterReturning
@AfterReturning在切入點return内容之後切入内容(可以用來對處理傳回值做一些加工處理)
11,在切面類中添加後置通知注解
5.5 異常通知@AfterThrowing
12,添加異常通知注解
5.6 最終通知@After
5.7 環繞通知@Around
13,添加環繞通知(注意參數)
5.8 切入點表達式@Pointcut
為方法添加@Pointcut注解,該方法就可以替代上面的切入點表達式,進而實作表達式複用的效果。
比較簡單,直接上代碼了
/**
* 切面類
*/
@Component //切面對象的建立權限依然交給spring容器
@Aspect //aspectj 架構的注解 辨別目前類是一個切面類
public class MyAspect{
/**
* 當較多的通知增強方法使用相同的 execution 切入點表達式時,編寫、維護均較為麻煩。
* AspectJ 提供了@Pointcut 注解,用于定義 execution 切入點表達式。
* 其用法是,将@Pointcut 注解在一個方法之上,以後所有的 execution 的 value 屬性值均
* 可使用該方法名作為切入點。代表的就是@Pointcut 定義的切入點。
* 這個使用@Pointcut 注解方法一般使用 private 的辨別方法,即沒有實際作用的方法。
*/
@Pointcut("execution(* com.kkb.service..*.*(..))")
private void pointCut(){
}
@Pointcut("execution(* com.kkb.service..*.add*(..))")
private void pointCut2(){
}
/**
* 聲明前置通知
* @param jp
*/
@Before("pointCut()")
public void before(JoinPoint jp){
System.out.println("前置通知:在目标方法執行之前被調用的通知");
String name = jp.getSignature().getName();
System.out.println("攔截的方法名稱:"+name);
Object[] args = jp.getArgs();
System.out.println("方法的參數格式:"+args.length);
System.out.println("方法參數清單:");
for (Object arg : args) {
System.out.println("\t"+arg);
}
}
}
6,SpringAOP的實作——XML方式
将上面注解實作的配置,轉移到spring的配置檔案(比如Spring.xml)中
1,将切面類中關于AspectJ的AOP注解全部删除(保留@AspectJ注解),其中的方法保留參數
/**
* 切面類
*/
@Component // 切面對象的建立權限依然交給spring容器
@Aspect // aspectj架構的注解 辨別目前類是一個切面類
public class MyAspect {
public void before(JoinPoint jp) {
System.out.println("前置通知:在目标方法執行之前被調用的通知");
// 擷取攔截到的方法名稱
String name = jp.getSignature().getName();
System.out.println("攔截的方法名稱:"+name);
// 擷取攔截到的方法的參數個數和清單
Object[] args = jp.getArgs();
System.out.println("方法的參數格式:"+args.length);
System.out.println("方法參數清單:");
for (Object arg : args) {
System.out.println("\t"+arg);
}
}
public boolean after(Object result) {
if (result != null){
boolean res = (boolean)result;
result = false;
}
System.out.println("後置通知:在目标方法執行之後被調用的通知,result: " + result);
return (boolean) result;
}
public void exception(JoinPoint jp, Throwable ex) {
System.out.println("異常通知:在目标方法執行出現異常的時候才會别調用的通知,否則不執行");
System.out.println(jp.getSignature()+"方法出現異常,異常資訊是:"+ex.getMessage());
}
public void myFinally() {
System.out.println("最終通知:無論是否出現異常都是最後被調用的通知");
}
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("環繞方法---目标方法的執行之前");
Object proceed = pjp.proceed();
System.out.println("環繞方法---目标方法的執行之後");
return proceed;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.xxy.service,com.xxy.aop"/>
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!--xml方式實作aop-->
<aop:config>
<!--聲明切入點的表達式,可以聲明多個-->
<aop:pointcut id="pt1" expression="execution(* com.xxy.service..*.add*(..))"/>
<aop:pointcut id="pt2" expression="execution(* com.xxy.service..*.update*(..))"/>
<aop:pointcut id="pt3" expression="execution(* com.xxy.service..*.del*(..))"/>
<aop:pointcut id="pt4" expression="execution(* com.xxy.service..*.insert*(..))"/>
<aop:aspect ref="myAspect">
<aop:before method="before" pointcut="execution(* com.xxy.service..*.*(..))"></aop:before>
<aop:after-returning method="after" pointcut-ref="pt2" returning="result"></aop:after-returning>
<aop:after-throwing method="exception" pointcut-ref="pt1" throwing="ex"></aop:after-throwing>
<aop:after method="myFinally" pointcut-ref="pt1"></aop:after>
<aop:around method="around" pointcut-ref="pt2"></aop:around>
</aop:aspect>
</aop:config>
</beans>