目录
一.切点查找
我们只有找到切点了,才能去添加增强逻辑,那么我们如何找到切点呢?
二.示例
1.通过execution表达式来找切点
创建F1类
public class F1 {
public void foo(){
System.out.println("foo...");
}
@Bean
public void bar(){
System.out.println("bar...");
}
}
我们需要找到foo()方法和被@Bean注解的地方,这两个作为切点,下面我们来实现:
class Test{
public static void main(String[] args) throws NoSuchMethodException {
F1 f1 = new F1();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
boolean b = pointcut.matches(f1.getClass().getDeclaredMethod("foo"), f1.getClass());
System.out.println(b);
AspectJExpressionPointcut pointcut1 = new AspectJExpressionPointcut();
pointcut1.setExpression("@annotation(org.springframework.context.annotation.Bean)");
boolean b1 = pointcut.matches(f1.getClass().getDeclaredMethod("foo"), f1.getClass());
System.out.println(b1);
}
}
通过matches()方法可以匹配切点,看他是否含有切点表达式中的的方法或注解
运行一下:
true
true
进程已结束,退出代码0
如果有,就返回true
2.在类和类的方法上查找切点
下面我们来判断类上和类的方法上是否含有@Transactional注解
我们创建F2和F3类
@Transactional
class F2{
public void foo(){
System.out.println("foo...");
}
public void bar(){
System.out.println("bar...");
}
}
class F3{
@Transactional
public void foo3(){
System.out.println("foo3...");
}
}
下面我们来匹配切点:
StaticMethodMatcher pointcut2 = new StaticMethodMatcher() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
//获取method上的注解信息
MergedAnnotations annotations = MergedAnnotations.from(method);
//如果方法上含有@Transactional
if(annotations.isPresent(Transactional.class)){
return true;
}
//如果类上含有@Transactional
MergedAnnotations annotations1 = MergedAnnotations.from(targetClass);
if(annotations1.isPresent(Transactional.class)){
return true;
}
return false;
}
};
System.out.println(pointcut2.matches(F2.class.getMethod("foo"), F2.class));
System.out.println(pointcut2.matches(F3.class.getMethod("foo3"), F3.class));
这里使用的是StaticMethodMatcher类中的matches方法来匹配,它接受两个参数:
1.哪个方法,2.目标类的类型
然后使用了MergedAnnotations 的from方法,该方法返回方法或者类上的注解信息,然后就可以判断了
下面运行一下:
true
true
进程已结束,退出代码0
3.接口上查找@Transactional注解
@Transactional
interface I4{
void cool();
}
class F4 implements I4{
@Override
public void cool() {
System.out.println("cool...");
}
}
我们可以更改他的查找策略
MergedAnnotations annotations1 = MergedAnnotations.from(targetClass,MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);
这样就可以成功的找到接口上的注解了
三.Aspect和Advisor
1.概念
Aspect是高级切面,它可以定义多个切点和通知
Advisor是低级切面,它只能定义一个切点和通知
Aspect底层会转化为每个Advisor
2.示例
(1)创建A1类
class A1 {
public void go() {
System.out.println("go...");
}
}
(2)创建切面类MyAspect
@Aspect
public class MyAspect {
@Before("execution(* go())")
public void before(){
System.out.println("before...");
}
@After("execution(* go())")
public void after(){
System.out.println("after...");
}
}
(3)创建配置类,加入低级切面Advisor
@Configuration
public
class MyConfig {
@Bean
public Advisor advisor(MethodInterceptor advice) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* go())");
//添加切点和通知
return new DefaultPointcutAdvisor(pointcut, advice);
}
@Bean
public MethodInterceptor advice() {
return invocation -> {
System.out.println("advisor before...");
Object result = invocation.proceed();
System.out.println("advisor after...");
return result;
};
}
}
这里我们把advisor需要的通知MethodInterceptor作为一个Bean,可以自动注入
(4)创建容器,注册beanFactory处理器
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
//注册config
context.registerBean("myConfig", MyConfig.class);
//注册aspect
context.registerBean("myAspect", MyAspect.class);
//注册bean后处理器解析@Bean注解
context.registerBean(ConfigurationClassPostProcessor.class);
context.refresh();
//获取这些bean
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
}
先运行一下,查看有哪些bean
myConfig
myAspect
org.springframework.context.annotation.ConfigurationClassPostProcessor
advisor
advice
下面我们需要获取所有的切面:
我们需要使用AnnotationAwareAspectJAutoProxyCreator
查看他的类图:

