微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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 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();
		// 设置拦截链名字(有先后顺序),也可以是 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", 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();
		// 设置拦截链名字(有先后顺序),也可以是 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", 2);
		System.out.println();
		bean.subtract(1, 2);
		System.out.println();
		bean.multiply(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*");
		// 设置拦截链名字(有先后顺序),也可以是 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", 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: {}, 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", 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, 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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