Spring Bean管理--依赖注入、自动装配

一、摘要

本文主要介绍Spring Bean管理的依赖注入(Dependency Injection,DI)部分:基于Setter方法注入,构造函数注入,自动装配注入,@Autowired注解注入等。

二、依赖注入

所谓注入,就是给某一个bean实例的属性设值时,无需显性编写Java代码就可以实现属性赋值;所谓依赖注入,则通常指bean实例引用了其它实例,如常见的service引用dao,则对于service来说,用到的dao就需要依赖注入,spring ioc容器在创建service bean时会根据既定规则自动注入dao实例。

1、基于构造函数的依赖注入

构造函数注入也就是通过构造方法注入依赖,构造函数的参数一般情况下就是依赖项,spring容器会根据bean中指定的构造函数参数来决定调用那个构造函数,看一个案例:

TextEditor.java

package com.marcus.spring.beans;

public class TextEditor {
	private SpellChecker spellChecker;
	private String generateType;
	private String text;
	
	@SuppressWarnings("rawtypes")
	private List keywords;
	
	public TextEditor() {
	}
	@SuppressWarnings("rawtypes")
	public TextEditor(List list) {
		System.out.println("Inside TextEditor constructor(list).");
		this.keywords = list;
	}	
	public TextEditor(SpellChecker spellChecker) {
		System.out.println("Inside TextEditor constructor(checker).");
		this.spellChecker = spellChecker;
	}	
	public TextEditor(SpellChecker spellChecker, String generateType) {
		System.out.println("Inside TextEditor constructor.");
		this.spellChecker = spellChecker;
		this.generateType = generateType;
	}
	public TextEditor(SpellChecker spellChecker, String generateType, String text) {
		System.out.println("Inside TextEditor constructor(checker, generteType, text).");
		this.spellChecker = spellChecker;
		this.generateType = generateType;
		this.text = text;
	}
	public String getText() {
		return this.text;
	}
	public void setText(String text) {
		this.text = text;
	}			
	public String getGenerateType() {
		System.out.println("TextEditor generate type: " + this.generateType);
		return this.generateType;
	}
	@SuppressWarnings("rawtypes")
	public List getKeyWords() {
		return this.keywords;
	}	
	public void spellCheck() {
		spellChecker.checkSpelling("TextEditor");
	}
}

SpellChecker.java

package com.marcus.spring.beans;

import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

public class SpellChecker {
	@SuppressWarnings("rawtypes")
	private List wordsList;
	
	@SuppressWarnings("rawtypes")
	private Set wordsSet;
	
	@SuppressWarnings("rawtypes")
	private Map wordsMap;
	
	Properties wordsProp;
	
	public SpellChecker() {
		System.out.println("Inside SpellChecker constructor.");
	}	
	public String checkSpelling(String text) {
		String result = "success";
		String errWords = "";
		if (text == null || text.trim().length() < 1 
				|| wordsList == null || wordsList.size() < 1) {
			return result;
		}
		String[] words = text.trim().split(",");
		for(String word : words) {
			if (word != null && word.trim().length() > 0) {
				if(! wordsList.contains(word.trim().toLowerCase())) {
					errWords += "".equals(errWords) ? word.trim() : "," + word.trim();
				}
			}
		}
		return errWords.length() < 1 ? result : errWords + " spells error!";
	}
	@SuppressWarnings("rawtypes")
	public void setWordsList(List wordsList) {
		this.wordsList = wordsList;
	}
	@SuppressWarnings("rawtypes")
	public List getWordsList() {
		System.out.println("List Elements :" + wordsList);
		return wordsList;
	}
	
	@SuppressWarnings("rawtypes")
	public void setWordsSet(Set wordsSet) {
		this.wordsSet = wordsSet;
	}
	@SuppressWarnings("rawtypes")
	public Set getWordsSet() {
		System.out.println("Set Elements :" + wordsSet);
		return wordsSet;
	}
	
