Spring04:注入(Injection)

1.注入

1.什么是注入:

通过Spring工厂及配置文件,为所创建对象的成员变量赋值。

2.为什么需要注入

以前我们为对象的成员变量赋值,直接通过编码的方式,这样是存在耦合的
在这里插入图片描述
如果我们以后想为name属性,设置别的值。那么又要改代码,存在耦合性,所以我们将对象属性的设置放到配置文件中(属性注入)。

3.如何进行注入(开发步骤)

  • 类的成员变量提供set get方法
  • 配置spring的配置文件
     <bean id="person" name="p1,p2" class="com.baizhiedu.basic.model.Person">
         <property name="id">
             <value>10</value>
         </property>
         <property name="name">
             <value>傻子</value>
         </property>
     </bean>
    

4.注入的好处:

解耦合:日后属性需要改动的话,就不需要在代码层面修改了,到配置文件中修改。

2.注入原理分析(简易版)

1.类似前面的工厂原理分析:会根据配置文件,进行反射创建对象(最终调用构造方法)。注入会根据配置文件内容的property标签,调用set方法对属性赋值。

  • Spring通过底层调用对象属性的set方法,完成成员变量的赋值,这种方法我们也称之为set注入
  • 会根据配置文件内容的property标签,调用set方法对属性赋值。

在这里插入图片描述

3.Set注入详解

1.针对不同类型的成员变量,我们在注入时,在<property>标签中,需要嵌套不同的子标签。

2.可能遇到的各种Set注入的类型主要分为两大类

  • JDK内置类型
  • 用户自定义类型
    在这里插入图片描述

3.1.Set注入JDK内置类型

在这里插入图片描述

1.8种基本类型+String

基本类型:字符类型char,布尔类型boolean以及数值类型byte、short、int、long、float、double。

property标签直接嵌套value标签

<bean id="person" name="p1,p2" class="com.baizhiedu.basic.model.Person">
	<property name="id">
	    <value>10</value>
	</property>
</bean>	

2.数组类型:

property标签嵌套list标签,list标签再嵌套value标签

//Person类数组属性
 private String[] email;

 public String[] getEmail() {
    
    
     return email;
 }

 public void setEmail(String[] email) {
    
    
     this.email = email;
 }

//applicationContext.xml
<bean id="person" name="p1,p2" class="com.baizhiedu.basic.model.Person"> 
 <property name="email">
     <list>
         <value>sdaS@1.com</value>
         <value>sdaS@2.com</value>
         <value>sdaS@3.com</value>
     </list>
 </property>
</bean>

3.Set集合属性:

  • 1.注意Set集合是无序的
//Person类Set属性
 private Set<String> tels;

 public Set<String> getTels() {
    
    
     return tels;
 }

 public void setTels(Set<String> tels) {
    
    
     this.tels = tels;
 }

//applicationContext.xml
<bean id="person" name="p1,p2" class="com.baizhiedu.basic.model.Person"> 
   <property name="tels">
       <set>
           <value>12312311313</value>
           <value>12666611313</value>
           <value>12789011313</value>
       </set>
   </property>
</bean>
  • 2.注意:set标签里面不一定嵌套value,要根据Set<>泛型来决定。如果没有指定泛型,说明可以存任何对象,那么set标签可以同时嵌套value(8种基本类型+String),list(数组,list),ref标签(自定义对象)。
//Person类Set属性
 private Set obj;

 get/set

//applicationContext.xml
<bean id="person" name="p1,p2" class="com.baizhiedu.basic.model.Person"> 
   <property name="tels">
       <set>
           <value>12312311313</value>
           <ref bean="idz值"/>
           <list>
           	<value>12313</value>
           </list>
       </set>
   </property>
</bean>

4.List集合属性

  • property标签嵌套list标签(同数组,因为list底层用的就是数组),list中嵌套的标签要根据List< E >泛型来决定。
  • 注意:list元素是允许重复的,而且是有序的,输出顺序同赋值时的顺序。
