底层注解 1. @Configuration详解 基本使用
Full模式与Lite模式 示例1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 @Configuration(proxyBeanMethods = false) public class MyConfig { @Bean public User user01 () { User zhangsan = new User ("zhangsan" , 18 ); zhangsan.setPet(tomcatPet()); return zhangsan; } @Bean("tom") public Pet tomcatPet () { return new Pet ("tomcat" ); } }
User.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package com.nianxi.entity;import lombok.AllArgsConstructor;import lombok.Builder;import lombok.Data;import lombok.NoArgsConstructor;@Data @AllArgsConstructor @NoArgsConstructor @Builder public class User { private String name; private Integer age; private Pet pet; public User (String zhangsan, int i) { this .name = zhangsan; this .age = i; } }
Pet.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package com.nianxi.entity;import lombok.AllArgsConstructor;import lombok.Builder;import lombok.Data;import lombok.NoArgsConstructor;@Data @AllArgsConstructor @NoArgsConstructor @Builder public class Pet { private String name; }
HelloApplication.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 package com.nianxi;import com.nianxi.config.MyConfig;import com.nianxi.entity.Pet;import com.nianxi.entity.User;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.ConfigurableApplicationContext;@SpringBootApplication public class HelloApplication {public static void main (String[] args) { ConfigurableApplicationContext run = SpringApplication.run(HelloApplication .class, args); String[] names = run.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); } Pet tom01 = run.getBean("tom" , Pet.class); Pet tom02 = run.getBean("tom" , Pet.class); System.out.println("组件:" +(tom01 == tom02)); MyConfig bean = run.getBean(MyConfig.class); System.out.println(bean); User user = bean.user01(); User user1 = bean.user01(); System.out.println(user == user1); User user01 = run.getBean("user01" , User.class); Pet tom = run.getBean("tom" , Pet.class); System.out.println("用户的宠物:" +(user01.getPet() == tom)); } }
以上就是我们Spring Boot 2.0最大的一个更新,即我们可以将配置类编写为轻量级模式和全模式 这两种配置模式。
那么,将配置类编写为轻量级模式有什么优点呢?优点就是Spring Boot不会来检查组件注册的方法(例如user01方法)返回的东东在容器中有没有,相当于就跳过了检查,这样,我们整个Spring Boot应用启动并运行起来就非常快了。而如果是将配置类编写为全模式,那么外界对它里面的方法的每一次调用,Spring Boot都会检查容器中是不是有了该方法已经返回了的组件。
虽然Spring Boot给我们带来了Full和Lite这两种配置模式,但是在Spring Boot的最佳实践中,我们推荐的做法是这样子的——如果我们只是向容器中单单注册组件,而且也不存在组件依赖,那么一般使用Lite模式,因为这样的话,我们Spring Boot应用整个的启动速度就会非常快,加载起来也会非常快哟;如果存在组件依赖,那么一般使用Full模式,因为这样能保证某一个组件它所依赖的组件就是容器中的组件。
最佳实战 配置 类组件之间无依赖关系 用Lite模式加速容器启动过程,减少判断 配置 类组件之间有依赖关系 ,方法会被调用得到之前单实例组件,用Full模式(默认) lite 英 [laɪt] 美 [laɪt] adj. 低热量的,清淡的(light的一种拼写方法);类似…的劣质品
2. @Import导入组件 @Bean、@Component、@Controller、@Service、@Repository,它们是Spring的基本标签,在Spring Boot中并未改变它们原来的功能。
@ComponentScan 指定扫描路径
@Import({Pet.class, User.class})给容器中自动创建出这两个类型的组件 、默认组件的名字就是全类名
MyConfig.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.nianxi.config;import com.nianxi.entity.Pet;import com.nianxi.entity.User;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Import;@Import({Pet.class, User.class}) @Configuration(proxyBeanMethods = false) public class MyConfig {}
HelloApplication.java 1 2 3 4 5 6 7 8 9 10 11 public static void main (String[] args) { ConfigurableApplicationContext run = SpringApplication.run(HelloApplication .class, args); String[] beanNamesForType = run.getBeanNamesForType(User.class); for (String s : beanNamesForType) { System.out.println(s); } Pet bean1 = run.getBean(Pet.class); System.out.println(bean1); }
3. @Conditional条件装配 条件装配:满足Conditional指定的条件,则进行组件注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 @Configuration(proxyBeanMethods = true) @ConditionalOnMissingBean(name = "tom") public class MyConfig { @Bean public User user01 () { User zhangsan = new User ("zhangsan" , 18 ); zhangsan.setPet(tomcatPet()); return zhangsan; } @Bean("tom22") public Pet tomcatPet () { return new Pet ("tomcat" ); } } public static void main (String[] args) { ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args); String[] names = run.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); } boolean tom = run.containsBean("tom" ); System.out.println("容器中Tom组件:" +tom); boolean user01 = run.containsBean("user01" ); System.out.println("容器中user01组件:" +user01); boolean tom22 = run.containsBean("tom22" ); System.out.println("容器中tom22组件:" +tom22); }
4. @ImportResource导入Spring配置文件 比如,公司使用bean.xml文件生成配置bean,然而你为了省事,想继续复用bean.xml,@ImportResource粉墨登场。
bean.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="user" class ="com.nianxi.entity.User" > <property name ="name" value ="zhangsan" /> <property name ="age" value ="18" /> </bean > <bean id ="pet" class ="com.nianxi.entity.Pet" > <property name ="name" value ="tomcat" /> </bean > </beans >
HelloApplication
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static void main (String[] args) { ConfigurableApplicationContext run = SpringApplication.run(HelloApplication .class, args); String[] names = run.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); } boolean user = run.containsBean("user" ); boolean pet = run.containsBean("pet" ); System.out.println("user:" +user); System.out.println("pet:" +pet); }
5. @ConfigurationProperties配置绑定 如何使用Java读取到properties文件中的内容,并且把它封装到JavaBean中,以供随时使用
传统方法: 1 2 3 4 5 6 7 8 9 10 11 12 13 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); } } }
Spring Boot一种配置配置绑定: @ConfigurationProperties + @Component
假设有配置文件application.properties
1 2 mycar.brand =BYD mycar.price =100000
只有在容器中的组件,才会拥有SpringBoot提供的强大功能
1 2 3 4 5 @Component @ConfigurationProperties(prefix = "mycar") public class Car {... }
Spring Boot另一种配置配置绑定: @EnableConfigurationProperties + @ConfigurationProperties
开启Car配置绑定功能 把这个Car这个组件自动注册到容器中1 2 3 4 @EnableConfigurationProperties(Car.class) public class MyConfig {... }
1 2 3 4 @ConfigurationProperties(prefix = "mycar") public class Car {... }
自动配置[源码分析] 自动包规则原理 Spring Boot应用的启动类:
1 2 3 4 5 6 @SpringBootApplication public class HelloApplication { public static void main (String[] args) { SpringApplication.run(HelloApplication.class, args); } }
分析下@SpringBootApplication
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @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 { ... }
重点分析@SpringBootConfiguration
,@EnableAutoConfiguration
,@ComponentScan
。
1.@SpringBootConfiguration 1 2 3 4 5 6 7 8 9 10 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration public @interface SpringBootConfiguration { @AliasFor( annotation = Configuration.class ) boolean proxyBeanMethods () default true ; }
@Configuration
代表当前是一个配置类。
2.@ComponentScan 指定扫描哪些Spring注解。
1 2 3 4 5 6 7 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Repeatable(ComponentScans.class) public @interface ComponentScan { --- }
3.@EnableAutoConfiguration 1 2 3 4 5 6 7 8 9 10 11 12 13 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration" ; Class<?>[] exclude() default {}; String[] excludeName() default {}; }
重点分析@AutoConfigurationPackage
,@Import(AutoConfigurationImportSelector.class)
。
1.@AutoConfigurationPackage 标签名直译为:自动配置包,指定了默认的包规则。
1 2 3 4 5 6 7 8 9 10 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage { String[] basePackages() default {}; Class<?>[] basePackageClasses() default {}; }
2.@Import()之前讲过,用来给容器中自动创建对应类型组件 利用Registrar给容器中导入一系列组件 将指定的一个包下的所有组件导入进HelloApplication所在包下。 初始加载自动配置类 @Import(AutoConfigurationImportSelector.class) 利用getAutoConfigurationEntry(annotationMetadata);
给容器中批量导入一些组件 调用List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)
获取到所有需要导入到容器中的配置类 利用工厂加载 Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader);
得到所有的组件 从META-INF/spring.factories
位置来加载一个文件。默认扫描我们当前系统里面所有META-INF/spring.factories
位置的文件 spring-boot-autoconfigure-2.3.4.RELEASE.jar
包里面也有META-INF/spring.factories
1 2 3 4 5 6 7 org.springframework.boot.autoconfigure.EnableAutoConfiguration =\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ ...
虽然我们127个场景的所有自动配置启动的时候默认全部加载,但是xxxxAutoConfiguration
按照条件装配规则(@Conditional
),最终会按需配置。
如AopAutoConfiguration
类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Configuration( proxyBeanMethods = false ) @ConditionalOnProperty( prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true ) public class AopAutoConfiguration { public AopAutoConfiguration () { } ... }
自动配置流程 以DispatcherServletAutoConfiguration
的内部类DispatcherServletConfiguration
为例子:
1 2 3 4 5 6 7 8 9 @Bean @ConditionalOnBean(MultipartResolver.class) @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) public MultipartResolver multipartResolver (MultipartResolver resolver) { return resolver; }
SpringBoot默认会在底层配好所有的组件,但是如果用户自己配置了以用户的优先 。
总结 :
SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。(xxxxProperties里面读取,xxxProperties和配置文件进行了绑定) 生效的配置类就会给容器中装配很多组件 只要容器中有这些组件,相当于这些功能就有了 定制化配置用户直接自己@Bean替换底层的组件 用户去看这个组件是获取的配置文件什么值就去修改。 xxxxxAutoConfiguration —> 组件 —> xxxxProperties里面拿值 —-> application.properties