天天看点

浅析Spring IOC(控制反转)

一、BeanFactory轻量级

三种访问ApplicationContext.xml方式:

1、Resource resource = new ClassPathResource(“application.xml”);

BeanFactory factory = new XmlBeanFactory(resource);

HelloWorld hello = factory.getBean(“fileHelloWorld”);

2、基于文件找到application.xml

InputStream is = new FileInputStream(“src/application.xml”);

BeanFactory factory = new XmlBeanFactory(is);

3、基于ApplicationContext

ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(new String[]{“application.xml”});

BeanFactory factory = (BeanFactory)appContext;

Bean的生命周期

1、实例化JavaBean

2、JavaBean实例初始化,即通过IOC注入其依赖性。这一阶段将完成JavaBean实例初始化

3、基于Spring应用对JavaBean实例的使用

4、IOC容器销毁JavaBean实例

Bean的创建

<bean name="fileHelloWorld" class="com.jader.FileHelloWorld">
           

HelloWorld实例将使用无参数构建器创建出来

<bean name="fileHelloWorld" class="com.jader.FileHelloWorld" factory-method="createHelloWorldInstance">
           

HelloWorld必须提供createHelloWorldInstance的静态方法

<bean name="helloWorldFactory" class="com.jader.FileHelloWorldFactory" />
<bean name="fileHelloWorld" class="com.jader.FileHelloWorld" factory-bean="helloWorldFactory' factory-method="createFileHelloWorldInstance">

           

此种情况类不需要提供静态方法,命名为fileHelloWorld的JavaBean实例将通过helloWorldFactory工厂类的createFileHelloWorldInstance方法获得

<bean name="fileHelloWorld" class="com.jader.FileHelloWorld">
    <constructor-arg>
        <ref bean="fileHello"/>
    </constructor>
</bean>
           

通过ref元素引用Spring配置文件中的fileHello,ref有以下几个属性:

1、bean:在当前Spring XML配置文件中,或者在同一个BeanFactory(applicationContext)中的其他JavaBean中

2、local:在当前Spring XML配置文件中,其依赖的JavaBean必须存在于当前Spring XML配置文件中,

3、parent:用于指定其依赖的父JavaBean定义

初始化JavaBean

1、如果开发者使用了元素的autowire属性,Spring能够自动将目标JavaBean需要注入的JavaBean找到并注入进来。

2、如果开发者指定了元素的dependency-check属性,则能够保证各个Spring配置文件中各个JavaBean之间的相互关系。

3、借助于setter方法能够将JavaBean的属性值注入进来,这些属性可以是Java原型(primitive)、对象类型,甚至可以是null

例如:

<bean id="helloWorld" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <property name="transactionManager">
        <ref local="transactionManager"/>
    </property>
    <property name="transactionAttributers">
        <props>
            <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
            <prop key="set*">PROPAGATION_REQUIRED</prop>
        </props>
    </property>
</bean>
           

Spring框架能够注入TransactionProxyFactoryBean中的transactionManager、target、transactionAttributes所需的属性值。比如:transactionManager属性取值通过ref引用了transactionManager的POJO服务。如果Spring配置文件中不存在事务管理器,开发者有两种做法:

单独定义新的transactionManager,例如:

<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
    <property name="userTransactionName">
        <value>java:comp/UserTransaction</value>
    </property>
</bean>
           

ref会引用transactionManager

定义内部JavaBean方法:

<propery name="transactionManager">
    <bean class="org.springframework.transaction.jat.JtaTransactionManager">
        <property name="userTransactionName">
            <value>java:comp/UserTransaction</value>
        </property>
    </bean>
</property>
           

通过定义内部JavaBean,其他JavaBean便不能够引用到他,而且开发者再也不能够重用JtaTransactionManager。现实场合存在这种需求,存在即是合理。

4、如果JavaBean实现了如下接口,还需要调用setBeanFactory方法

org.springframework.beans.factory.BeanFactoryAware

其定义了方法如下:

void setBeanFactory(BeanFactory beanFactory) throws BeansException;

