1.IOC概述
XXXXX:容器&依賴(lài)注入
2.BeanFactory/ApplicationContext
BeanFactory負責讀取Bean定義文件;管理對象的加載,生成;維護Bean對象與Bean對象之間的依賴(lài)關(guān)系;負責Bean的生命周期。

InputStream is = new FileInputStream("beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(is);
或者
ClassPathResource res = new ClassPathResource("beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(res);
1.提供更多的獲取資源文件的方法
a) FileSystemXmlApplication,相對路勁或者絕對路徑
b) ClassPathXmlApplicationContext,從classpath中讀取,可以以file:/
Classpath:,http://,classpath*: 表示所有的ClassPath路徑匹配
c) XmlWebApplicationContext,在web應用程序中,指定相對位置讀取定義文件
2.提供解析文字消息的方法
3.支持國際化消息
4.可以發(fā)布消息,對事件感興趣的Bean可以接收這些事件


<?xml version="1.0" encoding="GB2312"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
default-autowire="byName">
<bean id="reportDefineManager"
class="com.ReportDefineManager" />
<bean id="reportSourceDefineManager"
class="com.ReportSourceDefineManager" />
<!-- service -->
<bean id="reportDataService
class="com.ReportDataServiceImpl" />
</beans>
首先spring會(huì )按bean定義順序解析,解析的時(shí)候生成BeanDefinition,主要是bean所屬的class:
RootBeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClass(beanClass);
beanDefinition.setLazyInit(false);
另外,如果要給這個(gè)bean設置一下屬性,會(huì )設置到這個(gè)beanDefinition的PropertyValues中:
1.基本數據類(lèi)型被直接放入PropertyValues中
beanDefinition.getPropertyValues().addPropertyValue("id", id);
2.如果value不是值類(lèi)型而是引用類(lèi)型,那么就是一個(gè)RuntimeBeanReference:
beanDefinition.getPropertyValues().addPropertyValue(name,
new RuntimeBeanReference(ref));
3.如果是注冊一個(gè)內部Bean,因為這時(shí)候還沒(méi)有任何實(shí)例產(chǎn)生,則使用BeanDefinitionHolder
beanDefinition.getPropertyValues().
addPropertyValue("ref",
new BeanDefinitionHolder(classDefinition, id + "Impl"));
4.如果是列表,則用ManagedList
ManagedList methods = new ManagedList();
beanDefinition.getPropertyValues().addPropertyValue("methods", methods);
在解析完定義之后,就會(huì )開(kāi)始Bean的初始化,參考后面的Bean生命周期部分
(如果要獲取某個(gè)類(lèi)型的所有bean實(shí)例,可以使用BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext,
ProtocolConfig.class, false, false),會(huì )初始化沒(méi)有實(shí)例化的類(lèi))
3.Bean
Bean是spring的基礎,spring的各種其他操作都是對Bean的操作,或者說(shuō)有了Bean這些基礎以后,才能做更多的操作,下面是一個(gè)簡(jiǎn)單的Bean的配置:
public interface UserDao {
void save();
}
public class UserDaoImpl implements UserDao{
public void save() {
System.out.println("執行save方法...");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
</beans>
<!-- 別名,或者直接使用bean的name的屬性來(lái)設置別名,多個(gè)別名之間用逗號隔開(kāi) -->
<alias name="aliaName" alias="userDaoImplExtend"/>

一個(gè)Bean從建立都銷(xiāo)毀,會(huì )經(jīng)歷以下幾個(gè)執行階段:
1.Bean的建立(Bean建立之前,其實(shí)可以用BeanFactoryPostProcessor做些事情)
2.屬性注入
3.BeanNameAware的setBeanName
4.BeanFactoryAware的setBeanFactory
5.BeanPostProcessors的processBeforeInitialization
6.InitializingBean的afterPropertiesSet
7.執行xml定義中的init-method方法(與6意義相同)
8.BeanPostProcessors的processaAfterInitialzation
9.DisposableBean的destory
10.Xml定義中的destory-method(與9意義相同)
如果所有的方法都命名為init以及destroy方法,則可以在beans上定義default-init-method與default-destory-method屬性:
<beans default-init-method="init">
對應用程序來(lái)說(shuō),最佳的情況是它根本不知道自己被spring容器所管理,然而有時(shí)候,為了善用Spring提供的一些功能,必須讓Bean知道Spring容器管理它的一些細節,比如:讓Bean知道自己在容器中式哪個(gè)別名、讓它知道BeanFactory、Application的存在,即獲得BeanFactory或Application的實(shí)例。
【BeanNameAware】
在生命周期中可以看到,在設置了屬性之后,初始化方法之前,會(huì )將Bean在定義文件中的名稱(chēng)通過(guò)setBeanName方法設置給Bean
public interface BeanNameAware {
void setBeanName(String name);
}
【BeanFactoryAware】
在設置依賴(lài)關(guān)系之后,初始化方法之前,Spring會(huì )注入BeanFactory實(shí)例
public interface BeanFactoryAware {
void setBeanFactory(BeanFactory beanFactory);
}
【ApplicationContextAware】
Bean初始化之后,將會(huì )被注入ApplicationContext的實(shí)例
另外還有ResourceLoaderAware接口,可以讓Bean取得ResourceLoader實(shí)例。
public interface ApplicationContextAware {
void setApplicationContext(
ApplicationContext applicationContext);
}
Aware只是把spring的容器的一些屬性注入進(jìn)Bean,還可以通過(guò)Processor相關(guān)接口,設置、修正Bean的相關(guān)屬性,實(shí)現BeanPostProcessor:
public interface BeanPostProcessor {
/**
* 在Bean類(lèi)被初始化之前,Before類(lèi)初始化之后立即執行;After在類(lèi)初始化之后立即被執行
*/
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
比如,我們可以實(shí)現一個(gè)英文字母轉大寫(xiě)修正器類(lèi),作用是對String類(lèi)型的依賴(lài)注入之后,都轉為大寫(xiě)
/**
* Processor 加工
*/
public class BeanPostProcessorTest implements BeanPostProcessor{
/**
* 將設置的值轉換為大寫(xiě)
*/
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
Field[] fields=bean.getClass().getDeclaredFields();
for(Field field:fields){
if(field.getType().equals(String.class)){
field.setAccessible(true);
try{
String preValue=(String)field.get(bean);
field.set(bean, preValue.toUpperCase());
}catch(Exception e){
}
}
}
return bean;
}
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
}
<!-- processor -->
<bean id="uppercaseModifier"
class="com.bhsc.service.BeanPostProcessorTest"></bean>
前面看了在Bean實(shí)際產(chǎn)生之前可以通過(guò)BeanPostProcessor,在構造完成BeanFactory后,初始化其他任何bean之前初始化BeanFactoryPostProcessor的bean,可以對該BeanFactory進(jìn)行一些處理

<property name="location">hello.properties</property>
</bean>
這樣在spring的配置文件中就可以使用${aa}的形式注入屬性。
2.PropertyOverrideConfigurer
與上面的類(lèi)類(lèi)似,不過(guò)它可以在.properties文件中設置一些優(yōu)先的屬性,這樣,可以在Bean的定義中設置默認值,如果pro文件中有,則覆蓋。
helloBean.helloWorld="",便是將helloWorld這個(gè)Bean的helloWorld屬性覆蓋。
3.CustomEditorConfigurer
這個(gè)類(lèi)可以讀取實(shí)現java.beans.PropertyEditor接口的實(shí)現,并按區中的實(shí)現,將字符串值轉換為指定類(lèi)型的對象。
也可以參考:http://blog.sina.com.cn/s/blog_5f1fe33f0100hyx7.html
正常的情況下都是使用默認的構造函數去創(chuàng )建一個(gè)Bean,但是我們在實(shí)際的應用中常常會(huì )使用工廠(chǎng)模式去創(chuàng )建實(shí)例,如果要把這個(gè)用到spring中來(lái),有下面三種方式:
4.1.通過(guò)一個(gè)靜態(tài)工廠(chǎng)類(lèi)去生成Bean
<!-- 靜態(tài)工廠(chǎng)類(lèi),在配置了factory-method的情況下,每次getBean都會(huì )調用factoryMethod方法-->
<bean id="userDaoImpl2" class="com.asm.dao.impl.UserDaoImplFactory" factory-method="getUserDaoImpl"/>
public class UserDaoImplFactory {
public static UserDaoImpl getUserDaoImpl(){
return new UserDaoImpl();
}
}
4.2.通過(guò)實(shí)例工廠(chǎng)類(lèi)去生產(chǎn)Bean
<!-- 實(shí)例工廠(chǎng)類(lèi),這種方式需要配置兩個(gè)Bean,一個(gè)是普通Bean,另一個(gè)Bean配置如果通過(guò)工廠(chǎng)Bean獲取Bean,指定工廠(chǎng)Bean的名稱(chēng)和方法名稱(chēng) -->
<bean id="factory" class="com.bhsc.service.UserDaoImplFactory2"/>
<bean id="userDaoImpl3" factory-bean="factory"
factory-method="getUserDaoImpl"/>
實(shí)現了這個(gè)接口的Bean就不再被視為普通Bean,Spring會(huì )自動(dòng)檢測
public class PiFactoryBean implements FactoryBean {
public Object getObject() throws Exception {
return new Double(3.14159265358979);
}
public Class getObjectType() {
return Double.class;
}
public boolean isSingleton() {
return true;
}
}
<bean id="pi" class="example.chapter3.PiFactoryBean" />
測試
public static void main(String[] args) throws Exception {
XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("config.xml"));
System.out.println(factory.getBean("pi"));//返回PiFactoryBean 的工廠(chǎng)方法getObject返回的Double對象實(shí)例
//PiFactoryBean p = (PiFactoryBean)factory.getBean("&pi"); //加"&"返回工廠(chǎng)Bean的實(shí)例.
//System.out.println(p.getObject());
}
5.Bean的Scope
不過(guò)哪種創(chuàng )建方式,默認的,Bean都是單例的,每次都是返回同一個(gè)實(shí)例,可以設置scope=prototype,這樣,每次請求都是重新創(chuàng )建一個(gè)實(shí)例。另外還有request、session、global session這三個(gè)作用域主要用在web應用中。
ApplicationContext ctx =
new ClassPathXmlApplicationContext("beans.xml");
UserDao userDao = (UserDao) ctx.getBean("userDaoImpl");
userDao.save();
userDao=(UserDao) ctx.getBean("userDaoImpl2");
userDao=(UserDao) ctx.getBean("userDaoImpl2");
userDao=(UserDao) ctx.getBean("userDaoImpl3");
userDao=(UserDao) ctx.getBean("userDaoImpl3");
輸出:
==實(shí)例工廠(chǎng)==
執行save方法...
==靜態(tài)工廠(chǎng)==
==靜態(tài)工廠(chǎng)==
可以看到,默認情況下,Bean是在容器初始化的時(shí)候創(chuàng )建,所以在save之前就打印了"實(shí)例工廠(chǎng)",表示已經(jīng)創(chuàng )建了相應的Bean,后面的兩次調用2,并沒(méi)有再打印。
如果希望在調用的時(shí)候創(chuàng )建,可以使用lazy-init="true" ,如果希望希望該配置文件中的所有bean都延遲初始化,則應在beans根結點(diǎn)中使用lazy-init="true"。當scope=prototype時(shí),在調用getBean()方法時(shí)才會(huì )初始化。
6.Spring注入方式
Spring的注入方式分為2中,setter注入和構造函數注入,還有另外一種的接口注入,不過(guò)Spring不支持。
6.1.setter注入
<bean id="userDaoImpl" class="com.bhsc.service.UserDaoImpl">
<!-- 簡(jiǎn)單屬性注入 -->
<property name="id" value="10"></property>
<property name="username" value="XXX"></property>
<!-- 對象屬性-外部bean注入-->
<property name="tempBean" ref="tempBean"/>
<!-- 如果希望ref的Bean必須和定義的Bean在同一個(gè)設置文件中,則可以指定local屬性-->
<property name="tempBean" local-ref="tempBean"/>
<!-- 對象屬性-內部bean注入 -->
<property name="tempBean1">
<bean id="temp" class="com.bhsc.service.TempBean"/>
</property>
<!-- 集合屬性注入-->
<property name="list">
<list>
<value><null/></value>
<value>List值二</value>
<value>List值三</value>
</list>
</property>
<property name="set">
<set>
<value>Set值二</value><value>Set值一</value>
<value>Set值三</value>
</set>
</property>
<property name="map">
<map>
<entry key="one" value="一" />
<entry key="two" value="二" />
<entry key="three" value="三" />
</map>
</property>
<property name="pro">
<props>
<prop key="p1">first</prop>
<prop key="p2">second</prop>
<prop key="p3">third</prop>
</props>
</property>
</bean>
繼承注入:
<!-- 使用繼承的注入 -->
<bean abstract="true" id="parent" >
<property name="username" value="張某某"></property>
</bean>
<bean id="userDaoImplExtend"
class="com.bhsc.service.UserDaoImpl" parent="parent">
</bean>
6.2.構造函數注入
<bean id="userServiceBean2" class="com.asm.service.UserServiceBean">
<constructor-arg index="0" value="李某某"/>
<constructor-arg index="1" ref="userDaoImpl" />
<constructor-arg index="2">
<list>
<value>List值一</value>
<value>List值二</value>
<value>List值三</value>
</list>
</constructor-arg>
</bean>
public UserServiceBean(String username, UserDao userDao, Set<String> set) {
this.username=username;
this.userDao=userDao;
this.set=set;
}
6.3.接口注入
7.自動(dòng)綁定
autowire="byType"
說(shuō)明:除了byType外,autowire的可選屬性如下:
byName:根據類(lèi)中的字段名來(lái)查找對應的bean,如不能成功注入,則字段設為null.
byType:根據類(lèi)型裝配,如果發(fā)現多個(gè)類(lèi)型都能夠匹配,則拋出異常。
Consturctor:也byType相似,不同之處在于它應用于構造器的參數,如果容器中沒(méi)有找到與構造器參數類(lèi)型一致的bean,則拋出異常。
Autodetect:通過(guò)bean類(lèi)的自省機制來(lái)決定是使用consturctor還是byType方式進(jìn)行自動(dòng)裝配。如果發(fā)現默認的構造器,那么將使用byType方式。
8.資源、消息、事件
8.1.資源
ApplicationContext本身繼承了ResourceLoader接口,可以使用getResource方法,指定資源文件的URL來(lái)取得一個(gè)Resource接口的實(shí)例。
Resource resource=ctx.getResource("resource.txt");
取得資源的實(shí)例以后,可以使用etFile/getInputStream等方式來(lái)操作或者取得資源文件的相關(guān)資源。
8.2.消息
ApplicationContext還繼承了MessageSource接口,可以使用getMessage的各種版本方法來(lái)取得文字消息的資源文件,實(shí)現國際化。
可以通過(guò)ResourceBundleMessageSource來(lái)取得國際化消息。
9.事件
主要有三個(gè)部分,發(fā)布者/事件/訂閱者:http://my.oschina.net/u/218421/blog/37948
【事件】
public class EmailEvent extends ApplicationEvent
所有的事件必須是ApplicationEvent的子類(lèi)
【接收者】
public class EmailNotifier implements ApplicationListener{
@Override
public void onApplicationEvent(ApplicationEvent event)
{
// TODO Auto-generated method stub
if(event instanceof EmailEvent)
{
//notifier apppriate person
EmailEvent emailEvent = (EmailEvent)event;
System.out.println("我已收到通知:"+emailEvent.getEmail()+"要發(fā)郵件了。。");
}
}
}
【發(fā)布者】
通過(guò)ApplicationContext發(fā)布事件
public class EmailBean implements ApplicationContextAware{
private ApplicationContext ctx = null;
public ApplicationContext getCtx(){
return ctx;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException{
// TODO Auto-generated method stub
this.ctx = applicationContext;
}
public void sendEmail(String email,String title,String text){
if(backList.contains(email)){
EmailEvent evt = new EmailEvent(email,title, text);
ctx.publishEvent(evt);
return ;
}
}
}
在Spring執行期間,ApplicationContext本身就會(huì )發(fā)布一連串的事件,所有的事件都是ApplicationEvent的子類(lèi),比如:
a.ContextClosedEvent:ApplicationContext關(guān)閉的時(shí)候發(fā)布事件
b.ContextRefreshedEvent,Ac初始化或者refresh的時(shí)候發(fā)布
c.RequestHandledEvent,在web應用程序中,AC會(huì )發(fā)布該事件
事件傳播:如果打算發(fā)布事件通知ACListener的實(shí)例,例如,發(fā)布應用程序的心跳事件,讓?xiě)贸绦虻目蛻?hù)端知道應用程序是否還活著(zhù),可以使用AC的publishEvent方法。
聯(lián)系客服