• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Spring : @Aspect 切面的几种方式

武飞扬头像
魔道不误砍柴功
帮助1

1、@Aspect 注解方式

直接使用 @Aspect 注解方式,切面的功能都在这个类每个方法中,如下所示:


@Aspect
@Configuration
@ComponentScan(basePackages = {"com.gwm.spring.aopproxy","com.gwm.spring.interceptor"})
@EnableAspectJAutoProxy // 点击进入查看实现原理
public class JavAspect {

	@Pointcut("execution(* com.gwm.spring.aopproxy..*.*(..))")
	public void pointcutGwm() {}

	@Before(value = "pointcutGwm()")
	public void beforeTest(JoinPoint joinPoint) {
		System.out.println("beforeTest log info ===================");
	}

	/**
	 * 在方法[结束]之后(也就是return语句执行完了之后结束了整个方法调用栈)开始执行这个 after , 比 @AfterReturning 注解后执行!
	 */
	@After(value = "pointcutGwm()")
	public void afterTest(JoinPoint joinPoint) {
		System.out.println("afterTest log info===================");
	}

	@Around(value = "pointcutGwm()")
	public void aroundTest(ProceedingJoinPoint joinpoint) {
		System.out.println("aroundTest start log info= 在这里看试下能不能获取到方法上的注解==================");
		/**
		 * 可以直接从 MethodInvocation 获取到值,前提是不是多线程情况下
		 * 因为这里是从 ThreadLocal 中获取的值
		 * ExposeInvocationInterceptor 这个 Advice 就是做了一件事情,就是把 MethodInvocation 保存到 ThreadLocal 中
		 */
		MethodInvocation methodInvocation = ExposeInvocationInterceptor.currentInvocation();
		System.out.println("methodInvocation.getArguments() = "   methodInvocation.getArguments());
		/**
		 * 下面测试下在这里能不能获取到 method 上面的注解之类的修饰
		 * 答案是获取不到的,因为此时的 method 对象是接口上的 method 对象,不是具体实现类上的 method 对象
		 * 接口上是没有任何修饰符的,通过 ProxyGenerator 工具类可以获取到 Proxy 代理对象的字节码文件,M4 方法
		 * 就是加载的接口上的 method 对象,所以可以想办法去获取目标类上的 method,方法有很多
		 */
		Method method = methodInvocation.getMethod();
		DBMasterAnno annotation = method.getAnnotation(DBMasterAnno.class);
		System.out.println("此处是接口类上的 method 对象 flag=" method.isAnnotationPresent(DBMasterAnno.class));

		/**
		 * 方法一:拿到目标类之后,通过一下工具类查找具体的 method 对象
		 */
		Method specificMethod = AopUtils.getMostSpecificMethod(method, joinpoint.getTarget().getClass());

		DBMasterAnno annotation1 = specificMethod.getAnnotation(DBMasterAnno.class);
		System.out.println("此处才是实现类的 method 对象 flag=" specificMethod.isAnnotationPresent(DBMasterAnno.class));

		/**
		 * 获取到参数名称
		 */
		String[] parameterNames = new DefaultParameterNameDiscoverer().getParameterNames(method);
		System.out.println("parameterNames = "   parameterNames);

		try {
			/**
			 * 方法二:拿到目标类之后,通过自己遍历找到有注解修饰的 method 对象
			 */
			Method[] declaredMethods = joinpoint.getTarget().getClass().getDeclaredMethods();
			Arrays.asList(declaredMethods).forEach(e->{
				if (e.isAnnotationPresent(DBMasterAnno.class)) {
					System.out.println("此处才是实现类的 method 对象 flag=" specificMethod.isAnnotationPresent(DBMasterAnno.class));
				}
			});

		} catch (Exception e) {
			e.printStackTrace();
		}

		try {
			joinpoint.proceed();
		} catch (Throwable throwable) {
			throwable.printStackTrace();
		}
		System.out.println("aroundTest end log info===================");
	}

	/**
	 * 在方法 return 之就开始执行这个功能!
	 */
	@AfterReturning(value = "pointcutGwm()",returning = "retVal")
	public void afterReturningTest(JoinPoint joinPoint, Object retVal) {

		System.out.println("afterReturningTest log info===================" retVal);
		Object[] args = joinPoint.getArgs();
		System.out.println("args = "   args);
	}

	/**
	 * 只要发生了异常就会执行此方法
	 */
	@AfterThrowing(value = "pointcutGwm()", throwing = "e")
	public void afterThrowingTest(JoinPoint joinPoint,Throwable e) {
		System.out.println("afterThrowingTest error log info===================" e.getMessage());
	}


}

