当前位置: 首页 > 科技观察

进阶篇-SpringBoot2.x自定义starter启动器

时间:2023-03-23 10:51:59 科技观察

1.本文前言SpringBoot为我们提供了一个启动器pom,简化了企业级开发的大部分场景【如springb-boot-starter-web,springb-boot-starter-jdbc等],使用应用场景需要的starterpom,只需要导入对应的starter,就可以得到SpringBoot提供的自动配置的Bean。但是在很多情况下,我们可能需要自定义stater,方便公司内部系统调用通用配置模块时的自动加载和配置。比如公司内部很多系统都有认证授权模块,还有基于AOP的日志方面。这些技术在不同项目中的逻辑基本一致,可以通过启动器自动配置的形式配置这些功能,实现复现。使用效果。(一)Starter的概念SpringBoot之所以能够大大简化我们的开发,是因为用到的一个非常重要的技术就是Starter机制!Starter机制摒弃了之前xml中复杂的配置,将各种配置集成到Starter中。开发者只需要在maven中引入Starter依赖,SpringBoot就可以自动扫描出需要加载的配置信息并按下相应的默认配置。启动项目。所以Starter可以理解为一个可插拔的插件,它提供了一系列方便的依赖描述符,让我们可以一站式获得我们需要的所有Spring及相关技术的服务。应用只需要在maven中引入Starter依赖,SpringBoot会自动扫描需要加载的信息并启动对应的默认配置。我们可以把Starter看成是Springboot的场景启动器。(2)Starter的优势Starter可以让我们在开发过程中摆脱各种依赖库的冲突处理。可以简化原有xml中各种负载的配置信息。SpringBoot为一般研发中的各种场景提供了spring-boot-starter依赖。所有这些依赖模块都遵循约定的默认配置(“约定大于配置”),并允许我们调整这些配置。Starter的出现,极大的帮助开发者从繁琐的框架配置中解脱出来,可以更加专注于业务代码。(3)自定义Starter的场景在我们日常的开发工作中,经常会有一些独立于业务的配置模块。我们往往会将它们放在一个特定的包下,然后如果其他项目需要复用这个块功能时,只需要在pom中引用依赖,使用SpringBoot为我们完成自动组装。常见的自定义Starter场景如:动态数据源登录模块基于AOP技术实现日志切面...(四)自定义Starter命名规范SpringBoot官方推荐其官方starter命名为spring-boot-starter-xxx格式,以及第三方开发者定制的starter,按照xxxx-spring-boot-starter的规则命名,比如mybatis-spring-boot-starter。(5)自定义starter中的几个重要注解@Configuration:表明该类是一个配置类,将成为一个bean,由Spring管理。@EnableConfigurationProperties:启用属性配置,会读取指定类中的属性。@ConditionalOnClass:当类路径下有指定类时,自动配置。@ConditionalOnProperty:判断指定属性是否有指定值。@ConditionalOnMissingBean:当容器中没有指定的bean时创建这个bean。@Import:引入其他配置类二、自定义Starter记录日志案例(一)项目结构两个项目如下:log-spring-boot-starter[starter模块]、spring-boot-demo-starter-test[starter使用demo项目]。(2)log-spring-boot-starter项目pom文件添加依赖org.springframework.bootspring-boot-starter<依赖项>org.springframework.bootspring-boot-configuration-processortrueorg.springframework。启动spring-boot-starter-weborg.projectlomboklomboktrueorg.springframework.bootspring-boot-starter-testtest(3)写请求日志注解/***功能说明:请求日志评论*@authorTuYong*@date2022/9/720:48*/@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public@interfaceRequestLog{//接口方法的描述信息Stringdesc()default"";}(4)写配置类@Setter@Getter@ConfigurationProperties(prefix="request.log")publicclassLogProperties{privateBooleanenabled=Boolean.FALSE;}(5)写日志拦截器/***功能描述:日志拦截器*@authorTuYong*@date2022/9/720:49*/@Slf4jpublicclassLogInterceptorimplementsHandlerInterceptor{privatestaticfinalThreadLocalTHREAD_LOCAL=newThreadLocal<>();@OverridepublicbooleanpreHandle(HttpServletRequest请求,HttpServletResponse响应,对象处理程序)抛出异常{HandlerMethodhandlerMethod=(HandlerMethod)处理程序;RequestLogmethodAnnotation=handlerMethod.getMethodAnnotation(RequestLog.class);if(methodAnnotation!=null){longstart=System.currentTimeMillis();THREAD_LOCAL.set(开始);}返回真;}@OverridepublicvoidpostHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,ModelAndViewmodelAndView)throwsException{HandlerMethodhandlerMethod=(HandlerMethod)handler;RequestLogmethodAnnotation=handlerMethod.getMethodAnnotation(RequestLog.class);if(methodAnnotation!=null){方法method=handlerMethod.getMethod();StringrequestUri=request.getRequestURI();StringmethodName=method.getDeclaringClass().getName()+":"+method.getName();Stringdesc=methodAnnotation.desc();长端=System.currentTimeMillis();长开始=THREAD_LOCAL.get();长l=结束-开始;THREAD_LOCAL.remove();log.info("请求路径:{},请求方法:{},描述信息:{},总耗时:{}",requestUri,methodName,desc,l);}}}(6)编写自动配置类/***功能说明:自动配置*@authorTuYong*@date2022/9/720:55*/@Configuration@EnableConfigurationProperties({LogProperties.class})@ConditionalOnProperty(prefix="request.log",name={"enabled"},havingValue="true")publicclassLogAutoConfigurationimplementsWebMvcConfigurer{@OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){registry.addInterceptor(newLogInterceptor()).addPathPatterns("/api/**");WebMvcConfigurer.super.addInterceptors(注册表);}}(7)编写spring.factories,在resources/META-INF/下创建spring.factories文件,配置自动组装类路径org.springframework.boot.autoconfigure.EnableAutoConfiguration=\cn.javaxxw.springboot.LogAutoConfiguration(8)创建入门演示项目并引入入门依赖项cn.javaxxwlog-spring-boot-starter0.0.1-SNAPSHOTorg.springframework.bootspring-boot-starter-web(9)编写测试接口,使用starter中定义的注解@RestController@RequestMapping("api")publicclassApiController{@GetMapping("test")@RequestLog(desc="TestInterface")publicObjecttest(){return"testapi!";}}(10)配置文件server:port:8080#openstartercomponentrequest:log:enabled:true(11)功能测试通过请求测试接口可以看到starter中的拦截器已经生效(12)演示项目代码gitee:https://gitee.com/trazen/springboot-demo.git3,原理分析在第二章的开发过程中,有两个地方需要我们了解:(1)starter的自动识别和加载:spring.factories中EnableAutoConfiguration的原理(2)智能可配置的自动加载:注解在@Configuration配置类中。(1)配置类的自动加载机制会在SpringBoot的启动类中使用@SpringBootApplication注解。该注释引入了@EnableAutoConfiguration注释。然后@EnableAutoConfiguration将@Import(AutoConfigurationImportSelector.class)。AutoConfigurationImportSelector.class的selectImports方法最终会通过SpringFactoriesLoader.loadFactoryNames加载META-INF/spring.factories中的EnableAutoConfiguration配置值,也就是我们上面设置的资源文件。(2)智能可配置自动加载在实际项目中,我们并不总是希望使用默认配置。比如有时候想自己配置相关的功能,或者启动配置类只有在某些bean没有加载的时候才会加载。Starter可以实现这些常见的场景,并提供了如下解决方案:@Conditional*注解springbootstarter提供了一系列的@Conditional*注解,表示什么时候启用相应的配置。具体可以查看springboot的官方文档。常用的注解如下:@ConditionalOnBean:当容器中有指定的Bean时。@ConditionalOnClass:当类路径下有指定类时,自动配置。@ConditionalOnProperty:判断指定属性是否有指定值。@ConditionalOnMissingBean:当容器中没有指定的bean时创建这个bean。例如@ConditionalOnProperty(prefix="request.log",name={"enabled"},havingValue="true")在我们的例子中,请求日志拦截功能只有在应用配置request.log.enabled时才会启用=true生效。@ConfigurationProperties注解这个注解主要是为了解决以下场景:我想使用启动器的默认配置类,但是我想在配置中自定义一些参数。@ConfigurationProperties类完成这项工作。比如上面的例子,我们通过配置中的enabled参数来判断是否开启组件的日志拦截功能。