AutoConfiguration
之后,是时候开启我们的自定义模式了!毕竟离springboot-stater就只差一个demo,闲话不说,马上就来搞一个demo看看。
为了简单好理解,demo主要实现了以下两个功能:
-
使用aop,实现对方法参数的监控; -
定义一个自己的配置类,读取我们在yml或properties里面配置的参数,并在启动时打印参数绑定结果。
starter工程
看了自动配置原理之后,应该知道META-INF/spring.facotires
这个家伙是配置的重点了吧,我们很多的东西都是在这个里头。因此,我们自己的stater当然也少不了这个配置文件,先贴个工程结构。
代码清单
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
实体绑定的信息。
智能识图结果:
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
不出意外,你应该可以看到类似以下输出。
红色标记的是我们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