Spring AOP 实现方式

博文目录


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
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值