先看一个简单的demo,我们定义一个springboot项目,最简单的依赖:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
定义一个实体类:ServerBean
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "instance")
public class ServerBean {
private String ip;
private Integer port;
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
@Override
public String toString() {
return "ServerBean{" +
"ip='" + ip + '\'' +
", port=" + port +
'}';
}
}
之前的博客springboot配置详解,详细的讲解了@ConfigurationProperties的使用。
配置文件配置:
instance.ip=192.168.1.111
instance.port=8090
启动类启动,
@SpringBootApplication
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class,args);
System.out.println(context.getBean(ServerBean.class));
}
}
打印:
ServerBean{ip='192.168.1.111', port=8090}
我们发现这个程序自动的会把配置文件注入到bean中,原因在哪里呢?
之前我们讲过启动类上的注解@SpringBootApplication是一个复合注解,其由@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan三个注解组成,@SpringBootConfiguration和@ComponentScan我们在
springboot快速入门及@SpringBootApplication注解分析
分析过了,那么故名思义,我们知道肯定是@EnableAutoConfiguration注解的作用了。
修改启动类,
@EnableAutoConfiguration
@ComponentScan
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class,args);
System.out.println(context.getBean(ServerBean.class));
}
}
发现打印结果还是一样。然后再去修改一下启动类,使用@EnableConfigurationProperties也可以替换@EnableAutoConfiguration
@EnableConfigurationProperties
@ComponentScan
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class,args);
System.out.println(context.getBean(ServerBean.class));
}
}
@EnableConfigurationProperties注解一般和ConfigurationProperties注解搭配使用,可以将配置文件属性注入到bean中。
第二个demo
定义一个类UserRunnable,纳入到spring容器中,
package com.zhihao.miao.enable.bean;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
public class UserRunnable implements Runnable{
@Override
public void run() {
try{
for (int i = 0; i <10 ; i++) {
System.out.println("============"+i);
TimeUnit.SECONDS.sleep(1);
}
}catch (Exception e){
e.printStackTrace();
}
}
}
启动类:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class,args);
UserRunnable userRunnable = context.getBean(UserRunnable.class);
userRunnable.run();
System.out.println("end");
}
}
打印结果:
============0
============1
============2
============3
============4
============5
============6
============7
============8
============9
end
我们发现执行过程是一个同步的过程,只有userRunnable.run方法执行完毕之后才执行下面的打印过程。
修改一下代码,将UserRunnable的run方法上加入@Async注解
@Component
public class UserRunnable implements Runnable{
@Override
@Async
public void run() {
try{
for (int i = 0; i <10 ; i++) {
System.out.println("============"+i);
TimeUnit.SECONDS.sleep(1);
}
}catch (Exception e){
e.printStackTrace();
}
}
}
修改启动类,
@SpringBootApplication
@EnableAsync
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class,args);
context.getBean(Runnable.class).run();
System.out.println("end");
}
}
执行结果:
============0
end
============1
============2
============3
============4
============5
============6
============7
============8
============9
我们查看上面二个demo的Enable*注解的源码,
@EnableAsync注解
最后实现ImportSelector接口
@EnableAutoConfiguration注解
都使用到了@Import注解,最后也是实现ImportSelector接口。
@Import注解
@Import其实就是引入一个或多个配置,可以导入普通类,也可以导入配置类。
@Import用来导入一个或多个类(会被spring容器管理),或者配置类(配置类里的@Bean标记的类也会被spring容器管理)
看一个demo,定义四个实体类,User,People,Cat,Dog
public class User {
}
public class People{
}
public class Cat {
}
public class Dog {
}
public class MyConfig {
@Bean
public Dog dog(){
return new Dog();
}
@Bean
public Cat cat(){
return new Cat();
}
}
我们要将这四个类纳入到spring容器中,我们之前的做法是在User,People上加上了@Component注解(或者@Service,@Controller)或者在MyConfig类上加上@Configuration注解。很显然我们这边并没有这般做,使用@Import注解也可以加对象纳入到spring容器中。
启动类:
package com.zhihao.miao.imp;
import com.zhihao.miao.imp.bean.*;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;
@Import({User.class,People.class, MyConfig.class})
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class,args);
System.out.println(context.getBean(User.class));
System.out.println(context.getBean(Dog.class));
System.out.println(context.getBean(Cat.class));
System.out.println(context.getBean(People.class));
}
}
ImportSelector接口
Interface to be implemented by types that determine which @{@link Configuration}
class(es) should be imported based on a given selection criteria, usually one or more
annotation attributes.
接口被实现那些Configuration的类被导入到spring容器根据指定的一些条件,通常是一个到多个导入类的注解属性。
An ImportSelector may implement any of the following
org.springframework.beans.factory.Aware Aware interfaces, and their respective methods will be called prior to selectImports:
- org.springframework.context.EnvironmentAware EnvironmentAware
- org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware
- org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware
- org.springframework.context.ResourceLoaderAware ResourceLoaderAware
ImportSelectors are usually processed in the same way as regular @Import
annotations, however, it is also possible to defer selection of imports until all
@Configuration classes have been processed (see DeferredImportSelector
for details).
实现ImportSelectors接口的类通常与常规的@Import注解作用相同,然而,它也可能被延迟处理直到所有被@Configuration标记的类处理完之后采取处理。
定义一个MyImportSelector继承ImportSelector,ImportSelector返回的String[]数组是类的全类名会被纳入到spring容器内。
/**
* selectImports方法的返回值,必须是一个class(全称),该class会被spring容器所托管起来
*/
public class MyImportSelector implements ImportSelector{
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//获取注解的属性信息
System.out.println(importingClassMetadata.getAllAnnotationAttributes(EnableLog.class.getName()));
//这里可以获取到注解的详细信息,然后根据信息去动态的返回需要被spring容器管理的bean
return new String[]{"com.zhihao.miao.imp.bean.User",People.class.getName(),MyConfig.class.getName()};
}
}
定义一个EnableLog注解,可以得到属性的值,@Import(MyImportSelector.class),可以在MyImportSelector中获取name属性值。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(MyImportSelector.class)
public @interface EnableLog {
String name();
}
启动类,在启动类上加入@EnableLog(name="com.zhihao.miao")注解,
我们知道@EnableLog中@Import(MyImportSelector.class)会将MyImportSelector对象纳入到容器中。
package com.zhihao.miao.imp;
import com.zhihao.miao.imp.bean.*;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
@EnableLog(name="com.zhihao.miao")
public class Application2 {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application2.class,args);
System.out.println(context.getBean(User.class));
System.out.println(context.getBean(Dog.class));
System.out.println(context.getBean(Cat.class));
System.out.println(context.getBean(People.class));
}
}
打印结果:
{name=[com.zhihao.miao]}
2017-07-20 11:27:08.446 INFO 11551 --- [ main] com.zhihao.miao.imp.Application2 : Started Application2 in 11.754 seconds (JVM running for 12.614)
com.zhihao.miao.imp.bean.User@1e4d3ce5
com.zhihao.miao.imp.bean.Dog@3ddc6915
com.zhihao.miao.imp.bean.Cat@704deff2
com.zhihao.miao.imp.bean.People@379614be
ImportBeanDefinitionRegistrar接口
Interface to be implemented by types that register additional bean definitions when processing @{@link Configuration} classes. Useful when operating at the bean definition level (as opposed to {@code @Bean} method/instance level) is desired or necessary.
接口实现可以额外的注册类的定义到spring容器中。
Along with {@code @Configuration} and {@link ImportSelector}, classes of this type may be provided to the @{@link Import} annotation (or may also be returned from an {@code ImportSelector}).
An {@link ImportBeanDefinitionRegistrar} may implement any of the following
{@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective
methods will be called prior to {@link #registerBeanDefinitions}:
{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}
{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}
{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}
See implementations and associated unit tests for usage examples.
定义MyImportBeanDefinitionRegistrar实现ImportBeanDefinitionRegistrar接口,将User类,People类,Myconfig中的Dog和Cat类注入到spring容器中
package com.zhihao.miao.imp.bean;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
/**
* registerBeanDefinitions方法的参数有一个BeanDefinitionRegistry,
* BeanDefinitionRegistry可以用来往spring容器中注入bean
* 如此,我们就可以在registerBeanDefinitions方法里面动态的注入bean
*/
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
BeanDefinitionBuilder bdb = BeanDefinitionBuilder.rootBeanDefinition(People.class);
registry.registerBeanDefinition(People.class.getName(),bdb.getBeanDefinition());
BeanDefinitionBuilder bdb2 = BeanDefinitionBuilder.rootBeanDefinition(User.class);
registry.registerBeanDefinition(User.class.getName(),bdb2.getBeanDefinition());
BeanDefinitionBuilder bdb3 = BeanDefinitionBuilder.rootBeanDefinition(MyConfig.class);
registry.registerBeanDefinition(MyConfig.class.getName(),bdb3.getBeanDefinition());
}
}
主启动类:
package com.zhihao.miao.imp;
import com.zhihao.miao.imp.bean.*;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;
@Import(MyImportBeanDefinitionRegistrar.class)
public class Application3 {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application3.class,args);
System.out.println(context.getBean(User.class));
System.out.println(context.getBean(Dog.class));
System.out.println(context.getBean(Cat.class));
System.out.println(context.getBean(People.class));
}
}
打印结果
...
com.zhihao.miao.imp.bean.User@3e694b3f
com.zhihao.miao.imp.bean.Dog@1bb5a082
com.zhihao.miao.imp.bean.Cat@78691363
com.zhihao.miao.imp.bean.People@41d477ed
...
当然也可以写成一个注解,@EnableImportConfig
package com.zhihao.miao.imp.bean;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(MyImportBeanDefinitionRegistrar.class)
public @interface EnableImportConfig {
}
启动类:
package com.zhihao.miao.imp;
import com.zhihao.miao.imp.bean.*;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
@EnableImportConfig
public class Application4 {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application4.class,args);
System.out.println(context.getBean(User.class));
System.out.println(context.getBean(Dog.class));
System.out.println(context.getBean(Cat.class));
System.out.println(context.getBean(People.class));
}
}
也是可以将这些对象注入到spring容器的。