Spring Boot为我们提供了简化企业级开发绝大多数场景的
starter pom【比如springb-boot-starter-web,springb-boot-starter-jdbc等】, 使用应用场景所需要的starter pom,只需要引入对应的starter,即可以得到Spring Boot为我们提供的自动配置的Bean。
然而,可能在很多情况下,我们需要自定义stater,这样可以方便公司内部系统调用共同的配置模块的时候可以自动进行装载配置。比如公司的很多内部系统都有认证授权模块、以及基于AOP实现的日志切面等,这些技术在不同的项目中逻辑基本相同,而这些功能可以通过starter自动配置的形式进行配置,即可达到可复用的效果。
SpringBoot之所以大大地简化了我们的开发,用到的一个很重要的技术就是Starter机制!
Starter机制抛弃了以前xml中繁杂的配置,将各种配置统一集成进了Starter中,开发人员只需要在maven中引入Starter依赖,SpringBoot就能自动扫描出要加载的配置信息并按相应的默认配置来启动项目。
所以Starter可以理解为一个可拔插式的插件,提供了一系列便利的依赖描述符,使得我们可以获得所需的所有Spring和相关技术的一站式服务。应用程序只需要在maven中引入Starter依赖,SpringBoot就能自动扫描到要加载的信息并启动相应的默认配置,我们可以把Starter看做是Springboot的场景启动器。
在我们的日常开发工作中,经常会有一些独立于业务之外的配置模块,我们经常将其放到一个特定的包下,然后如果另一个工程需要复用这块功能的时候,只需要将其在pom中引用依赖即可,利用SpringBoot为我们完成自动装配即可。
常见的自定义Starter场景比如:
SpringBoot官方建议其官方推出的starter以spring-boot-starter-xxx的格式来命名,而第三方开发者自定义的starter则以xxxx-spring-boot-starter的规则来命名,比如 mybatis-spring-boot-starter。
两个工程如下:log-spring-boot-starter[starter模块],spring-boot-demo-starter-test[starter使用演示工程]。
org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-configuration-processor true org.springframework.boot spring-boot-starter-web org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test
/** * 功能描述: 请求日志注解 * @author TuYong * @date 2022/9/7 20:48 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RequestLog { //接口方法上的描述信息 String desc() default ""; }
@Setter @Getter @ConfigurationProperties(prefix = "request.log") public class LogProperties { private Boolean enabled = Boolean.FALSE; }
/** * 功能描述: 日志拦截器 * @author TuYong * @date 2022/9/7 20:49 */ @Slf4j public class LogInterceptor implements HandlerInterceptor { private static final ThreadLocalTHREAD_LOCAL = new ThreadLocal<>(); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HandlerMethod handlerMethod = (HandlerMethod) handler; RequestLog methodAnnotation = handlerMethod.getMethodAnnotation(RequestLog.class); if(methodAnnotation != null){ long start = System.currentTimeMillis(); THREAD_LOCAL.set(start); } return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { HandlerMethod handlerMethod = (HandlerMethod) handler; RequestLog methodAnnotation = handlerMethod.getMethodAnnotation(RequestLog.class); if(methodAnnotation != null){ Method method = handlerMethod.getMethod(); String requestUri = request.getRequestURI(); String methodName = method.getDeclaringClass().getName()+":"+method.getName(); String desc = methodAnnotation.desc(); long end = System.currentTimeMillis(); long start = THREAD_LOCAL.get(); long l = end - start; THREAD_LOCAL.remove(); log.info("请求路径:{},请求方法:{},描述信息:{},总计耗时:{}",requestUri,methodName,desc,l); } } }
/** * 功能描述: 自动配置 * @author TuYong * @date 2022/9/7 20:55 */ @Configuration @EnableConfigurationProperties({LogProperties.class}) @ConditionalOnProperty( prefix = "request.log", name = {"enabled"}, havingValue = "true" ) public class LogAutoConfiguration implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LogInterceptor()).addPathPatterns("/api/**"); WebMvcConfigurer.super.addInterceptors(registry); } }
在resources/META-INF/下建立spring.factories文件,配置自动装配类路径
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ cn.javaxxw.springboot.LogAutoConfiguration
cn.javaxxw log-spring-boot-starter 0.0.1-SNAPSHOT org.springframework.boot spring-boot-starter-web
@RestController @RequestMapping("api") public class ApiController { @GetMapping("test") @RequestLog(desc = "测试接口") public Object test(){ return "test api!"; } }
server: port: 8080 # 开启starter组件 request: log: enabled: true
通过请求测试接口,我们可以看到starter中的拦截器已经生效
gitee: https://gitee.com/trazen/springboot-demo.git
第二章节中的开发流程,有两个地方需要我们了解一下:
(1)starter的自动识别加载:spring.factories里的EnableAutoConfiguration原理。
(2)实现自动加载的智能化、可配置化:@Configuration配置类里注解。
在SpringBoot的启动类都会加上 @SpringBootApplication 注解。这个注解会引入 @EnableAutoConfiguration 注解。然后 @EnableAutoConfiguration 会 @Import(AutoConfigurationImportSelector.class) 。
AutoConfigurationImportSelector.class 的 selectImports 方法最终会通过 SpringFactoriesLoader.loadFactoryNames ,加载 META-INF/spring.factories 里的 EnableAutoConfiguration 配置值,也就是我们上文中设置的资源文件。
实际项目中,我们并不总是希望使用默认配置。比如有时候我想自己配置相关功能,或者只有某些bean没有被加载时才会加载starter配置类。这些常见的场景Starter都可以实现,并提供了如下的解决方案:
@Conditional*注解
springboot starter提供了一系列的@Conditional*注解,代表什么时候启用对应的配置,具体的可以去查看springboot的官方文档。常用注解如下:
比如我们案例中的 @ConditionalOnProperty(prefix = "request.log", name = {"enabled"},havingValue = "true") ,只有在应用配置 request.log.enabled = true 时该请求日志拦截功能才会生效。
@ConfigurationProperties注解
这个注解主要是为了解决如下场景:我想要使用starter的默认配置类,但是又想对配置中的某些参数进行自定义配置。@ConfigurationProperties类就是做这个工作的。例如上述例子中,我们在配置中使用enabled参数来决定是否启动该组件的日志拦截功能。