spring-boot系列
spring-boot自动配置
前言
本节主要讲Condition接口、切换内置服务器、@Enable*注解、@SpringBootApplication四个部分
一、Condition接口
1. 来历
Condition接口,是在spring4.0增加的条件判断功能,可以实现选择性的创建bean操作
2. 使用方法
@Conditional(ClassCondition.class),这个注解需要传递一个实现了Condition接口的class对象,
Condition接口只有一个boolean matches()方法,用此条件来判断这个bean是否创建
自定义注解@ConditionOnClass:
首先要继承@Conditional(ClassCondition.class),表明此注解是个衍生注解,
功能和@Conditional()完全一样,但是可以自己定义一个String[] value();
当其它类使用这个注解时,能够传入String数组,交给自定义的OnClassCondition类
使用,具体写法如下:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionOnClass {
String[] value();
}
自定义OnClassCondition:
metadata.getAnnotationAttributes(String annotationName).get("value")
方法能获取注解的value数组,这样可以将前面自定义的注解传入并获取
再将每个value进行判断来返回相应的布尔值,就可以实现不同条件是否创建bean
本例是通过Class.forName(className);来判断传入的类是否存在,存在就返回true
public class OnClassCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) {
String[] classNames = (String[]) metadata.getAnnotationAttributes(ConditionOnClass.class.getName()).get("value");
for (String className : classNames) {
try {
Class.forName(className);
} catch (ClassNotFoundException e) {
return false;
}
}
return true;
}
}
//至此,实现了必须存在这个对象才能创建这个bean
@ConditionOnClass("org.springframework.data.redis.core.RedisTemplate")
@Bean
public RedisTemplate redisTemplate(){
return new RedisTemplate();
}
在Maven:org.springframework.boot:spring-boot-autoconfigure中的condition包下,
有一系列的ConditionOn*的注解,就是用来实现各种bean动态创建的,
我们通常可以使用这些注解来决定bean是否创建
二、切换内置服务器
1.服务器切换
切换内置服务器jetty、undertow、netty,通过pom文件配置
在Maven:org.springframework.boot:spring-boot-autoconfigure中的web包下
的embedded包有四个web服务器,修改最后的名字就可以了
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-tomcat</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
三、@Enable*注解
1.介绍
@Enable*注解,用于开启某些功能,底层是@import注解,实现bean的动态加载
2.@Import详解
@Import(xxx.class)注解能够将一个类xxx导入spring容器中,通常将其配置spring类上
(例如@Configuration),作为导入bean的一种方式,此外,他还有两种
方式,一是导入实现了ImportSelector接口的类,通过数组导入:
@Import(MyImportSelector.class)
二是导入实现ImportBeanDefinitionRegistrar接口的类,通过bean工厂注册:
@Import(MyImportBeanDefinitionRegistrar)
以下是实现代码:
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{"com.lzb.other.bean.User","com.lzb.other.bean.Address"};
}
}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
AbstractBeanDefinition definition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();
registry.registerBeanDefinition("com.lzb.other.bean.User",definition);
}
}
@Import()注解底层应该有某种方法,使得一执行这个注解,就会将bean加入到容器中
3.自定义@Enable*类型注解
由于@Import()的功能,我们知道只要引入了这个注解就相当于定义了引入的bean,所以我们只
需要写一个衍生注解就可以实现功能:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import(User.class)
public @interface EnableUser {
}
@EnableUser配合getbean或者@Autowired就可以注入
四、@SpringBootApplication
1. @SpringBootApplication启动注解
@SpringBootApplication包括:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponontScan
2. @SpringBootConfiguration
@Configuration衍生注解,和spring-boot配套使用
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
3.@EnableAutoConfiguration
它是通过@Import导入了AutoConfigurationImportSelector.class,这个类是通过
父类实现了ImportSelector接口,并重写了selectImports()方法,来实现bean注入
AutoConfigurationImportSelector类有一个getCandidateConfigurations()方法,
这个方法就是用来加载META-INF/spring.factories配置文件,返回可以加载的配置类名的
字符串集合,这些字符串就是可加载的类名,进入spring.factories可以发现,他加载了许
多类名,但并不是所有的类都会被加载,因为每一个类都有一个@ConditionOn*注解,大多数
是@ConditionOnClass(*.class)这个注解用来判定是否加载该类。
这个文件十分重要,因为只要启动spring-boot项目,这个文件就会被加载并导入可加载的bean
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
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;
}
4.@EnableConfigurationProperties(Redis.properties)
原理就是@Enable*,它能够将配置了@ConfigurationProperties(prefix = "redis")
的属性文件类导入到容器中,并且优先使用application.yml设置的值
5.自定义starter
这是属性配置类,用来和接收application.yml设置的值,好传递给其它需要的类
package com.lzb.redis.configure;
import org.springframework.boot.context.properties.ConfigurationProperties;
@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;
}
}
这是提供bean的类,通过@EnableConfigurationProperties(RedisProperties.class)
注入配置文件的值,来实现参数的动态化
package com.lzb.redis.configure;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.Jedis;
@EnableConfigurationProperties(RedisProperties.class)
@Configuration
@ConditionalOnClass(Jedis.class)
public class RedisConfiguration {
/**
* 提供jedis的bean
*/
@Bean
@ConditionalOnMissingBean(name = "jedis")
public Jedis jedis(RedisProperties redisProperties){
return new Jedis(redisProperties.getHost(),redisProperties.getPort());
}
}
这是配置文件resources/META-INF/spring.factories
它是随着@EnableAutoConfiguration启动注解加载的,它会先找到这个类,然后对它进行
实例化bean的操作,如果满足@ConditionalOnClass(Jedis.class)和
@ConditionalOnMissingBean(name = "jedis"),就会将这个bean加入到容器中,否则
不创建这个bean ↓
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.lzb.redis.configure.RedisConfiguration