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

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