在执行时,Spring 会自己根据你配置的 Pointcut 来判断是否需要被代理增强。从而实现了 AOP 切面功能。

2、自定义 Adisor

如果不用 @Aspect 注解的话,也可以通过自定义 Advisor 的方式添加切面,但是自定义 Advisor 有点麻烦就是,它还需要有自己的 Advice、Pointcut,注意要加上 @Component 注解表示交给 Spring 管理,代码如下:


@Component
public class MyAdvisor extends AbstractPointcutAdvisor implements Serializable {

	private static final long serialVersionUID = 2669293150219020249L;

	@Override
	public Advice getAdvice() {
		return new MyMethodAdvice();
	}

	@Override
	public Pointcut getPointcut() {
		return new MyPointcut();
	}
}

需要添加一个自定义的 Advice,因为切面增强逻辑都写在这个 Adivce 中,如下所示:

public class MyMethodAdvice implements MethodInterceptor {
	
	/**
	 * @desc:
	 * @author: Gong
	 * @date: 2022/12/29 17:34
	 */
	@Nullable
	@Override
	public Object invoke(@Nonnull MethodInvocation invocation) throws Throwable {
		System.out.println("我是 MyMethodAdvice 增强逻辑....");

		Object aThis = invocation.getThis();
		Method method = invocation.getMethod();
		Object[] arguments = invocation.getArguments();
		Object invoke = method.invoke(aThis, arguments);

		System.out.println("aThis = "   aThis);
		// Arrays.asList(arguments).forEach(System.out::println);

		return invocation.proceed();
		// return invoke;
	}
}

然后还需要添加一个自定义的 Pointcut,而 Pointcut 是对方法和类过滤进行过滤,判断某个类、和某个类中的方法是否需要被代理,如下所示:

public class MyPointcut implements Pointcut {
	@Override
	public ClassFilter getClassFilter() {
		return new MyClassFilter();
	}

	@Override
	public MethodMatcher getMethodMatcher() {
		return new MyMethodMatcher();
	}
}

public class MyClassFilter implements ClassFilter {
	@Override
	public boolean matches(Class<?> clazz) {

		return clazz == Apple.class;
	}
}


public class MyMethodMatcher implements MethodMatcher {
	@Override
	public boolean matches(Method method, Class<?> targetClass) {

		return method.getName().equals("add");
	}

	/**
	 * 控制更细粒度的开关
	 * @return
	 */
	@Override
	public boolean isRuntime() {
		return false;
	}

	/**
	 * 可以对参数进行判断做到了更新粒度的切面控制
	 * @return
	 */
	@Override
	public boolean matches(Method method, Class<?> targetClass, Object... args) {
		return false;
	}
}

在执行时,Spring 就会收集到你自定义的 Advisor 来做切面逻辑。

3、自定义 Advice

如果你觉得自定义 Advisor 麻烦的话,那可以通过自定义 Advice 的方法方便点,一般自定义的 Advice 实现它的子类接口 MethodInterceptor 即可,如下所示:

/**
 * @author: Gong
 * @date: 2022/12/30 17:00
 * @desc: ...
 */
@Component
public class MyAdvice implements MethodInterceptor {
	@Nullable
	@Override
	public Object invoke(@Nonnull MethodInvocation invocation) throws Throwable {
		System.out.println(">>>>>>MyAdvice 增强逻辑...");
		return invocation.proceed();
	}
}

但是这里还需要定一个 PriorityOrdered 级别的 BeanPostProcessor,因为我们需要把上面的 MyAdvice 添加到切面的逻辑中去。PriorityOrdered 级别是因为自定义的 MyAdviceBeanPostProcessor 一定要比 AnnotationAwareAspectJAutoProxyCreator 先被注册到 Spring 容器 beanPostProcessors 中,这样等你执行 AnnotationAwareAspectJAutoProxyCreator 的 getBean() 时就能够被你自定义的 beanPostProcessors 包装增强。

/**
 * @author: Gong
 * @date: 2022/12/30 17:01
 * @desc: ...
 */
@Component
public class MyAdviceBeanPostProcessor implements BeanPostProcessor, PriorityOrdered {
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if (bean instanceof AnnotationAwareAspectJAutoProxyCreator) {
			AnnotationAwareAspectJAutoProxyCreator aspectJAutoProxyCreator = (AnnotationAwareAspectJAutoProxyCreator) bean;
			aspectJAutoProxyCreator.setInterceptorNames("myAdvice");
		}

		return bean;
	}

	@Override
	public int getOrder() {
		return 0;
	}
}

同时也要注意次 Advice 也是个全局的拦截器,Pointcut 默认都是 TRUE 不做任何类和方法的拦截过滤。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgjabie
系列文章
更多 icon
同类精品
更多 icon
继续加载