1.What is IoC
IoC设计模式-重点关注的是组件之间的依赖性、配置以及生命周期
以前我们在做代码使用实例之前必须创建对象的实例,但是IoC将创建对象的实例的任务由IoC容器或框架来完成,应用代码能直接使用对象.这就是IoC
2.使用IoC的好处
组件(类,代码)不需要在运行的时候寻找依赖对象,借助与设值方法能够在允许是将组件以来的其他组件注入进来,这就是依赖注入(Dependency
Injection)
借助IoC容器的组件依赖关系,测试将更加便利。在AppFuse在开发过程中使用Drive Test能很好的体现出来。
体现了Spring的非入侵性,即很少需要使用具体IoC容器提供的API
AppFuse中使用IoC的地方
BeanFactory提供了管理和操作JavaBean的基本功能,Bean的生命周期可分为四个阶段
1. Instantiation JavaBean
2. Initialize JavaBean,通过IoC注入其依赖性
3. Using JavaBean
4. Destroy Bean
ApplicationContext
ApplicationContext继承了BeanFactory, 含有BeanFactory的全部功能,通过ContextLoaderServlet或ContextLoaderListener能够自动创建ApplicationContext实例。应此AppFuse也是使用ApplicationContext的。
对于Spring ApplicationContext而言ContextLoaderListener和ContextLoaderServlet初始化完成后,所有的Bean实例将会被创建。除非你改掉他的默认设置
ApplicationContext接口实现经常用到的是ClassPathXmlApplicationContext(装载配置文件,单元测试),FileSystemXmlApplicationContext(从文件系统钟装载配置文件,单元测试),
XmlWebApplicationContext(供ContextLoaderListener或ContextLoaderServlet内部装载Spring配置文件使用)
AppFuse中的ApplicationContext
AppFuse中的ApplicationContext加载是通过ContextLoaderListener配置实现的,但是AppFuse不是直接在web.xml配置ContextLoaderListener监听器来实现ApplicationContext的初始化的,而是通过自定义的StartupListener监听器实现配置的。
ApplicationContext在Web.xml中的配置
<!—
Context Configuration locations for Spring XML files
-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:META-INF/applicationContext-*.xml
/WEB-INF/applicationContext-*.xml
</param-value>
</context-param>
所有与applicationContext-*.xml的(*)匹配的都是Spring的配置文件,比如appicationContext-service.xml , applicationContext-hibernate.xml
applicationContext-security.xml
ApplicationContext的初始化
StartupListener 监听器继承了ContextLoaderListener监听器 和ServletContextListener监听器。
Ø Web容器启动时,执行StartupListener监听器
Ø StartupListener监听器执行父类ServletContextListener的contextInitialized事件,在该初始化事件中,会调用Spring的contextInitialized事件初始化Spring的ApplicationContext。
public class StartupListener extends ContextLoaderListener implements ServletContextListener {
private static final Log log = LogFactory.getLog(StartupListener.class);
public void contextInitialized(ServletContextEvent event) {
if (log.isDebugEnabled()) {
log.debug("initializing context...");
}
// call Spring's context ContextLoaderListener to initialize
// all the context files specified in web.xml
super.contextInitialized(event);
//容器启动时,自动调用contextInitialized函数。
//取得全局Application Scope 上下文环境
ServletContext context = event.getServletContext();
String daoType = context.getInitParameter(Constants.DAO_TYPE);
// if daoType is not specified, use DAO as default
if (daoType == null) {
log.warn("No 'daoType' context carameter, using hibernate");
daoType = Constants.DAO_TYPE_HIBERNATE;
}
// Orion starts Servlets before Listeners, so check if the config
// object already exists
Map config = (HashMap) context.getAttribute(Constants.CONFIG);
if (config == null) {
config = new HashMap();
}
// Create a config object to hold all the app config values
//将DAO_TYPE_HIBERNATE = "hibernate"写入ServletContext
config.put(Constants.DAO_TYPE, daoType);
context.setAttribute(Constants.CONFIG, config);
// output the retrieved values for the Init and Context Parameters
if (log.isDebugEnabled()) {
log.debug("daoType: " + daoType);
log.debug("populating drop-downs...");
}
//调用setupContext(context)函数初始化Spring
setupContext(context);
}
/**
* 初始化Spring环境上下文,为getBean作准备,并且将ServletContext交给Spring管理
*/
public static void setupContext(ServletContext context) {
ApplicationContext ctx =
WebApplicationContextUtils.getRequiredWebApplicationContext(context);
//通过Ico反射注入,查找lookupManager和Dao属性
LookupManager mgr = (LookupManager) ctx.getBean("lookupManager"); //(Note 1:)
// get list of possible roles
//取得角色信息,存入ServletContext scope中。
context.setAttribute(Constants.AVAILABLE_ROLES, mgr.getAllRoles());
if (log.isDebugEnabled()) {
log.debug("drop-down initialization complete [OK]");
}
}
}
Note 1
从这句代码 LookupManager mgr = (LookupManager) ctx.getBean("lookupManager"); 体验一下AppFuse中用到Spring的面向接口编程,这个是Spring的一个特点-自然的面向接口编程。
我们知道AppFuse中代码职责分明,(dao ,service ,web)
LookupManagerImpl 是 Service 层的。用来管理POJO ,真正的数据操作委派给它引用的lookupDAO
的具体实现类 LookupDAOHibernate
AppFuse的Spring配置文件 applicationContext-service.xml
<bean id="lookupManager" class="net.smarthings.service.impl.LookupManagerImpl">
<property name="lookupDAO" ref="lookupDAO"/>
</bean>
<property name="lookupDAO" ref="lookupDAO"/>
先看前半句<property name="lookupDAO" 说明 Class LookupManagerImpl 中有一个给lookupDAO设置属性的方法setLookupDAO 注意改方法前面的set 如果是setSonic(Sonic son)的话,就应改这样设置<property name="sonic"
在Class LookupManagerImpl 中有这么一个方法
public void setLookupDAO(LookupDAO dao) {
//Spring动态注入了dao引用,从配置文件中得到了具体的lookupDAO接口对象
this.dao = dao;
}
再看看后半句。ref="lookupDAO" 引用了LookupDAOHibernate的对象 <bean id="lookupDAO" 也可以这么说:通过IOC动态加载了对象属性LookupDAOHibernate
AppFuse的Spring配置文件 applicationContext-hibernate.xml
<!-- LookupDAO: Hibernate implementation -->
<bean id="lookupDAO" class="net.smarthings.dao.hibernate.LookupDAOHibernate" >
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
在Class LookupManagerImpl 中有这么一个方法,用于得到角色的List
public List getAllRoles() {
// LookupManagerImp 将真正的数据处理委派给 LookupDAOHibernate
List roles = dao.getRoles();
List list = new ArrayList();
…….
…..
}