AspectJ
- 定义了切面表达式的语法和解析机制
- 提供了强大的织入工具
它是通过织入的方式:直接将切面在【编译时、后】或【JVM加载的时候】进行织入到.class代码中。百度相关示例
Spring AOP
目前 Spring AOP 一共有三种配置方式
- Spring 1.2 基于接口的配置:最早的 Spring AOP 是完全基于几个接口的,MethodBeforeAdvice, AfterReturningAdvice, MethodInterceptor 等。
- Spring 2.0 schema-based 配置:Spring 2.0 以后使用 XML 的方式来配置,使用 命名空间
- Spring 2.0 @AspectJ 配置:使用注解的方式来配置,这种方式最方便
注意: AspectJ 是 AOP 的实现, 但不是 Spring AOP 的实现
使用如下的 Calculate 计算类来演示
package com.mrathena.aop.usage;
public interface Calculate {
int add(int a, int b);
int subtract(int a, int b);
int multiply(int a, int b);
int divide(int a, int b);
}
package com.mrathena.aop.usage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CalculateImpl implements Calculate {
private static final Logger log = LoggerFactory.getLogger(CalculateImpl.class);
@Override
public int add(int a, int b) {
log.info("CalculateImpl.add");
return a + b;
}
@Override
public int subtract(int a, int b) {
log.info("CalculateImpl.subtract");
return a - b;
}
@Override
public int multiply(int a, int b) {
log.info("CalculateImpl.multiply");
return a * b;
}
@Override
public int divide(int a, int b) {
log.info("CalculateImpl.divide");
return a / b;
}
}
基于接口
public class CalculateMethodBeforeAdvice implements MethodBeforeAdvice {
private static final Logger log = LoggerFactory.getLogger(CalculateMethodBeforeAdvice.class);
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
log.info("{} - CalculateMethodBeforeAdvice.before", getClass());
}
}
public class CalculateAfterReturningAdvice implements AfterReturningAdvice {
private static final Logger log = LoggerFactory.getLogger(CalculateAfterReturningAdvice.class);
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
log.info("{} - CalculateAfterReturningAdvice.afterReturning", getClass());
}
}
/**
* Interceptor 也是一个 Advice
* interface MethodInterceptor extends Interceptor
* interface Interceptor extends Advice
*/
public class CalculateMethodInterceptor implements MethodInterceptor {
private static final Logger log = LoggerFactory.getLogger(CalculateMethodInterceptor.class);
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object result;
try {
log.info("CalculateMethodInterceptor.invoke.before");
result = invocation.proceed();
log.info("CalculateMethodInterceptor.invoke.afterReturning");
return result;
} catch (Throwable cause) {
log.info("CalculateMethodInterceptor.invoke.afterThrowing");
throw new RuntimeException(cause);
} finally {
log.info("CalculateMethodInterceptor.invoke.after");
}
}
}
Advice 方式
指定某个 Bean 的所有方法都使用指定的某些 Advice 做增强
@Configuration
public class Application {
@Bean
public Calculate calculate() {
return new CalculateImpl();
}
@Bean
public CalculateMethodBeforeAdvice calculateMethodBeforeAdvice() {
return new CalculateMethodBeforeAdvice();
}
@Bean
public CalculateAfterReturningAdvice calculateAfterReturningAdvice() {
return new CalculateAfterReturningAdvice();
}
@Bean
public ProxyFactoryBean calculateProxy() {
// 需要通过 ProxyFactoryBean 来生产 代理 Calculate
ProxyFactoryBean factoryBean = new ProxyFactoryBean();
// 设置拦截链名字(有先后顺序), 可以是 advice, 也可以是 interceptor(本质上也是 advice), 也可以是 advisor
factoryBean.setInterceptorNames("calculateMethodBeforeAdvice", "calculateAfterReturningAdvice");
factoryBean.setTarget(calculate());
return factoryBean;
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
Calculate bean = context.getBean("calculateProxy", Calculate.class);
System.out.println();
bean.add(1, 2);
System.out.println();
bean.divide(1, 0);
}
}
16:33:06.937 [main] INFO com.mrathena.aop.usage.基于接口.CalculateMethodBeforeAdvice - CalculateMethodBeforeAdvice.before
16:33:06.937 [main] INFO com.mrathena.aop.usage.CalculateImpl - CalculateImpl.add
16:33:06.937 [main] INFO com.mrathena.aop.usage.基于接口.CalculateAfterReturningAdvice - CalculateAfterReturningAdvice.afterReturning
16:33:06.938 [main] INFO com.mrathena.aop.usage.基于接口.CalculateMethodBeforeAdvice - CalculateMethodBeforeAdvice.before
16:33:06.938 [main] INFO com.mrathena.aop.usage.CalculateImpl - CalculateImpl.divide
Exception in thread "main" java.lang.ArithmeticException: / by zero
@Configuration
public class Application {
@Bean
public Calculate calculate() {
return new CalculateImpl();
}
@Bean
public CalculateMethodInterceptor calculateMethodInterceptor() {
return new CalculateMethodInterceptor();
}
@Bean
public ProxyFactoryBean calculateProxy() {
// 需要通过 ProxyFactoryBean 来生产 代理 Calculate
ProxyFactoryBean factoryBean = new ProxyFactoryBean();
// 设置拦截链名字(有先后顺序), 可以是 advice, 也可以是 interceptor(本质上也是 advice), 也可以是 advisor
factoryBean.setInterceptorNames("calculateMethodInterceptor");
factoryBean.setTarget(calculate());
return factoryBean;
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
Calculate bean = context.getBean("calculateProxy", Calculate.class);
System.out.println();
bean.add(1, 2);
System.out.println();
bean.divide(1, 0);
}
}
16:36:33.083 [main] INFO com.mrathena.aop.usage.基于接口.CalculateMethodInterceptor - CalculateMethodInterceptor.invoke.before
16:36:33.083 [main] INFO com.mrathena.aop.usage.CalculateImpl - CalculateImpl.add
16:36:33.083 [main] INFO com.mrathena.aop.usage.基于接口.CalculateMethodInterceptor - CalculateMethodInterceptor.invoke.afterReturning
16:36:33.083 [main] INFO com.mrathena.aop.usage.基于接口.CalculateMethodInterceptor - CalculateMethodInterceptor.invoke.after
16:36:33.084 [main] INFO com.mrathena.aop.usage.基于接口.CalculateMethodInterceptor - CalculateMethodInterceptor.invoke.before
16:36:33.084 [main] INFO com.mrathena.aop.usage.CalculateImpl - CalculateImpl.divide
16:36:33.084 [main] INFO com.mrathena.aop.usage.基于接口.CalculateMethodInterceptor - CalculateMethodInterceptor.invoke.afterThrowing
16:36:33.084 [main] INFO com.mrathena.aop.usage.基于接口.CalculateMethodInterceptor - CalculateMethodInterceptor.invoke.after
Exception in thread "main" java.lang.RuntimeException: java.lang.ArithmeticException: / by zero
Advisor 方式
Advice: 指定某个 Bean 的所有方法都使用指定的某些 Advice 做增强
Advisor: 扩展了 Advice. 指定某个 Bean 的 指定某些方法, 使用指定的某些 Advice 做增强
@Configuration
public class Application {
@Bean
public Calculate calculate() {
return new CalculateImpl();
}
@Bean
public CalculateMethodBeforeAdvice calculateMethodBeforeAdvice() {
return new CalculateMethodBeforeAdvice();
}
@Bean
public NameMatchMethodPointcutAdvisor calculateNameMatchMethodPointcutAdvisor() {
NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor();
// 通知(Advice) :是我们的通知类
// 通知者(Advisor):是经过包装后的细粒度控制方式。
// Advisor 持有 Advice 和 某个 Pointcut
advisor.setAdvice(calculateMethodBeforeAdvice());
advisor.setMappedNames("add", "subtract");
return advisor;
}
@Bean
public ProxyFactoryBean calculateProxy() {
ProxyFactoryBean factoryBean = new ProxyFactoryBean();
// 设置拦截链名字(有先后顺序), 可以是 advice, 也可以是 interceptor(本质上也是 advice), 也可以是 advisor
factoryBean.setInterceptorNames("calculateNameMatchMethodPointcutAdvisor");
factoryBean.setTarget(calculate());
return factoryBean;
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
Calculate bean = context.getBean("calculateProxy", Calculate.class);
System.out.println();
bean.add(1, 2);
System.out.println();
bean.subtract(1, 2);
System.out.println();
bean.multiply(1, 2);
System.out.println();
bean.divide(1, 0);
}
}
16:39:04.690 [main] INFO com.mrathena.aop.usage.基于接口.CalculateMethodBeforeAdvice - CalculateMethodBeforeAdvice.before
16:39:04.690 [main] INFO com.mrathena.aop.usage.CalculateImpl - CalculateImpl.add
16:39:04.694 [main] INFO com.mrathena.aop.usage.基于接口.CalculateMethodBeforeAdvice - CalculateMethodBeforeAdvice.before
16:39:04.695 [main] INFO com.mrathena.aop.usage.CalculateImpl - CalculateImpl.subtract
16:39:04.695 [main] INFO com.mrathena.aop.usage.CalculateImpl - CalculateImpl.multiply
16:39:04.695 [main] INFO com.mrathena.aop.usage.CalculateImpl - CalculateImpl.divide
Exception in thread "main" java.lang.ArithmeticException: / by zero
AutoProxyCreator 方式
Advice: 指定某个 Bean 的所有方法都使用指定的某些 Advice 做增强
Advisor: 扩展了 Advice. 指定某个 Bean 的 指定某些方法, 使用指定的某些 Advice 做增强
AutoProxyCreator: 扩展了 Advisor. 指定某些 Bean 的 某些方法, 使用指定的某些 Advice 做增强
@Configuration
public class Application {
@Bean
public Calculate calculate() {
return new CalculateImpl();
}
@Bean
public CalculateMethodBeforeAdvice calculateMethodBeforeAdvice() {
return new CalculateMethodBeforeAdvice();
}
@Bean
public NameMatchMethodPointcutAdvisor calculateNameMatchMethodPointcutAdvisor() {
NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor();
// 通知(Advice) :是我们的通知类
// 通知者(Advisor):是经过包装后的细粒度控制方式。
// advisor 持有 advice 和 某个 pointcut
// advisor 负责决定拦截哪些方法, advice 定义拦截后的逻辑
advisor.setAdvice(calculateMethodBeforeAdvice());
advisor.setMappedNames("add", "subtract");
return advisor;
}
@Bean
public RegexpMethodPointcutAdvisor calculateRegexpMethodPointcutAdvisor() {
RegexpMethodPointcutAdvisor regexpMethodPointcutAdvisor = new RegexpMethodPointcutAdvisor();
regexpMethodPointcutAdvisor.setAdvice(calculateMethodBeforeAdvice());
regexpMethodPointcutAdvisor.setPatterns("com.mrathena.aop.usage.CalculateImpl.add", "com.mrathena.aop.usage.CalculateImpl.subtract");
return regexpMethodPointcutAdvisor;
}
/**
* BeanNameAutoProxyCreator 和 DefaultAdvisorAutoProxyCreator 二者选一个留下
* 需要指定 BeanNames 和 InterceptorNames(advice, interceptor, advisor)
*/
@Bean
public BeanNameAutoProxyCreator calculateBeanNameAutoProxyCreator() {
BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
// 设置要创建代理的那些Bean的名字
beanNameAutoProxyCreator.setBeanNames("calc*");
// 设置拦截链名字(有先后顺序), 可以是 advice, 也可以是 interceptor(本质上也是 advice), 也可以是 advisor
// 拦截器使用 advice, 作用于所有方法
beanNameAutoProxyCreator.setInterceptorNames("calculateMethodBeforeAdvice");
// 拦截器使用 advisor, 作用于指定方法
beanNameAutoProxyCreator.setInterceptorNames("calculateRegexpMethodPointcutAdvisor", "calculateNameMatchMethodPointcutAdvisor");
beanNameAutoProxyCreator.setInterceptorNames("calculateNameMatchMethodPointcutAdvisor");
beanNameAutoProxyCreator.setInterceptorNames("calculateRegexpMethodPointcutAdvisor");
return beanNameAutoProxyCreator;
}
/**
* BeanNameAutoProxyCreator 和 DefaultAdvisorAutoProxyCreator 二者选一个留下
* DefaultAdvisorAutoProxyCreator 让容器中的所有 advisor 都生效
*/
// @Bean
// public DefaultAdvisorAutoProxyCreator calculateDefaultAdvisorAutoProxyCreator() {
// return new DefaultAdvisorAutoProxyCreator();
// }
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
Calculate bean = context.getBean("calculate", Calculate.class);
System.out.println();
bean.add(1, 2);
System.out.println();
bean.subtract(1, 2);
System.out.println();
bean.multiply(1, 2);
System.out.println();
bean.divide(1, 0);
}
}
16:40:28.030 [main] INFO com.mrathena.aop.usage.基于接口.CalculateMethodBeforeAdvice - CalculateMethodBeforeAdvice.before
16:40:28.030 [main] INFO com.mrathena.aop.usage.CalculateImpl - CalculateImpl.add
16:40:28.030 [main] INFO com.mrathena.aop.usage.基于接口.CalculateMethodBeforeAdvice - CalculateMethodBeforeAdvice.before
16:40:28.030 [main] INFO com.mrathena.aop.usage.CalculateImpl - CalculateImpl.subtract
16:40:28.030 [main] INFO com.mrathena.aop.usage.CalculateImpl - CalculateImpl.multiply
16:40:28.031 [main] INFO com.mrathena.aop.usage.CalculateImpl - CalculateImpl.divide
Exception in thread "main" java.lang.ArithmeticException: / by zero
基于 XML
百度
基于注解
@Aspect
public class CalculateAspect {
private static final Logger log = LoggerFactory.getLogger(CalculateAspect.class);
// Before: 方法执行前
// After: 方法执行后(不管是否异常)
// AfterReturning: 方法成功执行后
// AfterThrowing: 方法抛出异常后
// Around: 自己封装整个代理逻辑
// 可以不写切点, 直接把切点表达式写在 @Before 等括号中
@Pointcut("execution (* com.mrathena.aop.usage.CalculateImpl.*(..))")
public void all() {}
@Pointcut("execution (* com.mrathena.aop.usage.CalculateImpl.add(..))")
public void add() {}
@Pointcut("execution (* com.mrathena.aop.usage.CalculateImpl.subtract(..))")
public void subtract() {}
@Pointcut("execution (* com.mrathena.aop.usage.CalculateImpl.multiply(..))")
public void multiply() {}
@Pointcut("execution (* com.mrathena.aop.usage.CalculateImpl.divide(..))")
public void divide() {}
@Before("all()")
public void allBefore(JoinPoint joinPoint) {
log.info("AllBefore: {}, {}", joinPoint.getSignature().getName(), Arrays.asList(joinPoint.getArgs()));
}
@After("all()")
public void allAfter(JoinPoint joinPoint) {
log.info("AllAfter");
}
@AfterReturning(value = "all()", returning = "result")
public void allAfterReturning(JoinPoint joinPoint, Object result) {
log.info("AllAfterReturning: {}", result);
}
@AfterThrowing(value = "all()", throwing = "cause")
public void allAfterThrowing(JoinPoint joinPoint, Throwable cause) {
log.info("AllAfterThrowing: {}", cause.getMessage());
}
// @Before("add()")
// ajc: circular advice precedence: can't determine precedence between two or more pieces of advice that apply to the same join point: method-execution(int com.mrathena.aop.usage.CalculateImpl.add(int, int))
// 每一个方法只能被一个@Before横切
public void addBefore(JoinPoint joinPoint) {
log.info("AddBefore: {}, {}", joinPoint.getSignature().getName(), Arrays.asList(joinPoint.getArgs()));
}
}
@EnableAspectJAutoProxy
@Configuration
public class Application {
@Bean
public Calculate calculate() {
return new CalculateImpl();
}
@Bean
public CalculateAspect calculateAspect() {
return new CalculateAspect();
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
Calculate bean = context.getBean("calculate", Calculate.class);
System.out.println();
bean.add(1, 2);
System.out.println();
bean.subtract(1, 2);
System.out.println();
bean.multiply(1, 2);
System.out.println();
bean.divide(1, 0);
}
}
16:50:49.263 [main] INFO com.mrathena.aop.usage.基于注解.CalculateAspect - AllBefore: multiply, [1, 2]
16:50:49.263 [main] INFO com.mrathena.aop.usage.CalculateImpl - CalculateImpl.multiply
16:50:49.263 [main] INFO com.mrathena.aop.usage.基于注解.CalculateAspect - AllAfter
16:50:49.263 [main] INFO com.mrathena.aop.usage.基于注解.CalculateAspect - AllAfterReturning: 2
16:50:49.263 [main] INFO com.mrathena.aop.usage.基于注解.CalculateAspect - AllBefore: divide, [1, 0]
16:50:49.263 [main] INFO com.mrathena.aop.usage.CalculateImpl - CalculateImpl.divide
16:50:49.263 [main] INFO com.mrathena.aop.usage.基于注解.CalculateAspect - AllAfter
16:50:49.263 [main] INFO com.mrathena.aop.usage.基于注解.CalculateAspect - AllAfterThrowing: / by zero
Exception in thread "main" java.lang.ArithmeticException: / by zero