SpringBoot2
1.Springboot自動(dòng)配置原理分析
SpringApplication.run()啟動(dòng)應(yīng)用
掃描到@SpringBootApplication注解
調(diào)用@EnableAutoConfiguration注解
掃描@Import({AutoConfigurationImportSelector.class})
進(jìn)入AutoConfigurationImportSelector
調(diào)用selectImports()方法中的獲取所有配置集合
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
調(diào)用SpringFactoriesLoader.loadFactoryNames()讀取了ClassPath下面的META-INF/spring.factories文件
讀取文件中org.springframework.boot.autoconfigure.EnableAutoConfiguration里面所有的參數(shù)测蹲,開始運(yùn)行預(yù)設(shè)好的配置類
各個(gè)配置類根據(jù)@Conditional痴昧,決定是否實(shí)例化配置類內(nèi)部定義的bean粱胜,避免在bean初始化過(guò)程中由于條件不足绰垂,導(dǎo)致應(yīng)用啟動(dòng)失敗
1.1 @Conditional
使用
Conditional要配和Condition的實(shí)現(xiàn)類(ClassCondition)進(jìn)行使用
實(shí)現(xiàn)一個(gè)Condition接口巷波,并且重寫matchs方法
@Conditional注解中指定Condition接口的實(shí)現(xiàn)即可
案例1:需求
? 在spring的IOC容器中有一個(gè)User的Bean沧竟,現(xiàn)要求:
? 導(dǎo)入Jedis坐標(biāo)后前翎,加載該Bean杆融,沒(méi)導(dǎo)入燃乍,則不加載唆樊。
- 實(shí)現(xiàn)一個(gè)Condition接口,并且重寫matchs方法
public class ClassCondition implements Condition {
/**
* @param conditionContext 上下文對(duì)象刻蟹。用于獲取環(huán)境逗旁,IOC容器,ClassLoader對(duì)象
* @param annotatedTypeMetadata 注解元對(duì)象舆瘪。 可以用于獲取注解定義的屬性值
* @return
*/
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
try {
Class<?> cls = Class.forName("redis.clients.jedis.Jedis");
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
}
- @Conditional注解中指定Condition接口的實(shí)現(xiàn)
@Configuration
public class UserConfig {
@Bean
@Conditional(ClassCondition.class)
public User user(){
return new User();
}
}
- 驗(yàn)證
@SpringBootApplication
public class SpringbootTestApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringbootTestApplication.class, args);
Object user = context.getBean("user");
System.out.println(user);
}
}
案例2:需求
? 在spring的IOC容器中有一個(gè)User的Bean片效,現(xiàn)要求:
? 導(dǎo)入Jedis坐標(biāo)后,加載該Bean英古,沒(méi)導(dǎo)入淀衣,則不加載。? 將類的判斷定義為動(dòng)態(tài)的召调。判斷哪個(gè)字節(jié)碼文件存在可以動(dòng)態(tài)指定膨桥。
- 編寫自定義條件注解類
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ClassCondition.class)
public @interface ConditionOnClass {
String[] value();
}
- 修改ClassCondition
public class ClassCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
try {
Map<String, Object> map = annotatedTypeMetadata.getAnnotationAttributes(ConditionOnClass.class.getName());
String[] value = (String[]) map.get("value");
for (String className : value) {
Class<?> cls = Class.forName(className);
}
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
}
- 修改注解類中的UserConfig
@Configuration
public class UserConfig {
@Bean
@ConditionOnClass("redis.clients.jedis.Jedis")
public User user(){
return new User();
}
}
- 驗(yàn)證
@SpringBootApplication
public class SpringbootTestApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringbootTestApplication.class, args);
Object user = context.getBean("user");
System.out.println(user);
}
}
springboot中的條件注解有很多蛮浑,常用的條件注解
ConditionalOnProperty:判斷配置文件中是否有對(duì)應(yīng)屬性和值才初始化Bean
ConditionalOnClass:判斷環(huán)境中是否有對(duì)應(yīng)字節(jié)碼文件才初始化Bean
ConditionalOnMissingBean:判斷環(huán)境中沒(méi)有對(duì)應(yīng)Bean才初始化Bean
1.2 @EnableAutoConfiguration
@EnableXXXX常用來(lái)開啟某些功能,其實(shí)底層就是使用@Importe來(lái)導(dǎo)入配置類
問(wèn)題:springboot工程是否可以直接獲取jar包中定義的Bean
三種解決方案:
- 使用@ComponentScan掃描config包
- 可以使用@Import注解只嚣,加載類沮稚。
- 自定義注解,對(duì)@Import進(jìn)行封裝
@Import的四種用法
- 直接導(dǎo)入bean
@Import(User.class)
public class EnableApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(EnableApplication.class, args);
Map<String, User> map = context.getBeansOfType(User.class);
System.out.println(map);
}
}
- 導(dǎo)入配置類
@Import(UserConfig.class)
public class EnableApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(EnableApplication.class, args);
Map<String, User> map = context.getBeansOfType(User.class);
System.out.println(map);
}
}
- 導(dǎo)入 ImportSelector 實(shí)現(xiàn)類
/**
* ImportSelector 實(shí)現(xiàn)類
*/
public class UserImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{"com.itheima.springboot_test.domain.User"};
}
}
@Import(UserImportSelector.class)
public class EnableApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(EnableApplication.class, args);
Map<String, User> map = context.getBeansOfType(User.class);
System.out.println(map);
}
}
- 導(dǎo)入 ImportBeanDefinitionRegistrar 實(shí)現(xiàn)類
/**
* ImportBeanDefinitionRegistrar 實(shí)現(xiàn)類
*/
public class UserImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();
registry.registerBeanDefinition("user",beanDefinition);
}
}
@Import(UserImportBeanDefinitionRegistrar.class)
public class EnableApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(EnableApplication.class, args);
Map<String, User> map = context.getBeansOfType(User.class);
System.out.println(map);
}
}
小結(jié)
- @EnableAutoConfiguration 注解內(nèi)部使用 @Import(AutoConfigurationImportSelector.class)來(lái)加載配置類
- 配置文件位置:META-INF/spring.factories册舞,該配置文件中定義了大量的配置類蕴掏,當(dāng) SpringBoot 應(yīng)用啟動(dòng)時(shí),會(huì)自動(dòng)加載這些配置類环础,初始化Bean
- 并不是所有的Bean都會(huì)被初始化囚似,在配置類中使用Condition來(lái)加載滿足條件的Bean
1.3 自定義starter
需求:自定義redis-starter。要求當(dāng)導(dǎo)入redis坐標(biāo)時(shí)线得,SpringBoot自動(dòng)創(chuàng)建Jedis的Bean饶唤。
創(chuàng)建 springboot_test 模塊
創(chuàng)建 redis-spring-boot-starter 模塊,依賴 springboot_test的模塊
在 springboot_test模塊中初始化 Jedis 的 Bean。并定義META-INF/spring.factories 文件
在redis-spring-boot-autoconfigure創(chuàng)建RedisProperties類贯钩,方便從配置文件注入值募狂,創(chuàng)建RedisAutoConfiguration自動(dòng)配置類
@ConfigurationProperties(prefix = "redis")
public class RedisProperties {
private String host="localhost";
private int port=6379;
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
}
@Configuration
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoConfiguration {
@Bean
public Jedis jedis(RedisProperties redisProperties){
return new Jedis(redisProperties.getHost(),redisProperties.getPort());
}
}
在resource目錄下創(chuàng)建META-INF文件夾并創(chuàng)建spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.itheima.springboot_test.config.RedisAutoConfiguration
- 在測(cè)試模塊中引入自定義的 redis-starter 依賴,測(cè)試獲取 Jedis 的Bean角雷,操作 redis
2. SpringBoot事件監(jiān)聽
springboot的監(jiān)聽機(jī)制就是對(duì)java提供的時(shí)間監(jiān)聽機(jī)制的封裝
SpringBoot 在項(xiàng)目啟動(dòng)時(shí)祸穷,會(huì)對(duì)幾個(gè)監(jiān)聽器進(jìn)行回調(diào),我們可以實(shí)現(xiàn)這些監(jiān)聽器接口勺三,在項(xiàng)目啟動(dòng)時(shí)完成一些操作雷滚。
- ApplicationContextInitializer:要在resource文件夾下添加META-INF/spring.factories
- SpringApplicationRunListener:使用要添加構(gòu)造器
- CommandLineRunner:當(dāng)項(xiàng)目啟動(dòng)后執(zhí)行run方法
- ApplicationRunner:當(dāng)項(xiàng)目啟動(dòng)后執(zhí)行run方法
3. springboot部署
SpringBoot 項(xiàng)目開發(fā)完畢后,支持兩種方式部署到服務(wù)器:
- 打jar包
java -jar 文件路徑
- 打war包
引導(dǎo)類繼承SpringBootServletInitializer
@SpringBootApplication public class SpringbootDeployApplication extends SpringBootServletInitializer { public static void main(String[] args) { SpringApplication.run(SpringbootDeployApplication.class, args); } @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { return builder.sources(SpringbootDeployApplication.class); } }
指定打包的名稱
<build> <finalName>springboot</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>