DefaultPointcutAdvisor让切点可配置

1,741 阅读2分钟

我们在做框架的时候往往有这样的需求,不同的项目,相同的功能可能需要相同的增强,但是他们可能位于不同的包下,甚至不知道别人会使用什么样的包名.此时你不得不硬性规定,什么类必须写在什么包下,显然这很不友好.spring-aop当然也考虑到了这点,它提供了DefaultPointcutAdvisor这个类,可以让你自定义配置化的切点. 我们顺便聊聊advisor这个单词,看过很多书和文章对它的翻译要么是通知者,要么是顾问,直勾勾的百度翻译嘛,我认为这些翻译都沒有实际意义,我一直尝试着找一个词能让人一看就懂的对照翻译,可是始终找不到,有知道的小伙伴给个词语出来.我对他的解释是连接器,一个连接了切面与增强点的连接器,事实上它的功能就是这样,终于知道那些海归华侨为啥总是中英文混说了,因为一下子找不到合适的词翻译那个语境,废话说多了. 接着我们来看如何使用DefaultPointcutAdvisor,请看代码,记住前面编程的方法论,先写增强,我们做了个很简单的事情就是在方法前后各打印一行信息.

package com.qimo.omsa.demo.hello;

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

/**
 * @Description TODO
 * @Author 姚仲杰#80998699
 * @Date 2021/8/11 10:21
 */
public class MethodInvokeInterceptor implements MethodInterceptor {
    
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        beforeMethod();
        Object proceed = methodInvocation.proceed();
        afterMethod();
        return proceed;
    }
    
    private void beforeMethod(){
        System.out.println("system say:");
    }
    
    private void afterMethod(){
        System.out.println("say end");
    }
}

接着通过DefaultPointcutAdvisor,将增强织入目标方法,我们得先定义DefaultPointcutAdvisor的参数,我们可以通过配置文件传入,也可以通过注解传入,我们这里采用注解传入参数的方式

/**
 * @Description TODO
 * @Author 姚仲杰#80998699
 * @Date 2021/8/11 10:28
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({HelloRegistrar.class})
public @interface EnableHello {
    String basePackage() default "com.qimo.omsa.demo.hello";
    
    String[] pointcuts() default {};
    
    int aopOrder() default Integer.MAX_VALUE;
}

我们得先定义DefaultPointcutAdvisor的构造需要两个参数,如下:一个是目标切面,一个是增强.所以我们才先写了上面那个增强功能.

public DefaultPointcutAdvisor(Pointcut pointcut, Advice advice) {
   this.pointcut = pointcut;
   setAdvice(advice);
}

那么我们通过registrarDefaultPointcutAdvisor作为beandefinition注册到registry中,

package com.qimo.omsa.demo.hello;

import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;

/**
 * @Description TODO
 * @Author 姚仲杰#80998699
 * @Date 2021/8/11 10:34
 */
public class HelloRegistrar implements ImportBeanDefinitionRegistrar {
    
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
        BeanDefinitionRegistry registry) {
        if (registry.containsBeanDefinition("helloRegistrar")){
            return;
        }
        Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(EnableHello.class.getName());
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(annotationAttributes);
        String[] aopPointcuts = attributes.getStringArray("pointcuts");
        Integer aopOrder = attributes.getNumber("aopOrder");
        String basePackage = attributes.getString("basePackage");
        if(aopPointcuts == null || aopPointcuts.length <= 0) {
            aopPointcuts = new String[] {String.format("execution(* %s..*.*(..))", basePackage)};
        }
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(Stream.of(aopPointcuts).collect(Collectors.joining(" or ")));
        BeanDefinitionBuilder builder=BeanDefinitionBuilder.rootBeanDefinition(DefaultPointcutAdvisor.class);
        builder.addConstructorArgValue(pointcut);
        builder.addConstructorArgValue(new MethodInvokeInterceptor());
        builder.addPropertyValue("order", aopOrder);
        registry.registerBeanDefinition("helloAdvisor", builder.getBeanDefinition());
    }
}

写个测试用例

@Component
public class SayHello {
    
    public void hello(){
        System.out.println("Hello");
    }
}
@RestController
public class HelloAop {
    
    @Autowired
    SayHello hello;

    @RequestMapping("/hello")
    public void aop(){
        hello.hello();
    }


}

启动类加上

@EnableHello(basePackage = "com.qimo.omsa.demo.hello",pointcuts = {"execution(* com.qimo.omsa.demo.hello..*.*(..))"})

访问,打印如下信息

system say:
system say:
Hello
say end
say end