	@SuppressWarnings("rawtypes")
	public void setWordsMap(Map wordsMap) {
		this.wordsMap = wordsMap;
	}
	@SuppressWarnings("rawtypes")
	public Map getWordsMap() {
		System.out.println("Map Elements :" + wordsMap);
		return wordsMap;
	}
	
	public void setWordsProp(Properties wordsProp) {
		this.wordsProp = wordsProp;
	}
	public Properties getWordsProp() {
		System.out.println("Property Elements :" + wordsProp);
		return wordsProp;
	}
}

ioc-di.xml 配置文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <bean id="textEditor" class="com.marcus.spring.beans.TextEditor">
      <constructor-arg ref="spellChecker"/>
   </bean>
   <bean id="textEditor2" class="com.marcus.spring.beans.TextEditor">
      <constructor-arg ref="spellChecker"/>
      <constructor-arg type="java.lang.String" value="constructor(checker, generatType)"/>
   </bean>
   <bean id="textEditor3" class="com.marcus.spring.beans.TextEditor">
      <constructor-arg ref="spellChecker"/>
      <constructor-arg type="java.lang.String" index="1" value="constructor(checker, generatType)"/>
      <constructor-arg type="java.lang.String" index="2" value="Text, Wood, SuperMen"/>
   </bean> 
   <bean id="textEditor4" class="com.marcus.spring.beans.TextEditor">
      <constructor-arg type="java.util.List">
         <list>
            <value>INDIA</value>
            <value>Pakistan</value>
            <value>USA</value>
         </list>      	
      </constructor-arg>
   </bean>       
   <bean id="spellChecker" class="com.marcus.spring.beans.SpellChecker" />
</beans>

该配置文件定义了TextEditor的3个实例,对应TextEditor的3个构造函数。spring ioc容器会根据参数类型尝试查找合适的构造函数并创建TextEditor实例,因此<constructor-arg>的注入顺序并不重要。

请注意textEditor3,其构造函数后两个参数类型都是string,此时,我们必须标记参数位置索引index,否则对于spring ioc容器来说是一个灾难。

请注意textEditor4,其构造函数参数是一个集合,map、set等其它集合类似。

如果你想向一个对象传递一个引用,你需要使用标签的 ref 属性,如果你想直接传递值,那么你应该使用如上所示的 value 属性,如果参数是集合类型则参考textEditor4。

测试类BeanDIApp.java

package com.marcus.spring.beans;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BeanDIApp {
	public static void main(String[] args) {
		AbstractApplicationContext context = new ClassPathXmlApplicationContext("ioc-di.xml");
		
     System.out.println("=================================================");
		TextEditor textEditor = context.getBean("textEditor", TextEditor.class);
		System.out.println("textEditor: " + textEditor.toString());
		
		TextEditor textEditor2 = context.getBean("textEditor2", TextEditor.class);
		System.out.println("textEditor2: " + textEditor2.toString());

		TextEditor textEditor3 = context.getBean("textEditor3", TextEditor.class);
		System.out.println("textEditor3: " + textEditor3.toString());
		System.out.println("textEditor3->text: " + textEditor3.getText());

		TextEditor textEditor4 = context.getBean("textEditor4", TextEditor.class);
		System.out.println("textEditor4: " + textEditor4.toString());
		System.out.println("textEditor4->keywords: " + textEditor4.getKeyWords());
		context.close();
	}
}

输出结果

Inside SpellChecker constructor.
Inside TextEditor constructor(checker).
Inside TextEditor constructor(checker, generteType).
Inside TextEditor constructor(checker, generteType, text).
Inside TextEditor constructor(list).
=================================================
textEditor: com.marcus.spring.beans.TextEditor@14bf9759
textEditor2: com.marcus.spring.beans.TextEditor@5f341870
textEditor3: com.marcus.spring.beans.TextEditor@589838eb
textEditor3->text: Text, Wood, SuperMen
textEditor4: com.marcus.spring.beans.TextEditor@544fe44c
textEditor4->keywords: [INDIA, Pakistan, USA]

