前几天抽空重温了Spring参考手册,IoC部分做了笔记,AOP部分因为结合了AspectJ和XML Schemed Support,暂时没做笔记,除此之外,还看了看Spring的事务管理以及对Hibernate的底层支持.现在整理出我的Spring IoC学习笔记.
1 学习Spring的切入点----反向控制,学习反向控制,从Spring IoC容器的包开始.
org.springframework.beans及org.springframeword.context包是Spring IoC容器的基础.BeanFactory提供了配置框架及基本功能,而ApplicationContext则增加了更多支持企业核心内容的功能.
2 了解bean
简单地讲,bean就是由Spring容器初始化、装配及被管理地对象,除此之外,bean就没有特别之处了.
3 了解BeanFactory
在Spring中,BeanFactory是IoC容器的核心接口.它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖.
实际运行机制是这样的:
ApplicationContext context=new ClassPathXmlApplicationContext(new String[]{“applicationContext.xml”,”applicationContext-part2.xml”});
BeanFactory factory=(BeanFactory)context;
我们需要BeanFactory实例,在BeanFactory实例中最主要的一组方法就是getBean的重载方法.
4 <bean> 标签
Class:采用标准java命名机制.
Alias:别名.
构造器参数:<constructor-arg index=”0” value=”40” type=”java.lang.Integer”>
4.1 bean的实例化:
两种方法:
第一种是直接调用构造函数.
<bean name=”example” class=”src.Example”/>
第二种是静态工厂的工厂方法,其中,工厂方法必须是静态方法.
<bean name=”example” class=”src.ExampleFactory” factory-method=”createExample”/>
第三种是动态(实例)工厂的工厂方法
<bean name=”myFactory”/>
<bean name=”example” factory-bean=”myFactory” factory-method=”createExample”/>
这种方法的好处是工厂也被IoC了.
4.2 反向注入的类型
1 setter()方法 2 构造器
使用构造器注入可能会导致循环依赖,即A在构造器中注入B,而B在构造器中注入A,此时,会抛出BeanCurrentlyInCreationException异常.对于此问题,一个可能的解决方法就是修改源代码,将构造器注入改为setter注入.另一个解决方法就是完全放弃使用构造器注入,只使用setter注入.
4.3 注入的时机
注入默认采用singleton模式,在容器创建时便创建,这样的好处是可以及时发现配置问题,坏处是增加了不必要的内存和时间开销.
4.4 ref的三种用法
<ref bean=”beanname”>
<ref local=”beanname”>
<ref parent=”beanname”>不常见
其中beanname可以是id也可以是name,为了优化,还可以把ref改成idref,这时会在创建容器时对beanname进行检验.
4.5 内部类的配置
4.6 集合的配置 <list/>、<set/>、<map/>、<props/>
4.7 父子bean的集合属性合并(merge=true)
4.8 强类型集合(tiger适用)
Spring的IoC支持tiger的强类型集合
4.9 null与””的注入(<null/>、<value><value/>)
4.10 value与ref的简写形式,包括在map中
4.11 组合属性赋值
需要注意,组合中的任一级在初始化后不能为空,否则抛NullPointerException
4.12 depends-on
显示指定多个依赖,被指定的bean需要在该bean之前初始化,但并不一定该类与被指定的bean存在关联.
4.13 延迟初始化 <bean laze-init=”true”/> or <beans default-laze-init=”true”>…<beans/>
如果一个非延迟初始化bean依赖于一个延迟初始化bean,那么初始化前者的时候,会顺带初始化后者,也就是说,laze-init=”true”会实效.
4.14 自动装配 五种形式no 、byName 、byType 、constructor 、autodetect
优点:1 省事
2 同步更新
缺点:确定你用的对,否则可能抛异常.
4.15
形式:方法注入
手段:<lookup-method name=”methodName” bean=”返回类型的bean定义”>
目的:一个singleton类的某个方法每次调用需要创建不同的非singleton类实例.
注意:非singleton类实例必须是prototype的.
5 bean的作用域
五种:singleton,prototype,request,session,global session,其中后三种是针对web的.
如果采用后三种,只需要在web.xml中加入:
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
如果需要将后三种作用域的bean注入到其它bean中,还需要加入
<aop:scoped-proxy/>
之所以加入这条语句的原因,就是将这个bean配成动态代理模式,这样可以保证每次注入进依赖类中的对象不总是那一个,配上这个代理后,被注入类就就智能了了.
6 定制bean特性
6.1 Lifecycle接口
包括两个接口,InitializingBean和DisposableBean,实现这两个接口可的bean在初始化和析构时会调用前者的afterPropertiesSet()方法,以及后者的destroy()方法.
前者称为初始化回调,Spring建议我们不要采用实现接口InitializingBean来实现初始化回调,因为这样会带来与业务Bean的耦合,最好的方法是采用 bean的init-method属性.
后者称为析构回调,同样, Spring建议我们不要采用实现接口DisposableBean来实现析构回调,
因为这样会带来与业务Bean的耦合,最好的方法是采用 bean的destroy-method属性.
default-init-method 、defalult-destroy-method的用法.
7 非web环境优雅的关闭Spring IoC
在JVM里注册一个关闭钩子
AbstractApplicationContext ctx=new ClassPathXmlApplicationContext(new String[](“beans.xml”));
Ctx.registerShutdownHook();
8 BeanFactoryAware接口
被BeanFactory实例管理的bean是可怜的,因为他们默认不能感知BeanFactory实例的存在,但是,有了BeanFactoryAware接口,让这一切都变得可能.
实际上BeanFactoryAware接口里只有一个方法:setBeanFactory(),但就是这个方法,使bean可以获得BeanFactory的实例,进而获取其它的bean实例,当然这会影响耦合性.
现在来用BeanFactoryAware接口,其实还有更简单的方法,就是用org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean,该类实现了BeanFactoryAware接口,在使用时,bean包含一个ObjectFactory(怀疑这是前者的一个窄接口),然后把前者注入到这个接口对象里就可以,在注入的时候还需指定目标bean(就是你想通过beanFactory实例得到的bean),继而在程序中调用getObject()就可以得到你想要的bean.来看程序:
NewsFeed:
package org.test.objectFactoryCreatingFactoryBean;
public class NewsFeed {
private String news;
public String getNews() {
return this.toString()+news;
}
public void setNews(String news) {
this.news = news;
}
}
NewsFeedManager:
package org.test.objectFactoryCreatingFactoryBean;
import org.springframework.beans.factory.ObjectFactory;
public class NewsFeedManager {
private ObjectFactory factory;
public void setFactory(ObjectFactory factory) {
this.factory = factory;
}
public void printNews(){
NewsFeed news=(NewsFeed)factory.getObject();
System.out.println(news.getNews());
}
}
NewsFeedTest:
package org.test.objectFactoryCreatingFactoryBean;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class NewsFeedTest {
public static void main(String[] args) throws Exception{
ApplicationContext ctx=new ClassPathXmlApplicationContext("org//test//objectFactoryCreatingFactoryBean//beans.xml");
BeanFactory factory=(BeanFactory)ctx;
NewsFeedManager manager=(NewsFeedManager)factory.getBean("newsFeedManager");
manager.printNews();
manager.printNews();
}
}
Beans.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="newsFeedManager" class="org.test.objectFactoryCreatingFactoryBean.NewsFeedManager">
<property name="factory">
<bean class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
<property name="targetBeanName">
<idref local="newsFeed"/>
</property>
</bean>
</property>
</bean>
<bean id="newsFeed" class="org.test.objectFactoryCreatingFactoryBean.NewsFeed" singleton="false">
<property name="news">
<value>"that's too fit"</value>
</property>
</bean>
</beans>
运行后得到:
org.test.objectFactoryCreatingFactoryBean.NewsFeed@e5855a"that's too fit"
org.test.objectFactoryCreatingFactoryBean.NewsFeed@95fd19"that's too fit"
为了测试prototype,我又做了另一个测试,在这个例子中,我并不打算通过beanFactoryAware来得到其它的bean,我就想通过注入来得到其它的bean,但我还想每次得到的实例不同,就像上面这个练习的结果一样,于是我这样做:
NewsFeed:
package org.test.prototype;
public class NewsFeed {
private String news;
public String getNews() {
return this.toString()+news;
}
public void setNews(String news) {
this.news = news;
}
}
NewsFeedManager:
package org.test.prototype;
import org.springframework.beans.factory.ObjectFactory;
public class NewsFeedManager {
private NewsFeed newsFeed;
public void printNewsFeed(){
System.out.println(newsFeed.getNews());
}
public void setNewsFeed(NewsFeed news) {
this.newsFeed = news;
}
}
NewsFeedTest:
package org.test.prototype;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class NewsFeedTest {
public static void main(String[] args) throws Exception{
ApplicationContext ctx=new ClassPathXmlApplicationContext("org//test//prototype//beans.xml");
BeanFactory factory=(BeanFactory)ctx;
NewsFeedManager manager=(NewsFeedManager)factory.getBean("newsFeedManager");
manager.printNewsFeed();
manager.printNewsFeed();
}
}
Beans.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="newsFeedManager" class="org.test.prototype.NewsFeedManager">
<property name="newsFeed">
<ref bean="newsFeed"/>
</property>
</bean>
<bean id="newsFeed" class="org.test.prototype.NewsFeed" singleton="false">
<property name="news">
<value>"that's too fit"</value>
</property>
</bean>
</beans>
结果很失望:
org.test.prototype.NewsFeed@60420f"that's too fit"
org.test.prototype.NewsFeed@60420f"that's too fit"
9 bean的继承
目的:一个子bean定义可以从父bean继承构造器参数值、属性值、以及覆盖父bean的方法,并且可以有选择地增加新地值.如果指定了init-method,destroy-method和/或静态factory-method,它们就会覆盖父bean相应的设置.
剩余的设置将总是从子bean定义处得到:依赖、自动装配模式、依赖检查、singleton、作用域和延迟初始化.
10 Spring IoC的扩展点
10.1 BeanPostProcessor bean后置处理器
你想在bean的init()之前或之后再做些事情麽,请实现这个接口.
然后只需要在配置文件里加上<bean class=”xxxx.MyBeanPostProcessor”/>
10.2 BeanFactoryPostProcessor bean工厂后置处理器
与BeanPostProcessor相似,只是它的发生时间是在所有bean被初始化前
两个重要的实现:
PropertyPlaceholderConfigurer
PropertyOverrideConfigurer
看SS里是怎么配的.
<!-- 属性文件读入 bean工厂后置处理器实现类-->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath*:/config/jdbc.properties</value>
<value>classpath*:/config/hibernate.properties</value>
<value>classpath*:/config/mail.properties</value>
</list>
</property>
</bean>
然后就可以放心大胆的用配置文件里的key-value值了.
10.3 factoryBean接口
三个方法:
Object getObject()
Boolean isSingleton();
Class getObjectType()
11 messageSource接口
该接口实现了i18n,Spring提供了两个接口实现
org.springframework.context.support. MessageSourceResourceBundle
org.springframework.context.support. StaticMessageSource
其中前者较常用,它继承自java标准类库的ResourceBundle.
所以只需要配置:
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>org.test.messageSource.format</value>
<value>org.test.messageSource.exception</value>
</list>
</property>
</bean>
注意,id必须是messageSource,这样在容器加载的时候,由于ApplicationContext实现了MessageSource接口,所以该bean被注入到ApplicationContext实例中:
ApplicationContext ctx=new ClassPathXmlApplicationContext("org//test//messageSource//beans.xml");
System.out.println(ctx.getMessage("message", null, Locale.UK));
System.out.println(ctx.getMessage("argument.request", new String[]{"name"}, Locale.UK));
Format_en.properties:
message=hellohello!!!!
Exception_en.properties:
argument.request={0} is request!
输入如下:
hellohello!!!!
name is request!
当然还可以把messageSource这个bean注入到其它bean中.
也就说,因为有了messageSource接口以及MessageSourceResourceBundle接口实现, 利用IoC反向注入的模式,我们可以把i18n分离出业务.
12 用ApplicationContext实现事件机制
Spring中的事件机制是典型的Observer模式的实现.一个事件的发生会触发它所有的监听器.参考文档中的例子少了一个event类,我从网上找了一个例子.
MessageEvent:
package org.test.applicationEvent;
import org.springframework.context.ApplicationEvent;
public class MessageEvent extends ApplicationEvent {
public MessageEvent(Object source) {
super(source);
System.out.println(this.getTimestamp() + ":" + source);
}
}
MessageListener:
package org.test.applicationEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
public class MessageListener implements ApplicationListener {
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof MessageEvent) {
System.out.println("I got the message:" + event.getSource());
}
}
}
SenderBean:
package org.test.applicationEvent;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class SenderBean implements ApplicationContextAware {
private ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
public String sendMessage(String msg) {
MessageEvent event = new MessageEvent(msg);
this.applicationContext.publishEvent(event);
return msg;
}
}
Build.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="sender" class="org.test.applicationEvent.SenderBean" />
<bean id="listener" class="org.test.applicationEvent.MessageListener" />
</beans>
MessageTest:
package org.test.applicationEvent;
import java.util.Locale;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MessageTest {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
ApplicationContext ctx=new ClassPathXmlApplicationContext("org//test//applicationEvent//beans.xml");
SenderBean sender = (SenderBean)ctx.getBean("sender");
String msg = sender.sendMessage("test message");
System.out.println(msg);
}
}
最后的输出为:
1183099914140:test message
I got the message:test message
test message
13 ApplicationContext在web.xml中的配置
利用ContextLoader,可以实现非编程,声明式的初始化ApplicationContext实例.
Spring IoC总结:
作为Spring的双核之一,Spring IoC----反向注入做了一件非常了不起的事情,Spring IoC容器对应用程序中的实例进行统一管理,它负责初始化、配置、销毁甚至建立实例之间的联系,被Spring IoC管理的实例与普通的基本上没有什么区别,被Spring IoC管理的实例(我们还是叫它Bean吧,因为虽然Bean与普通类没太大区别,但是它要满足被注入的条件—要么setter,要么构造器),通常情况下感知不到它们是被管理的,它们只需关心它们应该关心的,当然在必要的时候,它们也可以感知到容器的存在.
Spring IoC最大的特点是低耦合.通过”反向”的注入使我们可以随意拆卸通常的类之间的依赖关系.
利用Spring IoC几乎支持所有的类初始化方式,包括工厂模式,singleton,propotype,通过MessageSource接口以及ResourceBundleMessageSource接口实现你甚至可以把i18n都独立出你的业务.利用LifeCycle的两个接口(虽然Spring不鼓励你利用这两个接口,取而代之的是两个方法)你可以定制Bean的初始化及析构行为.利用bean工厂后置处理器的实现类,你还可以将容器的配置做的更软.最后,Spring IoC容器的接口BeanFactory的扩展ApplicationContext还可以实现基于观察者模式的事件机制.