一、项目结构图:
二、关于读取配置文件属性和指定配置文件:
(1)、读取配置文件属性有以下几种方式:
<1>、使用@Value注解
<2>、使用Environment.getProperty方法
代码:
User.class:
public class User {
//通过environment.getProperty("autoPassword")来获取配置属性
@Autowired
private Environment environment;
//通过@Value来获取配置属性
@Value("autoUserName")
private String autoUserName;
public void show(){
System.out.println("show User-autoUserName:"+autoUserName+",autoPassword:"+environment.getProperty("autoPassword")+",pro.userName:"+environment.getProperty("pro.userName"));
}
}
<3>、启动springBoot的特性注解@ConfigurationProperties,POJO的方式批量读取
@ConfigurationProperties(prefix = "log")
@Component
public class LogConfiguration {
private String logUserName;
private String logPassword;
private Integer logAge;
public void show(){
System.out.println("LogConfiguration show-logUserName:"+logUserName+",logPassword:"+logPassword+",logAge:"+logAge);
}
public String getLogUserName() {
return logUserName;
}
public void setLogUserName(String logUserName) {
this.logUserName = logUserName;
}
public String getLogPassword() {
return logPassword;
}
public void setLogPassword(String logPassword) {
this.logPassword = logPassword;
}
public Integer getLogAge() {
return logAge;
}
public void setLogAge(Integer logAge) {
this.logAge = logAge;
}
}
理解:@ConfigurationProperties(prefix = “log”),启用SpringBoot特性,将配置文件的属性注入到对应的类属性里面,(只能注入前缀为log的)
三、设置读取那个静态配置文件:
通过@PropertySource或者@PropertySources注解指定配置文件路径
DevConfiguration.class:
@Component
@PropertySource("classpath:application-dev.properties") //指定那个配置文件
//@PropertySources({@PropertySource(""),@PropertySource("")})
public class DevConfiguration {
@Value("${dev.name}")
private String devName;
@Value("${dev.pass}")
private String devPass;
public void show(){
System.out.println("DevConfiguration show-devName:"+devName+",devPass:"+devPass);
}
}
四、动态的读取外部的配置文件:
可以实现EnvironmentPostProcessor接口:
代码:
MyEnvironmentPostProcessor.class:
//动态的读取外部文件
@Component
public class MyEnvironmentPostProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment configurableEnvironment, SpringApplication springApplication) {
try(InputStream input=this.getClass().getClassLoader().getResourceAsStream("application-pro.properties")) {
Properties properties = new Properties();
properties.load(input);
PropertiesPropertySource propertySource = new PropertiesPropertySource("myPost", properties);
configurableEnvironment.getPropertySources().addLast(propertySource);
}catch (Exception e){
e.printStackTrace();
}
}
}
理解:
通过postProcessEnvironment方法(configurableEnvironment:配置环境对象,springApplication:Spring容器应用)
1、通过this.getClass().getClassLoader().getResourceAsStream("application-pro.properties")读取到外部的配置文件。
2、properties.load(input); 创建一个Properties对象
3、PropertiesPropertySource propertySource = new PropertiesPropertySource("myPost", properties);
new一个propertySource 对象
4、configurableEnvironment.getPropertySources().addLast(propertySource);加载到配置环境里面,
这样就可以在spring容器中获取到这个外部配置文件的属性。
五、分析@SpringBootApplication注解:
@SpringBootApplication注解启动包含3个注解:
(1)、SpringBootConfiguration:标注这个类为注解类
(2)、@ComponentScan:配置自动扫描
(3)、@EnableAutoConfiguration //启动spring特性(注入配置文件字段,事务等),
上面演示的@ConfigurationProperties,POJO的方式批量读取配置文件,就要依靠这个注解(先启动spring特性)
五、分析@EnableAutoConfiguration注解:
其实@EnableAutoConfiguration注解的作用是返回一些特定的bean给Spring容器,
这些bean来完成Spring的一些特定的功能,
那么是怎么返回的呢?
这个我们先了解一下@EnableAutoConfiguration注解里面的@Import注解。
(1)、@Import:
@Import注解是用来导入bean的,一般导入一个继承了ImportSelector接口的bean,ImportSelector接口的抽象方法:selectImports会返回一个String[],这个数组内存的是bean的名字,spring容器会装配这些bean
代码:
MyImportSelector.class
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
//获取注解的属性信息
System.out.println(annotationMetadata.getAllAnnotationAttributes(EnableLog.class.getName()));
return new String[]{ImportRole.class.getName(),ImprotUser.class.getName()};
}
}
EnableLog.class
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({MyImportSelector.class})
public @interface EnableLog {
String name();
}
理解:
声明一个注解EnableLog ,这个注解上标注@Import({MyImportSelector.class}),
也就是导入MyImportSelector.class,MyImportSelector 实现了ImportSelector 接口,
通过接口方法,返回需要注入到Spring容器的bean。
(2)、写一个例子
演示@EnableAutoConfiguration是怎么启用SpringBoot的特性的?
比如为什么在配置类加上一个@EnableAutoConfiguration注解,就可以读取到配置文件!
这个例子我们实现的功能是:当spring容器装配我指定包的bean时,就打印这些bean的信息:
第一步:
创建一个实现BeanPostProcessor接口的类,
这个类的两个抽象方法会在每个bean装配时被调用,
在postProcessBeforeInitialization方法里面实现逻辑,当前bean属于packages包的时候,
打印 System.out.println("echo bean:"+bean);
EchoBeanPostProcessor.class
public class EchoBeanPostProcessor implements BeanPostProcessor {
private List<String> packages;
//spring容器装配每个bean,init之前
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//如果是这个包里面的,就打印
for (String pack: packages){
System.out.println(bean.getClass().getName());
if (bean.getClass().getName().startsWith(pack)){
System.out.println("echo bean:"+bean);
}
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
public List<String> getPackages() {
return packages;
}
public void setPackages(List<String> packages) {
this.packages = packages;
}
}
第二步:
创建一个实现ImportBeanDefinitionRegistrar接口的方法,
registerBeanDefinitions这个接口方法会在Spring容器启动时调用,
启动时获取注解里面的参数信息,装配EchoBeanPostProcessor到spring容器,
获取到注解上的参数注入到EchoBeanPostProcessor里面。
EchoImportBeanDefinitionRegistrar.class
/**
* Spring容器启动时候,动态注入bean(注入EchoBeanPostProcessor)
*/
public class EchoImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
Map<String, Object> arrt= annotationMetadata.getAnnotationAttributes(EnableEcho.class.getName());
String [] pack=(String[]) arrt.get("packages");
List<String> packages= Arrays.asList(pack);
BeanDefinitionBuilder bdb=BeanDefinitionBuilder.rootBeanDefinition(EchoBeanPostProcessor.class);
bdb.addPropertyValue("packages",packages);
System.out.println("packages:"+packages);
beanDefinitionRegistry.registerBeanDefinition(EchoBeanPostProcessor.class.getName(), bdb.getBeanDefinition());
}
第三步:
创建注解EnableEcho,这个注解导入EchoImportBeanDefinitionRegistrar.class,当标准此注解的时候,也就是启动了EchoImportBeanDefinitionRegistrar.class。
EnableEcho.class
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EchoImportBeanDefinitionRegistrar.class)
public @interface EnableEcho {
String [] packages();
}
第四步:
引用@EnableEcho注解:
AppImport.class
@EnableEcho(packages={"com.miller.test"})
public class AppImport {
public static void main(String[] args) {
ConfigurableApplicationContext context=SpringApplication.run(AppImport.class,args);
context.close();
}
}
总结:
整个过程就是标注一个注解@EnableEcho(packages={"com.miller.test"}),
这个注解里@Import(EchoImportBeanDefinitionRegistrar.class),
EchoImportBeanDefinitionRegistrar实现了ImportBeanDefinitionRegistrar,
里面的registerBeanDefinitions抽象方法会在Spring容器启动时候调用,
在registerBeanDefinitions里面实现装配EchoBeanPostProcessor,
EchoBeanPostProcessor实现了BeanPostProcessor接口,
在每个bean装配的时候,就会调用此接口的抽象方法,
然后就可以在抽象方法里面判断是否是指定的包内的bean。
@EnableAutoConfiguration可以启用Spring特性
其实大概的原理就是这样,无非就是标注了这个@EnableAutoConfiguration注解,
这个注解里面@Import了某些bean,这些bean里面实现了ImportSelector 接口
或者ImportBeanDefinitionRegistrar接口,通过这些接口的抽象方法,
就可以返回一系统的jar包里面的bean或者classPath里面的bean装配到Spring容器里面去,
从而实现了一些Spring特性。
至于更加具体的过程,就请看我的下一篇博客吧。