濮陽市建設(shè)分局網(wǎng)站百度賬號中心
Java進(jìn)擊框架:Spring(一)
- 前言
- 創(chuàng)建Spring項(xiàng)目
- Spring IoC容器和Beans介紹
- Bean的概述
- Spring IoC
- 配置元數(shù)據(jù)
- 實(shí)例化Bean
- 依賴注入
- 循環(huán)依賴
- 詳細(xì)配置
- 生命周期回調(diào)
- Bean定義繼承
- 基于注解的容器配置
- @Component和進(jìn)一步的原型注解
- 自動(dòng)檢測類和注冊Bean定義
- 使用JSR 330標(biāo)準(zhǔn)注釋
- ApplicationContext的附加功能
前言
Spring 誕生于 2003 年,輕量級的 Java 開源框架,是對早期 J2EE 規(guī)范復(fù)雜性的回應(yīng)。雖然有些人認(rèn)為Java EE和Spring是競爭,但Spring實(shí)際上是Java EE的補(bǔ)充。
從整體上看Spring可以分為五個(gè)部分(從上到下、從左到右):Data Access/Integration、Web、AOP、Core Container、Test。
- Data Access/Integration:數(shù)據(jù)訪問與集成,包括 JDBC、ORM、OXM、JMS 和 Transactions 模塊,主要提供數(shù)據(jù)庫底層操作和事務(wù)控制等支持。
- Web:提供了基本web集成特性,比如:多文件上傳功能、資源請求,數(shù)據(jù)綁定、通訊等支持。
- AOP:面向切面編程。比如:日志記錄、權(quán)限控制、性能統(tǒng)計(jì)等通用功能和業(yè)務(wù)邏輯分離的技術(shù)。
- Core Container:核心容器。提供控制反轉(zhuǎn)(IOC)和依賴注入(DI),上下文配置,表達(dá)式語言等支持。
- Test:測試模塊。使用Junit和TestNG對Spring組件進(jìn)行測試。
除了Spring Framework之外,還有其他項(xiàng)目,例如Spring Boot,Spring Security,Spring Data,Spring Cloud,Spring Batch等。
創(chuàng)建Spring項(xiàng)目
(1)以idea為例,先創(chuàng)建Maven項(xiàng)目。(2)然后再main文件下創(chuàng)建resouces文件。
(3)找到idea最右側(cè),標(biāo)記為Resource文件。
(4)然后引入依賴(spring5.3.23為例)。
<dependencies><!-- https://mvnrepository.com/artifact/org.springframework/spring-context --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.23</version></dependency><!-- https://mvnrepository.com/artifact/org.springframework/spring-core --><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>5.3.23</version></dependency><!-- https://mvnrepository.com/artifact/org.springframework/spring-beans --><dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId><version>5.3.23</version></dependency><!-- https://mvnrepository.com/artifact/org.springframework/spring-expression --><dependency><groupId>org.springframework</groupId><artifactId>spring-expression</artifactId><version>5.3.23</version></dependency></dependencies>
(5)再resources文件下創(chuàng)建xml文件,文件名自定義。
(6)寫入基本配置結(jié)構(gòu)
<?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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"></beans>
這樣就創(chuàng)建完成spring項(xiàng)目,后續(xù)通過此結(jié)構(gòu)進(jìn)行講解。
Spring IoC容器和Beans介紹
在 Spring 中,構(gòu)成應(yīng)用程序主干并由 Spring IoC 容器管理的對象稱為 Bean。 Bean 是由 Spring IoC 容器實(shí)例化,組裝和以其他方式管理的對象。Bean 及其之間的依賴關(guān)系反映在容器使用的配置元數(shù)據(jù)中。配置元數(shù)據(jù)用XML、Java注釋或Java代碼表示。它讓您能夠表達(dá)組成應(yīng)用程序的對象以及這些對象之間豐富的相互依賴關(guān)系。
Bean的概述
Spring IoC容器管理一個(gè)或多個(gè)beans。這些beans是用您提供給容器的配置元數(shù)據(jù)創(chuàng)建的(例如,以XML的形式<bean/>
定義)。
在容器本身中,這些bean定義表示為BeanDefinition對象,這些對象包含以下元數(shù)據(jù)(以及其他信息):
-
包限定類名:通常是正在定義的bean的實(shí)際實(shí)現(xiàn)類。
-
Bean行為配置元素,聲明bean在容器中的行為方式(范圍、生命周期回調(diào)等)。
-
對bean完成其工作所需的其他bean的引用。這些引用也稱為協(xié)作者或依賴者。
-
要在新創(chuàng)建的對象中設(shè)置的其他配置設(shè)置,例如,池的大小限制或在管理連接池的bean中使用的連接數(shù)。
這些元數(shù)據(jù)轉(zhuǎn)化為一組組成每個(gè)bean定義的屬性。下表描述了這些屬性:
屬性 | 介紹 |
---|---|
Class | 實(shí)例化bean |
Name | 命名bean |
Scope | bean范圍 |
Constructor arguments | 構(gòu)造器參數(shù) |
Dependency Injection | 依賴注入 |
Autowiring mode | 自動(dòng)裝配模式 |
Lazy initialization mode | 延遲初始化的bean |
Initialization method | 初始化方法 |
Destruction method | 銷毀方法 |
Spring IoC
IoC(Inversion of Control)控制反轉(zhuǎn),也稱為依賴注入(DI)。
我們可以先來重溫一下,初學(xué)Java時(shí),當(dāng)某個(gè)類需要調(diào)用其它類的方法,直接new這個(gè)類,再調(diào)用方法,如代碼所示:
public class B {public void b(){}
}
public class A {public static void main(String[] args) {B b = new B();b.b();}
}
這樣就很容易導(dǎo)致,耦合度太高,有了IOC容器后將主動(dòng)權(quán)交給了第三方進(jìn)行管理。
定義一個(gè)容器配置元數(shù)據(jù):
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="a" class="com.example.A"><constructor-arg ref="b"></constructor-arg></bean><bean id="b" class="com.example.B"></bean>
</beans>
定義構(gòu)造類,進(jìn)行注入:
public class B {public void b(){}
}
public class A {private B b;public A(B b) { this.b = b; }public void getb(){ b.b(); }public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A a = applicationContext.getBean("a", A.class);a.getb();}
}
org.springframework.beans
和org.springframework.context
包是Spring Framework的IoC容器的基礎(chǔ)。
org.springframework.context.ApplicationContext
接口代表Spring IoC容器。
你可以創(chuàng)建ClassPathXmlApplicationContext或者FileSystemXmlApplicationContext來啟動(dòng)對元數(shù)據(jù)的支持。
通過使用IoC,這里通過構(gòu)造器注入(還有其它方法)使類與類之間耦合性減小,所以IoC不是一種技術(shù),更多的是一種思想,它能指導(dǎo)我們?nèi)绾卧O(shè)計(jì)出松耦合、更優(yōu)良的程序。
通俗點(diǎn)說:IOC容器改變了依賴對象的創(chuàng)建方式,反向的向類注入所需要的其它對象 。
配置元數(shù)據(jù)
Spring IoC容器管理一個(gè)或多個(gè)beans。這些beans是用您提供給容器的配置元數(shù)據(jù)創(chuàng)建的(例如,以XML的形式<bean/>
定義)?;?strong>XML的配置元數(shù)據(jù)將這些beans配置為<bean/>
頂層中的元素<beans/>
元素。
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" name="a,a1;a3" class="com.example.A" scope="singleton"></bean><alias name="a" alias="as-a"></alias>
</beans>
id
屬性:標(biāo)識(shí)單個(gè)bean定義,唯一標(biāo)識(shí)。
public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A a = applicationContext.getBean("a", A.class);}
name
屬性:標(biāo)識(shí)單個(gè)bean定義,唯一標(biāo)識(shí),可以與id
相同,指定多個(gè)名稱可以使用逗號或者分號隔開。
public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A a = applicationContext.getBean("a1", A.class);}
alias
屬性:有時(shí)需要為在別處定義的bean引入別名,在基于XML的配置元數(shù)據(jù)中,可以使用元素來實(shí)現(xiàn)這一點(diǎn)。
public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A a = applicationContext.getBean("as-a", A.class);}
class
屬性:定義bean的類型,并使用完全限定的類名。如果id
和name
都沒有想要獲取bean可以通過完全限定的類名。
public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A a = applicationContext.getBean("com.example.A", A.class);}
scope
屬性:指定對象的作用范圍。最初只有兩種:singleton和prototype,隨著版本的不斷更新,新增類型:request、session、application、websocket。Bean的生命周期有三種:創(chuàng)建、運(yùn)行、銷毀。
(1)singleton:默認(rèn)值。Spring容器中只有一個(gè)實(shí)例。
創(chuàng)建:容器創(chuàng)建時(shí),對象創(chuàng)建。
運(yùn)行:容器存在,一直存活。
銷毀:容器銷毀,對象銷毀。
示例代碼:
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A" scope="singleton"></bean>
</beans>
public class A {public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A a = applicationContext.getBean("a", A.class);System.out.println(a);a = applicationContext.getBean("a", A.class);System.out.println(a);/** Output:* com.example.A@ff5b51f* com.example.A@ff5b51f*/}
}
運(yùn)行后我們發(fā)現(xiàn),當(dāng)范圍為singleton時(shí),打印輸出的內(nèi)存地址相同,容器只創(chuàng)建了一個(gè)實(shí)例。
(2)prototype:每次請求Spring容器都會(huì)創(chuàng)建一個(gè)新的實(shí)例。
創(chuàng)建:使用時(shí),對象創(chuàng)建。
運(yùn)行:對象使用時(shí),一直存活。
銷毀:對象長時(shí)間不用,且沒有別的對象引用時(shí),由Java的垃圾回收機(jī)制回收。
示例代碼:
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A" scope="prototype"></bean>
</beans>
public class A {public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A a = applicationContext.getBean("a", A.class);System.out.println(a);a = applicationContext.getBean("a", A.class);System.out.println(a);/** Output:* com.example.A@6e1567f1* com.example.A@5cb9f472*/}
}
運(yùn)行后我們發(fā)現(xiàn),當(dāng)范圍為prototype時(shí),打印輸出的內(nèi)存地址不相同,容器只創(chuàng)建了兩個(gè)實(shí)例。
(3)request:每個(gè)HTTP請求都有自己的bean實(shí)例,僅在web有效。
(4)session:將單個(gè)bean定義作用于HTTP Session的生命周期,僅在web有效。
(5)application:將單個(gè)bean定義作用于ServletContext的生命周期,僅在web有效。
(6)websocket:將單個(gè)bean定義作用于WebSocket的生命周期,僅在web有效。
(7)自定義范圍:bean作用域機(jī)制是可擴(kuò)展的。您可以定義自己的作用域,甚至重新定義現(xiàn)有的作用域,盡管后者被認(rèn)為是不好的做法,您需要實(shí)現(xiàn)org.springframework.beans.factory.config.Scope
接口。
public class NewScope implements Scope {public Object get(String s, ObjectFactory<?> objectFactory) { return null; }public Object remove(String s) { return null; }public void registerDestructionCallback(String s, Runnable runnable) { }public Object resolveContextualObject(String s) { return null; }public String getConversationId() { return null; }
}
實(shí)例化Bean
bean定義本質(zhì)上是創(chuàng)建一個(gè)或多個(gè)對象的方法。當(dāng)被訪問時(shí),容器查看命名bean的配方,并使用由該bean定義封裝的配置元數(shù)據(jù)來創(chuàng)建(或獲取)實(shí)際對象。
實(shí)例化 Bean 有四種方式:構(gòu)造函數(shù)實(shí)例化、靜態(tài)工廠方法實(shí)例化、實(shí)例工廠方法進(jìn)行實(shí)例化、接口實(shí)例化。
- 構(gòu)造函數(shù)實(shí)例化
當(dāng)您通過構(gòu)造器方法創(chuàng)建一個(gè)bean時(shí),所有普通的類都可以被Spring使用并與之兼容。Spring IoC容器實(shí)際上可以管理您希望它管理的任何類。您還可以在容器中包含更多奇特的非bean樣式的類。
使用基于XML的配置元數(shù)據(jù),您可以按如下方式指定bean類::
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A"></bean>
</beans>
示例代碼如下:
public class A {public A() {System.out.println("構(gòu)造方法實(shí)例化");}public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A a = applicationContext.getBean("a", A.class);System.out.println(a);/** Output:* 構(gòu)造方法實(shí)例化* com.example.A@462d5aee*/}
}
- 靜態(tài)工廠方法實(shí)例化
通過factory-method
屬性指定工廠方法,用靜態(tài)工廠方法創(chuàng)建的bean。在此示例中getInstance()
方法必須是static方法。以下示例顯示了如何指定工廠方法:
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A" factory-method="getInstance"></bean>
</beans>
示例代碼如下:
public class A {public static A getInstance(){System.out.println("靜態(tài)工廠實(shí)例");return new A();}public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A a = applicationContext.getBean("a", A.class);System.out.println(a);/** Output:* 靜態(tài)工廠實(shí)例* com.example.A@148080bb*/}
}
- 實(shí)例工廠方法進(jìn)行實(shí)例化
實(shí)例工廠方法是通過現(xiàn)有非靜態(tài)方法bean創(chuàng)建新的bean。要使用這種機(jī)制,將class
屬性為空,并且在factory-bean
屬性,指定當(dāng)前(或父或祖先)容器中bean的名稱,該容器包含創(chuàng)建對象時(shí)要調(diào)用的實(shí)例方法。屬性設(shè)置工廠方法本身的名稱factory-method
屬性。以下示例顯示了如何配置這樣的bean:
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A"></bean><bean id="a2" factory-bean="a" factory-method="getInstance"></bean>
</beans>
示例代碼如下:
public class A {public A getInstance(){System.out.println("實(shí)例化工廠方法");return new A();}public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A a = applicationContext.getBean("a2", A.class);System.out.println(a);/** Output:* 實(shí)例化工廠方法* com.example.A@6e1ec318*/}
}
- 接口實(shí)例化
BeanFactory接口提供了能夠管理任何類型對象的高級配置機(jī)制。ApplicationContext是BeanFactory的子接口。
BeanFactory定義了IOC容器的最基本形式,并提供了IOC容器應(yīng)遵守的的最基本的接口,也就是Spring IOC所遵守的最底層和最基本的編程規(guī)范。
public interface BeanFactory {String FACTORY_BEAN_PREFIX = "&";//省略部分代碼... ...Object getBean(String var1) throws BeansException;<T> ObjectProvider<T> getBeanProvider(Class<T> var1);boolean containsBean(String var1);boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;@NullableClass<?> getType(String var1) throws NoSuchBeanDefinitionException;String[] getAliases(String var1);
}
如果按照傳統(tǒng)的方式,則需要在中提供大量的配置信息。Spring提供了一個(gè)FactoryBean的工廠類接口,FactoryBean是個(gè)Bean,用戶可以通過實(shí)現(xiàn)該接口定制實(shí)例化Bean的邏輯。
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A"></bean>
</beans>
public class A implements FactoryBean<A> {public A getObject() throws Exception {System.out.println("FactoryBean a");return new A();}public Class<?> getObjectType() {return A.class;}public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A a = applicationContext.getBean("a", A.class);System.out.println(a);/** Output:* FactoryBean a* com.example.A@29ca901e*/}}
依賴注入
依賴注入(DI)是一個(gè)過程,通過構(gòu)造函數(shù)參數(shù)、工廠方法的參數(shù)等方式,在對象實(shí)例上設(shè)置的屬性來定義它們的依賴關(guān)系(即,它們使用的其他對象)。然后,容器在創(chuàng)建bean時(shí)注入這些依賴項(xiàng)。這個(gè)過程基本上是bean本身的逆過程(因此得名,控制反轉(zhuǎn))。
依賴注入(DI)有三種主要形式:基于構(gòu)造函數(shù)的依賴注入和基于Setter的依賴注入、自動(dòng)注入。
- 基于構(gòu)造函數(shù)的依賴注入
基于構(gòu)造函數(shù)的依賴注入(DI)是通過容器調(diào)用一個(gè)帶有多個(gè)參數(shù)的構(gòu)造函數(shù)來實(shí)現(xiàn)的,每個(gè)參數(shù)代表一個(gè)依賴項(xiàng)。
通過<constructor-arg>
標(biāo)簽進(jìn)行構(gòu)造函數(shù)注入;ref
屬性引用了另一個(gè)bean定義的名稱。id
屬性和ref
屬性之間的鏈接表示協(xié)作對象之間的依賴關(guān)系:
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A"><constructor-arg ref="c"></constructor-arg><constructor-arg ref="b"></constructor-arg></bean><bean id="b" class="com.example.B"></bean><bean id="c" class="com.example.C"></bean>
</beans>
示例代碼如下:
public class B {public void b(){System.out.println("b");}
}
public class C {public void c(){System.out.println("c");}
}
public class A {private B b;private C c;public A(B b,C c) {this.b = b;this.c = c;}public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A a = applicationContext.getBean("a", A.class);a.b.b();a.c.c();/** Output:* b* c*/}
}
再Bean中定義的順序,就是實(shí)例化bean時(shí)提供給相應(yīng)構(gòu)造函數(shù)的順序。
如果構(gòu)造參數(shù)為基本類型的有參構(gòu)造時(shí),可以通過type
屬性指定構(gòu)造函數(shù)參數(shù)的類型,value
屬性指定參數(shù)值:
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A"><constructor-arg type="Integer" value="123"></constructor-arg><constructor-arg type="String" value="c"></constructor-arg></bean>
</beans>
示例代碼如下:
public class A {private Integer b;private String c;public A(Integer b,String c) {this.b = b;this.c = c;}public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A a = applicationContext.getBean("a", A.class);System.out.println(a.b);System.out.println(a.c);/** Output:* 123* c*/}}
您也可以使用index
屬性顯式指定構(gòu)造函數(shù)參數(shù)的索引,如下例所示:
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A"><constructor-arg index="0" value="123"></constructor-arg><constructor-arg index="1" value="c"></constructor-arg></bean>
</beans>
- 基于Setter的依賴注入(屬性注入)
基于setter的依賴注入(DI)是由容器在調(diào)用無參數(shù)構(gòu)造函數(shù)或無參數(shù)構(gòu)造函數(shù)后調(diào)用bean上的setter方法來完成的。
下面的示例,通過<property>
標(biāo)簽指定bean的一個(gè)或多個(gè)屬性,顯示了一個(gè)只能通過使用純setter注入進(jìn)行依賴注入的類。
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A"><property name="b" ref="b"></property></bean><bean id="b" class="com.example.B"></bean>
</beans>
示例代碼如下:
public class A {private B b;public void setB(B b) {System.out.println("setter注入");this.b = b;}public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A a = applicationContext.getBean("a", A.class);System.out.println(a.b);/** Output:* setter注入* com.example.B@1990a65e*/}
}
- 自動(dòng)注入
你可以使用autowire
屬性來達(dá)到自動(dòng)裝配。autowire
屬性提供5中策略:
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><!--no和default:默認(rèn),不裝配。--><bean id="a" class="com.example.A" autowire="no"></bean><bean id="a" class="com.example.A" autowire="default"></bean><!--byType:通過屬性類型注入。--><bean id="a" class="com.example.A" autowire="byType"></bean><!--byName:通過屬性的名稱注入。--><bean id="a" class="com.example.A" autowire="byName"></bean><!--constructor:通過構(gòu)造函數(shù)注入。--><bean id="a" class="com.example.A" autowire="constructor"></bean>
</beans>
byType
和byName
都是通過setter()
方法注入,constructor
通過構(gòu)造函數(shù)屬性的類型注入。
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A" autowire="byType"></bean><bean id="b" class="com.example.B"></bean>
</beans>
示例代碼如下:
public class B{}
public class A{private B b;public void setB(B b) { this.b = b; }public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A a = applicationContext.getBean("a", A.class);System.out.println(a.b);/** Output:* com.example.B@4c762604*/}
}
如果未能匹配到類型或者名稱,則注入失敗:
public class A{private C c;public void setC(C c) { this.c = c; }public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A a = applicationContext.getBean("a", A.class);System.out.println(a.c);/** Output:* null*/}
}
你可以使用@Autowired
注解減少指定屬性或構(gòu)造函數(shù)參數(shù)的需要。
示例代碼如下:
@Component
public class B {}
@Component
public class A{@Autowiredprivate B b;public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.example");A a = applicationContext.getBean("a", A.class);System.out.println(a.b);/** Output:* com.example.B@3c419631*/}
}
- 使用方法注入
Spring的方法注入可分為兩種:查找方法注入和任意方法替換。
(1)查找方法注入
查找方法會(huì)導(dǎo)致IoC容器覆蓋給定的方法并返回bean屬性中給出的名稱。這是方法注入的一種形式。
在<bean>
標(biāo)簽里定義<lookup-method>
標(biāo)簽:
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A"><lookup-method name="getB" bean="b"></lookup-method></bean><bean id="b" class="com.example.B"></bean>
</beans>
name屬性指定方法名,bean為返回的類型
示例代碼如下:
public class B {public void b(){ System.out.println("b"); }
}
public class A{private B b;public B getB() { return b; }public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A a = applicationContext.getBean("a", A.class);System.out.println(a.getB());/** Output:* com.example.B@58c1c010*/}}
當(dāng)然你也可以使用注解@Lookup
,示例代碼如下:
@Component
public class A{private B b;// @Lookup("getB")查找指定bean方法@Lookuppublic B getB() {return b;}public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.example");A a = applicationContext.getBean(A.class);System.out.println(a.getB());/** Output:* com.example.B@58c1c010*/}
}
@Autowired
注解會(huì)將所有bean范圍改為單例,@Lookup
可以保證被引入的組件保持prototype模式。
(2)任意方法替換
與查找方法注入相比,方法注入的一個(gè)不太有用的形式是用另一個(gè)方法實(shí)現(xiàn)替換受管bean中的任意方法的能力。
對于基于XML的配置元數(shù)據(jù),您可以使用replaced-method
元素將現(xiàn)有的方法實(shí)現(xiàn)替換為另一個(gè)方法實(shí)現(xiàn)。
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A"><replaced-method name="method" replacer="AReplace"><!--參數(shù)類型--><arg-type>String</arg-type></replaced-method></bean><bean id="AReplace" class="com.example.AReplace"></bean>
</beans>
你可以通過<arg-type/>
指定多個(gè)重寫方法的參數(shù)類型。
示例代碼如下:
public class AReplace implements MethodReplacer {public Object reimplement(Object o, Method method, Object[] objects) throws Throwable {System.out.println("替換了:"+objects[0].toString());return null;}
}
public class A{public void method(String p){}public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A a = applicationContext.getBean(A.class);a.method("123");/** Output:* 替換了:123*/}
}
大多數(shù)Spring用戶并不直接使用這些類(即以編程方式),而是使用XMLbean定義、帶注釋的組件。然后,這些源在內(nèi)部被轉(zhuǎn)換為BeanDefinition并用于加載整個(gè)Spring IoC容器實(shí)例。
循環(huán)依賴
如果您主要使用構(gòu)造函數(shù)注入,就有可能創(chuàng)建一個(gè)無法解析的循環(huán)依賴場景。
比如:A類通過構(gòu)造函數(shù)注入需要B類的一個(gè)實(shí)例,B類通過構(gòu)造函數(shù)注入需要A類的一個(gè)實(shí)例。如果將類A和B的beans配置為相互注入,Spring IoC容器會(huì)在運(yùn)行時(shí)檢測到這種循環(huán)引用,并拋出一個(gè)BeanCurrentlyInCreationException。
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A"><constructor-arg name="b" ref="b"></constructor-arg></bean><bean id="b" class="com.example.B"><constructor-arg name="a" ref="a"></constructor-arg></bean>
</beans>
示例代碼如下:
public class B {private A a;public B(A a) {this.a = a;}
}
public class A {private B b;public A(B b) {this.b = b;}public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A a = applicationContext.getBean("a", A.class);System.out.println(a);/** Output: * Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?* ... 29 more*/}
}
您可以使用setter注入配置循環(huán)依賴項(xiàng)。
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A"><property name="b" ref="b"></property></bean><bean id="b" class="com.example.B"><constructor-arg name="a" ref="a"></constructor-arg></bean>
</beans>
示例代碼如下:
public class B {private A a;public B(A a) { this.a = a; }
}
public class A {private B b;public void setB(B b) { this.b = b; }public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A a = applicationContext.getBean("a", A.class);System.out.println(a);/** Output:* com.example.A@2fd66ad3*/}
}
Spring容器在創(chuàng)建容器時(shí)驗(yàn)證每個(gè)bean的配置。它在容器加載時(shí)檢測配置問題,例如,bean由于缺少或無效的屬性而拋出一個(gè)異常。
詳細(xì)配置
下面介紹其它的配置標(biāo)簽、屬性等。
p
命名空間
使用p
名稱空間可以簡潔的XML配置,我們先看看原來的配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A"><property name="id" value="123456"></property><property name="userName" value="張三"></property><property name="b" ref="b"></property></bean><bean id="b" class="com.example.B"></bean>
</beans>
使用p
名稱空間,首先再<beans>
標(biāo)簽中加入鏈接
xmlns:p=“http://www.springframework.org/schema/p”
然后我們就可以使用p
命名空間來配置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:p="http://www.springframework.org/schema/p"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="a" class="com.example.A"p:id="123456"p:userName="張三"p:b-ref="b"></bean><bean id="b" class="com.example.B"></bean>
</beans>
p:*-ref
表示這不是一個(gè)直接的值,而是對另一個(gè)bean的引用。
示例代碼如下:
public class A {private int id;private String userName;private B b;public void setId(int id) { this.id = id; }public void setUserName(String userName) { this.userName = userName; }public void setB(B b) { this.b = b; }public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A a = applicationContext.getBean("a", A.class);System.out.println(a.id+","+a.userName+","+a.b);/** Output:* 123456,張三,com.example.B@52525845*/}
}
c
命名空間
我們先來看看原始的構(gòu)造函數(shù)注入方式:
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><beans><bean id="a" class="com.example.A"><constructor-arg ref="b"></constructor-arg><constructor-arg name="name" value="張三"></constructor-arg></bean><bean id="b" class="com.example.B"></bean></beans>
</beans>
像p
命名空間一樣,可以使用c
命名空間來簡化配置構(gòu)造函數(shù)參數(shù),首先再<beans>
標(biāo)簽中加入鏈接。
xmlns:c=“http://www.springframework.org/schema/c”
然后我們就可以使用c
命名空間來配置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:c="http://www.springframework.org/schema/c"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><beans><bean id="a" class="com.example.A"c:b-ref="b"c:name="張三"></bean><bean id="b" class="com.example.B"></bean></beans>
</beans>
c:*-ref
表示對于另一個(gè)bean引用。
示例代碼如下:
public class A {private B b;private String name;public A(B b, String name) {this.b = b;this.name = name;}public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A a = applicationContext.getBean("a", A.class);a.b.b();System.out.println(a.name);/** Output:* b* 張三*/}
}
idref
標(biāo)簽
idref
標(biāo)簽只是傳遞id(一個(gè)字符串值,不是引用),配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A"><property name="content"><idref bean="b"></idref></property></bean><bean id="b" class="com.example.B"></bean>
</beans>
示例代碼如下:
public class B {}
public class A {private String content;public String getContent() {return content;}public void setContent(String content) {this.content = content;}public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A a = applicationContext.getBean("a", A.class);System.out.println(a.getContent());/** Output:* b*/}
}
前面的bean定義片段完全等同于(在運(yùn)行時(shí))下面的片段:
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A"><property name="content" value="b"/></bean><bean id="b" class="com.example.B"></bean>
</beans>
idref
標(biāo)簽讓容器在部署時(shí)驗(yàn)證被引用的命名bean是否確實(shí)存在。
ref
屬性(標(biāo)簽)
前面簡單介紹了ref
屬性(標(biāo)簽),ref
屬性(標(biāo)簽)是<constructor-arg/>
標(biāo)簽或者<property/>
標(biāo)簽定義的元素。您將bean的指定屬性值設(shè)置為由容器管理的另一個(gè)bean(協(xié)作者)的引用。
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A"><!--<property name="b" ref="b"></property>--><property name="b"><ref bean="b"></ref></property></bean><bean id="b" class="com.example.B"></bean>
</beans>
- Collections
依賴注入可以注入集合:<list/>
, <set/>
,<map/>
,以及<props/>
元素,以下示例顯示了如何使用它們:
(1)list
介紹兩種用法:字符串集合、對象集合
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A"><!--字符串list--><property name="list"><list><value>qwe</value><value>asd</value></list></property><!--對象list--><property name="listObject"><list><!--引用其它對象--><ref bean="b"></ref><bean class="com.example.B"><property name="name" value="a"/><property name="age" value="01"/></bean></list></property></bean><bean id="b" class="com.example.B"><property name="name" value="b"/><property name="age" value="00"/></bean>
</beans>
示例代碼如下:
public class B {private String name;private Integer age;public String getName() { return name; }public void setName(String name) { this.name = name; }public Integer getAge() { return age; }public void setAge(Integer age) { this.age = age; }
}
public class A {private List<String> list;private List<B> listObject;public void setList(List<String> list) { this.list = list; }public void setListObject(List<B> listObject) { this.listObject = listObject; }public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A a = applicationContext.getBean("a", A.class);System.out.println(JSONObject.toJSONString(a.list));System.out.println(JSONObject.toJSONString(a.listObject));/** Output:* ["qwe","asd"]* [{"age":0,"name":"b"},{"age":1,"name":"a"}]*/}
}
(2)set
set也有兩種用法:字符串set、對象set
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A"><!--字符串set--><property name="set"><set><value>qwe</value><value>asd</value></set></property><!--對象set--><property name="setObject"><set><!--引用其它對象--><ref bean="b"></ref><bean name="b" class="com.example.B"><property name="name" value="a"/><property name="age" value="01"/></bean></set></property></bean><bean id="b" class="com.example.B"><property name="name" value="b"/><property name="age" value="00"/></bean>
</beans>
示例代碼如下:
public class A {private Set<String> set;private Set<B> setObject;public void setSet(Set<String> set) { this.set = set; }public void setSetObject(Set<B> setObject) { this.setObject = setObject; }public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A a = applicationContext.getBean("a", A.class);System.out.println(JSONObject.toJSONString(a.set));System.out.println(JSONObject.toJSONString(a.setObject));/** Output:* ["qwe","asd"]* [{"age":0,"name":"b"},{"age":1,"name":"a"}]*/}
}
(3)map
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A"><property name="map"><map><entry key="name" value="a"></entry><entry key="age" value="01"></entry></map></property></bean>
</beans>
示例代碼如下:
public class A {private Map<String,String> map;public void setMap(Map<String, String> map) { this.map = map; }public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A a = applicationContext.getBean("a", A.class);System.out.println(JSONObject.toJSONString(a.map));/** Output:* {"name":"a","age":"01"}*/}
}
(4)props
本質(zhì)上是hashtable。
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A"><property name="props"><props><prop key="name">a</prop><prop key="age">01</prop></props></property></bean>
</beans>
示例代碼如下:
public class A {private Properties props;public void setProps(Properties props) { this.props = props; }public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A a = applicationContext.getBean("a", A.class);System.out.println(JSONObject.toJSONString(a.props));/** Output:* {"age":"01","name":"a"}*/}
}
映射鍵或值或設(shè)置值的值也可以是以下任何元素:
bean | ref | idref | list | set | map | props | value | null
- Null和空字符串值
如果將值設(shè)為null或者空字符串可以這樣:
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="b" class="com.example.B"><property name="name" value=""/><property name="age"><null></null></property></bean>
</beans>
示例代碼如下:
public class B {private String name;private Integer age;public void setName(String name) { this.name = name; }public void setAge(Integer age) { this.age = age; }public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");B b = applicationContext.getBean("b", B.class);System.out.println(b.age+","+b.name);/** Output:* null,*/}
}
- 使用
depends-on
如果一個(gè)bean是另一個(gè)bean的依賴項(xiàng),這通常意味著一個(gè)bean被設(shè)置為另一個(gè)bean的屬性。通常,您可以使用<ref/>
元素在基于XML的配置元數(shù)據(jù)中。depends-on
屬性表示bean之間的依賴關(guān)系,bean被初始化之前指定強(qiáng)制一個(gè)或多個(gè)bean被初始化。
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A" depends-on="b,c"></bean><bean id="b" class="com.example.B"></bean><bean id="c" class="com.example.C"></bean>
</beans>
示例代碼如下:
public class B {public void b(){ System.out.println("b"); }
}
public class C {public void c(){ System.out.println("c"); }
}
public class A {public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A a = applicationContext.getBean("a", A.class);/** Output:* ... ...Creating shared instance of singleton bean 'b'* ... ...Creating shared instance of singleton bean 'c'* ... ...Creating shared instance of singleton bean 'a'*/}
}
你也可以使用@DependsOn
注解,進(jìn)行bean的依賴關(guān)系初始化。
@Configuration
public class Config {@Bean("a")@DependsOn({"b"})public A getA(){return new A();}@Bean("b")public B getB(){return new B();}
}
當(dāng)然也可以再類上面使用注解
@Component
@DependsOn("b")
public class A{ }
@Component
public class B { }
- 惰性初始化的Beans
一般情況下,啟動(dòng)項(xiàng)目時(shí)會(huì)初始化所有的bean,當(dāng)不希望出現(xiàn)這種行為時(shí),可以通過將bean定義標(biāo)記為惰性初始化來防止單例bean的預(yù)實(shí)例化。惰性初始化的bean告訴IoC容器在第一次被請求時(shí)創(chuàng)建一個(gè)bean實(shí)例,而不是在啟動(dòng)時(shí)。
在<bean/>
元素上加入lazy-init=true
屬性:
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A" lazy-init="true"></bean><bean id="b" class="com.example.B"></bean><bean id="c" class="com.example.C"></bean>
</beans>
當(dāng)項(xiàng)目啟動(dòng)時(shí),bean不會(huì)被急切地預(yù)實(shí)例化,示例代碼如下:
public class A {public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");//使用時(shí)被創(chuàng)建
// A a = applicationContext.getBean("a", A.class);/** Output:* ... ...Creating shared instance of singleton bean 'b'* ... ...Creating shared instance of singleton bean 'c'*/}
}
你也可以使用@Lazy
注解惰性初始化。
@Component
public class C extends Base{ }
@Component
public class B extends Base{ }
@Lazy
public class A{public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.example");/** Output:* Creating shared instance of singleton bean 'b'* Creating shared instance of singleton bean 'c'*/}
}
- 設(shè)置主Bean
當(dāng)注入的Bean有多個(gè)候選項(xiàng)時(shí),應(yīng)該給Bean設(shè)置一個(gè)主bean,否則注入時(shí)會(huì)NoUniqueBeanDefinitionException錯(cuò)誤
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A" autowire="byType"></bean><bean id="b" class="com.example.B"></bean><bean id="c" class="com.example.C"></bean>
</beans>
public interface Base {}
public class B implements Base {}
public class C implements Base {}
public class A{private Base base;public void setBase(Base base) { this.base = base; }public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");}
}
我們可以使用primary
屬性,指定為主bean。
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A" autowire="byType"></bean><bean id="b" class="com.example.B" primary="true"></bean><bean id="c" class="com.example.C"></bean>
</beans>
示例代碼如下:
public class A{private Base base;public void setBase(Base base) { this.base = base; }public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A bean = applicationContext.getBean(A.class);System.out.println(bean.base);/** Output:* com.example.B@16f7c8c1*/}
}
也可以使用autowire-candidate
屬性標(biāo)記當(dāng)前bean是否會(huì)被注入候選項(xiàng),默認(rèn)true,false表示排除候選項(xiàng)。
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A" autowire="byType"></bean><bean id="b" class="com.example.B" autowire-candidate="false"></bean><bean id="c" class="com.example.C"></bean>
</beans>
你還可以使用@Primary
注解確定一個(gè)主要候選對象。
public interface Base {}
@Primary
@Component
public class B implements Base {}
@Component
public class C implements Base {}
@Component
public class A{@Autowiredprivate Base base;public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.example");A bean = applicationContext.getBean(A.class);System.out.println(bean.base);/** Output:* com.example.B@72a7c7e0*/}
}
<context:annotation-config/>
標(biāo)簽
通過在基于XML的Spring配置中包含<context:annotation-config/>
標(biāo)簽來隱式注冊以下后處理器:
ConfigurationClassPostProcessor
AutowiredAnnotationBeanPostProcessor
CommonAnnotationBeanPostProcessor
PersistenceAnnotationBeanPostProcessor
EventListenerMethodProcessor
<?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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd"><!--識(shí)別相應(yīng)的注解--><context:annotation-config/></beans>
- 限定符
<qualifier>
您可以將限定符值與特定的參數(shù)相關(guān)聯(lián),縮小類型匹配的范圍,以便為每個(gè)參數(shù)選擇特定的bean。
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><!--識(shí)別相應(yīng)的注解--><context:annotation-config/><bean id="a" class="com.example.A" autowire="byType"></bean><bean id="b" class="com.example.B"><qualifier value="b"></qualifier></bean><bean id="c" class="com.example.C"><qualifier value="c"></qualifier></bean>
</beans>
<qualifier>
標(biāo)簽搭配@Qualifier
注解使用, 示例代碼如下:
public interface Base {}
public class B implements Base {}
public class C implements Base {}
public class A{private Base base;public void setBase(@Qualifier("b")Base base) { this.base = base; }public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A bean = applicationContext.getBean(A.class);System.out.println(bean.base);/** Output:* com.example.B@7920ba90*/}
}
你也可以使用@Autowired
注解搭配@Qualifier
注解使用,示例代碼如下:
public interface Base {}
@Component
public class B implements Base {}
@Component
public class C implements Base {}
@Component
public class A{@Autowired@Qualifier("b")private Base base;public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.example");A bean = applicationContext.getBean(A.class);System.out.println(bean.base);/** Output:* com.example.B@70e8f8e*/}
}
還有一種更簡單的方式,使用JSR-250@Resource
注解,它在語義上被定義為通過其惟一的名稱來標(biāo)識(shí)特定的目標(biāo)組件,聲明的類型與匹配過程無關(guān)。
public interface Base {}
@Component
public class B implements Base {}
@Component
public class C implements Base {}
@Component
public class A{@Resource(name = "b")private Base base;public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.example");A bean = applicationContext.getBean(A.class);System.out.println(bean.base);/** Output:* com.example.B@769f71a9*/}
}
生命周期回調(diào)
為了與容器對bean生命周期的管理進(jìn)行交互,您可以實(shí)現(xiàn)Spring的InitializingBean和DisposableBean接口,讓bean在初始化和銷毀bean時(shí)執(zhí)行某些操作。
- 初始化回調(diào)
(1)使用XML進(jìn)行初始化
對于基于XML的配置元數(shù)據(jù),可以使用init-method
屬性指定具有void無參數(shù)簽名的方法的名稱。
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A" init-method="init"></bean>
</beans>
實(shí)例代碼如下:
public class A{public void init(){System.out.println("初始化bean");}public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A bean = applicationContext.getBean(A.class);System.out.println(bean);/** Output:* 初始化bean* com.example.A@b7dd107*/}
}
(2)InitializingBean接口
通過實(shí)現(xiàn)org.springframework.beans.factory.InitializingBean
接口重寫afterPropertiesSet()
方法執(zhí)行初始化工作。
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A"></bean>
</beans>
示例代碼如下:
public class A implements InitializingBean{public void afterPropertiesSet() throws Exception {System.out.println("初始化");}public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A bean = applicationContext.getBean(A.class);System.out.println(bean);/** Output:* 初始化bean* com.example.A@b7dd107*/}
}
(3)@PostConstruct
注解
@Component
public class A {@PostConstructpublic void init(){System.out.println("初始化");}public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.example");A bean = applicationContext.getBean(A.class);System.out.println(bean);/** Output:* 初始化bean* com.example.A@b7dd107*/}
}
(4)使用@Bean
的initMethod
屬性
@Configuration
public class Config {@Bean(initMethod = "init")public A a(){return new A();}
}
@Component
public class A {public void init(){System.out.println("初始化bean");}public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.example");A bean = applicationContext.getBean(A.class);System.out.println(bean);/** Output:* 初始化bean* com.example.A@b7dd107*/}
}
- 銷毀回調(diào)
銷毀回調(diào)和初始化回調(diào)的方式基本一致。你可以調(diào)用ApplicationContext類的registerShutdownHook()
方法和close()
方法進(jìn)行容器銷毀。
(1)使用XML進(jìn)行銷毀
使用destroy-method
屬性指定無參方法名。
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A" destroy-method="destroy"></bean>
</beans>
示例代碼如下:
public class A {public void destroy(){System.out.println("銷毀bean");}public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A bean = applicationContext.getBean(A.class);
// ((ClassPathXmlApplicationContext) applicationContext).registerShutdownHook();((ClassPathXmlApplicationContext) applicationContext).close();/** Output:* 銷毀bean*/}
}
(2)DisposableBean接口
通過實(shí)現(xiàn)org.springframework.beans.factory.DisposableBean
接口重寫destroy()
方法執(zhí)行銷毀工作。
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A"></bean>
</beans>
示例代碼如下:
public class A implements DisposableBean {public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A bean = applicationContext.getBean(A.class);
// ((ClassPathXmlApplicationContext) applicationContext).registerShutdownHook();((ClassPathXmlApplicationContext) applicationContext).close();/** Output:* 銷毀bean*/}public void destroy() throws Exception {System.out.println("銷毀bean");}
}
(3)@PreDestroy
注解
@Component
public class A {@PreDestroypublic void destroy() {System.out.println("銷毀bean");}public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.example");A bean = applicationContext.getBean(A.class);
// ((AnnotationConfigApplicationContext) applicationContext).registerShutdownHook();((AnnotationConfigApplicationContext) applicationContext).close();/** Output:* 銷毀bean*/}
}
(4)使用@Bean
的destroyMethod
屬性
@Configuration
public class Config {@Bean(destroyMethod = "destroy")public A a(){return new A();}
}
@Component
public class A {public void destroy() {System.out.println("銷毀bean");}public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.example");A bean = applicationContext.getBean(A.class);
// ((AnnotationConfigApplicationContext) applicationContext).registerShutdownHook();((AnnotationConfigApplicationContext) applicationContext).close();/** Output:* 銷毀bean*/}
}
- 默認(rèn)初始化和銷毀方法
你可以在頂層元素<beans/>
標(biāo)簽上,使用default-init-method
屬性和default-destroy-method
屬性,當(dāng)創(chuàng)建和組裝bean時(shí),如果bean類有這樣的方法,它將在適當(dāng)?shù)臅r(shí)候被調(diào)用。
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans default-init-method="init" default-destroy-method="destroy"><bean id="a" class="com.example.A"></bean><bean id="b" class="com.example.B"></bean>
</beans>
示例代碼如下:
public class B { }
public class A {public void init(){ System.out.println("初始化bean"); }public void destroy() { System.out.println("銷毀bean"); }public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A bean = applicationContext.getBean(A.class);
// ((ClassPathXmlApplicationContext) applicationContext).registerShutdownHook();((ClassPathXmlApplicationContext) applicationContext).close();/** Output:* 初始化bean* 銷毀bean*/}
}
Bean定義繼承
子bean定義從父定義繼承配置數(shù)據(jù)。子定義可以根據(jù)需要覆蓋一些值或添加其他值。使用父bean和子bean定義可以節(jié)省大量的輸入。實(shí)際上,這是一種模板形式。
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A" parent="b"><property name="name" value="child"></property></bean><bean id="b" class="com.example.B" abstract="true"><property name="name" value="parent"></property><property name="age" value="28"></property></bean>
</beans>
子類通過parent
屬性與父類建立關(guān)系,且可以覆蓋相應(yīng)的父設(shè)置。
示例代碼如下:
public class B {private String name;private Integer age;public void setName(String name) { this.name = name; }public void setAge(Integer age) { this.age = age; }
}
public class A{private String name;private Integer age;public String getName() { return name; }public void setName(String name) { this.name = name; }public Integer getAge() { return age; }public void setAge(Integer age) { this.age = age; }public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");A bean = applicationContext.getBean(A.class);System.out.println(JSONObject.toJSONString(bean));/** Output:* {"age":28,"name":"child"}*/}
}
如果父定義沒有指定類,則將父bean定義顯式標(biāo)記為abstract
是必需的,如下例所示:
<?xml version="1.0" encoding="UTF-8"?>
<!--省略鏈接地址-->
<beans><bean id="a" class="com.example.A" parent="b"><property name="name" value="child"></property></bean><bean id="b" abstract="true"><property name="name" value="parent"></property><property name="age" value="28"></property></bean>
</beans>
當(dāng)父類顯式標(biāo)記為abstract
,父bean不能自行實(shí)例化,因?yàn)樗遣煌暾?#xff0c;只能用作純模板bean定義。
基于注解的容器配置
開發(fā)人員不使用XML來描述bean連接,而是通過使用相關(guān)類、方法或字段聲明上的注釋將配置移入組件類本身。
對于配置Spring,注釋比XML更好嗎?
簡短的回答是“視情況而定”最長的答案是每種方法都有其優(yōu)點(diǎn)和缺點(diǎn),通常,由開發(fā)人員決定哪種策略更適合他們。無論選擇什么,Spring都可以容納兩種風(fēng)格,甚至可以將它們混合在一起。
Spring的Java配置支持中的核心構(gòu)件是@Configuration
注解的類和@Bean
注解的方法。@Configuration
是一個(gè)類級別的注釋,表示一個(gè)對象是bean定義的來源;@Bean
注解用于指示一個(gè)方法實(shí)例化、配置和初始化一個(gè)由Spring IoC容器管理的新對象。
@Configuration
public class Config {@Beanpublic A getA(){return new A();}
}
代碼相當(dāng)于下面的XML:
<beans><bean id="getA" class="com.example.A"></bean>
</beans>
我們可以使用@Scope
注解定義一個(gè)范圍,默認(rèn)范圍是singleton:
@Configuration
public class Config {@Bean@Scope("prototype")public A getA(){return new A();}
}
默認(rèn)情況下,配置類使用@Bean
方法的名稱作為結(jié)果bean的名稱。但是,可以指定name:
@Configuration
public class Config {@Bean("a")public A getA(){return new A();}
}
有時(shí),為bean提供更詳細(xì)的文本描述會(huì)很有幫助,您可以使用@Description
注釋。
@Configuration
public class Config {@Bean("a")@Description("this is a test")public A getA(){return new A();}
}
在前面的章節(jié)內(nèi)容中多次使用AnnotationConfigApplicationContext類啟用注解操作:
public class A{public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.example");}
}
對于XML可以使用ClassPathXmlApplicationContext類:
public class A{public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");}
}
spring的@Configuration
類支持的目標(biāo)不是100%完全替代Spring XML。一些工具,比如Spring XML名稱空間,仍然是配置容器的理想方式。
- 定義bean的優(yōu)先級
如果希望數(shù)組或列表中的項(xiàng)按特定順序排序,也可以使用@Order
或@Priority
注解。
public abstract class Base { }@Component
@Order(2)
//@Priority(2)
public class B extends Base{ }@Component
@Order(1)
//@Priority(1)
public class C extends Base{ }public class A{@Autowiredprivate List<Base> baseList;public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.example");A bean = applicationContext.getBean(A.class);System.out.println(bean.baseList);/** Output:* 沒有注解之前:[com.example.B@76508ed1, com.example.C@41e36e46]* 加了注解后:[com.example.C@15eb5ee5, com.example.B@4f209819]*/}
}
@Order
是Spring提供的注解,@Priority
是JSR 250標(biāo)準(zhǔn),都是值越小優(yōu)先級越高。
要注意它們不會(huì)影響bean的啟動(dòng)順序,這是由依賴關(guān)系和
@DependsOn
聲明。
@Resource
注解
Spring還通過使用JSR-250支持注入@Resource
注釋(jakarta.annotation.Resource
)或bean屬性setter方法。這是Jakarta EE中的常見模式。對于Spring管理的對象,Spring也支持這種模式。
@Resource
接受名稱屬性。默認(rèn)情況下,Spring將該值解釋為要注入的bean名稱。換句話說,它遵循按名稱語義,如以下示例所示:
public interface Base {}
@Component
public class B implements Base {}
@Component
public class A{@Resource(name = "b")private Base base;@Resourceprivate B b;public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.example");A bean = applicationContext.getBean(A.class);System.out.println(bean.base);/** Output:* com.example.B@769f71a9*/}
}
如果沒有顯式指定名稱,則默認(rèn)名稱是從字段名或setter方法派生的。
- 使用
@Value
@Value
通常用于注入外部化的屬性
application.properties文件內(nèi)容:
spring.application.name=study
配置屬性源:
@Configuration
@PropertySource("classpath:application.properties")
public class Config {}
示例代碼如下:
@Component
public class A{@Value("${spring.application.name}")private String name;public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.example");A bean = applicationContext.getBean(A.class);System.out.println(bean.name);/** Output:* study*/}
}
如果找不到屬性值,可以通過屬性名:默認(rèn)值
定義一個(gè)默認(rèn)值。
@Component
public class A{@Value("${spring.aaa:123}")private String name;public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.example");A bean = applicationContext.getBean(A.class);System.out.println(bean.name);/** Output:* 123*/}
}
spring提供了占位符配置器,你可以通過setPlaceholderPrefix()
方法和 setPlaceholderSuffix()
方法自定義占位符。
@Configuration
@PropertySource("classpath:application.properties")
public class Config {@Beanpublic static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer(){PropertySourcesPlaceholderConfigurer property = new PropertySourcesPlaceholderConfigurer();property.setPlaceholderPrefix("**");property.setPlaceholderSuffix("**");return property;}
}
Spring提供的內(nèi)置轉(zhuǎn)換器支持允許簡單的類型轉(zhuǎn)換(到Integer或者int例如)被自動(dòng)處理。
@Component和進(jìn)一步的原型注解
@Repository
注解是任何實(shí)現(xiàn)存儲(chǔ)庫角色或原型的類的標(biāo)記(也稱為數(shù)據(jù)訪問對象或DAO)。Spring提供了進(jìn)一步的原型注釋:@Component
, @Service
,以及@Controller
。@Component
是任何Spring管理的組件的通用原型。@Repository
, @Service
,以及@Controller
是專業(yè)化的@Component
對于更具體的用例(分別在持久層、服務(wù)層和表示層)。
您還可以組合元注解來創(chuàng)建“組合注解”。例如,在@RestController
,Spring MVC的注釋由以下部分組成@Controller
和@ResponseBody
。
自動(dòng)檢測類和注冊Bean定義
要自動(dòng)檢測這些類并注冊相應(yīng)的bean,您需要將@ComponentScan
添加到@Configuration
類,其中basePackages屬性是這兩個(gè)類的公共父包。
@Configuration@RestController
@ComponentScan(basePackages = "com.example")
public class Config {
}
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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd"><context:component-scan base-package="com.example"/>
</beans>
使用
<context:component-scan>
隱式地啟用了<context:annotation-config>
的功能。通常不需要包含<context:annotation-config>
元素。
您可以通過應(yīng)用自定義過濾器來自定義掃描,@ComponentScan
注解添加includeFilters(包含過濾器) 或者 excludeFilters(忽略過濾器) 的屬性(XML在<context:component-scan>
元素中配置子元素<context:include-filter />
或者<context:exclude-filter />
)
FilterType(過濾器類型):ANNOTATION(注解)、ASSIGNABLE_TYPE(指定類型)、ASPECTJ(切面)、REGEX(正則表達(dá)式)、CUSTOM(自定義)。
public class B {}
//自定義過濾
public class DemoFilter implements TypeFilter {public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();System.out.println(annotationMetadata.getClassName());return false;}
}
@Controller
public class A{public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class);/** Output:* com.example.B* com.example.DemoFilter*/}
}
@Configuration
@ComponentScan(basePackages = "com.example",includeFilters = {@ComponentScan.Filter(type = FilterType.CUSTOM, classes = DemoFilter.class),@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = B.class)},excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Component.class))
public class Config {
}
XML格式如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans><context:component-scan base-package="com.example"><context:include-filter type="custom" expression="com.example.DemoFilter"></context:include-filter><context:include-filter type="assignable" expression="com.example.B"></context:include-filter><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"></context:exclude-filter></context:component-scan>
</beans>
您也可以通過注解設(shè)置useDefaultFilters=false
@ComponentScan(basePackages = "com.example",useDefaultFilters = false)
或<component-scan/>
元素的use-default-filters="false"
屬性禁用默認(rèn)過濾器。
<beans><context:component-scan base-package="com.example" use-default-filters="false"/>
</beans>
這實(shí)際上禁用了@Component
, @Repository
, @Service
, @Controller
,@RestController
,@Configuration
。
當(dāng)在掃描過程中自動(dòng)檢測到一個(gè)組件時(shí),它的bean名稱由BeanNameGenerator掃描器已知策略。默認(rèn)情況下,任何Spring原型注釋(@Component
, @Repository
, @Service
,以及@Controller
)包含一個(gè)名稱value從而將該名稱提供給相應(yīng)的bean定義。如果未設(shè)置value值,默認(rèn)bean名稱生成器返回未大寫的非限定類名。
@Controller(value = "a")
public class A{}
自動(dòng)檢測的組件的默認(rèn)且最常見的作用域是singleton,但是,有時(shí)您需要一個(gè)不同的范圍,該范圍可以由@Scope
注解。
@Service
@Scope("prototype")
public class A{}
使用JSR 330標(biāo)準(zhǔn)注釋
Spring支持JSR-330標(biāo)準(zhǔn)注釋(依賴注入)。先引入Maven依賴。
<dependency><groupId>jakarta.inject</groupId><artifactId>jakarta.inject-api</artifactId><version>1.0</version></dependency>
使用@Named
注解代替@Component
注解,@Inject
注解代替@Autowired
注解,示例代碼如下:
@Named("b")
public class B{}
@Named
public class A{@Injectprivate B b;public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.example");A bean = applicationContext.getBean(A.class);System.out.println(bean.b);/** Output:* com.example.B@67a20f67*/}
}
還有其他的注解替換,比如:@ManagedBean
注解等價(jià)于@Component
注解,@Scope("singleton")
注解等價(jià)于@Singleton
注解。
ApplicationContext的附加功能
- 使用MessageSource進(jìn)行國際化
ApplicationContext接口擴(kuò)展了一個(gè)名為MessageSource的接口,因此提供了國際化(“i18n”)功能,用于支持信息的國際化和包含參數(shù)的信息的替換。
Spring的各種MessageSource實(shí)現(xiàn)遵循與標(biāo)準(zhǔn)JDK ResourceBundle相同的語言環(huán)境解析和回退規(guī)則。
定義兩個(gè)配置文件,語言類別簡稱結(jié)尾。
messages_en.properties:
spring.msg=hello,{0}
messages_zh.properties:
spring.msg=你好,{0}
通過調(diào)用MessageSource.getMessage()
方法解析,示例代碼如下:
@Configuration
public class Config {@Beanpublic ResourceBundleMessageSource messageSource(){ResourceBundleMessageSource resourceBundleMessageSource = new ResourceBundleMessageSource();//指定beannameresourceBundleMessageSource.setBasenames("i18n/messages");//設(shè)置字符編碼resourceBundleMessageSource.setDefaultEncoding("utf-8");return resourceBundleMessageSource;}
}
@Component
public class A{@Autowiredprivate MessageSource messageSource;public void test(){String message = messageSource.getMessage("spring.msg", new Object[]{"world"},"default", Locale.ENGLISH);System.out.println(message);message = messageSource.getMessage("spring.msg", new Object[]{"world"},"default", Locale.CHINESE);System.out.println(message);}public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.example");A bean = applicationContext.getBean(A.class);bean.test();/** Output:* hello,world* 你好,world*/}
}
以xml方式創(chuàng)建bean
<beans><bean id="messageSource"class="org.springframework.context.support.ResourceBundleMessageSource"><property name="basenames"><list><value>messages_en</value><value>messages_zh</value></list></property></bean>
</beans>
Spring提供了三個(gè)MessageSource實(shí)現(xiàn)方式:ResourceBundleMessageSource、ReloadableResourceBundleMessageSourc和StaticMessageSource。
- 標(biāo)準(zhǔn)和自定義事件
ApplicationContext中的事件處理是通過ApplicationEvent類和ApplicationListener接口提供的。如果將實(shí)現(xiàn)ApplicationListener接口的bean部署到上下文中,那么每當(dāng)ApplicationEvent發(fā)布到ApplicationContext時(shí),就會(huì)通知該bean。本質(zhì)上,這是標(biāo)準(zhǔn)的觀察者設(shè)計(jì)模式。
Spring提供的標(biāo)準(zhǔn)事件:ContextRefreshedEvent
、ContextStartedEvent
、ContextStoppedEvent
、ContextClosedEvent
、RequestHandledEvent
、ServletRequestHandledEvent
,您還可以創(chuàng)建和發(fā)布自己的自定義事件。
假設(shè)創(chuàng)建一個(gè)發(fā)送通知的功能,首先創(chuàng)建事件類,繼承ApplicationEvent抽象類:
public class MyEvent extends ApplicationEvent {public String message;public MyEvent(Object source, String message) {super(source);this.message = message;System.out.println("創(chuàng)建MyEvent");}
}
創(chuàng)建一個(gè)發(fā)布事件類,實(shí)現(xiàn)ApplicationEventPublisherAware 接口:
public class NotifyService implements ApplicationEventPublisherAware {private ApplicationEventPublisher publisher;public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {publisher = applicationEventPublisher;}public void sendMessage(String message){publisher.publishEvent(new MyEvent(this,message));}
}
容器自動(dòng)調(diào)用setApplicationEventPublisher()
。實(shí)際上,傳入的參數(shù)是Spring容器本身。
注冊bean:
<beans><bean id="notifyService" class="com.example.NotifyService"></bean>
</beans>
創(chuàng)建監(jiān)聽類,接收自定義ApplicationEvent類,可以實(shí)現(xiàn)ApplicationListener接口,為了更直觀觀測,創(chuàng)建兩個(gè)監(jiān)聽類:
public class Zhangsan implements ApplicationListener<MyEvent> {public void onApplicationEvent(MyEvent event) {System.out.println("zhangsan收到消息:"+event.message);}
}
public class Lisi implements ApplicationListener<MyEvent> {public void onApplicationEvent(MyEvent event) {System.out.println("lisi收到消息:"+event.message);}
}
注冊bean:
<beans><bean id="zhangsan" class="com.example.Zhangsan"></bean><bean id="lisi" class="com.example.Lisi"></bean>
</beans>
ApplicationListener指定事件類型,onApplicationEvent()
方法可以保持類型安全,避免任何向下轉(zhuǎn)換的需要。您可以注冊任意數(shù)量的事件監(jiān)聽器,但是請注意,默認(rèn)情況下,事件偵聽器同步接收事件。
執(zhí)行結(jié)果如下所示:
public class Test{public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");NotifyService bean = applicationContext.getBean(NotifyService.class);bean.sendMessage("hello world");/** Output:* 創(chuàng)建MyEvent* zhangsan收到消息:hello world* lisi收到消息:hello world*/}
}
你也可以使用@EventListener
注解,在bean的任意方法上注冊事件偵聽器:
@Component
public class NotifyService {@Autowiredprivate ApplicationEventPublisher publisher;public void sendMessage(String message){publisher.publishEvent(new MyEvent(this,message));}
}
@Component
public class Lisi {@EventListenerpublic void onApplicationEvent(MyEvent event) {System.out.println("lisi收到消息:"+event.message);}
}
@Component
public class Zhangsan {@EventListenerpublic void onApplicationEvent(MyEvent event) {System.out.println("zhangsan收到消息:"+event.message);}
}
@EventListener
注解也可以監(jiān)聽多個(gè)事件:
@Component
public class Lisi {@EventListener({MyEvent.class, MyEvent2.class})public void onApplicationEvent(Object event) {System.out.println(event);}
}
- 異步偵聽器
假設(shè):方法B調(diào)用方法A,你希望方法B不需要等待方法A執(zhí)行完成而是繼續(xù)往下執(zhí)行,可以使用@Async
注解。
創(chuàng)建任務(wù)執(zhí)行器(防止報(bào)異常),@EnableAsync
啟用異步注解:
@Configuration
@EnableAsync
public class Config {@Beanpublic AsyncTaskExecutor asyncTaskExecutor(){ThreadPoolTaskExecutor asyncTaskExecutor = new ThreadPoolTaskExecutor();asyncTaskExecutor.setCorePoolSize(10);asyncTaskExecutor.setMaxPoolSize(10);asyncTaskExecutor.initialize();return asyncTaskExecutor;}
}
指定異步注解方法
@Component
public class A{@Asyncpublic void a(){System.out.println("thread:"+Thread.currentThread().getName());try {Thread.sleep(20000);} catch (InterruptedException e) {e.printStackTrace();}}
}
需要注意@Async
注解,對同一個(gè)類中的方法和static方法無效,不能通過返回值來進(jìn)行后續(xù)操作。如果引發(fā)Exception,不會(huì)傳播到調(diào)用方。
測試注解是否生效
@Component
public class B {@AutowiredA a;public void test() {System.out.println("before");this.a.a();System.out.println("after");}public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.example");B bean = applicationContext.getBean(B.class);bean.test();/** Output:* before* after* thread:asyncTaskExecutor-1*/}
}
本章內(nèi)容主要介紹了Spring IoC 容器與Bean之間的關(guān)系及基本使用。