ProxyFactory代理工厂提供了基于切面构造代理对象的能力,Spring框架结合IOC对此进行了一层封装以适应多种场景。封装后为用户提供了一套Spring风格的“API”(使用方式)。
1 xml配置方式
引入AOP的schema:
<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"
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.3.xsd">
1.1 使用aop:aspect标签
定义目标类:
@Slf4j
public class ApplicationRunner {public void run() {
LOGGER.info("exec...");}
}
引入增强类:
@Slf4j
public class EnhanceLog {public void beforeExec() {
LOGGER.info("before exec");}public void afterExec() {
LOGGER.info("after exec");}
}
在xml文件中配置AOP:
<aop:config><aop:pointcut id="runnerExecPc" expression="execution(* *.run(..))"/><aop:aspect ref="enhanceLog"><aop:before method="beforeExec" pointcut-ref="runnerExecPc"/><aop:after method="afterExec" pointcut-ref="runnerExecPc"/></aop:aspect>
</aop:config>
1.2:使用aop:advisor标签
引入增强类LogAdvice,需要实现org.aopalliance.intercept.MethodInterceptor接口:
@Slf4j
public class LogAdvice implements MethodInterceptor {@Overridepublic Object invoke(MethodInvocation methodInvocation) throws Throwable {
LOGGER.info("before intercept exec");
methodInvocation.proceed();
LOGGER.info("after intercept exec");return null;}
}
在xml文件中配置AOP:
<aop:config><aop:pointcut id="runnerExecPc" expression="execution(* *.run(..))"/><aop:advisor advice-ref="logAdvice" pointcut-ref="runnerExecPc"/></aop:config>
其中:advisor将所有的逻辑都封装在了MethodInterceptor的invoke方法中,通过方法完成增强;aspect通过配置对外展示需要增强逻辑,而不需要实现MethodInterceptor等Advice系列接口。相对而言,aspect的代码侵入性较低。
2 注解方式
Spring通过整合AspectJ为AOP提供了注解形式的使用方式;因此使用注解时,需要添加对aspectjweaver的依赖(由org.aspectj提供)。
2.1 切面注解
@Aspect
注解在类上用于标记切面类;其他注解都可以添加在该类中的方法上。
2.2 增强注解
@Before @After @Around @AfterThrowing @AfterReturing被增强注解的方法内容作为增强。
@Before表示前置增强,@AfterReturing表示后置增强,@Around表示环绕增强,@AfterThrowing表示异常抛出增强,@After表示方法正常执行完或者异常抛出都会执行的增强逻辑;与Spring中定义的增强类型基本保持一致。
被上述注解标注的方法可以增加一个JoinPoint类型(Around为ProceedingJoinPoint类型)的参数(也可不加)用于获取上下文信息;另外,@AfterThrowing还可添加异常类型的参数,而@AfterReturing可以添加一个Object类型的参数(表示运行结果),以下通过案例的形式进行介绍。
添加配置类:
@Configuration
@ComponentScan(basePackages = "org.shangjack.demo.annotation")
@EnableAspectJAutoProxy
public class AopDemoConfiguration {
}
添加目标类:
@Component
@Slf4j
public class DataTask {public String syncData() {
LOGGER.info("start sync data");return "success";}
}
添加测试用例:
public class Application {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AopDemoConfiguration.class);DataTask dataTask = (DataTask)context.getBean("dataTask");
dataTask.syncData();}
}
2.2.1: 前置、后置
切面配置如下所示:
@Component
@Aspect
@Slf4j
public class MyNormalAspect {@Before("execution(public String org.shangjack.demo.annotation.DataTask.*(..))")public void beforeExec(JoinPoint joinPoint) {
LOGGER.info("[Before DataTask], joinPoint is {}.", joinPoint);}@AfterReturning("execution(public String org.shangjack.demo.annotation.DataTask.*(..))")public void afterExec(JoinPoint joinPoint) {
LOGGER.info("[After DataTask], result is {}, joinPoint is {}.", joinPoint);}
}
得到如下运行结果:
INF0 org.shangjack.demo.annotation.MyNormalAspect - [Before DataTask], joinPoint is execution(Stri
INF0 org.shangjack.demo.annotation.DataTask-start sync data
INF0 org.shangjack.demo.annotation.MyNormalAspect - [After DataTask],result is execution(String
2.2.2: 环绕通知
在切面中配置环绕增强,如下所示:
@Component
@Aspect
@Slf4j
public class MyAroundAspect {@SneakyThrows@Around("execution(public String org.shangjack.demo.annotation.DataTask.*(..))")public void aroundExec(ProceedingJoinPoint joinPoint) {
LOGGER.info("[Around DataTask] call before.");Object result = joinPoint.proceed();
LOGGER.info("[Around DataTask] call end, result is {}.", result);}
}
得到如下运行结果:
INF0 org.shangjack.demo.annotation.MyAroundAspect - [Around DataTask] call before.
INF0 org.shangjack.demo.annotation.DataTask-start sync data
INF0 org.shangjack.demo.annotation.MyAroundAspect - [Around DataTask] call end, result is success.
2.2.3: 异常抛出增强
在切面中配置异常抛出增强,如下所示:
@Component
@Aspect
@Slf4j
public class MyExceptionAspect {@AfterThrowing("execution(public String org.shangjack.demo.annotation.DataTask.*(..))")public void afterThrowingExec(JoinPoint joinPoint) {
LOGGER.info("[AfterThrow DataTask], joinPoint is {}.", joinPoint);}
}
得到如下运行结果:
INF0 org.shangjack.demo.annotation.DataTask
start sync data
INF0 org.shangjack.demo.annotation.MyExceptionAspect - [AfterThrow DataTask], joinPoint is execution(String org
3 expression表达式
参考: AspectJ官网 或《AspectJ使用与原理》