目錄
一 前言
二 什么是IoC容器
三 實(shí)例
1.通過XML配置規(guī)則
2.通過注解
3.Spring中的其他幾種注解
四 JavaBean生命周期的監(jiān)聽
五 總結(jié)
一 前言
接上篇小白新手web開發(fā)簡(jiǎn)單總結(jié)(四)-web應(yīng)用開發(fā)中的MVC總結(jié)下IoC容器的相關(guān)內(nèi)容。
IoC容器是Spring框架的一個(gè)核心功能。通過該容器來管理所有的JavaBean組件,提供組件的生命周期管理、配置和組裝、AOP支持、以及建立在AOP基礎(chǔ)上的聲明事務(wù)服務(wù)等。
二 什么是IoC容器
IoC(Inversion of Control 控制反轉(zhuǎn))容器
通常Java組件通過new一個(gè)實(shí)例的方式實(shí)現(xiàn)組件之間的引用,并且組件中的兩個(gè)實(shí)例都含有共享組件的時(shí)候,也不能共享該組件。如果系統(tǒng)中有大量的組件的時(shí)候,不僅要維護(hù)組件的生命周期,還要維護(hù)組件之間的依賴關(guān)系,大大增加了系統(tǒng)的復(fù)雜度,而且組件之間會(huì)相互耦合,這些控制流程完全有開發(fā)者自行控制。
而IoC容器就是將控制權(quán)進(jìn)行反轉(zhuǎn),將控制權(quán)交給了IoC容器,由IoC容器負(fù)責(zé)組件的創(chuàng)建和配置,負(fù)責(zé)組件的生命周期管理,而開發(fā)者只需要定義裝配規(guī)則,這樣就將組件的創(chuàng)建配置和組件的使用進(jìn)行分離。并且如果在一個(gè)組件的多個(gè)實(shí)例中含有共享組件的時(shí)候,是可以相互共享的,不需要多次注入。
在Spring的IoC容器中,所有的組件都被成為JavaBean組件,每個(gè)組件即一個(gè)Bean。
通常有兩種方式來定義組件的配置規(guī)則:
三 實(shí)例
通過一個(gè)簡(jiǎn)單的實(shí)例來看下這個(gè)JavaBean組件是怎么定義配置規(guī)則。
1.通過XML配置規(guī)則
- 1.在項(xiàng)目pom.xml中引入spring-context依賴
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
- 2.定義項(xiàng)目中需要使用的JavaBean
(1)定義一個(gè)RegisterService:通過輸入的手機(jī)號(hào)碼和密碼,來注冊(cè)該用戶,并將注冊(cè)成功之后給用戶發(fā)送注冊(cè)成功的短信。
public class RegisterService {
private MessageService messageService;
public void setMessageService(MessageService service) {
this.messageService = service;
}
public User register(String phone, String password) {
User user = new User();
user.phone = phone;
user.password = password;
//隨機(jī)生成一個(gè)用戶名
user.name = "小劉";
//需要將該信息寫入數(shù)據(jù)庫(kù)
SqlDataBase sql = new SqlDataBase();
sql.insert(user);
//注冊(cè)成功之后,給用戶發(fā)送短息
if (messageService == null) {
System.err.println("還沒有初始化MessageService");
return user;
}
messageService.sendRegisterSuccessMessage(phone);
return user;
}
}
(2)定義MessageService:?向用戶的手機(jī)發(fā)送短信(邏輯省略)
public class MessageService {
/**
* 發(fā)送短信
*
* @param phone
*/
public void sendRegisterSuccessMessage(String phone) {
String message = "您已經(jīng)成功注冊(cè)該網(wǎng)站賬戶";
System.out.println(String.format("已經(jīng)成功的向%s發(fā)送注冊(cè)短信:%s", phone, message));
}
}
(1)在resouce目錄下添加application-context.xml的文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www./schema/beans"
xmlns:xsi="http://www./2001/XMLSchema-instance"
xsi:schemaLocation="http://www./schema/beans
https://www./schema/beans/spring-beans.xsd">
<bean id="registerService" class="com.wj.spring.RegisterService">
<property name="messageService" ref="messageService"/>
</bean>
<bean id="messageService" class="com.wj.spring.MessageService">
</bean>
</beans>
其中id為該bean的唯一識(shí)別 ,通過?<property name="messageService" ref="messageService"/>的方式在RegisterService中增加了MessageService的引用,其實(shí)上面的XML配置的內(nèi)容就等價(jià)于下面的java代碼:
MessageService messageService = new MessageService();
RegisterService registerService = new RegisterService();
registerService.setMessageService(messageService);
- 4.通過讀取XML文件,獲取到ApplicationContext,也就是IoC容器
public class RegisterApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("config/application-context.xml");
RegisterService service = context.getBean(RegisterService.class);
//RegisterService service = (RegisterService)context.getBean("registerService");
User user = service.register("12345678901", "123456");
System.out.println("注冊(cè)之后的用戶名為:" user.name);
}
}
當(dāng)?ClassPathXmlApplicationContext要傳入的是這個(gè)application-context.xml的相對(duì)路徑。這樣就可以context.getBean()的方式獲取到定義的JavaBean。運(yùn)行項(xiàng)目如下:

