Spring註解版–spring給容器註冊組件的四種方法:
這是我看網上視頻教程之後,自己整理的,加強記憶。
文章目錄
- Spring註解版--spring給容器註冊組件的四種方法:
- 一. `@Configuration`&`@Bean`-自動掃描組件、掃描規則組件
- 1、創建一個webquick項目,內部添加pom依賴
- 2、比較xml註冊bean與註解註冊bean
- 3.@`ComponentScan`包掃描
- 4.@Scope指定作用範圍
- 5.`@Lazy`懶加載
- 6.@Conditional條件註解
- 二. `@Bean`[導入的第三方包裏面的組件]
- 三. `@Import`[快速給容器中導入一個組件]
- 四. 使用Spring提供的`FactoryBean`工廠(bean)
一. @Configuration
&@Bean
-自動掃描組件、掃描規則組件
1、創建一個webquick項目,內部添加pom依賴
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
給maven工程添加spring-context
的依賴。
2、比較xml註冊bean與註解註冊bean
(1)xml配置註冊bean
IOC在項目中添加一個resources文件夾,內部存放所有的配置文件以及資源。現在我們在內部創建一個bean.xml這個IOC容器的xml配置文件。
bean.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--<bean></bean>-->
<bean id="user" class="com.xiaojian.bean.User">
<property name="username" value="xiaojian"></property>
<property name="userage" value="11"></property>
</bean>
</beans>
如圖,我們給容器添加了一個bean,指向我們的一個User的實體類。此處的User的實體類,我們存放在java目錄下的bean文件夾中。
User實體類如下:
package com.xiaojian.bean;
public class User {
private String username;
private Integer userage;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getUserage() {
return userage;
}
public void setUserage(Integer userage) {
this.userage = userage;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", userage='" + userage + '\'' +
'}';
}
public User(String username, Integer userage) {
this.username = username;
this.userage = userage;
}
public User() {
}
}
我們在test目錄下面創建MainTest01測試類,測試獲取IOC容器中的bean。
測試類MainTest01:
package com.xiaojian.test;
import com.xiaojian.bean.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainTest01 {
public static void main(String[] args) {
ApplicationContext ApplicationContext = new ClassPathXmlApplicationContext("bean.xml");
User user = (User) ApplicationContext.getBean("user");
System.out.println(user);
}
}
測試結果:
(2)註解註冊bean
在config目錄下創建MainConfig類,此類爲創建bean的配置類。
package com.xiaojian.config;
import com.xiaojian.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//配置類==配置文件
@Configuration //告訴spring這是一個配置類
public class MainConfig {
//給容器註冊一個bean;類型爲返回值的類型;id默認是用方法名作爲id(不過我們也可以自己指定id,就是value值)
@Bean(value = "user01")
public User user01(){
return new User("小賤",13);
}
}
此class類文件中,@Configuration
註解告訴spring這是一個配置類,配置類的作用就是xml配置文件的作用。
@Bean
註解是給容器註冊一個bean,類型爲返回值的類型,id默認是用方法名作爲id,不過我們可以自己定義id,也就是@Bean
註解下value
屬性值。
@Bean
註解接口(系統內部的註解,我們直接使用):
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.context.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.core.annotation.AliasFor;
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
@AliasFor("name")
String[] value() default {};
@AliasFor("value")
String[] name() default {};
/** @deprecated */
@Deprecated
Autowire autowire() default Autowire.NO;
boolean autowireCandidate() default true;
String initMethod() default "";
String destroyMethod() default "(inferred)";
}
此註解接口中定義了value
這個屬性,就是bean的id。當使用value
這個屬性的時候,所有通過id想要獲取此bean,必須通過value
定義的id,而不是方法名。
我們在test目錄下再定義一個MainTest02這個類文件,測試註解方式註冊bean。
測試類MainTest02:
package com.xiaojian.test;
import com.xiaojian.bean.User;
import com.xiaojian.config.MainConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainTest02 {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
User user = (User) applicationContext.getBean("user01");
System.out.println(user);
//此處的方法是通過類型來獲取所有的bean的集合。
Map<String, User> beansOfType = applicationContext.getBeansOfType(User.class);
System.out.println(beansOfType);
//此處的方法是通過類型來獲取Bean的名稱,此處是通過User這個類,來獲取Bean的名稱。
String[] beanNamesForType = applicationContext.getBeanNamesForType(User.class);
for (String name : beanNamesForType) {
System.out.println(name);
}
//applicationContext下面還有好多的方法,可以有時間可以自己看看。
}
}
運行結果:
3.@ComponentScan
包掃描
(1)創建相關的類
首先在xiaojian這個文件夾下面分別創建controller、service、dao這三個文件夾,裏面在分別創建對應的BookController、BookService、BookDao這三個類文件,同時添加對應的註解。
BookController:
package com.xiaojian.controller;
import org.springframework.stereotype.Controller;
@Controller //此註解說明這是一個控制器
public class BookController {
}
BookService:
package com.xiaojian.service;
import org.springframework.stereotype.Service;
@Service //此註解作用是給容器掃描到,說明此類事服務層
public class BookService {
}
BookDao:
package com.xiaojian.dao;
import org.springframework.stereotype.Repository;
@Repository //此註解是爲了給容器掃描到,說明此類是dao層。
public class BookDao {
}
(2)xml配置中的包掃描方式
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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 包掃描,只要標註了@Controller、@Service、@Repository、@Component中的任何一個,都會被掃描-->
<context:component-scan base-package="com.xiaojian"></context:component-scan>
<!--<bean></bean>-->
<bean id="user" class="com.xiaojian.bean.User">
<property name="username" value="xiaojian"></property>
<property name="userage" value="11"></property>
</bean>
</beans>
xml文件中通過<context:component-scan base-package="" />
來掃描配置的bean。
(3)註解實現包掃描方式
我們只需要在@Configuration
主配置類中添加@ComponentScan( value="com.xiaojian")
就可以自動掃描xiaojian這個包下面的所有的添加了@Controller
、@Service
、@Repository
、@Component
。
MainConfig主配置類:
package com.xiaojian.config;
import com.xiaojian.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
//配置類==配置文件
@Configuration //告訴spring這是一個配置類
@ComponentScan(value = "com.xiaojian")
//包掃描路徑,與在xml文件中配置 <context:component-scan base-package="com.xiaojian"></context:component-scan> 作用一樣。
public class MainConfig {
//給容器註冊一個bean;類型爲返回值的類型;id默認是用方法名作爲id(不過我們也可以自己指定id,就是value值)
@Bean(value = "user01")
public User user01(){
return new User("小賤",13);
}
}
小的測試
在test目錄下添加一個測試類——MainTest03。
MainTest03:
package com.xiaojian.test;
import com.xiaojian.config.MainConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainTest03 {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
//此方法可以獲取所有的bean定義的名字
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String name:beanDefinitionNames){
System.out.println(name);
}
}
}
測試結果:
(4)@ComponentScan
屬性值
-
value
值:value
值是說明此掃描的掃描路徑,系統只會掃描路徑下的所有的添加了@Controller
、@Service
、@Repository
、@Component
註解的類。 -
excludeFilters
屬性:產生過濾規則,此指定掃描的時候按照什麼規則排除哪些組件。此寫法是一個Filter數組。
MainConfig:
package com.xiaojian.config; import com.xiaojian.bean.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.stereotype.Controller; import org.springframework.stereotype.Service; //配置類==配置文件 @Configuration //告訴spring這是一個配置類 @ComponentScan(value = "com.xiaojian",excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, Service.class}) }) //此處的type中的 FilterType.ANNOTATION表明排除註解方式的配置bean,classes表明排除的配置bean添加的註解的類型(此處主要排除@Controller、@Service兩個註解的配置類) //包掃描路徑,與在xml文件中配置 <context:component-scan base-package="com.xiaojian"></context:component-scan> 作用一樣。 public class MainConfig { //給容器註冊一個bean;類型爲返回值的類型;id默認是用方法名作爲id(不過我們也可以自己指定id,就是value值) @Bean(value = "user01") public User user01(){ return new User("小賤",13); } }
運行MainTest03測試類,測試結果:
系統的打印日誌中少了BookController、BookService這兩個。
-
includeFilters
屬性:產生過濾規則,此指定掃描的時候按照什麼規則只掃描哪些組件。此寫法是一個Filter數組。
(與上面相反)
不過此處需要注意,因爲在
@ComponentScan
中有個屬性useDefaultFilters
,默認爲true,而我們使用includeFilters
屬性的時候,需要將useDefaultFilters
設置爲false。MainConfig:
package com.xiaojian.config; import com.xiaojian.bean.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.stereotype.Controller; import org.springframework.stereotype.Service; //配置類==配置文件 @Configuration //告訴spring這是一個配置類 @ComponentScan(value = "com.xiaojian",includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class}) },useDefaultFilters = false) //必須把useDefaultFilters設置爲false才能使用includeFilters //包掃描路徑,與在xml文件中配置 <context:component-scan base-package="com.xiaojian"></context:component-scan> 作用一樣。 public class MainConfig { //給容器註冊一個bean;類型爲返回值的類型;id默認是用方法名作爲id(不過我們也可以自己指定id,就是value值) @Bean(value = "user01") public User user01(){ return new User("小賤",13); } }
運行MainTest03測試類,測試結果:
此處只有一個bookController,因爲includeFilters中只有一個Controller通行。
@ComponentScan
是一個可重複寫的註解,我們可以在一個類上多次添加此註解。或者我們可以使用
@ComponentScans
註解,內部添加多個@ComponentScan
(5)@Filter註解type屬性指定掃描規則
FilterType.ANNOTATION
:按照註解的方式(最常用)FilterType.ASPECTJ
:使用ASPECTJ
表達式(幾乎不常用)FilterType.REGEX
:使用正則表達式FilterType.ASSIGNABLE_TYPE
:按照給定的類型
@ComponentScan(value = "com.xiaojian",includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class}),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = {BookService.class})
},useDefaultFilters = false)
在MainConfig類文件的@ComponentScan
的includeFilters
屬性中添加一個FilterType.ASSIGNABLE_TYPE的Filter,此Filter通過BookService這個指定的類文件。
運行MainTest03測試類,測試結果:
FilterType.CUSTOM
:使用自定義規則
使用自定義規則的時候,這個值必須是一個TypeFilter規則的實現類。
首先我們在xiaojian這個文件夾下創建一個impls文件夾,在此文件夾下創建一個MyTypeFilter類去實現TypeFilter這個接口,接口中有一個返回boolean類型值的方法。
package com.xiaojian.impls;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import java.io.IOException;
public class MyTypeFilter implements TypeFilter{
/**
*
*
* @param metadataReader 讀取到的當前正在掃描的類的星系
* @param metadataReaderFactory 可以獲取到其他任何類的信息
* @return
* @throws IOException
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//獲取當前類註釋的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//獲取當前正在掃描的類的類信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//獲取當前類資源(類的路徑)
Resource resource = metadataReader.getResource();
//獲取類名
String className = classMetadata.getClassName();
//獲取父類名
String superClassName = classMetadata.getSuperClassName();
//獲取子類名
String[] memberClassNames = classMetadata.getMemberClassNames();
//獲取接口名
String[] interfaceNames = classMetadata.getInterfaceNames();
//上面那些方法我們有時間可以自己去看看。
System.out.println("---->"+className);
//判斷邏輯,爲true,則放行,爲false則攔截。
if (className.contains("vice")){
return true;
}
return false;
}
}
MainConfig類文件也做出相應的改變
package com.xiaojian.config;
import com.xiaojian.bean.User;
import com.xiaojian.impls.MyTypeFilter;
import com.xiaojian.service.BookService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
//配置類==配置文件
@Configuration //告訴spring這是一個配置類
@ComponentScan(value = "com.xiaojian",includeFilters = {
@ComponentScan.Filter(type = FilterType.CUSTOM,classes = {MyTypeFilter.class})
},useDefaultFilters = false)
//FilterType.CUSTOM是使用自定義規則,其實現的是MyTypeFilter這個類中放行的內容。
//包掃描路徑,與在xml文件中配置 <context:component-scan base-package="com.xiaojian"></context:component-scan> 作用一樣。
public class MainConfig {
//給容器註冊一個bean;類型爲返回值的類型;id默認是用方法名作爲id(不過我們也可以自己指定id,就是value值)
@Bean(value = "user01")
public User user01(){
return new User("小賤",13);
}
}
運行MainTest03測試類,測試結果:
如圖,只有一個bookService被放行,註冊到了IOC的容器中,因爲判斷邏輯只給包含"vice"的放行。而其他的兩個,是不在這個過濾器管轄範圍內的。
4.@Scope指定作用範圍
(1)默認情況(單實例)
- 創建MainConfig2
我們重新創建一個MainConfig類,類名爲MainConfig2,同時標註註解@Configuration
。
MainConfig2:
package com.xiaojian.config;
import com.xiaojian.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MainConfig2 {
//默認的都是單實例的
@Bean
public User user(){
return new User("小賤",333);
}
}
- 創建測試類
在test文件夾下面創建一個MainTest04這個測試類。
MainTest04:
package com.xiaojian.test;
import com.xiaojian.bean.User;
import com.xiaojian.config.MainConfig2;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainTest04 {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
//此方法可以獲取所有的bean定義的名字
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String name:beanDefinitionNames){
System.out.println(name);
}
User user = (User) applicationContext.getBean("user");
User user1 = (User) applicationContext.getBean("user");
//比較兩者是否爲同一個對象,觀察所有的bean的創建的默認情況下是單例還是多例。
System.out.println(user==user1);
}
}
運行MainTest04測試類,測試結果:
說明所有的bean默認情況下都是單例模式,就是所有創建的bean都是同一個。
(2)多實例案例
- 修改MainConfig2.java類文件,使得User這個bean爲多實例bean。
MainConfig2:
package com.xiaojian.config;
import com.xiaojian.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
@Configuration
public class MainConfig2 {
/**
* @Scope的作用範圍:
* singleton:單實例(系統默認情況下都是單實例的)
* prototype:多實例
* request:同一次請求創建一個實例
* session:同一個session創建一個實例
*
* 注意:
* - 系統默認的是單實例,IOC容器啓動會調用方法創建對象放到IOC容器中,以便以後每次獲取,就可以直接從容器中獲取
* - 多實例:IOC容器獲取的時候纔會創建,且獲取幾次容器會調用方法創建對象幾次。IOC容器啓動的時候並不會調用該方法創建對象到IOC容器中。
*/
//默認的都是單實例的
@Scope("prototype")
@Bean
public User user(){
return new User("小賤",333);
}
}
- 測試結果
運行MainTest04測試類,測試結果:
所以當bean被定義爲多實例的時候,每次調用都會創建一個bean對象,並且這些bean對象不是同一個。
(3)單實例與多實例的創建時間對比
- 單實例情況下:
修改MainConfig2.java文件:
MainConfig2:
package com.xiaojian.config;
import com.xiaojian.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
@Configuration
public class MainConfig2 {
/**
* @Scope的作用範圍:
* singleton:單實例(系統默認情況下都是單實例的)
* prototype:多實例
* request:同一次請求創建一個實例
* session:同一個session創建一個實例
*
* 注意:
* - 系統默認的是單實例,IOC容器啓動會調用方法創建對象放到IOC容器中,以便以後每次獲取,就可以直接從容器中獲取
* - 多實例:IOC容器獲取的時候纔會創建,且獲取幾次容器會調用方法創建對象幾次。IOC容器啓動的時候並不會調用該方法創建對象到IOC容器中。
*/
//默認的都是單實例的
// @Scope("prototype")
@Bean
public User user(){
System.out.println("給容器中添加User。。。");
return new User("小賤",333);
}
}
將User這個bean還原爲單實例對象。
修改測試類MainTest04.java:
MainTest04:
package com.xiaojian.test;
import com.xiaojian.bean.User;
import com.xiaojian.config.MainConfig2;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainTest04 {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
System.out.println("IOC容器啓動完畢");
}
}
此時系統只是簡單的運行了,創建了IOC容器,但是容器中並沒有做任何的操作。
運行MainTest04測試類,測試結果:
如圖,在系統啓動IOC容器的時候,系統已經給我們創建了user這個bean,也就是說,系統默認的是單實例,IOC容器啓動會調用方法創建對象放到IOC容器中,以便以後每次獲取,就可以直接從容器中獲取。
- 多實例情況下:
修改MainConfig2.java文件:
MainConfig2:
package com.xiaojian.config;
import com.xiaojian.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
@Configuration
public class MainConfig2 {
/**
* @Scope的作用範圍:
* singleton:單實例(系統默認情況下都是單實例的)
* prototype:多實例
* request:同一次請求創建一個實例
* session:同一個session創建一個實例
*
* 注意:
* - 系統默認的是單實例,IOC容器啓動會調用方法創建對象放到IOC容器中,以便以後每次獲取,就可以直接從容器中獲取
* - 多實例:IOC容器獲取的時候纔會創建,且獲取幾次容器會調用方法創建對象幾次。IOC容器啓動的時候並不會調用該方法創建對象到IOC容器中。
*/
//默認的都是單實例的
@Scope("prototype")
@Bean
public User user(){
System.out.println("給容器中添加User。。。");
return new User("小賤",333);
}
}
將User這個bean設置爲多實例對象。
修改測試類MainTest04.java:
MainTest04:
package com.xiaojian.test;
import com.xiaojian.bean.User;
import com.xiaojian.config.MainConfig2;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainTest04 {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
System.out.println("IOC容器啓動完畢");
User user = (User) applicationContext.getBean("user");
User user1 = (User) applicationContext.getBean("user");
System.out.println(user==user1);
}
}
運行MainTest04測試類,測試結果:
如圖,在系統啓動IOC容器的時候,系統並沒有給我們創建User這個bean,而是我們去調用這個bean的時候,系統會給我創建這個bean。也就是說,多實例的情況下,IOC容器獲取的時候纔會創建,且獲取幾次容器會調用方法創建對象幾次。IOC容器啓動的時候並不會調用方法創建對象到IOC容器中。
(4)@Scope
指定作用範圍
@Scope
的作用範圍:
- singleton:單實例(系統默認情況下都是單實例的)
- prototype:多實例
- request:同一次請求創建一個實例
- session:同一個session創建一個實例
上面的四個作用範圍,我們實際上用的比較多的是singleton以及prototype這兩種。
注意:
- 系統默認的是單實例,IOC容器啓動會調用方法創建對象放到IOC容器中,以便以後每次獲取,就可以直接從容器中獲取
- 多實例:IOC容器獲取的時候纔會創建,且獲取幾次容器會調用方法創建對象幾次。IOC容器啓動的時候並不會調用方法創建對象到IOC容器中。
5.@Lazy
懶加載
(1)懶加載含義
單實例bean的情況下,系統會在IOC容器啓動的時候創建單實例的bean,而懶加載的含義是讓容器在啓動的時候,不創建對象,而是等到第一次調用的時候在創建。
(2)案例
- 修改MainConfig2.java文件:
MainConfig2:
package com.xiaojian.config;
import com.xiaojian.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
@Configuration
public class MainConfig2 {
/**
* @Scope的作用範圍:
* singleton:單實例(系統默認情況下都是單實例的)
* prototype:多實例
* request:同一次請求創建一個實例
* session:同一個session創建一個實例
*
* 注意:
* - 系統默認的是單實例,IOC容器啓動會調用方法創建對象放到IOC容器中,以便以後每次獲取,就可以直接從容器中獲取
* - 多實例:IOC容器獲取的時候纔會創建,且獲取幾次容器會調用方法創建對象幾次。IOC容器啓動的時候並不會調用該方法創建對象到IOC容器中。
*/
//默認的都是單實例的
// @Scope("prototype")
@Lazy
@Bean
public User user(){
System.out.println("給容器中添加User。。。");
return new User("小賤",333);
}
}
我們在User這個bean上面添加@Lazy
這個註解,標明這個User的bean是懶加載,告訴容器其在本身啓動的時候不先創建此單實例的bean,等到調用的時候再創建。
- 修改測試類
修改MainTest04.java測試類:
MainTest04:
package com.xiaojian.test;
import com.xiaojian.bean.User;
import com.xiaojian.config.MainConfig2;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainTest04 {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
System.out.println("IOC容器啓動完畢");
User user = (User) applicationContext.getBean("user");
}
}
我們在系統啓動IOC容器完成後再調用這個User的bean,看看這個單實例的bean在什麼時候被創建的。
- 測試運行
運行MainTest04測試類,測試結果:
如圖,加上@Lazy
註解的User這個bean,在容器IOC啓動的時候並沒有被創建,而是在IOC調用這個bean的時候才被IOC容器創建。且以後再調用這個bean的時候,不會再創建這個bean。
6.@Conditional條件註解
@Conditional
:按照一定的條件進行判斷,滿足條件給容器中註冊bean
(1)創建類
- 創建主題類
我們在config文件夾下面創建MainConfig3.java類文件,以作新的案例。
MainConfig3:
package com.xiaojian.config;
import com.xiaojian.bean.User;
import com.xiaojian.impls.LinuxCondition;
import com.xiaojian.impls.WindowsCondition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MainConfig3 {
/**
* @Conditional:按照一定的條件進行判斷,滿足條件給容器中註冊bean
*
* @Conditional註解:
* 可以標註在類上,也可以標註在方法上
*
* 要求:
* 如果系統是windows,給容器中註冊xiaojian
* 如果系統是linux,給容器中註冊dashu
*/
@Conditional({WindowsCondition.class})
@Bean("xiaojian")
public User user01(){
return new User("xiaojian",22);
}
@Conditional({LinuxCondition.class})
@Bean("dashu")
public User user02(){
return new User("dashu",23);
}
}
- 創建WindowsCondition.java
我們在impls這個文件包下面創建WindowsCondition.java這個類文件。
WindowsCondition:
package com.xiaojian.impls;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
//判斷系統是否爲windows
public class WindowsCondition implements Condition{
/**
*
* @param conditionContext 判斷條件能使用的上下文(環境)
* @param annotatedTypeMetadata 註釋信息
* @return
*/
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
//1.能獲取到IOC使用的beanfactory
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
//2.獲取類加載器
ClassLoader classLoader = conditionContext.getClassLoader();
//3.獲取當前環境信息
Environment environment = conditionContext.getEnvironment();
//4.獲取到bean定義的註冊類
BeanDefinitionRegistry registry = conditionContext.getRegistry();
//我們可以通過registry這個對象,判斷容器中對的bean的註冊情況,同時也可以更新一些信息
//判斷操作系統
String property = environment.getProperty("os.name");
boolean windows = property.contains("Windows");
if (windows){
System.out.println("此電腦是windows系統");
return true;
}
return false;
}
}
- 創建LinuxCondition.java
我們在impls這個文件包下面創建LinuxCondition.java這個類文件。
LinuxCondition:
package com.xiaojian.impls;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
//判斷系統是否爲linux
public class LinuxCondition implements Condition{
/**
*
* @param conditionContext 判斷條件能使用的上下文(環境)
* @param annotatedTypeMetadata 註釋信息
* @return
*/
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
//1.能獲取到IOC使用的beanfactory
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
//2.獲取類加載器
ClassLoader classLoader = conditionContext.getClassLoader();
//3.獲取當前環境信息
Environment environment = conditionContext.getEnvironment();
//4.獲取到bean定義的註冊類
BeanDefinitionRegistry registry = conditionContext.getRegistry();
//我們可以通過registry這個對象,判斷容器中對的bean的註冊情況,同時也可以更新一些信息
//判斷操作系統
String property = environment.getProperty("os.name");
boolean linux = property.contains("Linux");
if (linux){
System.out.println("此電腦是linux系統");
return true;
}
return false;
}
}
- 測試類
我們在test的文件夾下創建MainTest05。
MainTest05:
package com.xiaojian.test;
import com.xiaojian.bean.User;
import com.xiaojian.config.MainConfig3;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.env.Environment;
import java.util.Map;
public class MainTest05 {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig3.class);
System.out.println("IOC容器啓動完成");
String[] beanNamesForType = applicationContext.getBeanNamesForType(User.class);
for (String name:beanNamesForType){
System.out.println(name);
}
//獲取User類型的所有的bean
Map<String, User> beansOfType = applicationContext.getBeansOfType(User.class);
System.out.println(beansOfType);
}
}
(2)測試結果
運行測試類MainTest05.java類文件中的主函數。
測試結果:
如圖所示,在IOC容器啓動過程中,系統會默認的去創建單實例的bean,而我們兩個bean上面都標註了條件註解,所以在創建bean的時候,它們都會先去啓動條件判斷執行的類,所以第一條是“這是windows系統”,之後纔是“IOC容器啓動完成”,而後我們可以看出系統打印出現在系統中已經存在的bean的名稱,因爲此係統是windows系統,所以只有"xiaojian"這個bean被註冊。
二. @Bean
[導入的第三方包裏面的組件]
此部分省略。
三. @Import
[快速給容器中導入一個組件]
1.導入單個組件
(1)創建Color.java類文件
我們在bean目錄下創建一個Color.java類文件。
Color:
package com.xiaojian.bean;
public class Color {
}
此類文件將作爲本次的bean對象。
(2)創建MainConfig4.java類文件
我們創建MainConfig4.java類文件,此文件作爲此案例的配置類。
MainConfig4:
package com.xiaojian.config;
import com.xiaojian.bean.Color;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import(Color.class) //@Import導入組件,id默認是組件的全類名
public class MainConfig4 {
}
此處我們使用@Import
註解來給IOC容器註冊bean,組件的id默認是組件的全類名。
(3)創建測試類
我們在test包下面創建一個MainTest06.java測試類。
MainTest06:
package com.xiaojian.test;
import com.xiaojian.config.MainConfig4;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainTest06 {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig4.class);
//獲取系統中的所有定義的bean名稱
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String name:beanDefinitionNames){
System.out.println(name);
}
}
}
(4)測試運行
運行MainTest06.java測試類,測試結果如下:
如圖,系統中已經註冊了Color這個bean,只不過其id是Color.java這個類文件的全路徑。
2.導入多個組件
(1)創建Animal.java類文件
我們在bean目錄下創建一個Animal.java類文件,內部爲空,只是簡單的作爲一個bean創建對象。
package com.xiaojian.bean;
public class Animal {
}
(2)修改MainConfig4.java類文件
導入多個組件,其實就是修改@Import
的導入的值,而這個註解本身是可以導入一個數組的,所以我們將上面的那個之後改成一個數組就行。
MainConfig4:
package com.xiaojian.config;
import com.xiaojian.bean.Animal;
import com.xiaojian.bean.Color;
import com.xiaojian.bean.User;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import({Color.class, Animal.class, User.class}) //@Import導入組件,id默認是組件的全類名
public class MainConfig4 {
}
(3)測試運行
運行MainTest06.java測試類,測試結果如下:
如圖,配置類中導入了三個組件,此處顯示三個組件的全類名。
3.ImportSelector
接口實現(第二種導入方法)
此方法是導入一個類,該類去實現ImportSelector
接口,這個接口內部有一個selectImports
方法需要方法重載,其會返回一個帶有系統需要導入的組件的全類名的數組,告訴IOC容器去創建哪些bean對象。
(1)修改MainConfig4.java類文件
MainConfig4:
package com.xiaojian.config;
import com.xiaojian.bean.Animal;
import com.xiaojian.bean.Color;
import com.xiaojian.bean.User;
import com.xiaojian.impls.MyImportSelector;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
//@Import({Color.class, Animal.class, User.class}) //@Import導入組件,id默認是組件的全類名
@Import({MyImportSelector.class})
//導入一個自定義的組件選擇控制類,其實現了ImportSelector這個接口,接口返回一個需要導入的組件的全類名的數組
public class MainConfig4 {
}
(2)創建MyImportSelector.java類文件
我們在impls這個包下面創建一個MyImportSelector.java類文件,去實現ImportSelector
這個接口。
MyImportSelector:
package com.xiaojian.impls;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
//自定義邏輯,返回需要導入的組件。
public class MyImportSelector implements ImportSelector {
//返回值就是要導入到容器中的組件的全類名
//AnnotationMetadata:當前標註@Import註解的類的所有註解信息
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
//方法不能返回一個null,否則會出現空指針異常。
return new String[]{"com.xiaojian.bean.Color","com.xiaojian.bean.User"};
}
}
此處的selectImports
方法返回一個數組,其內部包含Color、User這兩個實例對象,目的就是告訴IOC容器在啓動的時候去創建這兩個bean對象。
(3)測試運行
運行MainTest06.java測試類,測試結果如下:
如圖,控制檯打印出了Color以及User這兩個實體類的bean對象。
4.ImportBeanDefinitionRegistrar
接口實現(第三種導入方法)
此方法是導入一個類,該類去實現ImportBeanDefinitionRegistrar
接口,這個接口內部有一個registerBeanDefinitions
方法需要方法重載,我們可以在這個方法的內部註冊我們所需要的bean,同時我們也可以在這個方法中做一些其他的事情。
(1)創建MyImportBeanDefinitionRegistrar.java類文件
我們在impls這個包下面創建一個MyImportBeanDefinitionRegistrar.java類文件,去實現registerBeanDefinitions
這個接口。
MyImportBeanDefinitionRegistrar:
package com.xiaojian.impls;
import com.xiaojian.bean.Animal;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
public class MyImportBeanDefinitonRegistrar implements ImportBeanDefinitionRegistrar {
/**
*
* @param annotationMetadata 當前類的註解信息以及其他的信息
* @param beanDefinitionRegistry BeanDefinition註冊類
*
* 把所有的需要添加到容器中的bean:調用
* BeanDefinitionRegistry.registerBeanDefinition手動註冊進來
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
//判斷內部是否有User這個bean的信息
boolean user = beanDefinitionRegistry.containsBeanDefinition("User");
//判斷內部是否有Color這個bean的信息
boolean color = beanDefinitionRegistry.containsBeanDefinition("Color");
if (!user && !color){
//registerBeanDefinition這個方法用於註冊bean。
// 第一個參數是這個bean的名稱
// 第二個參數是一個BeanDefinition,我們用RootBeanDefinition這個實現
//指定bean定義信息:(bean的類型,bean的作用域。。。)
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Animal.class);
//註冊一個bean,指定bean的名稱
beanDefinitionRegistry.registerBeanDefinition("Animal", rootBeanDefinition);
}
}
}
(2)修改MainConfig4.java類文件
MainConfig4:
package com.xiaojian.config;
import com.xiaojian.bean.Animal;
import com.xiaojian.bean.Color;
import com.xiaojian.bean.User;
import com.xiaojian.impls.MyImportBeanDefinitonRegistrar;
import com.xiaojian.impls.MyImportSelector;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
//@Import({Color.class, Animal.class, User.class}) //@Import導入組件,id默認是組件的全類名
//@Import({MyImportSelector.class})
//導入一個自定義的組件選擇控制類,其實現了ImportSelector這個接口,接口返回一個需要導入的組件的全類名的數組
@Import(MyImportBeanDefinitonRegistrar.class)
//導入一個實現ImportBeanDefinitionRegistrar接口的類,該接口把所有需要註冊的bean通過內部的方法手動註冊進來。
public class MainConfig4 {
}
(3)測試運行
運行MainTest06.java測試類,測試結果如下:
如圖,控制檯只打印了一個Animal這個bean,因爲我們在方法中只註冊了一個這個bean。
四. 使用Spring提供的FactoryBean
工廠(bean)
1.創建ColorFactoryBean.java類文件
我們在impls這個包下面創建一個ColorFactoryBean.java類文件,此類實現FactoryBean
這個接口。
ColorFactoryBean:
package com.xiaojian.impls;
import com.xiaojian.bean.Color;
import org.springframework.beans.factory.FactoryBean;
//創建一個Spring定義的FactoryBean
public class ColorFactoryBean implements FactoryBean {
//返回一個Color對象,這個對象會添加到容器中
@Override
public Object getObject() throws Exception {
//此處返回一個Color對象,意思就是告訴IOC容器,我們註冊一個Color這個bean。
return new Color();
}
@Override
public Class<?> getObjectType() {
//此處是返回上面getObject()創建的bean對象的類型
return Color.class;
}
@Override
public boolean isSingleton() {
//此處是返回上面getObject()方法創建的bean對象是否爲單例模式
//true則爲單實例,容器中只保存一份;false,多實例,每次獲取都會返回一份。
return false;
}
}
此類實現FactoryBean
,我們需要重寫三個方法:
- getObject():此方法返回的是一個bean對象,就是將會在IOC容器中創建的對象
- getObjectType():此方法返回的是getObject()方法返回的對象的類型
- isSingleton():此方法返回的是getObject()方法返回的bean對象作用域範圍,就是是單例模式還是多例模式
2.創建MainConfig5.java類文件
我們在config包下面創建MainConfig5.java類文件,在此類文件中創建ColorFactoryBean這個bean對象的聲明。
MainConfig5:
package com.xiaojian.config;
import com.xiaojian.impls.ColorFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MainConfig5 {
//此處聲明瞭ColorFactoryBean這個bean的對象,告訴IOC容器
@Bean
public ColorFactoryBean colorFactoryBean(){
return new ColorFactoryBean();
}
}
3.創建測試類
在test包下面創建一個MainTest07.java類文件,進行相干的測試。
MainTest07:
package com.xiaojian.test;
import com.xiaojian.config.MainConfig5;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainTest07 {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig5.class);
//此處查看我們所有的bean對象的名稱
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String name:beanDefinitionNames){
System.out.println("此方法爲:"+name);
}
//工廠Bean獲取的時調用getObject方法創建的對象
//此處是爲了查看我們獲取的bean實際上的名稱
Object colorFactoryBean = applicationContext.getBean("colorFactoryBean");
System.out.println("bean的名稱"+colorFactoryBean.toString());
System.out.println("bean的類型:"+colorFactoryBean.getClass());
}
}
4.運行測試
運行MainTest07.java測試類,測試結果如下:
如圖,爲測試結果。圖中的1,是IOC容器創建的bean對象,而2是colorFactoryBean這個對象的實際的指向。也就是說,我們在MainConfig5.java類文件中創建的那個bean,我們在IOC容器中獲取的卻是Color,因爲我們在ColorFactoryBean的方法中返回了Color這個bean。