循环依赖

构造函数循环依赖,class A构造时引用B,class B构造时引用A,就是循环依赖,这种情况下spring ioc容器无法实例化这两个bean,会报Is there an unresolvable circular reference?看一个案例:

LoopDependA.java

package com.marcus.spring.beans;

public class LoopDependA {
	private LoopDependB dependB;
	public LoopDependA(){
	}
	public LoopDependA(LoopDependB dependB) {
		this.dependB = dependB;
	}
	public void setDependency(LoopDependB dependB) {
		this.dependB = dependB;
	}
	public LoopDependB getDependency() {
		return this.dependB;
	}
}

LoopDependB.java

package com.marcus.spring.beans;

public class LoopDependB {
	private LoopDependA dependA;
	public LoopDependB() {
	}
	public LoopDependB(LoopDependA dependA) {
		this.dependA = dependA;
	}
	
	public void setDependency(LoopDependA dependA) {
		this.dependA = dependA;
	}
	public LoopDependA getDependency() {
		return this.dependA;
	}
}

ioc-di-loop.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
	
	<!-- 构造函数循环依赖 -->
	<!--		 
   <bean id="loopA" class="com.marcus.spring.beans.LoopDependA">
      <constructor-arg ref="loopB"/>
   </bean>
   <bean id="loopB" class="com.marcus.spring.beans.LoopDependB">
      <constructor-arg ref="loopA"/>
   </bean>
    -->
   <bean id="loopA" class="com.marcus.spring.beans.LoopDependA">
   	<property name="dependency" ref="loopB" />
   </bean>
   <bean id="loopB" class="com.marcus.spring.beans.LoopDependB">
   	<property name="dependency" ref="loopA" />
   </bean>
</beans>

构造函数循环依赖时,spring ioc容器无法实例化这2个bean,简单理解就是竞争锁,A实例化时需要B(B尚未实例化),B实例化需要A(A尚未实例化),A等B,B等A,谁也创建不了。

换成Setter注入,则可以,原因是Setter注入是在bean实例化完成之后通过调用set方法完成。因此,强烈不建议在配置文件中使用循环依赖。

DILoopApp.java 测试类和输出结果

package com.marcus.spring.beans;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * BeanApp.
 */
public class DILoopApp {
	public static void main(String[] args) {
		AbstractApplicationContext context = new ClassPathXmlApplicationContext("ioc-di-loop.xml");
		
		LoopDependA loopA = context.getBean("loopA", LoopDependA.class);
		System.out.println("LoopDependA->getDependency(), " + loopA.getDependency());
		
		LoopDependB loopB = context.getBean("loopB", LoopDependB.class);
		System.out.println("LoopDependB->getDependency(), " + loopB.getDependency());
		
		context.close();
		
		//输出结果
		//LoopDependA->getDependency(), com.marcus.spring.beans.LoopDependB@77cd7a0
		//LoopDependB->getDependency(), com.marcus.spring.beans.LoopDependA@204f30ec
	}
}

小结

  • 引用类型请用标签的 ref 属性,简单值类型请用标签的value属性,如果是集合类型则参考上文的textEditor4实例配置
  • 多个参数请设置type属性
  • 多个参数类型一致时,如多个字符串等,请设置index属性
  • 避免循环依赖

2、基于Setter函数的依赖注入

Setter注入顾名思义,被注入的属性需要有set方法, Setter注入支持简单类型、集合类型和引用类型,Setter注入时在bean实例创建完成后执行。Setter注入与构造函数注入唯一的区别就是在基于构造函数注入中,我们使用的是〈bean〉标签中的〈constructor-arg〉元素,而在基于设值函数的注入中,我们使用的是〈bean〉标签中的〈property〉元素。看一个案例:

