springboot系列--自动配置原理

springboot系列--自动配置原理 一、容器功能一、组件添加功能一、ConfigurationConfiguration有两种模式Full模式与Lite模式。1、配置 类组件之间无依赖关系用Lite模式加速容器启动过程减少判断2、配置类组件之间有依赖关系方法会被调用得到之前单实例组件用Full模式/** * 1、配置类里面使用Bean标注在方法上给容器注册组件默认也是单实例的 * 2、配置类本身也是组件 * 3、proxyBeanMethods代理bean的方法 * Full(proxyBeanMethods true)、【保证每个Bean方法被调用多少次返回的组件都是单实例的】 * Lite(proxyBeanMethods false)【每个Bean方法被调用多少次返回的组件都是新创建的】 * 组件依赖必须使用Full模式默认。其他默认是否Lite模式 * * * */ Configuration(proxyBeanMethods false) //告诉SpringBoot这是一个配置类 配置文件 public class MyConfig { /** * Full:外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象 * return */ Bean //给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值就是组件在容器中的实例 public User user01(){ User zhangsan new User(zhangsan, 18); //user组件依赖了Pet组件 zhangsan.setPet(tomcatPet()); return zhangsan; } Bean(tom) public Pet tomcatPet(){ return new Pet(tomcat); } }二、Bean、Component、Controller、Service、Repository都可以实现往容器中注入添加组件三、ComponentScan、Import* 4、Import({User.class, DBHelper.class})* 给容器中自动创建出这两个类型的组件、默认组件的名字就是全类名****/Import({User.class, DBHelper.class})Configuration(proxyBeanMethods false) //告诉SpringBoot这是一个配置类 配置文件public class MyConfig {}四、Conditional条件装配满足Conditional指定的条件则进行组件注入1、ConditionalOnBean存在指定的bean则注入2、ConditionalOnMissingBean不存在指定的bean则注入3、ConditionalOnClass存在指定类则注入4、ConditionalOnMissingClass不存在指定的类则注入二、原生配置文件导入ImportResource(“classpath:beans.xml”)导入指定的bean.xml文件中的配置进入容器中形成bean三、配置绑定使用Java读取到properties文件中的内容实际项目中一般会把这里的配置放到nacos中并且把它封装到JavaBean中以供随时使用public class getProperties { public static void main(String[] args) throws FileNotFoundException, IOException { Properties pps new Properties(); pps.load(new FileInputStream(a.properties)); Enumeration enum1 pps.propertyNames();//得到配置文件的名字 while(enum1.hasMoreElements()) { String strKey (String) enum1.nextElement(); String strValue pps.getProperty(strKey); System.out.println(strKey strValue); //封装到JavaBean。 } } }一、ConfigurationProperties这个注解只是单纯的进行配置绑定并还没有注入容器所以如果还想通过spring的注解从容器中获取对象就需要使用Component或者其他一些注入容器中的注解把ConfigurationProperties修饰的对象也一起注入容器中。/** * 只有在容器中的组件才会拥有SpringBoot提供的强大功能 */ Component ConfigurationProperties(prefix mycar) public class Car { private String brand; private Integer price; public String getBrand() { return brand; } public void setBrand(String brand) { this.brand brand; } public Integer getPrice() { return price; } public void setPrice(Integer price) { this.price price; } Override public String toString() { return Car{ brand brand , price price }; } }二、EnableConfigurationProperties ConfigurationPropertiesEnableConfigurationProperties开启指定类的绑定功能且自动注入到容器中去也就是那个类使用了ConfigurationProperties修饰通过EnableConfigurationProperties指定那个类就可以在不使用Component的情况下注入进容器EnableConfigurationProperties(Car.class) //1、开启Car配置绑定功能 //2、把这个Car这个组件自动注册到容器中 public class MyConfig { }二、自动配置原理一、引导加载自动配置自动配置主要是同过SpringBootApplication这个类注解实现的这个注解是一个合成注解里面由多个注解组成。SpringBootConfigurationEnableAutoConfigurationComponentScan(excludeFilters { Filter(type FilterType.CUSTOM, classes TypeExcludeFilter.class),Filter(type FilterType.CUSTOM, classes AutoConfigurationExcludeFilter.class) })一、SpringBootConfiguration使用Configuration修饰这个注解。代表当前这个注解是一个配置类Target(ElementType.TYPE) Retention(RetentionPolicy.RUNTIME) Documented Configuration public interface SpringBootConfiguration { }二、ComponentScan指定扫描哪些Spring注解ComponentScan(excludeFilters { Filter(type FilterType.CUSTOM, classes TypeExcludeFilter.class), Filter(type FilterType.CUSTOM, classes AutoConfigurationExcludeFilter.class) }) excludeFilters :排除指定的组件 FilterType.CUSTOM按照自定义规则排除 TypeExcludeFilter.class自定义规则组件三、EnableAutoConfigurationAutoConfigurationPackage Import(AutoConfigurationImportSelector.class) public interface EnableAutoConfiguration {}一、AutoConfigurationPackage自动导包配置注解里面是Import(AutoConfigurationPackages.Registrar.class)1、AutoConfigurationPackages.Registrar.classOverride public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { register(registry, new PackageImport(metadata).getPackageName()); } metadata注解元信息也就是这个注解修饰的类的全路径类名 register扫描注解所在的类的包下的所有组件并注册进容器也就是SpringAnnotateApplication所在的包。所以springboot默认自动导入SpringAnnotateApplication所在的包下的所有组件。public static void register(BeanDefinitionRegistry registry, String... packageNames) { // BEAN:AutoConfigurationPackages.class.getName() // 判断容器中是否有自动配置类如果该bean已经注册则将要注册包名称添加进去 if (registry.containsBeanDefinition(BEAN)) { BeanDefinition beanDefinition registry.getBeanDefinition(BEAN); ConstructorArgumentValues constructorArguments beanDefinition.getConstructorArgumentValues(); constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames)); } else { // //如果该bean尚未注册则注册该bean参数中提供的包名称会被设置到bean定义中去 GenericBeanDefinition beanDefinition new GenericBeanDefinition(); // 注册的是BasePackages这个类 beanDefinition.setBeanClass(BasePackages.class); // 这是他的参数 beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames); beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); registry.registerBeanDefinition(BEAN, beanDefinition); } }AutoConfigurationPackages.Registrar这个类就干一个事注册一个Bean这个Bean就是org.springframework.boot.autoconfigure.AutoConfigurationPackages.BasePackages它有一个参数这个参数是使用了AutoConfigurationPackage这个注解的类所在的包路径,保存自动配置类以供之后的使用.二、Import({AutoConfigurationImportSelector.class}主要是AutoConfigurationImportSelector类中getAutoConfigurationEntry方法给容器进行批量导入组件。1、进入getAutoConfigurationEntry方法找到List configurations this.getCandidateConfigurations(annotationMetadata, attributes);这行代码这里就把指定路径下文件中的配置类加载进入容器了。2、进入getCandidateConfigurations只有一行List configurations SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());是主要代码从这里进入可以看到具体加载逻辑3、loadSpringFactories(Nullable ClassLoader classLoader)方法是主要的加载逻辑private static MapString, ListString loadSpringFactories(Nullable ClassLoader classLoader) { // 先从缓存中获取配置文件 MultiValueMapString, String result (MultiValueMap)cache.get(classLoader); if (result ! null) { return result; } else { // 如果没有从指定classLoader则默认加载系统下所有META-INF/spring.factories位置中的文件 try { EnumerationURL urls classLoader ! null ? classLoader.getResources(META-INF/spring.factories) : ClassLoader.getSystemResources(META-INF/spring.factories); MultiValueMapString, String result new LinkedMultiValueMap(); // 循环处理然后放入缓存 while(urls.hasMoreElements()) { URL url (URL)urls.nextElement(); UrlResource resource new UrlResource(url); Properties properties PropertiesLoaderUtils.loadProperties(resource); Iterator var6 properties.entrySet().iterator(); while(var6.hasNext()) { Map.Entry?, ? entry (Map.Entry)var6.next(); String factoryTypeName ((String)entry.getKey()).trim(); String[] var9 StringUtils.commaDelimitedListToStringArray((String)entry.getValue()); int var10 var9.length; for(int var11 0; var11 var10; var11) { String factoryImplementationName var9[var11]; result.add(factoryTypeName, factoryImplementationName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException var13) { throw new IllegalArgumentException(Unable to load factories from location [META-INF/spring.factories], var13); } } }spring-boot-autoconfigure这个包里面也有META-INF/spring.factories文件文件里面写死了spring-boot启动就要给容器中加载的所有配置类。虽然springboot启动时就默认加载META-INF/spring.factories下的所有配置文件但是这些配置文件并不是所有都会启动他们只会按条件装配只有符合某些条件才会开启。以下是几个springboot底层按条件开启配置的栗子1、springboot底层给容器中加入了文件上传解析器BeanConditionalOnBean(MultipartResolver.class) //容器中有这个类型组件ConditionalOnMissingBean(name DispatcherServlet.) //容器中没有这个名字 multipartResolver 的组件public MultipartResolver multipartResolver(MultipartResolver resolver) {//给Bean标注的方法传入了对象参数这个参数的值就会从容器中找。//SpringMVC multipartResolver。防止有些用户配置的文件上传解析器不符合规范// 有些用户可能配置了MultipartResolver这个类型的组件但是组件名字不规范return resolver;}2、优先使用用户配置BeanConditionalOnMissingBean // 如果容器中没有CharacterEncodingFilter 这个bean才开启相当于优先使用用户的用户没有会配置将会开启这个兜底public CharacterEncodingFilter characterEncodingFilter() {CharacterEncodingFilter filter new OrderedCharacterEncodingFilter();filter.setEncoding(this.properties.getCharset().name());filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.REQUEST));filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.RESPONSE));return filter;}三、总结1、SpringBoot启动时先加载META-INF/spring.factories下所有的自动配置类 xxxxxAutoConfiguration2、每个自动配置类按照条件进行生效默认都会绑定配置文件指定的值。xxxxProperties里面拿。xxxProperties和配置文件进行了绑定3、生效的配置类就会给容器中装配很多组件4、只要容器中有这些组件相当于这些功能就有了5、用户可以自己定制化配置有两种方式a、直接自己写配置类使用Bean替换底层的组件b、用户去看这个组件是获取的配置文件什么值就去修改。6、如果想要查看容器是否开启了对应组件有两种方式a、在启动类中获取容器中的bean查看容器中是否成功加载。b、还有一种就是在配置文件中配置debugtrue开启自动配置报告。Negative不生效Positive生效直接配置没有前缀。