spring容器是負責實例化、配置、組裝組件的容器。
容器的配置有很多,常用的是xml、java注解和java代碼。
在spring中ioc容器相關部分是context和beans中。其中context-support保存著許多線程的容器實現。比如annotationconfigapplicationcontext或者classpathxmlapplicationcontext。兩者只有接收的目標不同,前者接收java類后者接收xml文件。但作為spring容器的不同實現殊途同歸。
下面我通過spring文檔中的介紹來自己過一遍容器配置,來加深印象。這里我使用的是springboot。所以有些基于web.xml的配置不會涉及到。
@bean和@configuration
@configuration注解的類,表示這個類是一個配置類,類似于<beans></beans>或者.xml文件。
@bean注解用來說明使用springioc容器管理一個新對象的實例化、配置和初始化。類似于<bean></bean>,默認情況下,bean名稱就是方法名稱.
例子:
1
2
3
4
5
6
7
8
9
|
@configuration public class conf { @bean public helloservice helloservice() { return new helloserviceimpl(); } } |
這種配置方式就類似于xml配置中的
1
2
3
|
<beans> <bean id= "helloservice" class = "com.dust.service.impl.helloserviceimpl" /> </beans> |
等價于注解配置中的
1
2
3
4
5
6
7
8
9
|
@service public class helloserviceimpl implements helloservice { @override public string hello() { return "hello world" ; } } |
使用annotationconfigapplicationcontext實例化spring容器
這是在spring3.0加入的功能,除了接收@configuration注解的類作為輸入類之外還可以接受使用jsr-330元數據注解的簡單類和@component類。
當@configuration注解的類作為輸入時,@configuration類本身會被注冊為一個bean,在這個類中所有用@bean注解的方法都會被定義為一個bean。
具體有哪些類型的bean可以方法遍歷打印容器中的bean。
1
2
3
4
5
6
|
public static void main(string[] args) { applicationcontext context = new annotationconfigapplicationcontext(conf. class ); helloservice helloservice = context.getbean(helloservice. class ); string hello = helloservice.hello(); system.out.println(hello); } |
該實例的步驟為:
1. 創建annotationconfigapplicationcontext容器對象,同時將@configuration注解的conf.class作為參數傳入。
2. 容器回根據傳入的conf類來構建bean。其中就有helloservice
3. 通過bean的對象類型獲取到容器中保管的對象。
4. 執行對象方法
但是annotationconfigapplicationcontext并不僅使用@configuration類。任何@component或jsr-330注解的類都可以作為輸入提供給構造函數。例如:
1
2
3
4
5
6
|
public static void main(string[] args) { applicationcontext context = new annotationconfigapplicationcontext(helloserviceimpl. class , a. class , b. class ); helloservice helloservice = context.getbean(helloservice. class ); string hello = helloservice.hello(); system.out.println(hello); } |
上面假設myserviceimpl、a和b都用了spring的依賴注入的注解,例如@autowired。
使用register(class<?>…)的方式構建容器
也可以使用無參構造函數實例化annotationconfigapplicationcontext,然后使用register()方法配置。當使用編程方式構建annotationconfigapplicationcontext時,這種方法特別有用。
例子:
1
2
3
4
5
6
7
8
|
public static void main(string[] args) { annotationconfigapplicationcontext context = new annotationconfigapplicationcontext(); context.register(conf. class ); context.refresh(); helloservice helloservice = context.getbean(helloservice. class ); string hello = helloservice.hello(); system.out.println(hello); } |
其中的refresh方法是一個初始化工作。否則注冊的類并不會被生成bean。
使用scan(string …)組件掃描
組件掃描,只需要設置好對應包路徑,spring容器回自動掃描包下面所有能夠被容器初始化的java類。
使用注解:
1
2
3
4
5
6
7
8
9
10
11
|
@configuration @componentscan ( "com.example.springdemo.beans" ) public class conf { @bean public helloservice helloservice() { //用這種方法創建的service相當于用@service注解標注 return new helloserviceimpl(); } } |
在該路徑下還有一個配置文件:
1
2
3
4
5
6
7
8
9
10
|
@configuration public class conf2 { @bean public byeservice byeservice() { //用這種方法創建的service相當于用@service注解標注 return new byeserviceimpl(); } } |
然后是初始化容器:
1
2
3
4
5
6
7
8
|
public static void main(string[] args) { annotationconfigapplicationcontext context = new annotationconfigapplicationcontext(); context.register(conf. class ); context.refresh(); byeservice byeservice = context.getbean(byeservice. class ); string hello = byeservice.bye(); system.out.println(hello); } |
可以看到,雖然傳入的是conf類,但是由于包掃描機制,該容器同時創建了conf2類中的bean。
這就類似xml配置中的:
1
2
3
|
<beans> <context:component-scan base- package = "com.example.springdemo.beans" /> </beans> |
還可以直接調用容器的掃描方法
1
2
3
4
5
6
7
8
9
|
public static void main(string[] args) { annotationconfigapplicationcontext context = new annotationconfigapplicationcontext(); // context.register(conf.class); context.scan( "com.example.springdemo.beans" ); context.refresh(); byeservice byeservice = context.getbean(byeservice. class ); string hello = byeservice.bye(); system.out.println(hello); } |
springboot中的包掃描
springboot通過main方法啟動,其中的注解為@springbootapplication。通過查看該注解的代碼可以發現一下代碼段:
1
2
3
4
|
@aliasfor ( annotation = componentscan. class , attribute = "basepackages" ) |
由此可以知道@springbootapplication注解包括了包掃描注解,同時掃描的是該類的目錄以及子目錄的所有可以被spring容器初始化的類
annotationconfigwebapplicationcontext對于web應用的支持
annotationconfigapplicationcontext在webapplicationcontext中的變體為 annotationconfigwebapplicationcontext。當配置spring contextloaderlistener servlet 監聽器、spring mvc dispatcherservlet的時候,可以用此實現。
bean依賴
@bean注解方法可以具有描述構建該bean所需依賴關系的任意數量的參數。依賴的必須也是ioc容器中注冊的bean。
將上面的代碼修改后如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
@configuration public class conf { @bean public helloservice helloservice(byeservice byeservice) { return new helloserviceimpl(byeservice); } @bean public byeservice byeservice() { return new byeserviceimpl(); } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public class helloserviceimpl implements helloservice { private byeservice byeservice; public helloserviceimpl(byeservice byeservice) { this .byeservice = byeservice; } @override public string hello() { return "hello world" + byeservice.bye(); } } |
1
2
3
4
5
6
7
8
9
10
11
|
public static void main(string[] args) { annotationconfigapplicationcontext context = new annotationconfigapplicationcontext(); context.register(conf. class ); context.refresh(); helloservice helloservice = context.getbean(helloservice. class ); string hello = helloservice.hello(); system.out.println(hello); byeservice byeservice = context.getbean(byeservice. class ); string bye = byeservice.bye(); system.out.println(bye); } |
輸出結果:
hello worldgoodbye!
goodbye!
這種解決原理和基于構造函數的依賴注入幾乎相同。
生命周期回調
@bean注解支持任意的初始化和銷毀回調方法,這與spring xml 中bean元素上的init方法和destroy-method屬性非常相似:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
@bean (initmethod = "init" ) public helloservice helloservice(byeservice byeservice) { return new helloserviceimpl(byeservice); } @bean (destroymethod = "destroy" ) public byeservice byeservice() { return new byeserviceimpl(); } public interface byeservice { string bye(); void destroy(); } public interface helloservice { string hello(); void init(); } public static void main(string[] args) { annotationconfigapplicationcontext context = new annotationconfigapplicationcontext(); context.register(conf. class ); context.refresh(); context.close(); } |
輸出如下:
init helloservice!!
destroy byeservice!
默認情況下,ioc容器關閉后所有bean都會被銷毀,但是如果要引入一個生命周期在應用程序之外進行管理的組件,例如:datasource。那么只需要將@bean(destroymethod =””)添加到你的bean定義中即可禁用默認(推測)模式。
1
2
3
4
|
@bean (destroymethod= "" ) public datasource datasource() throws namingexception { return (datasource) jnditemplate.lookup( "myds" ); } |
當然,初始化的時候也可以先執行對應方法,而不用交給ioc容器
1
2
3
4
5
6
|
@bean public helloservice helloservice(byeservice byeservice) { helloservice helloservice = new helloserviceimpl(byeservice); helloservice.init(); return helloservice; } |
@scope和scope 代理
scope描述的是spring容器如何新建bean實例的。
- singleton:一個spring容器中只有一個bean的實例,此為spring的默認配置,全容器共享一個實例。
- prototype:每次調用新建一個bean實例。
- request:web項目中,給每一個 http request 新建一個bean實例。
- session:web項目中,給每一個 http session 新建一個bean實例。
- globalsession:這個只在portal應用中有用,給每一個 global http session 新建一個bean實例。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@bean //每次調用就創建一個新的bean @scope ( "prototype" ) public userinfo userinfo() { return new userinfo(); } @bean public userservice userservice() { userservice userservice = new userserviceimpl(); userservice.init(userinfo()); return userservice; } |
測試代碼:
1
2
3
4
5
6
7
8
9
10
11
|
public static void main(string[] args) { annotationconfigapplicationcontext context = new annotationconfigapplicationcontext(); context.register(conf. class ); context.refresh(); userservice userservice = context.getbean(userservice. class ); userservice userservice2 = context.getbean(userservice. class ); userinfo userinfo = context.getbean(userinfo. class ); userinfo userinfo2 = context.getbean(userinfo. class ); system.out.println(userservice == userservice2); system.out.println(userinfo == userinfo2); } |
輸出:
true
false
自定義bean命名
通常,bean的名稱是bean的方法名,但是可以通過name屬性重命名。有時一個單一的bean需要給出多個名稱,稱為bean別名。為了實現這個目標,@bean注解的name屬性接受一個string數組。
1
2
3
4
5
6
|
@bean (name = { "user" , "userservice" , "user" }) public userservice userservice() { userservice userservice = new userserviceimpl(); userservice.init(userinfo()); return userservice; } |
1
2
3
4
5
6
7
8
9
10
11
12
|
public static void main(string[] args) { annotationconfigapplicationcontext context = new annotationconfigapplicationcontext(); context.register(conf. class ); context.refresh(); object user = context.getbean( "user" ); object userservice = context.getbean( "userservice" ); object user = context.getbean( "user" ); system.out.println(user == userservice); system.out.println(user == user); system.out.println(userservice == user); } |
輸出:
true
true
true
bean描述
有時候需要提供一個詳細的bean描述文本是非常有用的。當對bean暴露(可能通過jmx)進行監控使,特別有用??梢允褂聾description注解對bean添加描述:
1
2
3
4
5
6
7
|
@bean (name = { "user" , "userservice" , "user" }) @description ( "這是用戶服務對象" ) public userservice userservice() { userservice userservice = new userserviceimpl(); userservice.init(userinfo()); return userservice; } |
1
2
3
4
5
6
7
|
public static void main(string[] args) { annotationconfigapplicationcontext context = new annotationconfigapplicationcontext(); context.register(conf. class ); context.refresh(); string description = context.getbeandefinition( "user" ).getdescription(); system.out.println(description); } |
輸出:
這是用戶服務對象
基于java組合配置
使用@import注解
和spring xml文件中使用元素來幫助模塊化配置類似,@import注解允許從另一個配置類加載@bean定義:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
@configuration @import (userconf. class ) public class conf { @bean (initmethod = "init" ) public helloservice helloservice(byeservice byeservice) { //用這種方法創建的service相當于用@service注解標注 return new helloserviceimpl(byeservice); } @bean (destroymethod = "destroy" ) public byeservice byeservice() { return new byeserviceimpl(); } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
@configuration public class userconf { @bean //每次調用就創建一個新的bean @scope ( "prototype" ) public userinfo userinfo() { return new userinfo(); } @bean (name = { "user" , "userservice" , "user" }) @description ( "這是用戶服務對象" ) public userservice userservice() { userservice userservice = new userserviceimpl(); userservice.init(userinfo()); return userservice; } } |
1
2
3
4
5
6
7
|
public static void main(string[] args) { annotationconfigapplicationcontext context = new annotationconfigapplicationcontext(); context.register(conf. class ); context.refresh(); string description = context.getbeandefinition( "user" ).getdescription(); system.out.println(description); } |
這種方法簡化了容器實例化,因為只需要處理一個類,而不是需要開發人員在構建期間記住大量的@configuration注解類。
java and xml 混合配置
java配置并不能100%替代xml配置,因此ioc容器支持兩者混合配置。不過這里有個區別就是以xml為中心還是以java配置為中心。
以xml為中心
1
2
3
4
5
6
7
8
9
10
11
12
|
@configuration public class datasourceconf { @autowired private datasource datasource; @bean public datasourceservice datasource() { return new datasourceerviceimpl(datasource); } } |
1
2
3
|
jdbc.url=jdbc:mysql: //39.108.119.174:3306/dust jdbc.username=root jdbc.password= 123456 |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<beans> <context:annotation-config/> <context:property-placeholder location= "classpath:jdbc.properties" /> <bean class = "com.example.springdemo.beans.datasourceconf" /> <bean class = "org.springframework.jdbc.datasource.drivermanagerdatasource" > <property name= "url" value= "${jdbc.url}" /> <property name= "username" value= "${jdbc.username}" /> <property name= "password" value= "${jdbc.password}" /> </bean> </beans> |
1
2
3
4
5
|
public static void main(string[] args) { applicationcontext context = new classpathxmlapplicationcontext( "classpath:spring/datasource.xml" ); datasourceservice datasourceservice = context.getbean(datasourceservice. class ); system.out.println(datasourceservice.tostring()); } |
以java類為中心
1
2
3
|
<beans> <context:property-placeholder location= "classpath:jdbc.properties" /> </beans> |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
@configuration @importresource ( "classpath:spring/datasource.xml" ) public class datasourceconf { @value ( "${jdbc.url}" ) private string url; @value ( "${jdbc.username}" ) private string username; @value ( "${jdbc.password}" ) private string password; @bean public datasourceservice datasource() { return new datasourceerviceimpl(url, username, password); } } |
1
2
3
4
5
6
7
8
9
10
11
|
public static void main(string[] args) { annotationconfigapplicationcontext context = new annotationconfigapplicationcontext(); context.scan( "com.example.springdemo.beans" ); context.refresh(); datasourceservice datasourceservice = context.getbean(datasourceservice. class ); system.out.println(datasourceservice.tostring()); // applicationcontext context = new classpathxmlapplicationcontext("classpath:spring/datasource.xml"); // datasourceservice datasourceservice = context.getbean(datasourceservice.class); // system.out.println(datasourceservice.tostring()); } |
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。