消息通知通常有5種:
- 前置通知
- 傳回通知
- 異常通知
- 後置通知
- 環繞通知
1.采用注解的方式
這裡使用簡單電腦的例子作為示範:
聲明一個Calculator的接口:
public interface Calculator {
int add(int i,int j);
int sub(int i,int j);
int mul(int i,int j);
int div(int i,int j);
}
這裡隻做簡單的加減乘除運算。
實作該接口:
@Component
public class CalculatorImpl implements Calculator{
@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;
}
}
開始有一個@Component用來執行個體化一個bean對象。
然後我們想在每個方法執行前輸出執行方法的名稱和參數就需要配置一個切面對象了。
//把這個類聲明為一個切面:需要把該類放入到IOC容器中,再聲明為一個切面
@Order //指定切面的優先級,值越小級别越高
@Aspect
@Component
public class LoggingAspect {
//聲明該方法是一個前置通知
@Before("execution(public int com.spring.aop.impl.Calculator.*(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);
}
}
@Order是用來指定切面的優先級的,如果聲明了多個切面,則可以根據@Order指定的值來判斷執行的順序,預設值越小的優先級越高。
@Aspect就是表面該類是一個切面對象。
此時隻要是Calculator中的方法被執行前就會輸出相應的參數資訊了。
當然,如果我們不需要所有的方法被執行前都輸出該資訊,我們同樣可以指定Calculator中特定的方法,隻需要把@Before中的參數修改下:
@Before("execution(public int com.spring.aop.impl.Calculator.add(int, int))")
這樣就隻在執行add方法時才會有提示資訊。
同樣,我們也可以實作後置,傳回,異常,環繞等通知。
後置通知:
//執行完方法之後掉用,無論是否存在異常
@After("execution(public int com.spring.aop.impl.Calculator.*(int, int))")
public void afterMethod(JoinPoint joinPoint) {
String methodName=joinPoint.getSignature().getName();
System.out.println("The method "+methodName+" end ");
}
傳回通知:
@AfterReturning(value="execution(public int com.spring.aop.impl.Calculator.*(int, int))",
returning="result")
public void afterReturning(JoinPoint joinPoint,Object result) {
String methodName=joinPoint.getSignature().getName();
System.out.println("The method "+methodName+" result: "+result);
}
異常通知:
@AfterThrowing(value="execution(public int com.spring.aop.impl.Calculator.*(int, int))",
throwing="ex")
public void afterThrowing(JoinPoint joinPoint,Exception ex) {
String methodName=joinPoint.getSignature().getName();
System.out.println("The method "+methodName+" Exception: "+ex);
}
環繞通知:
//類似動态代理的全過程:ProceedingJoinPoint類型的參數可以決定是否執行目标方法
//必須有傳回值,傳回值即為目标方法的傳回值
@Around("execution(public int com.spring.aop.impl.Calculator.*(int, int))")
public Object aroundMethod(ProceedingJoinPoint p) {
System.out.println("aroundMtethod");
Object result=null;
try {
//前置
result=p.proceed();
//後置
} catch (Throwable e) {
// TODO Auto-generated catch block
//異常
e.printStackTrace();
}
//傳回
return result ;
}
環繞通知是功能最強大的通知,但也是最複雜的通知,因為它也包含了其它的四種通知,如果所需要的通知比較簡單基礎就使用其餘四種通知就已經足夠了。
配置bean
<?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/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="com.spring.aop.impl"></context:component-scan>
<!-- 使AspjectJ注解起作用:自動為比對的類生成代理對象 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
記住需要選中bean的namespace中的aop和context 。
測試:
public static void main(String[] args) {
//1.建立IOC容器
ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
Calculator calculator=ctx.getBean(Calculator.class);
int result=calculator.add(1, 2);
System.out.println(result);
result=calculator.div(4, 2);
System.out.println(result);
}
輸出結果:
aroundMtethod
The method add begins with[1, 2]
The method add end
The method add result: 3
3
aroundMtethod
The method div begins with[4, 2]
The method div end
The method div result: 2
2
2.使用aop
基本的類和使用注解的都是相同的,隻需要去除那些注解就行,然後修改下配置檔案。
<?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/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">
<bean id="t" class="com.spring.aop.xml.CalculatorImpl"></bean>
<!-- 配置切面的bean -->
<bean id="loggingAspect" class="com.spring.aop.xml.LoggingAspect"></bean>
<!-- 配置AOP -->
<aop:config>
<aop:pointcut expression="execution(* com.spring.aop.xml.Calculator.*(int,int))" id="pointcut"/>
<aop:aspect ref="loggingAspect" order="2">
<aop:before method="beforeMethod" pointcut-ref="pointcut"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="ex"/>
</aop:aspect>
</aop:config>
</beans>
預設都是基于接口的代理,也就是aop:pointcut的expression中設定的往往都是關于接口的,當然也可以設定基于類的代理,隻需要将<aop:config>的proxy-target-class屬性設定為true就行了,預設是false。