springboot-starter自定义,写属于你自己的starter

上一篇《Spring源码解读(第十二弹)-starter自动配置原理,帮我找到老板,告诉他,我翅膀硬了了解了AutoConfiguration之后,是时候开启我们的自定义模式了!毕竟离springboot-stater就只差一个demo,闲话不说,马上就来搞一个demo看看。

为了简单好理解,demo主要实现了以下两个功能:

  1. 使用aop,实现对方法参数的监控;
  2. 定义一个自己的配置类,读取我们在yml或properties里面配置的参数,并在启动时打印参数绑定结果。

starter工程

看了自动配置原理之后,应该知道META-INF/spring.facotires这个家伙是配置的重点了吧,我们很多的东西都是在这个里头。因此,我们自己的stater当然也少不了这个配置文件,先贴个工程结构。

代码清单

springboot-starter自定义,写属于你自己的starter

my-starter-code_list.png
MyAnnotation: 自定义注解
MyAroundConfiguration: 环绕拦截标记有我们自定义注解的方法
MyProperties: 实体类,用来读取配置文件属性
UserProperties: 实体类,用来测试嵌套属性,该实体被MyProperties持有
MyPropertiesPrintRunner: 用来在启动之后打印读取到的MyProperties的信息
MyArgsMonitorAutoConfiguration: 自动配置主类,所有自动配置bean的入口
spring.factories: 自动配置的核心文件,用来配置自动配置入口配置类

代码明细

MyAnnotation

@Target(ElementType.METHOD)
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {}

自定义注解,想必各位大佬已经了如指掌,小弟就无需向各位大佬解释了。

MyAroundConfiguration

public class MyAroundConfiguration {

private final Logger log = LoggerFactory.getLogger(getClass());

@Bean
public PointcutAdvisor myAopPointCutAdvisor() {
return new StaticMethodMatcherPointcutAdvisor(new MyAopAroundPointCutAdvice()) {
@Override
public boolean matches(Method method, Class<?> aClass) {
// 匹配方法上标有特定注解的方法进行aop拦截
return method.getAnnotation(MyAnnotation.class) != null;
}
};
}

public class MyAopAroundPointCutAdvice implements MethodInterceptor {

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
log.info("------------------my args monitor start--------------------");
Object[] args = mi.getArguments();
for (int i = 0; i < args.length; i++) {
log.info("args[{}]={}", i, args[i]);
}
Object ret = mi.proceed();
log.info("------------------my args monitor end--------------------");
return ret;
}
}

}

卧槽?这是啥?为什么我的AOP不需要@Aspect,@Around不要惊讶,鄙人的AOP就是这么优秀,这种写法大家在网上都找不到的哦(不信的可以试试),先把B格直接拉满!再慢慢往下聊。

简单解释一下,这个其实就是AOP,Aop的三要素 PointCut, Advice,Advisor@Aspect只不过是依靠你给的表达式给你封装了一层,我只是换了种方式向Spring提供了三要素而已。还不太看得懂的道友也别急,你的道行会慢慢提升的!加油!

MyAopAroundPointCutAdvice实现了MethodInterceptor,想必各位看到invoke的时候已经不陌生了, MethodInvocation就是被我们代理的方法,在这个方法中做方法的前后处理,打印出当前方法的参数以及顺序。

MyProperties、UserProperties

@ConfigurationProperties(prefix = MyProperties.MY_PREFIX)
public class MyProperties {
public static final String MY_PREFIX = "myconfig";

/**
* 不加该注解也能正常得到值
*/

@NestedConfigurationProperty
private UserProperties user;
private String job;
}

public class UserProperties {
private String username;
private Integer age;
private String sex;
}

绑定配置文件属性的类,如果你的starter有一些配置配置需要读取的话可以采用这种方式来读取,你可以想象一下MybatisProperties

@ConfigurationProperties注解可以把指定前缀的配置文件参数绑定到当前实体对象上,不解释了哈,相信各位道友已经无师自通了!

MyPropertiesPrintRunner

public class MyPropertiesPrintRunner implements ApplicationRunner, Ordered {

private final Logger log = LoggerFactory.getLogger(getClass());

private final MyProperties myProperties;

public MyPropertiesPrintRunner(MyProperties myProperties) {
this.myProperties = myProperties;
}

@Override
public void run(ApplicationArguments args) throws Exception {
log.info("From my args monitor starter, MyProperties: {} ", myProperties);
}

@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}

一个spring的runner,在项目启动之后,用来打印我们绑定到MyProperties实体上的配置文件信息。

MyArgsMonitorAutoConfiguration

@Configuration(proxyBeanMethods = false)
@AutoConfigureAfter(AopAutoConfiguration.class)
@EnableConfigurationProperties(MyProperties.class)
public class MyArgsMonitorAutoConfiguration
{

private MyProperties myProperties;

public MyArgsMonitorAutoConfiguration(MyProperties myProperties) {
this.myProperties = myProperties;
}

@Bean
public MyPropertiesPrintRunner getMyConfigService() {
return new MyPropertiesPrintRunner(myProperties);
}

@Import({MyAroundConfiguration.class})
public static class MyArgsMonitorConfig
{
}

}

