@Configuration配置spring并啟動spring容器
@Configuration用于定義配置類链沼,標(biāo)注在類上材原,相當(dāng)于把該類作為spring的xml配置文件中的<beans>
讥裤,作用是用于配置spring容器(應(yīng)用上下文)
實(shí)例說明:
配置類
@Configuration
public class MyConfig {
public MyConfig() {
System.out.println("TestConfig容器初始化E荏荨L桌EА悉默!");
}
}
相當(dāng)于xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
測試類
public class TestConfig {
@Test
public void test(){
AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyConfig.class);
}
}
運(yùn)行結(jié)果
TestConfig容器初始化!9赌隆3巍!
@Configuration&@Bean給容器中注冊組件
@Bean標(biāo)注在方法上鞭缭,相當(dāng)于xml配置配置中的<code></bean></code>
代碼實(shí)例說明:
注冊類
public class ConfigurationBean {
public void start() {
System.out.println("容器初始化開始F噬拧!A肜薄Vㄉ埂!");
}
}
配置類
@Configuration
public class MyConfiguration {
//可以指定bean 的名稱沦童,也可以指定注入容器之前加載的方法
@Bean()
public ConfigurationBean configurationBean(){
return new ConfigurationBean();
}
}
相當(dāng)于xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="configurationBean" class="zfcoding.bean.ConfigurationBean" ></bean>
</beans>
測試方法
@Test
public void ConfigurationTest(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfiguration.class);
ConfigurationBean configurationBean=(ConfigurationBean) applicationContext.getBean("configurationBean");
System.out.println(configurationBean);
}
運(yùn)行結(jié)果:
容器初始化開始B乇簟!M狄拧6胀!
zfcoding.bean.ConfigurationBean@77be656f
說明幾點(diǎn):
(1)氏豌、@Bean注解標(biāo)注在方法上喉酌,如果未通過@Bean指定bean的名稱,則默認(rèn)與標(biāo)注的方法名相同;
(2)泪电、@Bean注解標(biāo)注在方法上般妙,如果想通過@Bean指定bean的名稱,例如 @Bean("configurationBean1"),那么容器中實(shí)例化bean的名稱為configurationBean1相速;
@Bean 指定初始化和銷毀的方法及@Scope 碟渺,@Lazy
容器bean管理的生命周期:
我們可以自定義初始化和銷毀的方法,容器在bean進(jìn)行到當(dāng)前生命周期的時候來調(diào)用我們自定義的初始化和銷毀的方法突诬。
@Bean 中指定初始化和銷毀的方法苫拍。
@Bean(initMethod = "init",destroyMethod = "destory")
xml
<bean id="student" class="zfcoding.bean.Student" init-method="init" destroy-method="destory"></bean>
@Scope
默認(rèn)情況下: singleton 單實(shí)例(默認(rèn)值),ioc容器啟動會調(diào)用方法創(chuàng)建對象放到ioc容器當(dāng)中旺隙。以后每次獲取直接獲取對象绒极。
prototype :多實(shí)例,ioc容器啟動并不會調(diào)用方法創(chuàng)建對象放到容器當(dāng)中催束。集峦,而是每次獲取的時候才會調(diào)用方法創(chuàng)建對象伏社,調(diào)用一次創(chuàng)建一次抠刺。
@Lazy 懶加載,
針對單實(shí)例bean,默認(rèn)在容器啟動的時候創(chuàng)建對象
懶加載:容器啟動不創(chuàng)鍵對象摘昌,第一次使用Bean創(chuàng)建對象速妖,并初始化。
實(shí)例說明:
public class Student {
public Student() {
System.out.println("Student容器加載開始4侠琛:比荨!稿饰!");
}
//對象創(chuàng)建完成锦秒,并賦值好崭孤,調(diào)用初始化方法......
public void init() {
System.out.println("Student.init()初始化開始.....");
}
//銷毀針對(單實(shí)例)歌逢,容器關(guān)閉的時候調(diào)用。
//多實(shí)例(@Scope("prototype")):容器不會管理這個bean,容器也不回調(diào)用銷毀的方法预麸。
public void destory() {
System.out.println("Student.destory().........");
}
}
@Configuration
public class MyConfigStudent {
//@Lazy
//@Scope("prototype")
@Bean(value = "student",initMethod = "init",destroyMethod = "destory")
public Student student(){
return new Student();
}
}
測試類一
public class MyConfigStudentTest {
@Test
public void test(){
AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyConfigStudent.class);
}
}
運(yùn)行結(jié)果
Student容器加載開始B履贰I妗!捺宗!
Student.init()初始化開始.....
那么說明柱蟀,ioc容器啟動會調(diào)用方法創(chuàng)建對象放到ioc容器當(dāng)中。以后每次獲取直接獲取對象蚜厉。
如果我們使用@Lazy注解(單實(shí)例)长已,容器啟動不創(chuàng)鍵對象,測試結(jié)果沒有輸出信息。
測試類二,我們第一次使用Bean創(chuàng)建對象术瓮,并初始化
public class MyConfigStudentTest {
@Test
public void test(){
AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyConfigStudent.class);
Student student = (Student)applicationContext.getBean("student");
System.out.println(student);
applicationContext.close();
}
}
運(yùn)行結(jié)果
Student容器加載開始=汗!=锔早抠!
Student.init()初始化開始.....
zfcoding.bean.Student@7364985f
Student.destory().........
說明懶加載(@Lazy):當(dāng)容器啟動不創(chuàng)鍵對象,而是第一次使用Bean創(chuàng)建對象撬讽,并初始化蕊连。
InitializingBean 和DisposableBean
InitializingBean接口為bean提供了初始化方法的方式,它只包括afterPropertiesSet方法游昼,凡是繼承該接口的類甘苍,在初始化bean的時候都會執(zhí)行該方法。
測試類
public class TestInitializingBean implements InitializingBean {
public void afterPropertiesSet() throws Exception {
System.out.println("TestInitializingBean.afterPropertiesSet()......");
}
public void init() {
System.out.println("TestInitializingBean.init().......");
}
}
配置類
@Configuration
public class MyInitializingBeanConfig {
@Bean(value = "testInitializingBean",initMethod = "init")
public TestInitializingBean testInitializingBean(){
return new TestInitializingBean();
}
}
測試類
public class TestnitializingBeanConfig {
@Test
public void test(){
AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyInitializingBeanConfig.class);
}
}
運(yùn)行結(jié)果:
TestInitializingBean.afterPropertiesSet()......
TestInitializingBean.init().......
從結(jié)果可以看出烘豌,在Spring初始化bean的時候载庭,如果該bean實(shí)現(xiàn)了InitializingBean接口,并且同時在配置文件中指定了init-method廊佩,系統(tǒng)則是先調(diào)用afterPropertieSet()方法囚聚,然后再調(diào)用init-method中指定的方法。
那么這種方式在spring中是怎么實(shí)現(xiàn)的呢标锄,通過查看Spring加載bean的源碼類AbstractAutowiredCapableBeanFactory類中的invokeInitMethods(),如下:
protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
throws Throwable {
判斷該bean是否實(shí)現(xiàn)了實(shí)現(xiàn)了InitializingBean接口顽铸,如果實(shí)現(xiàn)了InitializingBean接口,則只掉調(diào)用bean的afterPropertiesSet方法
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws Exception {
((InitializingBean) bean).afterPropertiesSet();
return null;
}
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
((InitializingBean) bean).afterPropertiesSet();
}
}
if (mbd != null) {
String initMethodName = mbd.getInitMethodName();
if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
總結(jié):
Spring為bean提供了兩種初始化bean的方式料皇,一種是實(shí)現(xiàn)InitializingBean接口谓松,重寫afterPropertiesSet方法,另一種四在配置文件中通過init-method指定践剂,但是如果同時指定的話是先調(diào)用InitializingBean實(shí)現(xiàn)的方法鬼譬。
@PostConstruct和@PreDestroy
可以使用JS250,@PostConstruct 標(biāo)注在方法上面,bean創(chuàng)建完成并且屬性賦值完成逊脯,來執(zhí)行初始化方法
@PreDestroy 优质,在容器銷毀bean之前通知我們進(jìn)行清理工作.
@ComponentScan&@Configuration給容器注冊組件
@Configuration+@ComponentScan 注入組件, 只是把標(biāo)注了注解為@Controller 男窟,@Service盆赤, @Repository, @Component 加入到Spring容器中.
xml 方式的配置
<context:component-scan base-package="zfcoding"></context:component-scan>
代碼實(shí)例說明:
需要注入的類
@Component
public class PersonComponent {
}
@Repository
public class PersonDao {
}
@Service
public class PersonService {
}
@Controller
public class PersonController {
}
配置類
@Configuration
//@ComponentScan(basePackages = "zfcoding",
//// includeFilters =@ComponentScan.Filter(type=FilterType.ANNOTATION,classes = Controller.class),useDefaultFilters=false )
@ComponentScan(basePackages = "zfcoding",
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, Service.class}))
public class MyCompoentScan {
}
測試的方法
@Test
public void ComponentSanTest(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyCompoentScan.class);
String[] definitionNames = applicationContext.getBeanDefinitionNames();
for (String definitionName:definitionNames){
System.out.println(definitionName);
}
}
運(yùn)行結(jié)果
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
myCompoentScan
personComponent
personDao
說明:
excludeFilters 排除不需要加載的組件
includeFilters 只包含需要加載的組件歉眷,使用的時候需要把 useDefaultFilters=false牺六,才能生效
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, Service.class}),指定的過濾規(guī)則,排除注解標(biāo)注是@Controller和@Service 類汗捡。
@Conditional-設(shè)置組件作用域
按照一定的條件進(jìn)行判斷淑际,滿足條件給容器中注冊bean
自定義Conditional條件需要編寫一個類畏纲,這個類需要實(shí)現(xiàn)Condition接口,我們直接使用就@Conditional({WindowsEnvironment.class})
@Conditional既可以定義在方法上春缕,也可以定義在類上盗胀,代碼實(shí)現(xiàn)自定義條件需要實(shí)現(xiàn)Condition接口
條件類
public class WindowsEnvironment implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
BeanDefinitionRegistry registry = context.getRegistry();
if(property.contains("Windows 10")){
return true;
}
return false;
}
}
public class LiunxEnvironment implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
if (environment.containsProperty("Liunx")){
return true;
}
return false;
}
}
配置類
@Configuration
public class MyCondition {
@Conditional({WindowsEnvironment.class})
@Bean
public ConfigurationBean configurationBean(){
return new ConfigurationBean("張三");
}
@@Conditional({LiunxEnvironment.class})
@Bean
public ConfigurationBean configurationBean1(){
return new ConfigurationBean("李四");
}
}
測試方法
public class CoditionTest {
@Test
public void test(){
AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyCondition.class);
String[] beanNamesForType = applicationContext.getBeanNamesForType(ConfigurationBean.class);
for (String s:beanNamesForType){
System.out.println(s);
}
}
}
運(yùn)行結(jié)果
configurationBean
@Import 給容器快速導(dǎo)入一個組件
1、@Import(要導(dǎo)入的組件)锄贼,容器就會自動注冊這個組件票灰,id默認(rèn)是全類名。
2宅荤、ImportSelector 屑迂, 自定義邏輯返回要導(dǎo)入的組件 編寫的類學(xué)要實(shí)現(xiàn) ImportSelector 接口,返回需要導(dǎo)入的主鍵的全類名數(shù)組冯键。
3惹盼、ImportBeanDefinitionRegistrar,自定義邏輯需要編寫類實(shí)現(xiàn)ImportBeanDefinitionRegistrar接口,手動注冊bean到容器當(dāng)中惫确。
代碼實(shí)例
public class Red {
}
public class Blue {
}
//
@Import({Red.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
public class MyImport {
}
public class MyImportSelector implements ImportSelector {
//AnnotationMetadata :當(dāng)前標(biāo)注@Import 注解類的所有注解信息
//返回類的全類名
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"zfcoding.bean.Blue"};
}
}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
//importingClassMetadata:當(dāng)前類的注解信息
//BeanDefinitionRegistry BeanDefinition注冊類手报,把所有需要添加到容器中的bean,BeanDefinitionRegistry.registerBeanDefinition手工注冊
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean red = registry.containsBeanDefinition("zfcoding.bean.Red");
if (red){
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Blue.class);
registry.registerBeanDefinition("blue",rootBeanDefinition);
}
}
}
測試類
public class ImportTest {
@Test
public void test(){
AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyImport.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String s:beanDefinitionNames){
System.out.println(s);
}
}
}
運(yùn)行結(jié)果
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactorymyImport
zfcoding.bean.Red
zfcoding.bean.Blue
blue
總結(jié):
給容器中導(dǎo)入組件:
? 1、@Configuration&@Bean給容器中注冊組件
? 2改化、@ComponentScan&@Configuration(@Controller 掩蛤,@Service, @Repository所袁, @Component)
? 3盏档、@Import 快速的給容器中導(dǎo)入一個組件
@Value賦值 和@PropertySource 加載外部配置文件
@Value
? 1、基本數(shù)值
? 2燥爷、SPEL #{}
? 3、 可以寫${},去配置文件中的值(運(yùn)行環(huán)境中的值)
使用@PropertySource去取外部配置文件中的值保存到運(yùn)行的環(huán)境當(dāng)中懦窘。加載完尾部的配置文件之后前翎,使用${}取出配置文件中的值.
代碼實(shí)例:
代碼省略setter(),getter(),toString()方法。
public class Person {
@Value("張三")
private String name;
@Value("#{20-1}")
private Integer age;
@Value("${person.sex}")
private String sex;
}
//<context:property-placeholder location="person.properties"></context:property-placeholder>
@PropertySource("classpath:person.properties")
@Configuration
public class MyConfigValue {
@Bean
public Person person(){
return new Person();
}
}
person.properties
person.sex=1
測試類
@Test
public void test1(){
AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyConfigValue.class);
Person person = (Person) applicationContext.getBean("person");
System.out.println(person);
}
運(yùn)行結(jié)果
Person{name='張三', age=19, sex='1'}
@Autowired &@Qualifier&@Primary
自動裝配:
@Autowired自動注入畅涂,單個默認(rèn)優(yōu)先按照類型去容器中找到對應(yīng)的組件港华,默認(rèn)一定要將屬性賦值好,沒有找到就會報錯午衰,可以使用@Autowired (required=false),非必須的 ;
@Qualifier 立宜,指定需要裝配的組件id ;
@Primary 讓Spring 進(jìn)行自動裝配的時候, 首選裝配的組件(配置類中)臊岸,也可以使用@Qualifier指定需要裝配的bean的名字.
@Resource &@Inject
Spring還支持@Resource(JSR250)和@Inject (JSR330){java規(guī)范的注解}
@Resource 和@Autowired一樣實(shí)現(xiàn)自動裝配的功能橙数,默認(rèn)按照組件的名稱進(jìn)行裝配。
@Inject需要導(dǎo)入javax.inject的包帅戒,和@Autowired功能一樣灯帮,沒有required=false的功能。
@Profile 環(huán)境搭建
Spring 為我們提供可以根據(jù)當(dāng)前環(huán)境,動態(tài)的激活和切換一系列組件的功能
下面我們來使用數(shù)據(jù)源說明這個問題:
@Profile指定哪個環(huán)境下才能被注冊到容器當(dāng)中钟哥,加了@Profile迎献,只有這個環(huán)境激活的時候才能注冊到spring容器當(dāng)中,默認(rèn)是defalut
代碼實(shí)例:
@PropertySource("classpath:db.properties")
@Configuration
public class MyConfigProfile implements EmbeddedValueResolverAware {
@Value("${db.username}")
private String username;
@Value("${db.password=}")
private String password;
private StringValueResolver stringValueResolver;
private String driveClass;
@Profile("default")
@Bean("testDataSource")
public DataSource dataSource() throws PropertyVetoException {
ComboPooledDataSource dataSource=new ComboPooledDataSource();
dataSource.setUser(username);
dataSource.setPassword(password);
dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/springboot?serverTimezone=UTC&useSSL=false&autoReconnect=true&tinyInt1isBit=false&useUnicode=true&characterEncoding=utf8");
dataSource.setDriverClass(driveClass);
return dataSource;
}
@Profile("dev")
@Bean("devDataSource")
public DataSource devDataSource() throws PropertyVetoException {
ComboPooledDataSource dataSource=new ComboPooledDataSource();
dataSource.setUser(username);
dataSource.setPassword(password);
dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/springboot?serverTimezone=UTC&useSSL=false&autoReconnect=true&tinyInt1isBit=false&useUnicode=true&characterEncoding=utf8");
dataSource.setDriverClass(driveClass);
return dataSource;
}
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.stringValueResolver=resolver;
driveClass = stringValueResolver.resolveStringValue("db.driverClass");
}
}
db.properties
db.username=root
db.password=root
db.driverClass=com.mysql.jdbc.Driver
測試代碼:
public class DataSourceProfileTest {
@Test
public void test() {
AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyConfigProfile.class);
String[] beanNamesForType = applicationContext.getBeanNamesForType(DataSource.class);
for (String s:beanNamesForType){
System.out.println(s);
}
}
/* 使用無參構(gòu)造器
* 設(shè)置激活的環(huán)境
* 注冊主配置類
* 啟動刷新容器
*/
@Test
public void test1(){
//使用無參構(gòu)造器
AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext();
//設(shè)置激活的環(huán)境
applicationContext.getEnvironment().setActiveProfiles("dev");
//注冊主配置類
applicationContext.register(MyConfigProfile.class);
//啟動刷新容器
applicationContext.refresh();
String[] beanNamesForType = applicationContext.getBeanNamesForType(DataSource.class);
for (String s:beanNamesForType){
System.out.println(s);
}
}
}
測試案例運(yùn)行結(jié)果
testDataSource
devDataSource
小結(jié):
@Profile還可以標(biāo)記在類上腻贰,表示只有指定環(huán)境的時候吁恍,整個配置文件里面的配置才能開始生效。沒有標(biāo)注環(huán)境表示的bean播演,在任何環(huán)境下都是加載的(前提是配置類是生效的),還可以通過命令行參數(shù):<code>-Dspring.profiles.active=test</code>激活對應(yīng)的環(huán)境宾巍。
我是阿福,公眾號「阿福聊編程」作者顶霞,對后端技術(shù)保持學(xué)習(xí)愛好者,我會經(jīng)常更新JAVA技術(shù)文章选浑,在進(jìn)階的路上,共勉古徒!
歡迎大家關(guān)注我的公眾號,后臺回復(fù)666隧膘,領(lǐng)取福利,你懂的疹吃。
[圖片上傳失敗...(image-f04d6e-1592872109509)]