<bean id="person" name="p1,p2" class="com.baizhiedu.basic.model.Person"> 
   <property name="address">
		<list>
		   <value>11111</value>
		   <value>2222</value>
		   <value>2222</value>
		</list>
   </property>
</bean>

5.Map集合属性

  • Map的特点:键值对,每对键值对又是一个Map.Entry对象。
  • key有特定的子标签,子标签根据对应的泛型类型选择;值没有子标签,且要根据对应的泛型类型选择。
  • 注意:为什么在key标签和entry标签中嵌套了value标签,因为Map< String, String>属性的泛型是String, String。
  • key标签的value标签:表示键的value;entry标签的value标签:表示值的value
<property name="qqs">
    <map>
        <entry>
            <key><value>txl</value></key>
            <value>1309984539</value>
        </entry>
        <entry>
            <key><value>others</value></key>
            <value>9999999</value>
        </entry>
    </map>
</property>

6.Properties集合属性

后面进行Spring与MyBatis的整合,需要大量的使用这个类型。

  • Properties类型是一种特殊的Map:key和value都只能是String。
  private Properties properties;

  public Properties getProperties() {
    
    
      return properties;
  }

  public void setProperties(Properties properties) {
    
    
      this.properties = properties;
  }
  
  <property name="properties">
      <props>
          <prop key="sss">wwww</prop>
          <prop key="eee">tttt</prop>
      </props>
  </property>

7.复杂的JDK类型(Date)

需要自定义类型转换器来处理。

3.2.Set注入用户自定义类型

在这里插入图片描述
1.第一种方式:

  • 为成员变量提供set get方法

    public class UserServiceImpl implements UserService {
          
          
    
          private UserDAO userDao;
      
          public UserDAO getUserDao() {
          
          
              return userDao;
          }
      
          public void setUserDao(UserDAO userDao) {
          
          
              this.userDao = userDao;
          }
      
          @Override
          public void register(User user) {
          
          
              userDao.register(user);
          }
      
          @Override
          public void login(String name, String password) {
          
          
              userDao.login(name, password);
          }
      }
    
  • 配置文件中进行注入(赋值)

      <bean id="userService" class="com.baizhiedu.basic.UserServiceImpl">
          <property name="userDao">
              <bean class="com.baizhiedu.basic.UserDAOImpl"></bean>
          </property>
      </bean>
    
  • 第一种赋值方式存在的问题:

    • 1.配置文件代码冗余:AServiceImpl需要userDao属性,BServiceImpl也需要userDao属性,那么配置文件中就需要写两句<bean class="com.baizhiedu.basic.UserDAOImpl"></bean>。如果更多的Service对象需要这个属性,配置文件中就需要写更多。

在这里插入图片描述

    • 2.每写一个bean标签就意味着创建了一个UserDao对象,所以被注入的对象属性(UserDao)多次创建,浪费(JVM内存资源)。

2.第二种方式:

  • 注意:< ref bean=“userDao”>< /ref>中的bean属性是其他bean标签的id值。
 <bean id="userDao" class="com.baizhiedu.basic.UserDAOImpl"></bean>
 <bean id="userService" class="com.baizhiedu.basic.UserServiceImpl">
     <property name="userDao">
         <ref bean="userDao"></ref>
     </property>
 </bean>

在这里插入图片描述

  • 注意:Spring4.x废除了< ref local = “”/>,这个语法基本等效于< ref bean=""/>

3.3.Set注入的简化写法

1.基于属性的简化:

  1. value属性,代替value标签:value属性只能用在JDK的八种基本类型+String类型。
        <property name="id">
            <value>10</value>
        </property>
        //value属性,代替value标签
        <property name="id" value="10"></property>

		
		<property name="userDAO">
		   <ref bean="userDAO"/>
		</property>
		//ref属性代替ref标签
		<property name="userDAO" ref="userDAO"/>