TextEditor.java, SpellChecker.java 同上例

ioc-di2.xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <bean id="textEditor" class="com.marcus.spring.beans.TextEditor">
   	<property name="spellChecker" ref="spellChecker"/>
   	<property name="text" value="Text, Wood, SuperMen"/>
   </bean>
   
   <bean id="textEditor2" class="com.marcus.spring.beans.TextEditor"
      p:spellChecker-ref="spellChecker" p:text="Text, Wood, SuperMen, Compute"/>

   <bean id="spellChecker" class="com.marcus.spring.beans.SpellChecker">
   	<property name="wordsList">
    	<list>
            <value>text</value>
            <value>wood</value>
            <value>superman</value>
    	</list>
   	</property>
   </bean>
</beans>

实例textEditor设置了2个属性:spellChecker和text,textEditor2功能等同textEditor,但是我们发现设置属性值时简单了很多。

可以使用 p-namespace 以一种更简洁的方式来设置bean属性值,要求配置文件必须引入xmlns:p=“http://www.springframework.org/schema/p”,参考textEditor2 bean配置。

spellChecker bean,使用了集合类型赋值,其它集合类型Map、Set等同理。

测试类 BeanDIApp2

package com.marcus.spring.beans;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeanDIApp2 {
	public static void main(String[] args) {
		AbstractApplicationContext context = new ClassPathXmlApplicationContext("ioc-di2.xml");
		
System.out.println("=================================================");
		TextEditor textEditor = context.getBean("textEditor", TextEditor.class);
		System.out.println("textEditor: " + textEditor.toString());
		System.out.println("textEditor->checkText: " + textEditor.getText());
		System.out.println("textEditor->spellCheck: " + textEditor.spellCheck());
		
		TextEditor textEditor2 = context.getBean("textEditor2", TextEditor.class);
		System.out.println("textEditor2: " + textEditor2.toString());
		System.out.println("textEditor2->checkText: " + textEditor2.getText());
		System.out.println("textEditor2->spellCheck: " + textEditor2.spellCheck());
		
		context.close();
	}
}

输出结果

Inside TextEditor constructor().
Inside SpellChecker constructor.
Inside TextEditor constructor().
=================================================
textEditor: com.marcus.spring.beans.TextEditor@29ca901e
textEditor->checkText: Text, Wood, SuperMen
textEditor->spellCheck: SuperMen spells error!
textEditor2: com.marcus.spring.beans.TextEditor@5649fd9b
textEditor2->checkText: Text, Wood, SuperMen, Compute
textEditor2->spellCheck: SuperMen,Compute spells error!

小结

  • Setter注入与构造函数注入唯一的区别就是在基于构造函数注入中,我们使用的是〈bean〉标签中的〈constructor-arg〉元素,而在基于设值函数的注入中,我们使用的是〈bean〉标签中的〈property〉元素
  • 如果你要把一个引用传递给一个对象,那么你需要使用标签的 ref 属性,而如果你要直接传递一个值,那么你应该使用 value 属性,集合类型则需要使用property
  • 可以使用 p-namespace 以一种更简洁的方式来设置bean属性值

三、自动装配

上文已经介绍如何使用〈bean〉元素来声明 bean 和通过使用 XML 配置文件中的〈constructor-arg〉和〈property〉元素来注入 。Spring 容器可以在不使用〈constructor-arg〉和〈property〉元素的情况下自动装配相互协作的 bean 之间的关系,这有助于减少编写Bean配置文件的时间,有byName,byType,constructor三种装配模式。

模式 描述
no 这是默认的设置,它意味着没有自动装配。
byName 由属性名自动装配。
byType 由属性数据类型自动装配。
constructor 类似于 byType,但该类型适用于构造函数参数类型。如果在容器中没有一个构造函数参数类型的 bean,则一个致命错误将会发生。

