本篇文章贯彻”脉络到细节“的指针,为了让大家通俗易懂,面向应用编程。
假如你现在想要开发一款AOP中间件,提供公众服务,需要进行动态配置扫描切面(pointcut),由客户端定制化处理切面实现方法级别的AOP代理,如何去考虑?
这里有几个技术点我们需要考虑:
1 无缝衔接springboot-auto-configuration
2 由客户端定制化setup自己的组件(具有公共集成特性的无缝对接服务)
依照前面几个点我们来讨论一下细节
1 无缝衔接如何处理?
我们都知道在springboot2.0为我们准备了定制化注解如下
@ConditionalOnProperty //YML或者properties文件根据指定配置加载属性
@ConditionalOnMissingBean //当容器里边有对应BEAN存在时加载BEAN
@ConfigurationProperties //无条件加载属性
@Bean //伴随这属性配置加载时的BEAN加载
@ConditionalOnClass classpath下有对应class文件存在时加载BEAN
@Configuration //
@Import //注入BEAN
这几个注解里边除了@import其他的应该都好理解对吧,讲到第2点的时候我们在回来讲@import注解有什么用。
这些定制化注解在自己服务集成springboot的时候很有用,加入springboot注解,另外在配置对应属性或者BEAN就可以在当前spring容器里边用了,但这只是单服务集成springboot,那么如果你自己有中间件的研发需求需要集成springboot,并且可作为类似springboot组件无缝被其他服务集成,如何处理?
这里可以引出两点:
我们在组件里边用了springboot-auto(B项目),我们自己的服务也是springboot项目(A项目),那么是不是就是
A dependency B 就可以自动应用B中的包和自动化配置了? 经过测试默认是无效的,原因就是因为B服务也是一个容器默认A容器里边的BEAN是无法使用B容器里边的BEAN的,那么这个时候大家肯定回去百度,百度下来的结果我给大家总结下来两个:
那么我们假设一下现在有一个组件需要做到系统无缝集成,依赖SPRINGBOOT,那么我们的需求就是这样了:
无需任何而外配置,无缝。
接着我们就来探讨第二点了
2 由客户端定制化setup自己的组件
我现在的需求是AOP,AOP是什么?我就不百度了,我的理解是一种切面编程技术,用以运行时动态截取目标对象以对目标对象进行修改,从而达到运行时修改目标运行时系统行为的目的。
传统实现如下:
// * @date 19-10-22
// * @auther jackliang
// * @description TODO
// */
//@Aspect
//@Log4j
//public class LogerSannerUnifiedAop {
//
//
// @Pointcut("execution(public * com..*.*service..*(..))")
// public void ResultSannerUnifiedAop() {
// }
//
// @Around("ResultSannerUnifiedAop()")
// public Object arround(ProceedingJoinPoint point) throws Throwable {
//
// }
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResultAssembly {
/**
* User-defined message.
* @return
*/
String message() default "";
/**
* User-defined return status code.
* @return
*/
int code () default 200;
}
@Aspect
public class ResultAnnotationUnifiedAop {
@Pointcut("@annotation(com.xxxx.spring.boot.aop.anno.ResultAssembly)")
public void ResultUnifiedAop() {
}
@Around("ResultUnifiedAop()")
public ResponseEntity around(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature)point.getSignature();
Method method = signature.getMethod();
ResultAssembly result_assembly = method.getAnnotation(ResultAssembly.class);
以上分别是基于注解的和基于包扫描的切面技术
这些都不符合笔者最初的预期,笔者计划实现定制化包扫描,加注解的太麻烦,切面扫描的无法实现为何:
@Pointcut("execution(public * com..*.*service..*(..))")我们可以转化一下变成下面这样
String str = "";
@pointcut(str)
这样的话是不是就可以实现str的定制化了呢,往往想得简单事与愿违我们看下下面的代码:
有一个编译异常,@pointCut里边的内容只能是常量,也就是说程序启动是不能被更改的所以这个方式肯定是不行的,编译都过不去
所以引出下面这种方式,这也是druid 过滤器配置切面的方式
@ConditionalOnProperty("spring.xxxx,xxxx.aop-patterns")
public class xxxxxSpringAopConfiguration {
@Bean
public Advice advice() {
return new xxxxInterceptor();
}
@Bean
public Advisor advisor(StatProperties properties) {
return new RegexpMethodPointcutAdvisor(properties.getAopPatterns(), advice());
}
@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
}
很显然我们可以用RegexpMethodPointcutAdvisor进行自动化扫描,在启动的时候依据项目修改包路经
第二点我们重点是定制化,表示AOP,AOP讲完了我们开始讲定制化
我们怎么样做到不侵入配置,直接加入依赖就可以自动扫描,解藕包命名规则的呢?
springboot 官网提供了metedata模块:
这个包程序就可以作为依赖组件程序的切入点,A项目启动会启动B组件指定的入口启动类,这个细节我曾经问过对应开源项目作者,但是没有很好的回复,这里总结一下。
这个时候我们就需要引入第一节的伏笔@import,我们来看下是怎么用的
/**
* @date 19-10-24
* @auther jackliangz
* @description TODO
*/
@Configuration
@Import({
ScanerAspectAutoConfiguration.class,
ResponseValueAdviceHanlder.class})
public class AnnotationAspectKiliResultConfigure {
}
基于上面讲到的spring.factory的配置我们指定入口然后在这里就可以加入自己想要加入的配置启动项配合spring-metedada无缝启动,被依赖项目无需加入任何代码的自动化配置
那么做到自动化配置我们还可以画龙点睛的加上
这个是为了我们在springboot属性文件中加入时候的提示例如:
那么接入这个metadata的配置就可以实现springboot-starter的无缝集成了
以上内容为中间件研发基础内容,笔者拜着仰望大佬的心态写下这篇文章,这件事情告诉了笔者,不一定所有的技术别人都会和你无偿分享,即便是在中国,即便已开源,万事无绝对。