我们在做框架的时候往往有这样的需求,不同的项目,相同的功能可能需要相同的增强,但是他们可能位于不同的包下,甚至不知道别人会使用什么样的包名.此时你不得不硬性规定,什么类必须写在什么包下,显然这很不友好.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);
}
那么我们通过registrar将DefaultPointcutAdvisor作为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