相關(guān)的代碼已經(jīng)上傳到github:https://github.com/wenjing-bonnie/build-spring.git:對(duì)應(yīng)的com/wj/spring下的相關(guān)代碼。
2.通過注解
使用XML文件的方式,可以把所有的JavaBean的都一一列舉出來,并且通過配置注入能直觀的了解到每個(gè)JavaBean的依賴關(guān)系,但是缺點(diǎn)也就顯現(xiàn)出來,這種寫法非常繁瑣,每增加一個(gè)組件,就必須在XML將該組件添加進(jìn)去。所以就有了注解這種方式,可以讓IoC容器自動(dòng)掃描所有的Bean并組裝他們。
- 1.同上面的1
- 2.同樣定義同上面的JavaBean,只不過在JavaBean增加@Component注解,來標(biāo)示是一個(gè)JavaBean組件
(1)定義一個(gè)RegisterService
@Component
public class RegisterAnnotationService {
// @Autowired將指定類型的Bean直接注入到指定的字段
@Autowired
private MessageAnnotationService messageAnnotationService;
}
通過?@Autowired將指定類型的Bean直接注入到指定的字段。當(dāng)然@Autowired也可以注解List<JavaBean>集合中,這樣每增加一個(gè)同類型的JavaBean,都會(huì)被IoC容器自動(dòng)裝配到該集合中。如果要設(shè)置JavaBean被裝配的順序,則可通過在組件上增加@Order(1)、@Order(2)……
默認(rèn)情況下,當(dāng)給定字段標(biāo)記了?@Autowired,如果IoC容器沒有找到對(duì)應(yīng)的JavaBean,則會(huì)拋出NoSuchBeanDefinitionException異常,所以可以增加@Autowired(required=false)來避免該異常。
(2)定義MessageService
@Component
public class MessageAnnotationService {
}
通過增加上面的兩個(gè)注解的方式,就完成了JavaBean之間的配置規(guī)則。
默認(rèn)的將JavaBean標(biāo)記為@Component,那么IoC容器會(huì)自動(dòng)創(chuàng)建一個(gè)單例,即容器初始化的時(shí)候創(chuàng)建JavaBean,容器銷毀的時(shí)候銷毀。在整個(gè)容器運(yùn)行期間,getBean()獲取的是一個(gè)實(shí)例。
其實(shí)可以通過@Scope來標(biāo)記該實(shí)例的作用域:即@Scope(value = "singleton")或@Scope( "singleton"),該value對(duì)應(yīng)的有四種作用域
singleton :唯一的bean實(shí)例,默認(rèn)的為單例。即getBean()獲取的是一個(gè)實(shí)例。
prototype:每次容器返回的都是一個(gè)新的實(shí)例。即getBean()獲取的都是一個(gè)新的實(shí)例。
request:每次Http請(qǐng)求的時(shí)候都會(huì)產(chǎn)生一個(gè)新的實(shí)例,僅在當(dāng)前request請(qǐng)求中有效。
session:每次Http請(qǐng)求的時(shí)候都會(huì)產(chǎn)生一個(gè)新的實(shí)例,僅在當(dāng)前session內(nèi)有效。
@Configuration //用來標(biāo)明是一個(gè)配置類,在創(chuàng)建AnnotationConfigApplicationContext,需要傳入該類
@ComponentScan //用來告訴容器,自動(dòng)掃描當(dāng)前類所在的包以及子包,把所有標(biāo)注為@Component的Bean都自動(dòng)創(chuàng)建出來,并根據(jù)@Autowired進(jìn)行裝配
public class RegisterAnnotationApplication {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(RegisterAnnotationApplication.class);
RegisterAnnotationService service = context.getBean(RegisterAnnotationService.class);
//RegisterService service = (RegisterService)context.getBean("registerService");
User user = service.register("12345678901", "123456");
System.out.println("注冊(cè)之后的用戶名為:" user.name);
}
}
不同于通過XML文件的配置方式,在讀取ApplicationContext的時(shí)候,需要將該類增加一個(gè)?@Configuration ?用來標(biāo)明是一個(gè)配置類(其實(shí)@Configuration可以用@Component來代替,也就是如果分不清這個(gè)組件具體什么作用,那就直接用@Component),在創(chuàng)建AnnotationConfigApplicationContext的時(shí)候,需要傳入該類;增加@ComponentScan 用來告訴容器,自動(dòng)掃描當(dāng)前類所在的包以及子包,把所有標(biāo)注為@Component的Bean都自動(dòng)創(chuàng)建出來,并根據(jù)@Autowired進(jìn)行裝配。
運(yùn)行項(xiàng)目結(jié)果和上面的一致。相關(guān)代碼已經(jīng)上傳github:https://github.com/wenjing-bonnie/build-spring.git:對(duì)應(yīng)的com/wj/springannotation下的相關(guān)代碼。
3.Spring中的其他幾種注解
1.JavaBean組件相關(guān)的注解
像之前大體有印象的一些@Controller/@RestController、@Service、@Respository等這些都是用來注解為JavaBean,只不過這是在后面的Spring MVC具有一些特殊功能的JavaBean,如果分不清該組件屬于哪一種,就直接使用@Component來注解。
2.讀取配置文件、資源文件等相關(guān)注解
使用IoC容器,可以很方便的把這些文件引入進(jìn)來,方便程序讀取,省掉了很多繁瑣的代碼來獲取InputStream。在Maven項(xiàng)目中,經(jīng)常會(huì)將這些文件放置到Resources目錄下,所以Spring框架就提供了org.springframework.core.io.Resource來可以直接讀取到文件的InputStream
@Value("readme.txt")
private Resource readmeTxt;
那么在項(xiàng)目中就可以通過readmeTxt.getInputStream()獲取到該txt的inputstream。
- ?(2)讀取Resouces下的一些key/value結(jié)構(gòu)的文件
經(jīng)常有些配置文件是key/value形式的存在文件中,在使用上面的方式,顯得不太方便,那么IoC容器還提供了更簡(jiǎn)便的方式來讀取。通過添加@PropertySource注解的方式,就可以直接根據(jù)key來獲取到對(duì)應(yīng)的value。例如有一個(gè)配置文件config/application.properties的內(nèi)容如下:
application.ipaddress = 192.168.110.118
我們只需要在需要讀取該配置文件的組件上添加?@PropertySource("config/application.properties"),那么就可以通過key來得到對(duì)應(yīng)的value值。
@Configuration
@PropertySource("config/application.properties")
public class ApplicationConfig {
@Value("${application.ipaddress}")
public String ipAddress;
}
?@Value("${application.ipaddress}"):為沒有默認(rèn)值的value,如果想給定默認(rèn)值的話,則?@Value("${application.ipaddress:192.168.0.0}")。
- (3)在其他組件中引用配置文件的相關(guān)內(nèi)容
還是上面的例子,如果在其他地方也要引用config/application.properties里面的配置項(xiàng),其實(shí)可以直接通過@Value("#{bean.property}")的方式來得到對(duì)應(yīng)的配置項(xiàng),bean就是ApplicationConfig的實(shí)例applicationConfig(該實(shí)例不需要引入,只是組件名的首字母小寫)的ipAddress屬性值引入到OtherAnnotation。
@Component
public class OtherAnnotation {
@Value("#{applicationConfig.ipAddress}")
public String ipAddress;
}
通過該注解方式,在OtherAnnotation中可以不用引入config/application.properties就可以讀取到定義在ApplicationConfig里面的配置項(xiàng)。與(2)不同的是,在(2)中使用的是@Value("${key:defaultValue}")的形式讀取配置文件中的值,而在OtherAnnotation中通過@Value("#{bean.property}")讀取在ApplicationConfig中讀到的配置項(xiàng)。
四 JavaBean生命周期的監(jiān)聽
當(dāng)我們將一個(gè)JavaBean添加到IoC容器時(shí),有的時(shí)候可能需要在容器初始化該JavaBean的時(shí)候監(jiān)聽消息等,在容器關(guān)閉的時(shí)候清理資源。那么就可以通過在該JavaBean只能夠定義兩個(gè)方法,在項(xiàng)目中引入javax.annotation依賴,通過javax.annotation的@PostContruct和@PreDestroy添加到上述的兩個(gè)方法中。其過程如下:
(1)在pom.xml添加javax.annotation依賴
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
(2)在MessageAnnotationService中需要承載初始化和銷毀的方法上添加@PostContruct和@PreDestroy
public class MessageAnnotationService {
@PostConstruct
public void init() {
System.out.println("MessageAnnotationService init");
}
@PreDestroy
public void destroy() {
System.out.println("MessageAnnotationService destroy");
}
}
這樣在IoC容器初始化和銷毀的時(shí)候,就會(huì)看到對(duì)應(yīng)的方法的調(diào)用 。
五 總結(jié)
1.Spring的核心功能就是IoC容器,提供組件的創(chuàng)建和裝配,只需要定義配置規(guī)則,IoC容器會(huì)自動(dòng)完成所有的過程;
2.可以通過XML文件和注解兩種方式來定義組件之間的配置規(guī)則;
3.終于搞明白了項(xiàng)目中為什么有那么多bean的配置文件;
4.使用@Component來注解一個(gè)類為組件,那么IoC容器在啟動(dòng)的時(shí)候,就會(huì)自動(dòng)初始化該組件;
5.可以用@Scope來指定組件實(shí)例的作用域,是單例還是每次都是一個(gè)新的實(shí)例;
6.一個(gè)ApplicationContext就是一個(gè)IoC容器,可以從ApplicationContext中通過getBean()來獲取到對(duì)應(yīng)的組件實(shí)例;
7.使用javax.annotation可以在組件初始化或者銷毀的時(shí)候做一些事情;
8.可以使用Resource輕松得到resouces下的文件的inputStream;
9.可以通過?@PropertySource的方式輕松得到配置文件的key/value;
10.之前有點(diǎn)印象的@Controller/@RestController、@Service、@Respository等其實(shí)都是一些組件,只不過是一些擁有特殊功能的組件。
每天都在進(jìn)步一點(diǎn),加油?。?!?
來源:https://www./content-4-862751.html
|