5、Spring框架提供了若干接口,供开发者改变配置再BeanFactory中JavaBean的行为适用,其中InitializingBean接口介绍如下:

org.springframework.beans.factory.InitializingBean在BeanFactory初始化JavaBean时,BeanFactory会调用那些实现了InitializingBean接口的JavaBean中包含的方法:

void afterPropertiesSet() throws Exception;// InitializingBean定义的方法

6、最后通过在元素中包含init-method属性能够达到同InitializingBean一样的目的,即:

<bean name="fileHelloWorld" class="com.openv.spring.HelloWorld" init-method="init">
           

HelloWorld将实现init方法

通过以上6步,完成JavaBean实例的初始化工作

使用JavaBean

借助于getBean方法,开发者就能够在应用中使用他了

销毁JavaBean

一旦将基于Spring的应用停止,Spring框架将调用那些JavaBean实例中存在的生命周期方法,比如:DisposableBean接口的JavaBean或者那些在Spring配置文件中指定了destory-method属性的JavaBean。

最终Spring将销毁JavaBean实例。

注意:这些内容只适合于那些通过singleton方法创建的JavaBean实例,对于那些以prototype方式创建的JavaBean实例,Spring并不能够控制器生命周期,因为这种JavaBean实例创建成功,整个JavaBean将交付给Spring应用去管理。

其中,DisposableBean介绍如下:

org.springframework.beans.factory.DisposableBean:在BeanFactory销毁JavaBean时,BeanFactory会调用那些实现了DisposableBean接口的JavaBean中包含的如下方法:

void destory() throws Exception;

其中,destroy是DisposableBean定义的方法。

当然,通过在元素中包含destroy-method属性能够达到同DisposableBean一样的目的,即:

如果应用同事实现了两种方法,Spring先执行DisposableBean中的的destroy方法。

抽象Bean和子Bean定义

将公共内容放置在抽象bean中,子Bean能够重用抽象Bean中定义的内容,而且还能够“重载”他们。

PropertyPlaceholderConfigurer和PropertyOverrideConfigurer

对于PropertyPlaceholderConfigurer而言,他能够在Spring配置文件外部配置其他应用使用到的属性,比如:通过Java属性文件配置数据库连接信息、LDAP(目录访问协议)、活动目录连接信息。

例如:Apache DBCP数据源实例:

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName"
        <value>${jdbc.driverClassName}</value>
    </property>
    <property name="url">
        <value>${jdbc.url}</value>
    </property>
    <property name="username">
        <value>${jdbc.username}</value>
    </property>
    <property name="password">
        <value>${jdbc.password}</value>
    </property>
</bean>
其中$里面的驱动名、url、用户名、密码的实际值通过Java属性文件获得。
<bean id="propertyConfigurer" class="org.springframework.bean.factory.config.ProperryPlaceholderConfigurer">
    <property name="location">
        <value>jdbc.properties</value>
    </property>
</bean>
           

BeactoryAware与BeanNameAware

对于某些基于Spring的应用而言,有一些将应用的BeanFactory实例注入到JavaBean实例中。比如:为在某JavaBean实例中动态获得BeanFactory创建的某单例JavaBean,但是该单例JavaBean并没有显式的使用它,此时借助于BeanFactoryAware能够满足开发者要求,例如:

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName">
        <value>java:/MysqlDS</value>
    </property>
</bean>
<bean id="exampleServiceTarget" class="com.openv.spring.service.impl.ExampleManagerImpl">
    <property name="userInfo">
        <ref local="userInfoDao" />
    </property>
</bean>
           

如果在exampleServiceTarget中需要使用dataSource,但是在Spring配置文件中并没有显式的配置对dataSource的引用。此时,需要借助于BeanFactoryAware,即ExampleManagerImpl需要实现BeanFactoryAware接口。

如果需要在JavaBean实例中获悉其配置的名字,可以考虑实现BeanNameAware接口:

org.springframework.beans.factory.BeanNameAware

其定义了如下方法:

void setBeanName(String name);

