关键词:
SpringBoot自动装配原理及自定义start开发
前言:
大部分互联网公司都是使用SpringBoot,或SpringCloud作为开发框架。在我们开发中,很可能遇到要开发公共组件提供给其他人调用的场景。随着项目版本的迭代,很可能把公共组件打成jar包。我们在自己的业务类中通过使用@Autowired注解来调用jar包中的公共类,公共方法等。但是如果公共给组件要作升级,也就是二次开发,导致以前定义的一些类名,方法名等发生改变,那么也要更改对应业务代码所依赖jar包的相关代码。这就会造成对jar包的强依赖性。以上问题就会对公共组件的使用是否便捷,维护起来是否方便都有很高的要求。我们期望我们开发的公共组件的jar包,能够直接依赖到我们业务项目中的pom文件里面,而公共组件的版本迭代不影响我们的业务代码。这就运用到了SpringBoot的自动装配。
在我们用Spring、SpringMVC(也就是Web框架)的时候通常都会配置web.xml、applicationContext.xml等配置文件,用Mybaties的时候通常也会配置SqlSessionFactory。但是SpringBoot并没有Web框架,和Mybaties等等这些功能,那么SpringBoot又是如何简化掉这些配置文件的呢?这就需要了解下面要讲的SpringBoot的自动装配。
SpringBoot可以省略版本号的原因:
为什么我们用SpringBoot时,通过在pom文件中加上下面的依赖(parent)就可以省略其他依赖的版本号呢?
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- <version>2.5.1</version>-->
</dependency>
我们点开 <artifactId>spring-boot-starter-parent</artifactId>这个依赖,发现如下依赖,不难理解就是管理依赖的意思。
//定义了核心依赖包的版本号
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.5.1</version>
</parent>
我们继续点开 <artifactId>spring-boot-dependencies</artifactId>,发现如下的pom文件,其中<dependencyManagement>标签就是用来管理依赖的作用。其实SpringBoot的父级工程已经帮我们把很大一部分的依赖都依赖好了,例如apache的activeMq,redis等等。所以我们再用的时候只需要依赖最外层的包就行。我们在开发中通常会遇到在导入pom依赖的时候经常因为各种依赖包的版本不匹配,依赖包冲突这样的问题。SpringBoot这么作的好处就是帮助我们进行版本以及依赖包的管理,防止版本号不匹配,依赖冲突这样问题的产生。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-amqp</artifactId>
<version>$activemq.version</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-blueprint</artifactId>
<version>$activemq.version</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-broker</artifactId>
<version>$activemq.version</version>
</dependency>
</dependencyManagement>
//省略部分代码,关键是dependencyManagement标签
application.yml(.yaml、.properties)文件如何引用的
我们知道SpringBoot默认使用的是Tomcat提供Web服务,但是如果我们想到替换成 jetty,undertow。就可以通过配置application.yml(.yaml、.properties)文件来进行配置覆盖。
那么SpringBoot如何把 .yml文件依赖进来呢?我们通过点开<artifactId>spring-boot-starter-parent</artifactId>这个依赖就会发现spring-boot-starter-parent这个依赖通过<resource>这个标签将.yml文件注入进来了。如下:
//加载资源文件
<resource>
<directory>$basedir/src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/application*.yml</include>
<include>**/application*.yaml</include>
<include>**/application*.properties</include>
</includes>
</resource>
SpringBoot自动装配之@SpringBootApplication注解
@SpringBootApplication注解通常作为启动类的注解,大家都不陌生。而SpringBoot自动装配主要基于@SpringBootApplication注解。
这是一个启动类:
@SpringBootApplication
public class DemoApplication
public static void main(String[] args)
SpringApplication.run(DemoApplication.class, args);
我们点开@SpringBootApplication注解。发现如下代码:
@SpringBootApplication注解是一个复合注解
者四个注解属于Java的元注解。元注解:修饰自定义注解的注解
@Target(ElementType.TYPE) //定义注解的作用范围 (类、方法、属性)
@Retention(RetentionPolicy.RUNTIME) //定义注解的生命周期。(注解保留多久,是在编译期起作用或者运行期起作用)
@Documented //Javadoc 可以配合@param,@see(超链接)注解使用
@Inherited //被它修饰的自定义注解可被子类继承
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = @Filter(
type = FilterType.CUSTOM,
classes = TypeExcludeFilter.class
), @Filter(
type = FilterType.CUSTOM,
classes = AutoConfigurationExcludeFilter.class
)
)
public @interface SpringBootApplication
.....省略
显然上面的四个注解与SpringBoot的自动装配关系不大。
自动装配之@SpringBootConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
我们发现@SpringBootConfiguration注解中只有一个默认属性,我们点开SpringBootConfiguration 注解上的@Configuration注解发现有同样的默认属性。也就是说@SpringBootConfiguration注解相当于@Configuration注解。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration
@AliasFor(
annotation = Component.class
)
String value() default "";
boolean proxyBeanMethods() default true;
boolean proxyBeanMethods() default true这个属性是默认使用CGLIB动态代理。如果@Configuration这个属性值为false那么就与@Component注解起到一样的作用。
@Configuration修饰的类相当于一个配置类(可以理解为 .xml文件)。@Configuration注解的作用就是保证bean的唯一性,而@Component注解不能保证。也就是说在用@Configuration注解修饰的类中获取到的bean是唯一的。
注意:@Configuration注解的原因我们能够在启动类里面能够定义bean,而不用去单独写一个配置类。
自动装配之@EnableAutoConfiguration
我们点开@EnableAutoConfiguration注解发现,有两个注解对自动装配起到作用,分别是:@AutoConfigurationPackage、@Import
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class) //导入参数类到IOC容器
public @interface EnableAutoConfiguration
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default ;
String[] excludeName() default ;
1. @Import(AutoConfigurationImportSelector.class)注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import
Class<?>[] value();
参数如果是ImportSelect的实现类,注册selectImports返回的数组到IOC容器(类的全路径),也就是注册类。批量注册
public String[] selectImports(AnnotationMetadata annotationMetadata)
if (!this.isEnabled(annotationMetadata))
return NO_IMPORTS;
else
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
点开AutoConfigurationImportSelector
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata)
if (!this.isEnabled(annotationMetadata))
return EMPTY_ENTRY;
else
//获取注解属性
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
//从META-INF/spring.factories加载EnableAutoConfiguration类
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
//去重
configurations = this.removeDuplicates(configurations);
//排除掉一些类
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
//检查
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.getConfigurationClassFilter().filter(configurations);
//发布事件,监听
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes)
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
return configurations;
protected Class<?> getSpringFactoriesLoaderFactoryClass()
return EnableAutoConfiguration.class;
点开loadFactoryNames
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader)
ClassLoader classLoaderToUse = classLoader;
if (classLoader == null)
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
String factoryTypeName = factoryType.getName();
return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
发现路径:META-INF/spring.factories
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader)
Map<String, List<String>> result = (Map)cache.get(classLoader);
if (result != null)
return result;
else
HashMap result = new HashMap();
//发现路径:META-INF/spring.factories
try
Enumeration urls = classLoader.getResources("META-INF/spring.factories");
....后面省略
我们找到autoconfigure这个包,打开spring.factories文件,如下图:
我们在spring.factories选择org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration打开发现DispatcherServletRegistrationConfiguration 上面有一个@bean标签。所以SpringBoot就是通过这种形式把DispatcherServletRegistrationConfiguration加载到IOC容器里面。
@Conditional(DispatcherServletAutoConfiguration.DispatcherServletRegistrationCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletAutoConfiguration.DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration
protected DispatcherServletRegistrationConfiguration()
@Bean(
name = "dispatcherServletRegistration"
)
@ConditionalOnBean(
value = DispatcherServlet.class,
name = "dispatcherServlet"
)
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet, WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig)
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet, webMvcProperties.getServlet().getPath());
registration.setName("dispatcherServlet");
registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
multipartConfig.ifAvailable(registration::setMultipartConfig);
return registration;
2. @AutoConfigurationPackage注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(Registrar.class) //保存扫描路径,提供给Spring-data-jpa查询 @Entity
public @interface AutoConfigurationPackage
String[] basePackages() default ;
Class<?>[] basePackageClasses() default ;
@Import(Registrar.class) //保存扫描路径,提供给Spring-data-jpa查询 @Entity
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports
Registrar()
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry)
AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
public Set<Object> determineImports(AnnotationMetadata metadata)
return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
总结:@Import是SpringBoot自动装配的核心注解。
工作流程
- 参数如果是ImportBeanDefinitionRegistrar的实现类,支持手工注册bean
- 参数如果是ImportSelect的实现类,注册selectImports返回的数组到IOC容器(类的全路径),也就是注册类。批量注册
- 参数类如果是普通类,将该类实例化交给IOC容器管理(除开上面两种都是普通类)
自定义starter组件
- 1.新建两个模块(命名规范)
- xxx-spring-boot-autoconfigure 完成自动配置的核心功能
- xxx-spring-boot-starter 管理pom.xml依赖
- 这两个模块可以合成一个包
- 2.使用@ConfigurationProperties接受配置参数
- 3.使用@Configuration+@Bean注册需要的bean @EnableConfigurationProperties开启参数接受
- 4.利用META-INF/spring-factories加载配置类
- 4.1 也可以在启动类@Import加载配置类
- 4.2 自定义注解 @Import
不了解的小伙伴可以参考项目,链接:https://download.csdn.net/download/qq_45065241/19652562?spm=1001.2014.3001.5503
springboot自动装配定义与自定义starter原理,及如何实现自定义装配
前言SpringBoot自动装配定义与自定义starter,基于约定大于配置的原则,实现Spring组件自动装配的目的。 装配的依赖(方式) 模式注解、@Enable模块、条件装配、工厂加载机制。激活自动化装配、实现自动化装配、配置自动... 查看详情
从源码中理解springboot自动装配原理
一、什么是自动装配SpringBoot定义了一套接口规范,这套规范规定:SpringBoot在启动时会扫描外部引用jar包中的META-INF/spring.factories文件,将文件中配置的类型信息加载到Spring容器,并执行类中定义的各种操作。对于外部jar包来说,... 查看详情
玩转springboot原理篇(自动装配前凑之自定义stater)(代码片段)
的pom文件中可以看出,mybatis-spring-boot-starter包会自动引入mybatis-spring-boot-autoconfigure以及mybatis相关依赖包。SqlSessionFactoryExceptionlogger.debug(encoding=xmlns:xsi=xsi:schemaLocation=<modelVersion><groupI 查看详情
springboot自动装配原理
1 查看详情
掌握了springboot的自动装配原理后你会发现自定义starter也是非常容易的哦!(代码片段)
程序员必备技能之SpringBoot的自动装配原理,很详细,建议收藏!!! 在实际项目中我们需要手动来手写Starter组件的场景相对来说还是比较少的,但是对于自定义Starter组件的原理大家还是需要掌握清楚,第一个是... 查看详情
掌握了springboot的自动装配原理后你会发现自定义starter也是非常容易的哦!(代码片段)
程序员必备技能之SpringBoot的自动装配原理,很详细,建议收藏!!! 在实际项目中我们需要手动来手写Starter组件的场景相对来说还是比较少的,但是对于自定义Starter组件的原理大家还是需要掌握清楚,第一个是... 查看详情
springboot自动装配原理
目录工作原理剖析自动配置生效工作原理剖析SpringBoot关于自动配置的源码在spring-boot-autoconfigure-x.x.x.x.jar中:SpringBoot的启动类上有一个@SpringBootApplication注解,这个注解是SpringBoot项目必不可少的注解。那么自动配置原理一定和这... 查看详情
springboot自动装配以及启动原理解析
---- 查看详情
springboot——springboot四大核心之自动装配(源码解析)(代码片段)
...配原理 5.总结1.开始首先肯定要说一下SpringBoot的四大核心了:自动装配:简单配置甚至零配置即可运行项目起步依赖 查看详情
springboot自动装配的原理分析
关于 SpringBoot 的自动装配功能,相信是每一个 Java 程序员天天都会用到的一个功能,但是它究竟是如何实现的呢?今天阿粉来带大家看一下。自动装配案例首先我们通过一个案例来看一下自动... 查看详情
springboot的自动装配原理自定义starter与spi机制
...;开发人员压根不能全身心的投入到业务中去。因此,SpringBoot诞生了,虽然本质上还是属于Spring,但是SpringBoot的优势在于以下两个特点:(1)约定大于配置SpringBoot定义了项目的基本骨架,例如各个环... 查看详情
springboot1.x之启动配置原理及自定义starter
1启动配置原理1.1创建SpringApplication对象@SuppressWarnings({"unchecked","rawtypes"})privatevoidinitialize(Object[]sources){//保存主配置类if(sources!=null&&sources.length>0){this.sources.addAll(Arrays.asLis 查看详情
springboot自动装配原理详细讲解(清楚明白)
注意看代码加的中的注解1.启动类上因为加上了@EnableEurekaServer这个注解才可以实现自动装配@SpringBootApplication@EnableEurekaServerpublicclassEurekaApplicationpublicstaticvoidmain(String[]args)SpringApplication.run(EurekaApp 查看详情
springboot基础自动装配原理(代码片段)
目录1、SpringBoot项目构建1.1、官网生成1.2、IDE在线模板生成2、常见配置2.1、入口类和相关注解2.2、Banner2.3、常规配置2.4、日志2.5、profile环境切换2.6、静态资源3、核心原理3.1、自动装配1、搭配@Configuration注解使用2、实现ImportSe... 查看详情
springboot的自动装配原理自定义starter与spi机制,一网打尽(代码片段)
...;开发人员压根不能全身心的投入到业务中去。因此,SpringBoot诞生了,虽然本质上还是属于S 查看详情
springboot的自动装配原理
原理初探自动配置:pom.xml1.父依赖其中它主要是依赖一个父项目,主要是管理项目的资源过滤及插件!<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2. 查看详情
springboot自动装配原理详细讲解(清楚明白)(代码片段)
注意看代码加的中的注解1.启动类上因为加上了@EnableEurekaServer这个注解才可以实现自动装配@SpringBootApplication@EnableEurekaServerpublicclassEurekaApplicationpublicstaticvoidmain(String[]args)SpringApplication.run(EurekaApp 查看详情
玩转springboot原理篇(自动装配源码剖析)(代码片段)
*ReturntheAutoConfigurationEntrybasedontheAnnotationMetadata*oftheimportingConfiguration*annotationMetadatatheannotationmetadataoftheconfigurationclass*theauto-configurationsthatshouldbeimported*/Auto 查看详情