2.基于命名空间p的简化:

在这里插入图片描述
在这里插入图片描述
命名空间p就相当于property标签的作用:
在这里插入图片描述
配置文件写法:

<bean id="person1" class="com.baizhiedu.basic.model.Person" p:name="tttxxxll" p:id="66666"></bean>

//等价于
<bean id="person1" class="com.baizhiedu.basic.model.Person">
   <property name="id" value="66666"></property>
   <property name="name">
       <value>tttxxxll</value>
   </property>
</bean>


<bean id="userDao" class="com.baizhiedu.basic.UserDAOImpl"></bean>
<bean id="userService1" class="com.baizhiedu.basic.UserServiceImpl" p:userDao-ref="userDao"/>
//等价于原写法
<bean id="userService" class="com.baizhiedu.basic.UserServiceImpl">
    <property name="userDao">
        <!--<bean class="com.baizhiedu.basic.UserDAOImpl"></bean>-->
        <ref bean="userDao"></ref>
    </property>
</bean>

4.构造注入

注入:通过Spring的配置文件,为成员变量赋值
Set注入:Spring调用Set方法,通过配置文件,为成员变量赋值
构造注入:Spring调用构造方法,通过配置文件,为成员变量赋值。

注意点:<bean/>标签是通过反射创建对象,反射最后还是会调用构造方法创建对象。之前的Set注入,都是使用无参构造创建对象。

4.1.开发步骤

1.提供有参(为了通过构造方法注入属性)构造方法

public class Customer implements Serializable {
    
    
    private String name;
    private int age;

    public Customer(String name, int age) {
    
    
        this.name = name;
        this.age = age;
    }
}

2.Spring的配置文件

注意:一个<constructor-arg/>标签代表一个参数。
注意:因为有参构造中的参数属于8种基本类型和String,所以<constructor-arg/>标签的子标签用<value/>

  <!--演示构造注入-->
  <bean id="customer" class="com.baizhiedu.basic.model.Customer">
      <constructor-arg>
          <value>tttxxxlll</value>
      </constructor-arg>
      <constructor-arg>
          <value>66666666</value>
      </constructor-arg>
  </bean>

4.2.构造注入重载

方法签名:方法名称+形参数据类型列表。方法的签名,来区分不同方法的标示符,方法名和形参数据类型列表可以唯一的确定一个方法,与方法的返回值没有关系。

重载:参数表不同,方法名相同。

1.参数个数不同时:

  • 通过<constructor-arg>标签的数量进行区分。

2.参数个数相同时,靠type属性区分

    public Customer(String name) {
    
    
        this.name = name;
    }

    public Customer(int age) {
    
    
        this.age = age;
    }
    
    //调用Customer(String name)构造注入
    <bean id="customer" class="com.baizhiedu.basic.model.Customer">
        <constructor-arg type="java.lang.String">
            <value>tttxxxlll</value>
        </constructor-arg>
    </bean>
    
	//调用Customer(int age)构造注入
    <bean id="customer" class="com.baizhiedu.basic.model.Customer">
        <constructor-arg type="int">
            <value>66666666</value>
        </constructor-arg>
    </bean>

3.思考:如果两个构造方法的参数类型也一样呢?该怎么区分?

    public Customer(String name) {
    
    
        this.name = name;
    }

    public Customer(String age) {
    
    
        this.age = age;
    }
  • 这是错误的!这两个方法的方法签名是一样的,所以可以被认为是同一个方法。写让不能在一个类中定义两个相同的方法,报错!

5. 注入总结

在这里插入图片描述
实际开发中,应用Set注入还是构造注入?

  • Set注入更多,因为构造柱如有重载,较麻烦。而且Spring框架底层大量应用了Set注入。

猜你喜欢

转载自blog.csdn.net/tttxxl/article/details/115326403