Spiring系列__03IOC補(bǔ)充
這篇文章是對(duì)前一篇的一些補(bǔ)充:
1.SpringIOC容器可以管理Bean的生命周期:
- 通過(guò)構(gòu)造器或工廠方法創(chuàng)建bean的實(shí)例;
- 為bean屬性設(shè)置值或者引入其他bean;
- 調(diào)用bean的初始化方法,此時(shí)bean就可以使用了;
- 容器關(guān)閉時(shí),調(diào)用bean的清理方法。
在bean的聲明里定義init-method和的story-method,來(lái)定義bean的初始化方法和銷(xiāo)毀方法。
2.bean的后置處理器
bean的后置處理器允許spring在初始化方法前后,對(duì)bean進(jìn)行額外的處理,其會(huì)對(duì)IOC容器的所有bean實(shí)例進(jìn)行逐一處理,而非單一實(shí)例。例如:對(duì)bean實(shí)例檢查屬性的正確性或進(jìn)行相應(yīng)的更改。
當(dāng)你想自定義一個(gè)后置處理器時(shí),需要實(shí)現(xiàn)Spring提供的BeanPostProcessor接口。
package com.spring.demo.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//添加過(guò)濾規(guī)則
if ("car".equals(beanName)) {
System.out.println("postProcessBeforeInitialization:" + beanName + bean);
return bean;
}
return null;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
<!--
配置 bean 后置處理器: 不需要配置 id 屬性,
IOC 容器會(huì)識(shí)別到他是一個(gè) bean 后置處理器, 并調(diào)用其方法
-->
<bean class="com.spring.demo.bean.MyBeanProcessor"/>
在這個(gè)例子中,我只是在初始化bean之前對(duì)bean進(jìn)行了校驗(yàn),只是實(shí)例化car類(lèi)型。
當(dāng)你自定義了一個(gè)后置處理器,并將其注冊(cè)到了Spring容器中,bean的聲明周期方法也會(huì)發(fā)生了一些改變:
- 通過(guò)構(gòu)造器或工廠方法創(chuàng)建bean的實(shí)例;
- 為bean屬性設(shè)置值或者引入其他bean;
- 將bean實(shí)例傳遞給后置處理器的postProcessBeforeInitialization方法;
- 調(diào)用bean的初始化方法,此時(shí)bean就可以使用了;
- 將bean實(shí)例傳遞給后置處理器的postProcessAfterInitialization方法;
- 容器關(guān)閉時(shí),調(diào)用bean的清理方法。
3.bean的創(chuàng)建方式續(xù)
前一篇已經(jīng)介紹了了通過(guò)反射來(lái)創(chuàng)建bean的方法,現(xiàn)在介紹剩下的兩種:
1.工廠方法
其理念和設(shè)計(jì)模式中的工廠方法一樣。
1.靜態(tài)工廠方法
靜態(tài)工廠方法創(chuàng)建bean,不需要?jiǎng)?chuàng)建靜態(tài)工廠實(shí)例,只需調(diào)用對(duì)應(yīng)的靜態(tài)方法即可。
<!--
bean的配置方式2:工廠方法
靜態(tài)工廠方法:
id指定唯一標(biāo)識(shí)
class指定的是靜態(tài)工廠方法的類(lèi)
factory-method指定創(chuàng)建bean的工廠方法
-->
<bean id="carByStaticFactory" class="com.spring.demo.bean.StaticCarFactory" factory-method="getCar">
<constructor-arg value="mini"/>
</bean>
//靜態(tài)工廠
package com.spring.demo.bean;
import java.util.HashMap;
import java.util.Map;
/**
* 靜態(tài)方法來(lái)創(chuàng)建Car實(shí)例
*/
public class StaticCarFactory {
private static Map<String, Car> cars = new HashMap<>(10);
static {
cars.put("mini", new Car("mini", null, 200, 3000000.00));
cars.put("BMW", new Car("BMW", null, 200, 5000000.00));
}
public static Car getCar(String name) {
return cars.get(name);
}
}
//測(cè)試方法
@Test
public void testStaticFactory() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean-autowire.xml");
Car car = (Car) context.getBean("carByStaticFactory");
System.out.println(car);
}
2.實(shí)例工廠方法
與靜態(tài)工廠方法不同,實(shí)例工廠方法需要實(shí)例一個(gè)工廠對(duì)象,其余的與靜態(tài)工廠方法類(lèi)似。
<!--
bean的配置方式2:工廠方法
實(shí)例態(tài)工廠方法:與靜態(tài)工廠方法類(lèi)似
id指定唯一標(biāo)識(shí)
class指定的是靜態(tài)工廠方法的類(lèi)
factory-bean指定工廠工廠方法實(shí)例
factory-method指定創(chuàng)建bean的工廠方法
-->
<bean id="carFactory" class="com.spring.demo.bean.CarFactory"></bean>
<bean id="carByFactory" class="com.spring.demo.bean.Car" factory-bean="carFactory"
factory-method="getCar">
<constructor-arg value="mini"></constructor-arg>
</bean>
package com.spring.demo.bean;
import java.util.HashMap;
import java.util.Map;
public class CarFactory {
private Map<String, Car> cars = new HashMap<>(10);
{
cars.put("mini", new Car("mini", null, 200, 3000000.00));
cars.put("BMW", new Car("BMW", null, 200, 5000000.00));
}
public Car getCar(String name) {
return cars.get(name);
}
}
@Test
public void testFactory() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean-autowire.xml");
Car car = (Car) context.getBean("carByFactory");
System.out.println(car);
}
2.FactoryBean
Spring容器提供了一種新的bean的配置方式:FactoryBean。
Spring容器中有兩種Bean:一種是普通的bean,另一種是工廠bean,即FactoryBeean。FactoryBean提供了getObject()來(lái)返回bean的實(shí)例。當(dāng)你通過(guò)這種方式時(shí),需要實(shí)現(xiàn)Spring的FactoryBean接口。
<!--
bean的配置方式3:FactoryBean
-->
<bean id="carByFacoryBean" class="com.spring.demo.bean.CarFactoryBean"/>
package com.spring.demo.bean;
import org.springframework.beans.factory.FactoryBean;
/**
* 自定義FactoryBean需要實(shí)現(xiàn)對(duì)應(yīng)的方法
*/
public class CarFactoryBean implements FactoryBean<Car> {
/**
* 該方法返回創(chuàng)建的bean的引用
* @return
* @throws Exception
*/
@Override
public Car getObject() throws Exception {
return new Car("BMW", "mini", 300, 2000000.00);
}
/**
* 返回配置的bean的對(duì)應(yīng)的泛型
* @return
*/
@Override
public Class<?> getObjectType() {
return Car.class;
}
/**
* 是否單例
* @return
*/
@Override
public boolean isSingleton() {
return true;
}
}
@Test
public void testFactory() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean-autowire.xml");
Car car = (Car) context.getBean("carByFactory");
System.out.println(car);
}
3.注解方式
可以采用注解方式來(lái)配置bean,不過(guò)需要配置主鍵掃描(其實(shí)組件掃描也可以通過(guò)注解實(shí)現(xiàn),達(dá)到純注解配置)
組件掃描(component scanning): Spring 能夠從 classpath 下自動(dòng)掃描, 偵測(cè)和實(shí)例化具有特定注解的組件.
- @Component: 基本注解, 標(biāo)識(shí)了一個(gè)受 Spring 管理的組件:
- @Respository: 標(biāo)識(shí)持久層組件
- @Service: 標(biāo)識(shí)服務(wù)層(業(yè)務(wù)層)組件
- @Controller: 標(biāo)識(shí)表現(xiàn)層組件
對(duì)于掃描到的組件,spring默認(rèn)使用類(lèi)名小寫(xiě)來(lái)命名,也可以通過(guò)value顯示指定。
<context:component-scan base-package="com.spring.demo.autowire"/>
@Test
public void testComponentScan() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean-fac.xml");
CarController controller = (CarController) context.getBean("carController");
controller.test();
}
1.組件裝配
context:component-scan 元素還會(huì)自動(dòng)注冊(cè) AutowiredAnnotationBeanPostProcessor 實(shí)例, 該實(shí)例可以自動(dòng)裝配具有 @Autowired 和 @Resource 、@Inject注解的屬性。
1.使用 @Autowired 自動(dòng)裝配 Bean
@Autowired 注解自動(dòng)裝配具有兼容類(lèi)型的單個(gè) Bean屬性
- 構(gòu)造器, 普通字段(即使是非 public), 一切具有參數(shù)的方法都可以應(yīng)用@Authwired 注解
- 默認(rèn)情況下, 所有使用 @Authwired 注解的屬性都需要被設(shè)置. 當(dāng) Spring 找不到匹配的 Bean 裝配屬性時(shí), 會(huì)拋出異常, 若某一屬性允許不被設(shè)置, 可以設(shè)置 @Authwired 注解的 required 屬性為 false
- 默認(rèn)情況下, 當(dāng) IOC 容器里存在多個(gè)類(lèi)型兼容的 Bean 時(shí), 通過(guò)類(lèi)型的自動(dòng)裝配將無(wú)法工作. 此時(shí)可以在 @Qualifier 注解里提供 Bean 的名稱. Spring 允許對(duì)方法的入?yún)?biāo)注 @Qualifiter 已指定注入 Bean 的名稱
- @Authwired 注解也可以應(yīng)用在數(shù)組類(lèi)型的屬性上, 此時(shí) Spring 將會(huì)把所有匹配的 Bean 進(jìn)行自動(dòng)裝配.
- @Authwired 注解也可以應(yīng)用在集合屬性上, 此時(shí) Spring 讀取該集合的類(lèi)型信息, 然后自動(dòng)裝配所有與之兼容的 Bean.
- @Authwired 注解用在 java.util.Map 上時(shí), 若該 Map 的鍵值為 String, 那么 Spring 將自動(dòng)裝配與之 Map 值類(lèi)型兼容的 Bean, 此時(shí) Bean 的名稱作為鍵值
2.使用 @Resource 或 @Inject 自動(dòng)裝配 Bean
Spring 還支持 @Resource 和 @Inject 注解,這兩個(gè)注解和 @Autowired 注解的功用類(lèi)似
- @Resource 注解要求提供一個(gè) Bean 名稱的屬性,若該屬性為空,則自動(dòng)采用標(biāo)注處的變量或方法名作為 Bean 的名稱
- @Inject 和 @Autowired 注解一樣也是按類(lèi)型匹配注入的 Bean, 但沒(méi)有 reqired 屬性
建議使用 @Autowired 注解
4.bean的作用域
Spring容器的有以下幾種作用域:
- 單例(singleton): 在整個(gè)應(yīng)用中,只創(chuàng)建bean的一個(gè)實(shí)例。
- 原型(prototype): 每次注入或者通過(guò)Spring應(yīng)用上下文獲取的時(shí)候,都會(huì)創(chuàng)建一個(gè)新的bean的實(shí)例。
- 請(qǐng)求(request): 在web應(yīng)用中,為每次請(qǐng)求創(chuàng)建一個(gè)bean實(shí)例。
- 會(huì)話(session): 在web應(yīng)用中,為每個(gè)會(huì)話創(chuàng)建一個(gè)bean實(shí)例。
在默認(rèn)情況下,Spring應(yīng)用上下文中所有bean都是單例的(線程不安全),這在大多數(shù)情況下是合理的;但是,有些情況下,你不希望如此。
比如,電商應(yīng)用中的購(gòu)物車(chē),若是單例的,則所有人添加的商品都會(huì)添加到一個(gè)購(gòu)物車(chē)?yán)锩妫@就GG了。而現(xiàn)實(shí)生活中,你的購(gòu)物車(chē)是只有你能看到并進(jìn)行更改的。假設(shè)有一個(gè)Service對(duì)象來(lái)執(zhí)行這一業(yè)務(wù)邏輯:用戶登錄后添加商品到購(gòu)物車(chē)?yán)锩妗N覀儊?lái)分析一下:這個(gè)Service對(duì)象,應(yīng)該是單例的并且是隨著Spring容器加載而實(shí)例化好的;但是購(gòu)物車(chē)對(duì)象的對(duì)象應(yīng)該是session,并且應(yīng)該在用戶登錄后開(kāi)始向購(gòu)物車(chē)添加商品的時(shí)候才進(jìn)行實(shí)例化。而且,Service對(duì)象要想完成這一業(yè)務(wù)邏輯,需要依賴購(gòu)物車(chē)對(duì)象。
簡(jiǎn)單概況:現(xiàn)在,一個(gè)作用域?yàn)閟ingleton的Serivce對(duì)象需要對(duì)一個(gè)作用域?yàn)閟ession的對(duì)象依賴。
這時(shí),我們可以采取以下方案進(jìn)行設(shè)計(jì):
/**
* 購(gòu)物車(chē)對(duì)象,正常應(yīng)該一個(gè)用戶創(chuàng)建一次會(huì)話的時(shí)候就創(chuàng)建一個(gè)對(duì)象,作用域?yàn)閟ession
*/
@Component
@Scope(value = WebApplicationContext.SCOPE_SESSION,
proxyMode = ScopedProxyMode.INTERFACES)
public class ShoppingCart {
}
/**
* 購(gòu)物車(chē)對(duì)象,正常應(yīng)該一個(gè)用戶創(chuàng)建一次會(huì)話的時(shí)候就創(chuàng)建一個(gè)對(duì)象,作用域?yàn)閟ession
*/
@Component
@Scope(value = WebApplicationContext.SCOPE_SESSION,
proxyMode = ScopedProxyMode.INTERFACES)
public class ShoppingCart {
}
解釋一下:Spring并不會(huì)將實(shí)際的購(gòu)物車(chē)對(duì)象注入到service對(duì)象中(購(gòu)物車(chē)還沒(méi)創(chuàng)建,也根本沒(méi)法注入),而是注入一個(gè)購(gòu)物車(chē)對(duì)象的代理,這個(gè)代理會(huì)暴露和購(gòu)物車(chē)一樣的方法,所以service對(duì)象就會(huì)認(rèn)為他是一個(gè)購(gòu)物車(chē)。
而proxyMode屬性的值為ScopedProxyMode.INTERFACES,表明這個(gè)代理要實(shí)現(xiàn)ShoppingCart接口,并將調(diào)用委托給bean。
如果shoppingcart是接口的話,這時(shí)可以的;但是當(dāng)其是一個(gè)具體類(lèi)的時(shí)候,Spring就不許使用CGlib來(lái)生成基于累的代理。
當(dāng)然,當(dāng)你采用xml配置文件的形式的時(shí)候,只需要設(shè)置如下屬性即可:
<bean id="cart" class="" scope="session">
<aop:scoped-proxy/>
</bean>

浙公網(wǎng)安備 33010602011771號(hào)