自动装配确实可以减少bean定义文件代码编写,也无需关心引用的id或name是否写错,但是自动装配也有其缺陷和局限性:

  • 不能自动装配所谓的简单类型包括基本类型,字符串和集合类
  • 自动装配不如显式装配精确,所以如果可能的话尽可能使用显式装配

自动装配的一个案例,如下:

TextEditor.java 代码如上文

ioc-di-autoby.xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

	<!-- autowire byName -->
   <bean id="textEditor" class="com.marcus.spring.beans.TextEditor" autowire="byName">
      <property name="text" value="Text, Wood, SuperMen" />
      <property name="generateType" value="autoWire = byName" />
   </bean>
   
   	<!-- autowire byName -->
   <bean id="textEditor2" class="com.marcus.spring.beans.TextEditor" autowire="byType">
      <property name="text" value="Text, Wood, SuperMen, byType" />
      <property name="generateType" value="autoWire = byType" />
   </bean>
   
    <!-- autowire constructor -->
   <bean id="textEditor3" class="com.marcus.spring.beans.TextEditor" autowire="constructor">
        <constructor-arg index="1" value = "autoWire = constructor" />
   	<constructor-arg value = "Text, Wood, SuperMen, byType" />
   </bean>
   
   <bean id="spellChecker" class="com.marcus.spring.beans.SpellChecker">
    <property name="wordsList">
    	<list>
            <value>text</value>
            <value>wood</value>
            <value>superman</value>
    	</list>
   	</property>
   </bean>
   <bean id="spellChecker2" class="com.marcus.spring.beans.SpellChecker" autowire-candidate="false">
    <property name="wordsList">
    	<list>
            <value>text</value>
            <value>wood</value>
            <value>superman</value>
    	</list>
   	</property>
   </bean>   
</beans>

DIAutoByApp.java 测试类

package com.marcus.spring.beans;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class DIAutoByApp {
	public static void main(String[] args) {
		AbstractApplicationContext context = new ClassPathXmlApplicationContext("ioc-di-autoby.xml");
		
		TextEditor textEditor = context.getBean("textEditor", TextEditor.class);
		System.out.println("textEditor->generateType: " + textEditor.getGenerateType());
		System.out.println("textEditor->checkText: " + textEditor.getText());
		System.out.println("textEditor->spellCheck: " + textEditor.spellCheck());
		System.out.println("");
		
		TextEditor textEditor2 = context.getBean("textEditor2", TextEditor.class);
		System.out.println("textEditor2->generateType: " + textEditor2.getGenerateType());
		System.out.println("textEditor2->checkText: " + textEditor2.getText());
		System.out.println("textEditor2->spellCheck: " + textEditor2.spellCheck());
		System.out.println("");
		
		TextEditor textEditor3 = context.getBean("textEditor3", TextEditor.class);
		System.out.println("textEditor3->generateType: " + textEditor3.getGenerateType());
		System.out.println("textEditor3->checkText: " + textEditor3.getText());
		System.out.println("textEditor3->spellCheck: " + textEditor3.spellCheck());
		
		context.close();
	}
}

输出结果

textEditor->generateType: autoWire = byName
textEditor->checkText: Text, Wood, SuperMen
textEditor->spellCheck: SuperMen spells error!

textEditor2->generateType: autoWire = byType
textEditor2->checkText: Text, Wood, SuperMen, byType
textEditor2->spellCheck: SuperMen,byType spells error!

textEditor3->generateType: autoWire = constructor
textEditor3->checkText: Text, Wood, SuperMen, byType
textEditor3->spellCheck: SuperMen,byType spells error!

1、byName

这种模式由属性名称指定自动装配。例如上文的textEditor,其定义设置为自动装配 byName,并且它包含 spellChecker 属性(即,它有一个 setSpellChecker(…) 方法),那么 Spring 就会查找定义名为 spellChecker 的 bean,并且用它来设置这个属性。你仍然可以使用<property> 或<constructor-arg>标签设置其余属性。

