先看一個(gè)簡(jiǎn)單的demo,我們定義一個(gè)springboot項(xiàng)目电禀,最簡(jiǎn)單的依賴:
<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>
定義一個(gè)實(shí)體類: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配置詳解,詳細(xì)的講解了@ConfigurationProperties的使用拍柒。
配置文件配置:
instance.ip=192.168.1.111
instance.port=8090
啟動(dòng)類啟動(dòng)灯节,
@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}
我們發(fā)現(xiàn)這個(gè)程序自動(dòng)的會(huì)把配置文件注入到bean中史隆,原因在哪里呢魂务?
之前我們講過啟動(dòng)類上的注解@SpringBootApplication是一個(gè)復(fù)合注解,其由@SpringBootConfiguration泌射,@EnableAutoConfiguration粘姜,@ComponentScan三個(gè)注解組成,@SpringBootConfiguration和@ComponentScan我們?cè)?br>
springboot快速入門及@SpringBootApplication注解分析
分析過了魄幕,那么故名思義相艇,我們知道肯定是@EnableAutoConfiguration注解的作用了。
修改啟動(dòng)類纯陨,
@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));
}
}
發(fā)現(xiàn)打印結(jié)果還是一樣坛芽。然后再去修改一下啟動(dòng)類留储,使用@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中咙轩。
第二個(gè)demo
定義一個(gè)類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();
}
}
}
啟動(dòng)類:
@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");
}
}
打印結(jié)果:
============0
============1
============2
============3
============4
============5
============6
============7
============8
============9
end
我們發(fā)現(xiàn)執(zhí)行過程是一個(gè)同步的過程活喊,只有userRunnable.run方法執(zhí)行完畢之后才執(zhí)行下面的打印過程丐膝。
修改一下代碼,將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();
}
}
}
修改啟動(dòng)類钾菊,
@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");
}
}
執(zhí)行結(jié)果:
============0
end
============1
============2
============3
============4
============5
============6
============7
============8
============9
我們查看上面二個(gè)demo的Enable*注解的源碼帅矗,
@EnableAsync注解
最后實(shí)現(xiàn)ImportSelector接口
@EnableAutoConfiguration注解
都使用到了@Import注解,最后也是實(shí)現(xiàn)ImportSelector接口煞烫。
@Import注解
@Import其實(shí)就是引入一個(gè)或多個(gè)配置浑此,可以導(dǎo)入普通類,也可以導(dǎo)入配置類滞详。
@Import用來導(dǎo)入一個(gè)或多個(gè)類(會(huì)被spring容器管理)凛俱,或者配置類(配置類里的@Bean標(biāo)記的類也會(huì)被spring容器管理)
看一個(gè)demo,定義四個(gè)實(shí)體類料饥,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();
}
}
我們要將這四個(gè)類納入到spring容器中原叮,我們之前的做法是在User,People上加上了@Component注解(或者@Service凰狞,@Controller)或者在MyConfig類上加上@Configuration注解篇裁。很顯然我們這邊并沒有這般做,使用@Import注解也可以加對(duì)象納入到spring容器中赡若。
啟動(dòng)類:
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.
接口被實(shí)現(xiàn)那些Configuration的類被導(dǎo)入到spring容器根據(jù)指定的一些條件达布,通常是一個(gè)到多個(gè)導(dǎo)入類的注解屬性。
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).
實(shí)現(xiàn)ImportSelectors接口的類通常與常規(guī)的@Import注解作用相同逾冬,然而黍聂,它也可能被延遲處理直到所有被@Configuration標(biāo)記的類處理完之后采取處理。
定義一個(gè)MyImportSelector繼承ImportSelector身腻,ImportSelector返回的String[]數(shù)組是類的全類名會(huì)被納入到spring容器內(nèi)产还。
/**
* selectImports方法的返回值,必須是一個(gè)class(全稱)嘀趟,該class會(huì)被spring容器所托管起來
*/
public class MyImportSelector implements ImportSelector{
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//獲取注解的屬性信息
System.out.println(importingClassMetadata.getAllAnnotationAttributes(EnableLog.class.getName()));
//這里可以獲取到注解的詳細(xì)信息脐区,然后根據(jù)信息去動(dòng)態(tài)的返回需要被spring容器管理的bean
return new String[]{"com.zhihao.miao.imp.bean.User",People.class.getName(),MyConfig.class.getName()};
}
}
定義一個(gè)EnableLog注解,可以得到屬性的值她按,@Import(MyImportSelector.class)牛隅,可以在MyImportSelector中獲取name屬性值炕柔。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(MyImportSelector.class)
public @interface EnableLog {
String name();
}
啟動(dòng)類,在啟動(dòng)類上加入@EnableLog(name="com.zhihao.miao")注解媒佣,
我們知道@EnableLog中@Import(MyImportSelector.class)會(huì)將MyImportSelector對(duì)象納入到容器中匕累。
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));
}
}
打印結(jié)果:
{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.
接口實(shí)現(xiàn)可以額外的注冊(cè)類的定義到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實(shí)現(xiàn)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方法的參數(shù)有一個(gè)BeanDefinitionRegistry也糊,
* BeanDefinitionRegistry可以用來往spring容器中注入bean
* 如此炼蹦,我們就可以在registerBeanDefinitions方法里面動(dòng)態(tài)的注入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());
}
}
主啟動(dòng)類:
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));
}
}
打印結(jié)果
...
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
...
當(dāng)然也可以寫成一個(gè)注解,@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 {
}
啟動(dòng)類:
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));
}
}
也是可以將這些對(duì)象注入到spring容器的显设。