其执行时间在调用InitializingBean afterPropertiesSet或者自定义init-method之前,而在JavaBean属性正常赋值之后。

二、ApplicationContext

Spring框架引入了ApplicationContext接口,开发者不需要手工创建ApplicationContext实例,便可以声明的方式使用它。例如:

org.springframework.web.context.ContextLoaderServlet或者

org.springframework.web.context.ContextLoaderListener

能够在web应用启动的时候自动实例化ApplicationContext对象,对于SpringBeanFactory而言,如果用户没有调用getBean()方法,则使用到的JavaBean实例将会被创建。因此在BeanFactory中使用了延迟装载的机制,这主要是同BeanFactory的应用场合有关。对于Spring ApplicationContext而言,一旦ContextLoaderServlet或ContextLoaderListener初始化成功所有的JavaBean实例将会被创建(除非开发者改变了ApplicationContext的默认行为,比如显式的设置延迟装载行为)

开发者应该注意到Log4jConfigServlet,供配置Spring应用的日志使用,使用方法同ContextLoaderServlet,如下:

<context-param>
    <param-name>log4jConfigLocation</param-name>
    <param-value>/WEB-INF/classes/log4j.properties</param-value>
</context-param>
<servlet>
    <serlvet-name>log4jConfigServlet</servlet-name>
    <serlvet-class>
        org.springframework.web.util.Log4jConfigServlet
    </servlet-class>
    <load-on-startup></load-on-startup>
</servlet>
           

(>0表示容器在应用启动时就加载并初始化这个servlet,

当值小于0或者没有指定时,则表示容器在该servlet被选择时才会去加载,

正数的值越小,该servlet的优先级越高,应用启动时就越先加载)

浅析Spring IOC(控制反转)

开发者应该注意到Log4jConfigListener类供配置Spring应用的日志使用,由于ApplicationContext含有BeanFactory的所有功能,因此对于开发J2EE应用场合推荐使用ApplicationContext。

1、Web应用中创建ApplicationContext

Web应用自动过程中将自动初始化监听器,而ContextLoaderListener就是监听器,例如:

<context-param>
    <param-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
<listener>
    <listener-class>
        org.springframework.web.context.ContextLoaderListener
    </listener-class>
</listener>
           

2、ApplicationContextAware

在为某JavaBean实例动态获得ApplicationContext创建的某单例JavaBean,但是该单例JavaBean并没有显式的使用(在Spring配置文件没有显式的给出对单例JavaBean的引用,例如未使用元素),借助于ApplicationContextAware能够满足开发者的要求

介绍如下:

org.springframework.context.ApplicationContextAware

其定义了如下方法:

void setApplicationContext(ApplicationContext applicationContext);

现有如下示例代码:

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean" >
    <property name="jndiName">
        <value> java:/MySqlDS</value>
    </property>
</bean>
<bean id="exampleServiceTarget" class="com.openv.spring.service.impl.ExampleManagerImpl">
    <property name="userInfo">
        <ref local="userInfoDao" />
    </property> 
</bean>
           

如果在exampleServiceTarget需要使用到dataSource,但是Spring配置文件中没有显式的配置对dataSource的引用,此时可以借助ApplicationContextAware接口。

示例代码:

public class ExampleManagerImpl implements ApplicationContextAware{
    private ApplciationContext applicationContext;
    public void setApplicationContext(ApplicationContext applicationContext){
        this.applicationContext = applicationContext;
    }

    pubic DataSource getDataSource(){
        DataSource ds = null;
        ds = (DataSource)applicationContext.getBean("dataSource");
        retrun ds;
    }
}
           

这同BeanFactoryAware类似。

3、ApplicationContext接口实现

ClassPathXmlApplicationContext:在Web应用中,开发者可以从其classpath中,即WEB-INF/classes或WEB-INF/lib的jar中装载Spring配置文件

FileSystemXMLApplicationContext:开发者可以从文件系统中装载Spring配置文件

XMLWebApplicationContext:供ContextLoaderListener或ContextLoaderServlet内部装载Spring配置文件使用。