Spring自定义AOP切面

Spring自定义AOP切面切面切面需要实现PointcutAdvisor接口,包含切点和通知。packagecom.morris.spring.aop;importorg.aopalliance.aop.Advice;importorg.springframework.aop.Pointcut;importorg.springframework.aop.support.AbstractPointcutAdvisor;@SuppressWarnings(“serial”)publicclassUserSe

Spring自定义AOP切面"

切面

切面需要实现PointcutAdvisor接口,包含切点和通知。

package com.morris.spring.aop;

import org.aopalliance.aop.Advice;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AbstractPointcutAdvisor;

@SuppressWarnings("serial")
public class UserServiceAdvisor extends AbstractPointcutAdvisor { 
   

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

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

切点

切点需要实现Pointcut,里面要实现对类的匹配和方法的匹配。

这里不对类进行校验,所以使用Spring内部的ClassFilter.TRUE。

对方法的匹配需要实现MethodMatcher,所以UserServicePointcut同时实现了MethodMatcher和Pointcut。

package com.morris.spring.aop;

import com.morris.spring.annotation.Abcd;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AopUtils;

import java.lang.reflect.Method;

public class UserServicePointcut implements MethodMatcher, Pointcut { 
   
	@Override
	public boolean matches(Method method, Class<?> targetClass) { 
   
		if(method.getName().equals("query")) { 
   
			return true;
		}
		return false;
	}

	@Override
	public boolean isRuntime() { 
   
		return false;
	}

	@Override
	public boolean matches(Method method, Class<?> targetClass, Object... args) { 
   
		return false;
	}

	@Override
	public ClassFilter getClassFilter() { 
   
		return ClassFilter.TRUE;
	}

	@Override
	public MethodMatcher getMethodMatcher() { 
   
		return this;
	}
}

通知

通知主要是对目标方法的增强,这里只是打印。

package com.morris.spring.aop;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class UserServiceAdvice implements MethodInterceptor { 
   

	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable { 
   
		System.out.println(invocation.getMethod().getName()+"-------------------------");
		return invocation.proceed();
	}
}

测试类

package com.morris.spring.demo.annotation;

import com.morris.spring.aop.UserServiceAdvisor;
import com.morris.spring.service.UserService;
import com.morris.spring.service.UserServiceImpl;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy // 开启AOP
public class CustomerAspectDemo { 
   

	public static void main(String[] args) { 
   
		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
		applicationContext.register(UserServiceImpl.class);
		applicationContext.register(UserServiceAdvisor.class); // 自定义切面
		applicationContext.register(CustomerAspectDemo.class); // 开启AOP
		applicationContext.refresh();
		UserService userService = applicationContext.getBean(UserService.class);
		userService.query("hello");
	}
}

运行结果如下:

query-------------------------
UserServiceImpl hello

发现通知中的增强代码执行了。

尝试获得目标方法上的注解信息

UserServiceImpl.query()方法上面加上@Abcd(“abcd”)
注解:

public class UserServiceImpl implements UserService { 
   

	@Abcd("abcd")
	@Override
	public void query(String name) { 
   
		System.out.println("UserServiceImpl " + name);
	}
}

com.morris.spring.aop.UserServicePointcut#matches(java.lang.reflect.Method, java.lang.Class<?>)

public boolean matches(Method method, Class<?> targetClass) { 
   
	if(method.isAnnotationPresent(Abcd.class)) { 
   
		return true;
	}
	return false;
}

运行结果会发现通知中的增强代码并没有被执行,这是为什么呢?目标方法query()上面明明有@Abcd注解,为什么获取不到呢?

在matches()方法中打上断点,会发现这个方法会被调用两次,第一次会返回true,第二次返回false,为什么两次结果会不一样呢?

先来看一下这两次调用的调用时机:

  1. 第一次调用时,匹配方法,如果匹配上了就会生成代理对象,Method所在的类为com.morris.spring.service.UserServiceImpl。

  2. 第二次调用时,调用代理对象的方法时会再次匹配,因为有的方法不需要代理,Method所在的类为com.morris.spring.service.UserService。

第二次调用时Method是来自于UserService接口,而接口上面的方法是没有注解的,这点也可以从动态代理生成的类中看出:

static { 
   
	try { 
   
	    m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
		// 来自接口
	    m3 = Class.forName("com.morris.spring.service.UserService").getMethod("query", Class.forName("java.lang.String"));
	    m4 = Class.forName("com.morris.spring.service.UserService").getMethod("hi", Class.forName("com.morris.spring.entity.D"));
	    m2 = Class.forName("java.lang.Object").getMethod("toString");
	    m0 = Class.forName("java.lang.Object").getMethod("hashCode");
	} catch (NoSuchMethodException var2) { 
   
	    throw new NoSuchMethodError(var2.getMessage());
	} catch (ClassNotFoundException var3) { 
   
	    throw new NoClassDefFoundError(var3.getMessage());
	}
}

而UserService.query()方法上面并没有@Abcd注解,那么要怎么样才能获得注解的信息呢?Spring提供了下面的工具类:

Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
if(specificMethod.isAnnotationPresent(Abcd.class)) { 
   
	return true;
}

方法参数级别更细粒度的匹配

MethodMatcher还有两个方法:

boolean isRuntime();
boolean matches(Method method, Class<?> targetClass, Object... args);

这两个方法配合使用能在运行时对参数的值进行匹配。

public boolean isRuntime() { 
   
	return true;
}

public boolean matches(Method method, Class<?> targetClass, Object... args) { 
   
	if(null != args && null != args[0] && "morris".equals(args[0])) { 
   
		System.out.println("matches args");
		return true;
	}
	return false;
}

需要满足两个条件这个带方法参数的matches()才会执行(这个方法只会执行一次,真正调用时才会知道参数的具体值):

  1. 不带方法参数的matches()返回true。

  2. isRuntime()返回true。

今天的文章Spring自定义AOP切面分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。

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

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注