目錄
一、爲什麼要用aop
首先來描述一個問題:
在項目的開發中經常會出現代碼冗餘的問題,就像下面的代碼,ComputerService實現類中add()方法和div()方法都要執行相同的輸出語句,aop就是用來解決這個問題。
package club.affengkuang.computer.service;
/**
* 服務層計算器接口
*
* @author
*/
public interface IComputerService {
int add(int a,int b);
int div(int a,int b);
}
package club.affengkuang.computer.service;
import org.springframework.stereotype.Service;
/**
* 服務層計算器接口實現類
*
* @author
*/
@Service
public class ComputerService implements IComputerService{
public int add(int a, int b) {
System.out.println(this.getClass().getName()+":The add method begins.");
System.out.println(this.getClass().getName()+":Parameters of the add method: ["+a+","+b+"]");
int result = a+b;
return result;
}
public int div(int a, int b) {
System.out.println(this.getClass().getName()+":The add method begins.");
System.out.println(this.getClass().getName()+":Parameters of the add method: ["+a+","+b+"]");
int result = a/b;
return result;
}
}
二、aop的使用
首先在application.xml文件添加如下兩個標籤,其中context:component-scan標籤的作用見博客:詳述context:component-scan作用
而第十二行的標籤作用是將@Aspect註解修飾的類變成一個代理類,生成的這個代理類便可解決上面所說的代碼冗餘的問題。
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<context:component-scan base-package="club.affengkuang"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
然後再來創建如下ComputerAOP類,該類就是上面說的代理類:
第九行:用@Aspect註解修飾,使該類能被aop:aspectj-autoproxy標籤獲取
第十行:用@Component註解修飾,使該類被context:component-scan標籤掃描到IOC容器中
第十四行:@Before註解修飾,其中的execution表達式指定了一個目標類"club.affengkuang.computer.service.ComputerService",而aop:aspectj-autoproxy標籤可以獲取該表達式指定的目標類,並將ComputerAOP類變成該目標類的代理類;而在目標類中還指定了返回值爲int的public方法,表示before()方法將會在目標方法之前執行。
第十六行:用getArgs()方法可獲取目標方法中傳入的參數,並形成一個Object類數組
第十七、十八行:獲取所調用的方法的方法名(即:add、div)
package club.affengkuang.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class ComputerAOP {
//在目標方法執行之前執行
@Before("execution(public int club.affengkuang.computer.service.ComputerService.*(..))")
public void before(JoinPoint jp) {
Object [] args = jp.getArgs();
Signature signature =jp.getSignature();
String name = signature.getName();
System.out.println(this.getClass().getName()+":The "+name+" method begins.");
System.out.println(this.getClass().getName()+":Parameters of the "+name+" method: ["+args[0]+","+args[1]+"]");
}
}
有了代理類,在add()和div()方法中的兩句輸出語句就可以省略了,在測試類中創建ComputerService類對象時,IOC中會自動先創建一個ComputerAOP代理類對象,它們會在add()和div()方法被調用時,先調用before()方法
在如下測試類中將computerService對象打印,就會發現創建的並不是ComputerService類對象而是代理類對象:
package club.affengkuang.test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import club.affengkuang.computer.service.IComputerService;
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
IComputerService computerService = applicationContext.getBean(IComputerService.class);
System.out.println(computerService.getClass().getName());
int result = computerService.div(1,2);
System.out.println(result);
applicationContext.close();
}
}
三、aop的幾種類型
1.@Before
第16-25行:就是上面演示的before()方法,作用是在目標方法被調用時,在其之前執行。
2.@After
第27-33行:顧名思義,該方法會在目標方法被調用結束後執行
注意:該方法無論目標方法是否出現異常,都會執行
3.@AfterReturning
第35-41行:該方法會在目標方法返回結果後執行。
該方法與上一個@After的區別就是,如果目標方法出現異常, 則該方法無法執行
其中,returning=“result”獲取的就是目標方法的返回值
4.@AfterThrowing
第43-46行:該方法會在目標方法拋出異常時執行
其中,e就是目標方法拋出的異常
package club.affengkuang.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class ComputerAOP {
//在目標方法執行之前執行
@Before("execution(public int club.affengkuang.computer.service.ComputerService.*(..))")
public void before(JoinPoint jp) {
Object [] args = jp.getArgs();
Signature signature =jp.getSignature();
String name = signature.getName();
System.out.println(this.getClass().getName()+":The "+name+" method begins.");
System.out.println(this.getClass().getName()+":Parameters of the "+name+" method: ["+args[0]+","+args[1]+"]");
}
//目標方法執行完;無論目標方法是否出現異常,都會執行
@After("execution(public int club.affengkuang.computer.service.ComputerService.*(..))")
public void after(JoinPoint jp) {
Signature signature = jp.getSignature();
String name = signature.getName();
System.out.println(this.getClass().getName()+":The "+name+" method ends.");
}
//目標方法返回結果時執行;如果目標方法出現異常, 則無法執行
@AfterReturning(value = "execution(public int club.affengkuang.computer.service.ComputerService.*(..))",returning="result")
public void afterReturning(JoinPoint jp,Object result) {
Signature signature = jp.getSignature();
String name = signature.getName();
System.out.println(this.getClass().getName()+":Result of the "+name+" method:"+result);
}
@AfterThrowing(value = "execution(public int club.affengkuang.computer.service.ComputerService.*(..))",throwing="e")
public void afterThrowing(JoinPoint jp, Exception e) {
System.out.println(e.getMessage());
}
}
5.@Around
該類型的方法與前幾個不太一樣,它一個方法可以代替上面所有方法:
第15-17行:和前面的代碼一樣,獲取目標方法的參數列表及方法名
第18行:getTarget()可獲取目標類創建的對象
第25行:pjp.proceed()方法的作用是調用目標方法,這也是@Around方法的特色之處,有了它,想在目標方法被調用之前執行的代碼就寫在該行之前,想在目標方法被調用之後執行的代碼就寫在該行之後,想在目標方法拋出異常時執行的代碼,就寫在try-catch中
package club.affengkuang.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class ComputerAOP {
@Around(value = "execution(public int club.affengkuang.computer.service.ComputerService.*(..))")
public Object around(ProceedingJoinPoint pjp) {
Object [] args = pjp.getArgs();//傳入目標方法的參數
Signature signature = pjp.getSignature();
String name = signature.getName();
Object object = pjp.getTarget();//目標類創建的對象
System.out.println("#################"+object.getClass().getName());
try {
Object result=null;
try {
System.out.println(this.getClass().getName()+":The "+name+" method begins.");
System.out.println(this.getClass().getName()+":Parameters of the "+name+" method: ["+args[0]+","+args[1]+"]");
result = pjp.proceed();//調用目標方法,並且返回目標方法的結果
} finally {
System.out.println(this.getClass().getName()+":The "+name+" method ends.");
}
System.out.println(this.getClass().getName()+":Result of the "+name+" method:"+result);
return result;
} catch (Throwable e) {
System.out.println(e.getMessage());
}
return -1;
}
}