增强类型
增强又称通知(Advice),指代植入切面的事件,增强(通知)有以下几种类型:
- 前置(before):在方法执行之前执行通知
- 后置(after):在方法执行后通知,无论其结果如何
- 后置返回(after-returning):只有方法成功完成后才能在方法执行和执行通知
- 后置异常(after-throwing):只有方法抛出异常而退出方法执行后才能运行通知
- 环绕(around):在调用通知方法之前和之后运行通知
通过实现接口的方式实现增强
首先要明确我们实现上述五种通知需要实现哪些接口。
通知类型 | 接口 |
---|---|
Before | org.springframework.aop.MethodBeforeAdvice |
AfterReturn | org.springframework.aop.AfterReturningAdvice |
AfterThrow | org.springframework.aop.ThrowsAdvice |
Around | org.aopalliance.intercept.MethodInterceptor |
//前置增强:BeforeAdvisor.java
package com.aop.advisor;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class BeforeAdvisor implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("--- 前置通知 --");
}
}
//后置返回增强:AfterReturnAdvisor.java
package com.aop.advisor;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class AfterReturnAdvisor implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("--- 后置返回通知 ----");
}
}
//异常增强:ThrowsAdvisor.java
package com.aop.advisor;
import org.springframework.aop.ThrowsAdvice;
public class ThrowsAdvisor implements ThrowsAdvice {
public void afterThrowing(Exception e) throws Throwable{
System.out.println("-- 异常通知 --");
}
}
配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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">
<bean id="service" class="com.aop.service.Service"></bean>
<bean id="beforeAdvisor" class="com.aop.advisor.BeforeAdvisor"></bean>
<bean id="afterReturnAdvisor" class="com.aop.advisor.AfterReturnAdvisor"></bean>
<bean id="throwsAdvisor" class="com.aop.advisor.ThrowsAdvisor"></bean>
<aop:config>
<aop:pointcut id="test" expression="execution(* com.aop.service.*.*(..))" />
<aop:advisor advice-ref="beforeAdvisor" id="beforeAdvice" pointcut-ref="test"/>
<aop:advisor advice-ref="afterReturnAdvisor" id="afterReturnAdvice" pointcut-ref="test"/>
<aop:advisor advice-ref="throwsAdvisor" id="throwsAdvice" pointcut-ref="test"/>
</aop:config>
</beans>
测试用例如下:
//Service.java
package com.aop.service;
public class Service {
public void print(){
System.out.println("service方法被调用");
}
public void error(){
int a =1/0;
}
}
//TestMain.java
package com.aop;
import com.aop.service.Service;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestMain {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-mvc.xml");
Service service = (Service) ac.getBean("service");
service.print();
service.error();
}
}
测试结果如下:
通过AspectJ实现增强
我们需要创建描述增强的类。
//AspectJAdvisor.java
package com.aop.advisor;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class AspectJAdvisor {
public void before(JoinPoint joinPoint){
String res = "[+]前置通知 targetObject:" + joinPoint.getTarget()
+" kind:" + joinPoint.getKind();
System.out.println(res);
}
public void after(JoinPoint joinPoint){
String res = "[+]后置通知 targetObject:" + joinPoint.getTarget()
+" kind:" + joinPoint.getKind();
System.out.println(res);
}
public void afterReturn(JoinPoint joinPoint){
String res = "[+]后置返回通知 targetObject:" + joinPoint.getTarget()
+" kind:" + joinPoint.getKind();
System.out.println(res);
}
public void afterThrow(JoinPoint joinPoint){
String res = "[+]后置抛错通知 targetObject:" + joinPoint.getTarget()
+" kind:" + joinPoint.getKind();
System.out.println(res);
}
/**
* ProceedingJoinPoint只支持Around Advice
* @param joinPoint
*/
public void around(ProceedingJoinPoint joinPoint){
String res = "[+]环绕前置通知 targetObject:" + joinPoint.getTarget()
+" kind:" + joinPoint.getKind();
System.out.println(res);
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
String ret = "[+]环绕后置通知 targetObject:" + joinPoint.getTarget()
+" kind:" + joinPoint.getKind();
System.out.println(ret);
}
}
配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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">
<bean id="service" class="com.aop.service.Service"></bean>
<bean id="aspectJAdvisor" class="com.aop.advisor.AspectJAdvisor"></bean>
<aop:config>
<aop:aspect ref="aspectJAdvisor">
<aop:pointcut id="test" expression="execution(* com.aop.service.*.*(..))"/>
<aop:before method="before" pointcut-ref="test"/>
<aop:after method="after" pointcut-ref="test"/>
<aop:after-returning method="afterReturn" pointcut-ref="test"/>
<aop:after-throwing method="afterThrow" pointcut-ref="test"/>
<aop:around method="around" pointcut-ref="test"/>
</aop:aspect>
</aop:config>
</beans>
测试用例继续使用通过接口实现增强中的用例。我们得出的结果如下:
使用AspectJ+注解的方式实现增强
package com.aop.advisor;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class AspectJAdvisor {
@Pointcut("execution(* com.aop.service.Service.print())")
private void pointCutOnPrint(){}
@Pointcut("execution(* com.aop.service.Service.error())")
private void pointCutOnError(){}
@Before("pointCutOnPrint()")
public void before(JoinPoint joinPoint){
String res = "[+]前置通知 targetObject:" + joinPoint.getTarget()
+" kind:" + joinPoint.getKind();
System.out.println(res);
}
@After("pointCutOnPrint()")
public void after(JoinPoint joinPoint){
String res = "[+]后置通知 targetObject:" + joinPoint.getTarget()
+" kind:" + joinPoint.getKind();
System.out.println(res);
}
@AfterReturning("pointCutOnPrint()")
public void afterReturn(JoinPoint joinPoint){
String res = "[+]后置返回通知 targetObject:" + joinPoint.getTarget()
+" kind:" + joinPoint.getKind();
System.out.println(res);
}
@AfterThrowing("pointCutOnError()")
public void afterThrow(JoinPoint joinPoint){
String res = "[+]后置抛错通知 targetObject:" + joinPoint.getTarget()
+" kind:" + joinPoint.getKind();
System.out.println(res);
}
/**
* ProceedingJoinPoint只支持Around Advice
* @param joinPoint
*/
@Around("pointCutOnPrint()")
public void around(ProceedingJoinPoint joinPoint){
String res = "[+]环绕前置通知 targetObject:" + joinPoint.getTarget()
+" kind:" + joinPoint.getKind();
System.out.println(res);
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
String ret = "[+]环绕后置通知 targetObject:" + joinPoint.getTarget()
+" kind:" + joinPoint.getKind();
System.out.println(ret);
}
}
配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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">
<context:component-scan base-package="com"></context:component-scan>
<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"></aop:aspectj-autoproxy>
<bean id="service" class="com.aop.service.Service"></bean>
</beans>
测试用例不变,结果如图