【spring】BeanFactory源码+实例(下)FactoryBean+依赖注入

本文详细介绍了Spring框架中的FactoryBean接口及其应用案例,并深入探讨了依赖注入(DI)的不同方式,包括构造函数注入和setter方法注入等。此外,还讲解了如何在Spring中配置不同作用域的bean。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自:https://www.cnblogs.com/digdeep/p/4518571.html 

作用域部分来自:https://www.cnblogs.com/qq78292959/p/3716827.html

                             https://blog.youkuaiyun.com/canot/article/details/50697345

 

前言:

    源码+实例:你、值得拥有

 

3. FactoryBean接口

实现了FactoryBean接口的bean是一类叫做factory的bean。其特点是,spring会在使用getBean()调用获得该bean时,会自动调用该bean的getObject()方法,所以返回的不是factory这个bean,而是这个bean.getOjbect()方法的返回值

public interface FactoryBean<T> {
    T getObject() throws Exception;
    Class<?> getObjectType();
    boolean isSingleton();
}

典型的例子有spring与mybatis的结合:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
      <property name="dataSource" ref="dataSource" />
      <property name="configLocation" value="classpath:config/mybatis-config-master.xml" />
      <property name="mapperLocations" value="classpath*:config/mappers/master/**/*.xml" />
    </bean>

我们看上面该bean,因为实现了FactoryBean接口,所以返回的不是 SqlSessionFactoryBean 的实例,而是她的 SqlSessionFactoryBean.getObject() 的返回值:

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {

  private static final Log logger = LogFactory.getLog(SqlSessionFactoryBean.class);

  private Resource configLocation;

  private Resource[] mapperLocations;

  private DataSource dataSource;
  
  public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
      afterPropertiesSet();
    }

    return this.sqlSessionFactory;
  }

其实他是一个专门生产 sqlSessionFactory 的工厂,所以才叫 SqlSessionFactoryBean。 而SqlSessionFactory又是生产SqlSession的工厂。

还有spring与ibatis的结合:

  <!-- Spring提供的iBatis的SqlMap配置 -->
    <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
        <property name="configLocation" value="classpath:sqlmap/sqlmap-config.xml" />
        <property name="dataSource" ref="dataSource" />
    </bean>
SqlMapClientFactoryBean 返回的是 getObject() 中返回的 sqlMapClient, 而不是 SqlMapClientFactoryBean 自己的实例
public class SqlMapClientFactoryBean implements FactoryBean<SqlMapClient>, InitializingBean {
    private Resource[] configLocations;
    private Resource[] mappingLocations;
    private Properties sqlMapClientProperties;
    private DataSource dataSource;
    private boolean useTransactionAwareDataSource = true;
    private Class transactionConfigClass = ExternalTransactionConfig.class;
    private Properties transactionConfigProperties;
    private LobHandler lobHandler;
    private SqlMapClient sqlMapClient;
    public SqlMapClient getObject() {
        return this.sqlMapClient;
    }

 

4. 依赖注入(DI)

1) 依赖注入的方式分为构造函数注入和setter方法注入:

 构造函数注入:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg index="0" value="7500000"/>
//对于非简单参数,需要使用ref
    <constructor-arg index="1" ref="bar"/>
</bean>
<bean id="bar" class="x.y.Bar"/>

setter方法注入:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
      <property name="dataSource" ref="dataSource" />
      <property name="configLocation" value="classpath:config/mybatis-config.xml" />
      <property name="mapperLocations" value="classpath*:config/mappers/**/*.xml" />
    </bean>

2)集合等复杂类型的注入:

<bean id="moreComplexObject" class="example.ComplexObject">
    <!-- results in a setAdminEmails(java.util.Properties) call -->
    <property name="adminEmails">
        <props>
            <prop key="administrator">administrator@example.org</prop>
            <prop key="support">support@example.org</prop>
            <prop key="development">development@example.org</prop>
        </props>
    </property>
    <!-- results in a setSomeList(java.util.List) call -->
    <property name="someList">
        <list>
            <value>a list element followed by a reference</value>
            <ref bean="myDataSource" />
        </list>
    </property>
    <!-- results in a setSomeMap(java.util.Map) call -->
    <property name="someMap">
        <map>
            <entry key="an entry" value="just some string"/>
            <entry key ="a ref" value-ref="myDataSource"/>
        </map>
    </property>
    <!-- results in a setSomeSet(java.util.Set) call -->
    <property name="someSet">
        <set>
            <value>just some string</value>
            <ref bean="myDataSource" />
        </set>
    </property>
</bean>

也很简单,list属性就是 <list>里面包含<value>或者<ref>或者<bean>, set也类似。map是<map>里面包含<entry>这个也好理解,因为map的实现就是使用内部类Entry来存储key和value. Properties是 <props>里面包含<prop>.

5. <bean> 元素可以配置的属性:

<bean> 除了 id 和 class 属性之外,还有一些可选的属性:

1) scope属性

默认<bean> 的 scope就是 singleton="true", springmvc和struts2的重要区别之一就是spring的controll是单例的,而struts2的action是:scope="prototype" ,还有 scope="request" , scope="session",scope="globalSession"(仅用于portlet)

     1.singleton:ioc容器只有一个共享bean实例(存储在单例缓存中),对该bean的后继请求和引用都返回被缓存的对象实例;设计模式里的单例:一个classloader中只有一个class存在,不一样滴;

      让Spring容器释放被singleton作用域bean占用资源的一种可行方式是,通过使用 bean的后置处理器,该处理器持有要被清除的bean的引用

