spring基础——DI(四)

  接上一篇《spring基础——DI(三)》

  3.5 引用其他bean

    上一篇已经介绍了注入常量、集合等基本数据类型和集合数据类型,承接上文继续介绍注入依赖bean及注入内部bean。引用其他bean的步骤与注入常量的步骤一样,可以通过构造器注入及setter注入引用其他bean。

  • 构造器方式注入:通过constructor-arg标签ref属性来引用其他bean,这是最简化的配置:或者通过constructor-arg标签的子ref标签来引用其他bean。

     

           

  • setter方式注入:通过property标签的ref属性来引用其他bean,或者通过property标签的子ref标签来引用其他bean。

    

    

 1 <!-- 定义依赖Bean -->
 2 <bean id="helloApi" class="cn.javass.spring.chapter2.helloworld.HelloImpl"/>
 3 <!-- 通过构造器注入 -->
 4 <bean id="bean1" class="cn.javass.spring.chapter3.bean.HelloApiDecorator">
 5   <constructor-arg index="0" ref="helloApi"/>
 6 </bean>
 7 <!-- 通过构造器注入 -->
 8 <bean id="bean2" class="cn.javass.spring.chapter3.bean.HelloApiDecorator">
 9    <property name="helloApi"><ref bean=" helloApi"/></property>
10 </bean>
 1 package cn.javass.spring.chapter3.bean;
 2 import cn.javass.spring.chapter2.helloworld.HelloApi;
 3 public class HelloApiDecorator implements HelloApi {
 4 private HelloApi helloApi;
 5   //空参构造器
 6    public HelloApiDecorator() {
 7   }
 8   //有参构造器
 9    public HelloApiDecorator(HelloApi helloApi) {
10    this.helloApi = helloApi;
11 }
12 public void setHelloApi(HelloApi helloApi) {
13    this.helloApi = helloApi;
14    }
15    @Override
16    public void sayHello() {
17    System.out.println("==========装饰一下===========");
18    helloApi.sayHello();
19    System.out.println("==========装饰一下===========");
20    }
21 }
 1 @Test
 2 public void testBeanInject() {
 3    BeanFactory beanFactory = new ClassPathXmlApplicationContext("chapter3/beanInject.xml");
 4    //通过构造器方式注入
 5    HelloApi bean1 = beanFactory.getBean("bean1", HelloApi.class);
 6    bean1.sayHello();
 7    //通过setter方式注入
 8    HelloApi bean2 = beanFactory.getBean("bean2", HelloApi.class);
 9    bean2.sayHello();
10 }
  • 其他引用方式:除了基本配置除外,spring还提供了两种更高级的配置方式:<ref local="">、<ref parent="">。<ref local="">用于引用通过id属性指定的bean,它能利用xml解析器的验证功能在读取配置文件时来验证引用的bean是否存在。

          <ref parent="">用于引用父容器的bean,不引用当前容器的bean。

                                          

   注:关于父子容器相关概念参照《Spring-SpringMVC父子容器&AOP使用总结(转)》。

  3.6 内部bean定义

    内部bean就是在property或constructor-arg内部通过bean标签定义bean。该bean不管是否指定id或name,该bean都会有唯一的匿名标识符,而且不能指定别名,该内部bean对其他bean都不可见。

    

1 <bean id="bean" class="cn.javass.spring.chapter3.bean.HelloApiDecorator">
2   <property name="helloApi">
3     <bean id="helloApi" class="cn.javass.spring.chapter2.helloworld.HelloImpl"/>
4   </property>
5 </bean>
1 @Test
2 public void testInnerBeanInject() {
3   ApplicationContext context = new ClassPathXmlApplicationContext("chapter3/innerBeanInject.xml");
4   HelloApi bean = context.getBean("bean", HelloApi.class);
5   bean.sayHello();
6 }

  3.7 注入null

    spring通过value标签或value属性注入常量值,所有注入的数据都是字符串,那如何注入null值?spring提供了null标签。

1 <bean class="...HelloImpl4">
2   <property name="message"><null/></property>
3   <property name="index" value="1"/>
4 </bean>

  3.8 简写配置

    接下来总结以下依赖注入配置及简写形式:

  • 构造器注入:

    1)常量值:

1 <!--简写-->
2 <constructor-arg index="0" value="常量"/>
3 <!--全写-->
4 <constructor-arg index="0"><value>常量</value></constructor-arg>

    2)引用:

1 <!--简写-->
2 <constructor-arg index="0" ref="引用"/>
3 <!--全写-->
4 <constructor-arg index="0"><ref bean="引用"/></constructor-arg>
  • setter注入:

    1)常量值:

1 <!--简写-->
2 <property name="message" value="常量"/>
3  <!--全写-->
4 <property name="message"><value>常量</value></ property>

    2)引用:

1 <!--简写-->
2 <property name="message" ref="引用"/>
3  <!--全写-->
4 <property name="message"><ref bean="引用"/></ property>

    3)数组:<array>没有简写形式

    4)列表:<list>没有简写形式

      5)集合:<set>没有简写形式

   6)字典:

 1 <!--简写-->
 2  <map>
 3    <entry key="键常量" value="值常量"/>
 4    <entry key-ref="键引用" value-ref="值引用"/>
 5  </map>
 6 <!--全写-->
 7 <map>
 8   <entry><key><value>键常量</value></key><value>值常量</value></entry>
 9   <entry><key><ref bean="键引用"/></key><ref bean="值引用"/></entry>
10 </map>

    7)Properties:没有简写形式

  • 使用p命名空间简化setter注入:  

    使用p命名空间来简化setter配置如下

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4  xmlns:p="http://www.springframework.org/schema/p"
 5  xsi:schemaLocation="
 6  http://www.springframework.org/schema/beans
 7  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
 8   <bean id="bean1" class="java.lang.String">
 9      <constructor-arg index="0" value="test"/>
10   </bean>
11   <bean id="idrefBean1" class="cn.javass.spring.chapter3.bean.IdRefTestBean" p:id="value"/>
12    <bean id="idrefBean2" class="cn.javass.spring.chapter3.bean.IdRefTestBean" p:id-ref="bean1"/>
13 </beans>

  。xmlns:p="http://www.springframework.org/schema/p": 首先指定命名空间;

  。<bean id="" class="" p:id=""/>:常量setter注入方式,等价于<property name="" value=""/>。

     。<bean id="" class="" p:id-ref=""/>:引用setter注入方式,等价于<property name="" ref=""/>。

  

  3.9 循环依赖

    循环依赖就是循环引用,就是两个或多个bean之间相互持有对方,比如CircleA引用CircleB,CircleB引用CircleC,CircleC又引用CircleA,则他们始终反应为一个环。

    构造器循环依赖:表示通过构造器注入构成的循环依赖,此依赖是无法解决的,只能抛出BeanCurrentlyInCreateException。

1 <bean id="circleA" class="cn.javass.spring.chapter3.bean.CircleA">
2   <constructor-arg index="0" ref="circleB"/>
3 </bean>
4 <bean id="circleB" class="cn.javass.spring.chapter3.bean.CircleB">
5   <constructor-arg index="0" ref="circleC"/>
6 </bean>
7 <bean id="circleC" class="cn.javass.spring.chapter3.bean.CircleC">
8   <constructor-arg index="0" ref="circleA"/>
9 </bean>
 1 @Test(expected = BeanCurrentlyInCreationException.class)
 2 public void testCircleByConstructor() throws Throwable {
 3 try {
 4  new ClassPathXmlApplicationContext("chapter3/circleInjectByConstructor.xml");
 5  }
 6  catch (Exception e) {
 7  //因为要在创建circle3时抛出;
 8  Throwable e1 = e.getCause().getCause().getCause();
 9  throw e1;
10  }
11 }

    setter循环依赖:表示通过setter注入方式构成的循环依赖,对于setter注入构成的循环依赖是通过spring容器提供的通过提前暴露刚完成构造器注入但还未完成其他步骤(如setter注入)的bean来完成的,而且只能解决单例作用域的bean的循环依赖。

 1 <!-- 定义 Bean 配置文件,注意 scope 都是“prototype”-->
 2 <bean id="circleA" class="cn.javass.spring.chapter3.bean.CircleA" scope="prototype">
 3    <property name="circleB" ref="circleB"/>
 4  </bean>
 5  <bean id="circleB" class="cn.javass.spring.chapter3.bean.CircleB" scope="prototype">
 6    <property name="circleC" ref="circleC"/>
 7  </bean>
 8  <bean id="circleC" class="cn.javass.spring.chapter3.bean.CircleC" scope="prototype">
 9    <property name="circleA" ref="circleA"/>
10  </bean>
 1 //测试代码cn.javass.spring.chapter3.CircleTest
 2 @Test(expected = BeanCurrentlyInCreationException.class)
 3 public void testCircleBySetterAndPrototype () throws Throwable {
 4  try {
 5  ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(
 6 "chapter3/circleInjectBySetterAndPrototype.xml");
 7  System.out.println(ctx.getBean("circleA"));
 8  }
 9  catch (Exception e) {
10  Throwable e1 = e.getCause().getCause().getCause();
11  throw e1;
12  }
13 }

  对于prototype作用域bean,spring容器无法完成依赖注入,因为prototype作用域的bean,spring容器不进行缓存,因此无法提前暴露一个创建中的bean。对于singleton作用域的bean可以禁用循环引用 。

  

原文地址:https://www.cnblogs.com/ouhouki/p/9693341.html