我们选取它的父类AbstractAdvisorAutoProxyCreator中的
findEligibleAdvisors方法,因为它是protected修饰的,所以我们不能直接调用它,这里我们可以把我们的测试类的包名修改为它的包名:
package org.springframework.aop.framework.autoproxy;
这样我们就可以使用findEligibleAdvisors方法了,它是判断哪个类是否有资格获取切面,
我们使用一下它:
在这之前先要把它注册到容器中去
//注册解析aspect的后处理器
context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);
AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
//查找谁有资格被切点匹配
List<Advisor> advisors = creator
.findEligibleAdvisors(org.springframework.aop.framework.autoproxy.A1.class, "A1");
advisors.forEach(System.out::println);
然后我们运行一下,查看有哪些切面:
org.springframework.aop.interceptor.ExposeInvocationInterceptor.ADVISOR
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* go())]; advice [com.jjh.aspect.MyConfig$$Lambda$89/0x00000008001b2440@a4add54]
InstantiationModelAwarePointcutAdvisor: expression [execution(* go())]; advice method [public void com.jjh.aspect.MyAspect.before()]; perClauseKind=SINGLETON
InstantiationModelAwarePointcutAdvisor: expression [execution(* go())]; advice method [public void com.jjh.aspect.MyAspect.after()]; perClauseKind=SINGLETON
我们发现这里出现了四个切面,解释一下:
第一个切面是Spring为每一个代理都添加的切面,是默认的
第二个切面是我们通过配置类中添加的Advisor
第三和第四个是@Aspect注解中的@Before和@After创建的两个低级切面,@Aspect这个MyAspect是一个高级切面,它底层创建了两个Advisor低级切面
四.两个重要方法
1.findEligibleAdvisors
这个就是上面那个方法,它主要是对目标类进行 匹配,查找是否有切面是针对它的,如果有,就返回切面的集合,没有返回空
2.wrapIfNecessary
该方法是对上面的切面就行判断,如果这个切面集合不为空,就对这个目标类创建代理对象,如果集合为空,就返回这个对象本身
3.下面我们来验证一下:
/**
* wrapIfNecessary方法对上面找的的切面集合做一个判断
* 如果有切面,就给这个目标对象创建代理
* 如果没有切面,就返回这个对象本身
*/
Object o = creator
.wrapIfNecessary(new org.springframework.aop.framework.autoproxy.A1(), "a1", "a1");
System.out.println(o.getClass());
我们运行一下,查看它返回对象的类型:
class org.springframework.aop.framework.autoproxy.A1$$EnhancerBySpringCGLIB$$a7ae3c69
我们看到是cglib代理的,说明我们的假设成功
下面我们调用这个代理对象的go()方法:
//对这个代理对象进行类型转换为A1,调用方法,查看是否增强
org.springframework.aop.framework.autoproxy.A1 o1 =
(org.springframework.aop.framework.autoproxy.A1) o;
o1.go();
运行查看:
advisor before...
before...
go...
after...
advisor after...
进程已结束,退出代码0
可以看到已经实现了增强功能,说明了aop的底层就是通过代理来实现的!
五.代理创建时机
代理创建顺序为以下:
创建 -> (*) ->依赖注入->初始化->(*)
分为两种情况:
1.当有循环依赖时,代理对象将在依赖注入之前被创建
2.单向注入时,代理对象将在初始化之后被创建
六.切面执行顺序
高级切面可以使用@Order()来设置,值越小,越前执行
低级切面可以通过setOrder()方法来设置优先级
七.高级切面向低级切面转换
我们来模拟一下高级切面转化为低级切面
@Aspect
public class MyAspect {
@Before("execution(* go())")
public void before(){
System.out.println("before...");
}
@After("execution(* go())")
public void after(){
System.out.println("after...");
}
}
我们先把这个@Before注解的方法创建一个Before低级切面
//高级切面向低级切面转换
MyAspect myAspect = context.getBean(MyAspect.class);
Method method = null;
try {
method = myAspect.getClass().getDeclaredMethod("before");
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());
//创建切面集合
List<Advisor> list = new ArrayList<>();
//判断method上是否有@Before注解
if (method.isAnnotationPresent(Before.class)) {
//如果有,获取注解中的切点表达式值
String expession = method.getAnnotation(Before.class).value();
//创建切点
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(expession);
//创建一个通知
AspectJMethodBeforeAdvice advice =
new AspectJMethodBeforeAdvice(method,pointcut,factory);
//创建一个低级切面
Advisor advisor = new DefaultPointcutAdvisor(pointcut,advice);
//添加入集合中
list.add(advisor);
}
//遍历集合
for (Advisor advisor : list) {
System.out.println(advisor);
}
}
运行一下:
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* go())]; advice [org.springframework.aop.aspectj.AspectJMethodBeforeAdvice: advice method [public void com.jjh.aspect.MyAspect.before()]; aspect name '']
发现成功的将before切面创建出来了
本文详细介绍了SpringAOP中的切点查找方法,包括通过execution表达式、类和方法查找以及接口上的注解。此外,还探讨了Aspect和Advisor的概念、创建实例以及代理的创建时机和执行顺序。
832

被折叠的 条评论
为什么被折叠?