2、byType

这种模式由属性类型指定自动装配。例如上文的textEditor2,在定义设置为自动装配 byType,并且它包含 SpellChecker 类型的 spellChecker 属性,那么 Spring 就会查找定义类型为 SpellChecker 的 bean,并且用它来设置这个属性。

如果存在多个类型一样的bean,如上文的spellChecker,spellChecker2,如果没有特殊处理,则spring ioc容器在bean实例化时就会报错,不知道该注入spellChecker还是spellChecker2。这时,我们需要设置spellChecker2的autowire-candidate=“false”,则表示spellChecker2不参与依赖注入,所以spring容器会将spellChecker注入到textEditor。

3、constructor

这种模式与 byType 非常相似,但它应用于构造器参数。在 XML 配置文件中 bean的 autowire 属性设置为 constructor。然后,spring尝试把构造函数的参数与配置文件中 beans 类型进行匹配,如果找到匹配项,它会注入这些 bean。如上文的textEditor3,构造函数有个参数类型为SpellChecker,Spring 会查找定义类型SpellChecker 的 bean,并用它来设置构造函数的参数。

四、基于注解的自动装配

从 Spring 2.5 开始就可以使用注解来配置依赖注入。而不是采用 XML 来描述一个 bean 定义,你可以使用相关类,方法或字段声明的注解,将 bean 配置移到bean类本身。这里介绍3个重要的注解:@Required,@Autowired,@Qualifier。

使用注解时必须启动注解驱动
<context:annotation-config />

一个案例,如下:

bean Profile.java

package com.marcus.spring.beans;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

public class Profile {
	@Autowired
	@Qualifier("student1")
	private Student student;

	public Profile() {
		System.out.println("Inside Profile constructor.");
	}
	
	public void setStudent(Student student) {
		this.student = student;
	}

	public void printInfo() {
		System.out.println("Student-Name: " + student.getName());
		System.out.println("Student-Age: " + student.getAge());
	}
}

bean Student.java

package com.marcus.spring.beans;

import org.springframework.beans.factory.annotation.Required;

public class Student {
	private Integer age;
	private String name;
	@Required
	public void setAge(Integer age) {
		this.age = age;
	}
	public Integer getAge() {
		return age;
	}
	@Required
	public void setName(String name) {
		this.name = name;
	}
	public String getName() {
		return name;
	}
}

ioc-di-annotation.xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd">
   
   <context:annotation-config/>

   <!-- Definition for student bean -->
   <bean id="student1" class="com.marcus.spring.beans.Student">
      <property name="name"  value="Zara" />
      <property name="age"  value="11" />
   </bean>
   
   <bean id="student2" class="com.marcus.spring.beans.Student">
      <property name="name"  value="Student2" />
      <property name="age"  value="22" />
   </bean>
   
   <bean id="profile" class="com.marcus.spring.beans.Profile" />
   <bean id="profile2" class="com.marcus.spring.beans.Profile">
   	<property name="student" ref="student2" />
   </bean>
</beans>

测试类和输出结果,DIAnnotationApp.java

package com.marcus.spring.beans;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class DIAnnotationApp {
	public static void main(String[] args) {
		AbstractApplicationContext context = new ClassPathXmlApplicationContext("ioc-di-annotation.xml");
		
		Profile profile = context.getBean("profile", Profile.class);
		profile.printInfo();
		System.out.println("");
		
		Profile profile2 = context.getBean("profile2", Profile.class);
		profile2.printInfo();
		System.out.println("");
		
		context.close();
		
		//输出结果
		//Student-Name: Zara
		//Student-Age: 11

		//Student-Name: Student2
		//Student-Age: 22
	}
}

1、@Required注解

@Required 注解应用于 bean 属性的 setter 方法,它表明受影响的 bean 属性在配置时必须放在 XML 配置文件中,否则容器会抛出一个 BeanInitializationException 异常。如上文的Student,其age和name都设为@Required,则xml配置文件中,必须指定:

<property name=“name” value=“Student2” />
<property name=“age” value=“22” />

否则spring容器初始化时会报BeanInitializationException异常。

2、@Autowired 注解

@Autowired 自动装配更灵活,控制的更细微。
@Autowired 注解可以在 setter 方法、field和构造函数中使用。
@Autowired 的(required=false)选项,默认情况下,@Autowired 注解意味着依赖是必须的,它类似于 @Required 注释,然而,你可以使用 @Autowired 的 (required=false) 选项关闭默认行为。

属性中(field)的@Autowired

无需setter方法。

public class Profile {
	@Autowired
	@Qualifier("student1")
	private Student student;

	public Profile() {
		System.out.println("Inside Profile constructor.");
	}
	public void printInfo() {
		System.out.println("Student-Name: " + student.getName());
		System.out.println("Student-Age: " + student.getAge());
	}
}

Setter方法中的@Autowired

public class Profile2 {
	private Student student;

	public Profile2() {
		System.out.println("Inside Profile2 constructor.");
	}
	
	@Autowired
	@Qualifier("student2")
	public void setStudent(Student student) {
		this.student = student;
	}

	public void printInfo() {
		System.out.println("Student-Name: " + student.getName());
		System.out.println("Student-Age: " + student.getAge());
	}
}

构造函数中的@Autowired

构造函数中的@Autowired,不能使用@Qualifier,这时Profile如果要注入student问题就来了,上文我们注册了2个student bean,这时spring ioc容器就懵了,不知道引用哪个student bean,导致spring ioc容器初始化时会报错。

在根据类型不能成功匹配时,我们只好使用byName,通过构造函数的参数名称与xml配置文件中的bean id匹配实现student bean注入。如本案例构造函数参数名称为student3,xml配置文件中定义了一个id为student3的bean,因此spring容器启动时会找到student3并注入到profile3。

package com.marcus.spring.beans;

import org.springframework.beans.factory.annotation.Autowired;

public class Profile3 {

	private Student student;

	@Autowired
	public Profile3(Student student3) {
		System.out.println("Inside Profile3 constructor.");
		this.student = student3;
	}
	
	public void printInfo() {
		System.out.println("Student-Name: " + student.getName());
		System.out.println("Student-Age: " + student.getAge());
	}
}

配置文件

   <bean id="student1" class="com.marcus.spring.beans.Student">
      <property name="name"  value="Zara" />
      <property name="age"  value="11" />
   </bean>
   <bean id="student2" class="com.marcus.spring.beans.Student">
      <property name="name"  value="Student2" />
      <property name="age"  value="22" />
   </bean>
   <bean id="student3" class="com.marcus.spring.beans.Student">
      <property name="name"  value="Student3" />
      <property name="age"  value="33" />
   </bean>
   <bean id="profile" class="com.marcus.spring.beans.Profile" />
   <bean id="profile2" class="com.marcus.spring.beans.Profile2" />
   <bean id="profile3" class="com.marcus.spring.beans.Profile3" />

基于@Resource注解的自动装配

spring不但支持自己定义的@Autowired注解,还支持几个由JSR-250规范定义的注解,它们分别是@Resource、@PostConstruct以及@PreDestroy。

@Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入,而@Resource默认按 byName自动注入罢了。@Resource有两个属性是比较重要的,分是name和type,Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。

Profile4.java

public class Profile4 {
	@Resource(name="student1")
	private Student student;

	public Profile4() {
		System.out.println("Inside Profile4 constructor.");
	}
	public void printInfo() {
		System.out.println("Student-Name: " + student.getName());
		System.out.println("Student-Age: " + student.getAge());
	}
}

xml配置片段

<bean id="profile4" class="com.marcus.spring.beans.Profile4" />

猜你喜欢

转载自blog.csdn.net/chuangxin/article/details/82838207