Spring AOP
AspectJ:(Java社區裏最完整最流行的AOP框架)
spring自身也有一套AOP框架,但相比較於AspectJ,更推薦AspectJ
在Spring2.0以上版本中,可以使用基於AspectJ註解或基於XML配置的AOP。
基於AspectJ註解:
要在Spring中聲明AspectJ切面,只需要在IOC容器中將切面聲明爲Bean實例,當在Spring IOC容器中初始化AspectJ切面之後,
在AspectJ註解中,切面只是一個帶有@AspectJ註解的Java類。
通知是標註有某種註解的的簡單的java方法
-@After:後置通知:在目標方法執行之後執行,無論是否發生異常
-@AfterReturning:返回通知,在目標方法返回結果之後執行
-@AfterThrowing:異常通知,在目標方法拋出異常之後通知。
-@Around 環繞通知,圍繞着目標方法執行。
基於AspectJ註解添加前置通知步驟:
1)加入jar包
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar
commons-logging-1.1.3.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
2)在配置文件中加入aop和context的命名空間
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
3)基於註解的方式
1.在配置文件里加入如下配置:
<!-- 配置自動掃描的包 -->
<context:component-scan base-package="com.wul.spring.aop.impl"></context:component-scan>
<!-- 使AspectJ註解起作用:自動爲匹配的類生成代理對象 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
(當Spring IOC 容器偵測到Bean配置文件中的<aop:aspectj-autoproxy>元素時,會自動與AspectJ切面匹配的Bean創建代理。)2.聲明一個切面的類並把橫切關注點代碼抽象到切面的類中:
I.切面首先是一個IOC中的bean,即加入@Component註釋
II.切面還需要加入@Aspect
3.在類中聲明各種通知
I.聲明一個方法II.在方法前加入@Before註解
III.利用方法簽名編寫AspectJ切入點表達式
關於方法簽名編寫的切點表達式:
方法名:String methodName = joinPoint.getSignature().getName();
參數值:List<Object> args = Arrays.asList(joinPoint.getArgs());
前置通知:
//聲明該方法是一個前置通知:在目標方法開始之前執行
// @Before("execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.add(int ,int))")
@Before("execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.*(int ,int))")
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
List<Object> args = Arrays.asList(joinPoint.getArgs());
System.out.println("The method "+methodName +" begins with "+ args);
}
//後置通知:在目標方法之後(無論是否發生異常),執行的通知,
//在後置通知中還不能訪問目標方法執行的結果。執行結果須在返回通知中訪問。
@After("execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.*(int ,int))")
public void afterMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method "+ methodName+" ends");
}
返回通知:
//返回通知:在目標方法正常結束執行後的通知
//返回通知是可以訪問到目標方法的返回值的
@AfterReturning(value="execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.*(int,int))"
, returning = "result")
public void afterRunningMethod(JoinPoint joinPoint , Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method "+ methodName+" ends with the Result "+ result);
}
異常通知:
//在目標方法出現異常時會執行的代碼,
//可以訪問到異常對象,且可以指定在出現特定異常時在執行通知代碼
//如下面Exception ex,若是指定爲NullpointerException ex就不會執行通知代碼
@AfterThrowing(value="execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.*(int,int))"
, throwing="ex")
public void afterThrowingMethod(JoinPoint joinPoint,Exception ex){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method "+ methodName+"occurs exception:"+ex);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
String methodName = method.getName();
//日誌
System.out.println("The method "+methodName+" begin with "+Arrays.asList(args));
//執行方法
Object result = null;
try{
//前置通知
result = method.invoke(target, args);
//返回通知,可以訪問到方法的返回通知
}catch(Exception e){
e.printStackTrace();
//異常通知:可以訪問到方法出現的異常
}
//後置通知:因爲方法可能出錯,所以訪問不到方法的返回值。
//日誌
System.out.println("The method "+methodName + " ends with "+result);
return result;
}
環繞通知:(類似於動態代理的過程)
package com.wul.spring.aop.impl;
import java.util.Arrays;
import java.util.List;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
//把這個類聲明爲一個切面:需要把該類放入到IOC容器中,再聲明爲一個切面
@Aspect
@Component
public class LogginAspect {
//聲明該方法是一個前置通知:在目標方法開始之前執行
// @Before("execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.add(int ,int))")
@Before("execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.*(int ,int))")
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
List<Object> args = Arrays.asList(joinPoint.getArgs());
System.out.println("The method "+methodName +" begins with "+ args);
}
//後置通知:在目標方法之後(無論是否發生異常),執行的通知,
//在後置通知中還不能訪問目標方法執行的結果。執行結果須在返回通知中訪問。
@After("execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.*(int ,int))")
public void afterMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method "+ methodName+" ends");
}
//返回通知:在目標方法正常結束執行後的通知
//返回通知是可以訪問到目標方法的返回值的
@AfterReturning(value="execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.*(int,int))"
, returning = "result")
public void afterRunningMethod(JoinPoint joinPoint , Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method "+ methodName+" ends with the Result "+ result);
}
//在目標方法出現異常時會執行的代碼,
//可以訪問到異常對象,且可以指定在出現特定異常時在執行通知代碼
//如下面Exception ex,若是指定爲NullpointerException ex就不會執行通知代碼
@AfterThrowing(value="execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.*(int,int))"
, throwing="ex")
public void afterThrowingMethod(JoinPoint joinPoint,Exception ex){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method "+ methodName+"occurs exception:"+ex);
}
//壞繞通知:需要攜帶ProceedingJoinPoint類型的參數
//環繞通知類似於動態代理的全過程:ProceedingJoinPoint類型的參數可以決定是否執行目標方法
//且環繞通知必須有返回值,返回值即目標方法的返回值。
@Around("execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.*(int,int))")
public Object aroundMethod(ProceedingJoinPoint pjd){
Object result = null;
String methodName = pjd.getSignature().getName();
Object args = Arrays.asList(pjd.getArgs());
//執行目標方法
try {
//前置通知
System.out.println("Arround:The method "+methodName +" begins with "+ args);
result = pjd.proceed();
//後置通知
System.out.println("Arround:The method "+ methodName+" ends");
} catch (Throwable e) {
e.printStackTrace();
//異常通知
System.out.println("Arround:The method "+ methodName+"occurs exception:"+e);
//throw new RuntimeException(e);
//不拋出異常的話,異常就被上面抓住,執行下去,返回result,result值爲null,轉換爲int
}
//返回通知
System.out.println("Arround:The method "+ methodName+" ends with the Result "+ result);
//return 100;
return result;
}
}
AtithmeticCalculator.java
package com.wul.spring.aop.impl;
public interface AtithmeticCalculator {
int add(int i, int j);
int sub(int i, int j);
int mul(int i, int j);
int div(int i, int j);
}
AtithmeticCalculatorImpl.java
package com.wul.spring.aop.impl;
import org.springframework.stereotype.Component;
@Component
public class AtithmeticCalculatorImpl implements AtithmeticCalculator {
@Override
public int add(int i, int j) {
int result = i + j;
return result;
}
@Override
public int sub(int i, int j) {
int result = i - j;
return result;
}
@Override
public int mul(int i, int j) {
int result = i * j;
return result;
}
@Override
public int div(int i, int j) {
int result = i / j;
return result;
}
}
LogginAspect.java
package com.wul.spring.aop.impl;
import java.util.Arrays;
import java.util.List;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
//把這個類聲明爲一個切面:需要把該類放入到IOC容器中,再聲明爲一個切面
@Aspect
@Component
public class LogginAspect {
//聲明該方法是一個前置通知:在目標方法開始之前執行
// @Before("execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.add(int ,int))")
@Before("execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.*(int ,int))")
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
List<Object> args = Arrays.asList(joinPoint.getArgs());
System.out.println("The method "+methodName +" begins with "+ args);
}
//後置通知:在目標方法之後(無論是否發生異常),執行的通知,
//在後置通知中還不能訪問目標方法執行的結果。執行結果須在返回通知中訪問。
@After("execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.*(int ,int))")
public void afterMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method "+ methodName+" ends");
}
//返回通知:在目標方法正常結束執行後的通知
//返回通知是可以訪問到目標方法的返回值的
@AfterReturning(value="execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.*(int,int))"
, returning = "result")
public void afterRunningMethod(JoinPoint joinPoint , Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method "+ methodName+" ends with the Result "+ result);
}
//在目標方法出現異常時會執行的代碼,
//可以訪問到異常對象,且可以指定在出現特定異常時在執行通知代碼
//如下面Exception ex,若是指定爲NullpointerException ex就不會執行通知代碼
@AfterThrowing(value="execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.*(int,int))"
, throwing="ex")
public void afterThrowingMethod(JoinPoint joinPoint,Exception ex){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method "+ methodName+"occurs exception:"+ex);
}
//壞繞通知:需要攜帶ProceedingJoinPoint類型的參數
//環繞通知類似於動態代理的全過程:ProceedingJoinPoint類型的參數可以決定是否執行目標方法
//且環繞通知必須有返回值,返回值即目標方法的返回值。
@Around("execution(public int com.wul.spring.aop.impl.AtithmeticCalculator.*(int,int))")
public Object aroundMethod(ProceedingJoinPoint pjd){
Object result = null;
String methodName = pjd.getSignature().getName();
Object args = Arrays.asList(pjd.getArgs());
//執行目標方法
try {
//前置通知
System.out.println("Arround:The method "+methodName +" begins with "+ args);
result = pjd.proceed();
//後置通知
System.out.println("Arround:The method "+ methodName+" ends");
} catch (Throwable e) {
e.printStackTrace();
//異常通知
System.out.println("Arround:The method "+ methodName+"occurs exception:"+e);
//throw new RuntimeException(e);
//不拋出異常的話,異常就被上面抓住,執行下去,返回result,result值爲null,轉換爲int
}
//返回通知
System.out.println("Arround:The method "+ methodName+" ends with the Result "+ result);
//return 100;
return result;
}
}
applicationContext.xml
<?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 http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- 配置自動掃描的包 -->
<context:component-scan base-package="com.wul.spring.aop.impl"></context:component-scan>
<!-- 使AspectJ註解起作用:自動爲匹配的類生成代理對象 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
Main.java
package com.wul.spring.aop.impl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
//1.創建Spring的IOC容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.從IOC容器中獲取bean的實例
AtithmeticCalculator arithmeticCalculator = ctx.getBean(AtithmeticCalculator.class);
//3.使用bean
int result = arithmeticCalculator.add(3,6);
System.out.println("result: "+result);
result = arithmeticCalculator.div(10,5);
System.out.println("result: "+result);
result = arithmeticCalculator.div(10,0);
}
}