自动配置的入口类。记住是入口类!!!这点很重要,你的starter在被别人引入的时候,你必须要保证所有的bean都需要从这个一个或者几个入口类中被配置

想不通的朋友想想@SpringbootApplication注解中的exclude属性,这个是可以排除掉你的自动配置启动的。如果你的starter除了入口类之外,自己就已经有类似@Service之类的注解的话,那么如果被别个项目误扫描到,那你就有可能影响到别个项目的启动甚至更严重的问题。

如果你的大部分工作也是写这之类的组件,那你应该很清楚的知道组件不应该影响到业务项目,即使你的组件因为各种bug不能正常工作,也不能影响正常的业务逻辑!(因业务逻辑必须报错的情况除外)

所以starter需要注意的问题,也是很多朋友很容易忽略的一点,就是你的starter中所有需要交给Spring管理的bean,理应且有必要全部由一个或多个自动配置类入口提供,而这个入口就是需要配置到spring.factories中的

大家可以看到,除了我的这个自动配置类,我其他的所有类都没有类似@Service之类的注解,所有的bean的配置都在这个类里面。

解释一下当前类的注解。

@AutoConfigureAfter: 指出当前类需要在某个自动配置完成之后才开始配置;@EnableConfigurationProperties: 为@ConfigurationProperties注解提供支持,什么意思呢;解释一下这个,把@ConfigurationProperties标注的类(MyProperties)注册成bean,以支持依赖注入,本身这些类是不会被注册成bean的,当然我们可以在配置类上加@Component注解。使用这个注解可以在我们需要某个配置类注册成bean的时候,才使用,侵入性小,避免了配置类上加@Component注解。

spring.factories

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
com.wt.myaop.MyArgsMonitorAutoConfiguration

配置我们自动配置类的入口

打包安装

starter准备完毕之后,install到本地,然后我们开启我们的Demo项目,引入我们的starter,看是否正常工作。

mvn clean install

Demo工程

引入依赖

<dependency>
<groupId>com.wt.starter</groupId>
<artifactId>spring-boot-myaop-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>

代码清单

springboot_source_learn
├─ pom.xml
└─ src/main
├─ java/com.wt.test
|- service
|- MyStarterService.java
|- MystarterServiceImpl.java
|- controller
|- MyStarterController.java
|- resources
|- application.yml
MyStarterService/Impl: 使用到我们starter中的自定义注解,用来测试starter的正确性
MyStarterController: 一个测试接口,用来调用我们的MyStarterService
application.yml: 配置我们的MyProperties的相关属性

代码明细

MyStarterServiceImpl

@Service
public class MyStarterServiceImpl implements MyStarterService {

@Override
@MyAnnotation
public void helloStarter(String msg, Long currentTime) {
System.out.println("----hello starter,msg = " + msg + ",currentTime = " + currentTime);
}

}

一个简单的方法,打印一个当前时间。

MyStarterController

@RestController
@RequestMapping("/starter")
public class MyStarterController {

@Resource
private MyStarterService myStarterService;

@RequestMapping("/test")
public Object starter() {
myStarterService.helloStarter("wt", System.currentTimeMillis());
return "success";
}

}

调用我们的测试方法。

application.yml

myconfig:
job: programer
user:
username: wt
age: 25
sex: real_man

只贴了MyProperties相关的一些属性。

见证奇迹的时候

属性绑定结果

我们启动我们的Demo项目,不出意外,你能看到我们的日志中打印的MyProperties实体绑定的信息。

springboot-starter自定义,写属于你自己的starter

myProperties_result.png

智能识图结果:

2023-03-25 11:38:15.666  INFO ... : From my args monitor starter, MyProperties: MyConfig{user=UserConfig{username='wt', age=25, sex='real_man'}, job='programer'}

方法拦截结果

接下来访问接口,看看我们的starter是否正常工作。

$ curl GET http://localhost:8080/starter/test

不出意外,你应该可以看到类似以下输出。

springboot-starter自定义,写属于你自己的starter

my_args_monitor_result.png

红色标记的是我们starter中的输出,黄色标记的是我们Demo中的service的输出,说明我们的starter生效了。

智能识图:

2023-03-25 11:38:39.914  ... : ------------------my args monitor start------------------
2023-03-25 11:38:39.914 ... : args[0]=wt
2023-03-25 11:38:39.914 ... : args[1]=1679715519914
----hello starter,msg = wt,currentTime = 1679715519914
2023-03-25 11:38:39.914 ... : ------------------my args monitor end--------------------

好了,看到这里starter部分就算结束了,总的来讲这个家伙也不难,对吧,嘻嘻。

胜利的旗帜在向各位招手,道友,加油!

Good Luck!

示例工程自提点

starter: https://gitee.com/wt123/learn/tree/master/spring-boot-myaop-starter

Demo: https://gitee.com/wt123/learn/tree/master/springboot_source_learn

原文始发于微信公众号(心猿易码):springboot-starter自定义,写属于你自己的starter

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

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/133251.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!