<bean id="role" class="spring.chapter2.maryGame.Role" scope="singleton"/>
或者
<bean id="role" class="spring.chapter2.maryGame.Role" singleton="true"/>

    struts2的Action不是线程安全的,要求在多线程环境下必须是一个线程对应一个独立的实例,不能使用 singleton。

   只要是带数据成员变量的类,为了防止多个线程混用数据,就不能使用singleton。对于我们用到的Service、Dao,之所以用了singleton,就是因为他们没有用到数据成员变量,如果谁的 Service需要数据成员变量,请设置singleton=false。 
    有状态的bean都使用Prototype作用域,而对无状态的bean则应该使用singleton作用域。 

 

     2. prototype:每一次请求(将其注入到另一个bean中或程序调用容器getBean)都产生一个新的bean实例,相当与一个new操作;

       作用域:spring容器在初始化 配置 装饰 或装配完一个prototype实例后,将其交给客户端管理便不再过问;

        不管何种作用域,容器都会调用所有对象的初始化生命周期回调方法,而对prototype而言,任何配置好的析构生命周期回调方法都将不会被调用。 清除prototype作用域的对象并释放任何prototype bean所持有的昂贵资源,都是客户端代码的职责。

 

      3.  request针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效,配置实例:

     request、session、global session使用的时候首先要在初始化web的web.xml中做如下配置:

如果你使用的是Servlet 2.4及以上的web容器,那么你仅需要在web应用的XML声明文件web.xml中增加下述ContextListener即可: 

<web-app>
   ...
  <listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
  </listener>
   ...
</web-app>

如果是Servlet2.4以前的web容器,那么你要使用一个javax.servlet.Filter的实现: 

<web-app>
 ..
 <filter> 
    <filter-name>requestContextFilter</filter-name> 
    <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
 </filter> 
 <filter-mapping> 
    <filter-name>requestContextFilter</filter-name> 
    <url-pattern>/*</url-pattern>
 </filter-mapping>
   ...
</web-app>

接着既可以配置bean的作用域了:

<bean id="role" class="spring.chapter2.maryGame.Role" scope="request"/>

 

4、session每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效,配置实例:

和request配置实例的前提一样,配置好web启动文件就可以如下配置: 

<bean id="role" class="spring.chapter2.maryGame.Role" scope="session"/>

 

5、global session

      类似于标准的HTTP Session作用域,仅在基于portlet的web应用中才有意义。Portlet规范定义了全局Session的概念,它被所有构成某个 portlet web应用的各种不同的portlet所共享。在global session作用域中定义的bean被限定于全局portlet Session的生命周期范围内。如果你在web中使用global session作用域来标识bean,那么web会自动当成session类型来使用。

配置实例:

和request配置实例的前提一样,配置好web启动文件就可以如下配置: 

<bean id="role" class="spring.chapter2.maryGame.Role" scope="global session"/>

 

6、自定义:在spring2.0中作用域是可以任意扩展的,你可以自定义作用域,甚至你也可以重新定义已有的作用域(但是你不能覆盖singleton和prototype),spring的作用域由接口org.springframework.beans.factory.con**.Scope来定义,自定义自己的作用域只要实现该接口即可

 

 

2)abstract属性,是否是抽象的bean:

<bean id="baseDAO" abstract="true">
        <property name="dataSource" ref="dataSource" />
        <property name="sqlMapClient" ref="sqlMapClient" />
    </bean>    
    <bean id="collectionDAO" class="net.minisns.dal.dao.CollectionDAOImpl" parent="baseDAO" />
    <bean id="commentDAO" class="net.minisns.dal.dao.CommentDAOImpl" parent="baseDAO" />

3)depends-on 依赖于某个bean,其必须先初始化:<bean id="xxx" class="xxx" depends-on="refbean" />

4)lazy-init="true" 是否延迟初始化,默认为 false

5) dependency-check 是否对bean依赖的其它bean进行检查,默认值为 none,可取值有:none, simple, object, all等

 

6)factory-method 和 factory-bean用于静态工厂和非静态工厂:

<bean id="bar" class="...StaticBarInterfaceFactory" factory-method="getInstance"/>
<bean id="barFactory" class="...NonStaticBarInterfaceFactory"/> 
<bean id="bar" factory-bean="barFactory" factory-method="getInstance"/>

7)init-method, destory-method 指定bean初始化和死亡时调用的方法,常用于 dataSource的连接池的配置

8) lookup-method 方法注入:

<bean id="newsBean" class="..xxx" singleton="false"> 
<bean id="mockPersister" class="..impl.MockNewsPersister">
  <lookup-method name="getNewsBean" bean="newsBean"/> 
</bean>  

表示 mockPersister 有一个依赖属性 newsBean,该属性的每次注入都是通过调用newsBean.getNewsBean() 方法获得的。

9) autowire 是否启用自动装配依赖,默认为 no, 其它取值还有:byName, byType, constructor

 

转自:https://www.cnblogs.com/digdeep/p/4518571.html 

作用域部分来自:https://www.cnblogs.com/qq78292959/p/3716827.html

                             https://blog.youkuaiyun.com/canot/article/details/50697345

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值