Spring核心之IoC的兩種實現方式

前言

在剛接觸java的時候,映像最深刻的一句話就是:需要使用對象的時候,就直接new一個對象即可。但是,像對於Mybatis中的核心對象SqlSessionFactory、工作流引擎Activity中的核心對象ProcessEngine,還有我們的業務邏輯組件、數據庫操作組件等等,只要核心對象一創建,就應該在應用執行期間都存在,且不應該在應用期間重複創建。我們當然可以用單例模式處理,但是當大量對象充斥着我們的應用,還有各個核心對象之間的依賴關係,管理起來就很頭疼。所以利用Spring的IoC來管理就可以爲我們減少這些問題。

IoC(Inversion of Control,IoC),控制反轉,它更是一個容器,用來管理我們應用中核心對象的容器,這些核心對象在Spring中稱爲Spring Bean(簡稱Bean)。這個容器具備着以下兩個功能:

1、通過描述管理Bean,包括髮布和獲取Bean;

2、通過描述完成Bean之前的依賴關係。

何爲控制反轉?在我們剛接觸java的時候,如果要在A對象裏面使用B對象,就應該是在A中new一個B對象,這種過程是一個正向的,那麼什麼是反向呢?就是A類不再主動去獲取B,而是被動等待,等待IoC容器獲取一個B的實例,然後反向的注入到A類中。曾經有一個很美妙的形容來形容這種關係:叫好萊塢原則(Don't call me, I will call you,不要呼叫我,等着我呼叫你),基於這一原則,提出了一個更爲準確的概念叫依賴注入(DI,Dependency Injection)。所以,控制反轉(IoC)和依賴注入(DI)是對同一件事情的不同描述,從某個方面講,DI就是描述着IoC的過程。

在IoC容器中,所有的bean都有BeanFactory來創建、配置、管理,它採用了經典的工廠模式。它是一個頂級容器接口。BeanFactory接口裏面的主要有以下方法(可自行點進BeanFactory源碼查看):

方法 說明

Object getBean(String name) throws BeansException;

<T> T getBean(String name, Class<T> requiredType) throws BeansException;

Object getBean(String name, Object... args) throws BeansException;

<T> T getBean(Class<T> requiredType) throws BeansException;

<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

多個getBean的方法,可以通過Bean的name(by name),也可以通過bean的類型(by type)。
boolean containsBean(String name); 是否包含Bean
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

Bean是否是單例,這裏需要注意的是,默認情況下,

Bean都是以單例形式存在的,與其相反的是原型模式的Bean,

當我們創建了一個原型模式下的Bean,I

oC容器就會創建一個新的Bean返回給調用者,這與Bean的作用域有關。

boolean isPrototype(String name) throws NoSuchBeanDefinitionException; Bean是否是原型,也就是"多例"

boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

是否類型匹配
Class<?> getType(String name) throws NoSuchBeanDefinitionException; 獲取Bean的類型
String[] getAliases(String name); 獲取Bean的別名

 

在Spring的發展中,在BeanFactory的基礎上,還設計了一個更爲高級的接口,ApplicationContext,它通過繼承上級接口進而繼承BeanFactory,它在BeanFactory的基礎上,擴展了消息國際化接口(MessageSource)、環境可配置接口(EnvironmentCapable)、應用事件接口(ApplicationEventPublisher)和資源模式解析接口(ResourcePatternResolver)。那麼,BeanFactory有的方法ApplicationContext自然也會有,後面的示例就直接用ApplicationContext進行。

ApplicationContext接口有以下3個常用實現類,可以實例化任何一個類來創建Spring的ApplicationContext容器。

ApplicationContext實現類 說明
ClassPathXmlApplicationContext 它可以從當前類路徑中檢索配置文件並裝載它來創建容器的實例
FileSystemXmlApplicationContext 它和ClassPathXmlApplicationContext唯一的區別在於讀取配置文件的方式,它不在從類路徑下獲取配置文件,它可以通過指定位置的方式讀取配置文件,還可以獲取類路徑之外的資源
AnnotationConfigApplicationContext 它一般在使用註解方式配置Bean的時候,通過指定配置類的方式創建容器實例

 

環境配置

pom文件(這裏只使用IoC,所以不用引入Spring的其它組件,這篇文章討論的是實現IoC的xml和註解形式,所以直接引入最高版本)

<!-- spring -->
	<dependency>
	    <groupId>org.springframework</groupId>
	    <artifactId>spring-core</artifactId>
	    <version>5.1.5.RELEASE</version>
	</dependency>
	<dependency>
	    <groupId>org.springframework</groupId>
	    <artifactId>spring-context</artifactId>
	    <version>5.1.5.RELEASE</version>
	</dependency>
	<dependency>
	    <groupId>org.springframework</groupId>
	    <artifactId>spring-beans</artifactId>
	    <version>5.1.5.RELEASE</version>
	</dependency>
	<!-- 日誌 -->
	<dependency>
	    <groupId>org.apache.logging.log4j</groupId>
	    <artifactId>log4j-core</artifactId>
	    <version>2.11.1</version>
	</dependency>
	<dependency>
	    <groupId>log4j</groupId>
	    <artifactId>log4j</artifactId>
	    <version>1.2.17</version>
	</dependency>
	<dependency>
	    <groupId>org.apache.logging.log4j</groupId>
	    <artifactId>log4j-api</artifactId>
	    <version>2.11.1</version>
	</dependency>
	<dependency>
	    <groupId>commons-logging</groupId>
	    <artifactId>commons-logging</artifactId>
	    <version>1.2</version>
	</dependency>

日誌

#全局的日誌配置
log4j.rootLogger=ERROR, stdout
#自定義日誌

#控制檯輸出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

利用spring的插件生成一個spring的配置文件,項目結構如下(沒有插件的可參考這篇安裝)

利用XML形式實現IoC

1、簡單的示例

<bean id="user" class="com.zepal.spring.bean.User"/>
public class User {
	
}
public class Test {

	public static void main(String[] args) {
		//這裏還可以直接使用頂級接口BeanFactory
		ApplicationContext context = new FileSystemXmlApplicationContext("classpath:applicationContext.xml");
		//按類型獲取by type
		User user1 = context.getBean(User.class);
		//按名稱獲取by name(在配置文件中,我們將bean聲明爲了user)
		User user2 = (User) context.getBean("user");
		System.out.println(user1 == user2);
		System.out.println("是否單例 :" + context.isSingleton("user"));
		System.out.println("是否原型 :" + context.isPrototype("user"));
	}
}
true
是否單例 :true
是否原型 :false

說明:在調用getBean()方法之前,容器不會實例化任何對象,只有在需要使用User對象的時候,纔會創建Bean,有點懶加載的意思,這使它更適合於物力資源受限制的應用程序,尤其是內存限制的環境。Spring中Bean的生命週期包括實例化Bean、初始化Bean、使用Bean、銷燬Bean。

2、依賴注入

實現依賴注入有三種方式

接口注入:基於接口將調用與實現分離。這種依賴注入的方式必須實現容器所規定的接口,使程序代碼和容器的API綁定在一起,這不是理想的依賴注入方式,所以Spring也不支持這種方式;

Setter注入:基於JavaBean的set()方法爲屬性賦值。在實際開發中得到了最廣泛的應用。

public class User {
	
	private String name;
	
	public void setName(String name) {
		this.name = name;
	}
}

構造器注入:基於構造方法爲屬性賦值,容器通過調用類的構造方法,將其所需的依賴關係注入其中。

public class User {
	
	private String name;
	
	public User(String name) {
		super();
		this.name = name;
	}
}

在Spring中,無論使用哪種容器,都要從配置文件中讀取JavaBean的信息,再根據自定義信息去創建JavaBean的實例對象並注入其依賴屬性,常用的比如整合Mybatis、Redis等。由此可見,Bean的配置也是針對配置文件進行的。

--setter注入

JavaBean對象

/**
 * 假設這是我們項目中的mybatis
 * */
public class Mybatis {

	private String driver;
	
	private String url;
	
	private String userName;
	
	private String passWord;

	//......
}

配置Bean(注意:在JavaBean的屬性是List集合類型或數組時,需要使用<list>標籤爲其每一個元素賦值

<bean id="mybatis" class="com.zepal.spring.bean.Mybatis">
		<property name="driver" value="驅動"/>
		<property name="url" value="連接地址"/>
		<property name="userName" value="用戶名"/>
		<property name="passWord" value="密碼"/>
</bean>
public class Test {

	public static void main(String[] args) {
		//這裏還可以直接使用頂級接口BeanFactory
		ApplicationContext context = new FileSystemXmlApplicationContext("classpath:applicationContext.xml");
		//按類型獲取by type
		Mybatis mybatis = context.getBean(Mybatis.class);
		System.out.println(mybatis.getDriver());
		System.out.println(mybatis.getUrl());
		System.out.println(mybatis.getUserName());
		System.out.println(mybatis.getPassWord());
		//這裏初始化Mybatis之後,在實際開發中就相當於初始化了SqlSessionFactory,
		//就可以直接使用openSession()的方法直接開始盤CRUD了。
	}
}
驅動
連接地址
用戶名
密碼

--構造器注入

其它不變,修改配置文件。注意:這裏要和構造方法的參數順序一致,否則會產生異常。<constructor-arg>標籤提供index和type屬性可以自定義順序,爲構造方法入參,index是索引,type是參數類型。

<bean id="mybatis" class="com.zepal.spring.bean.Mybatis">
		<constructor-arg value="驅動"/>
		<constructor-arg value="連接地址"/>
		<constructor-arg value="用戶名"/>
		<constructor-arg value="密碼"/>
	</bean>

運行結果一致

驅動
連接地址
用戶名
密碼

--引用其它的bean,通過ref屬性。(比如,我們的數據庫信息是直接加載進Spring的context作用域中,在配置SqlSessionFactory的Bean的時候,是引用了這個bean)

public class User {
	
	private Mybatis mybatis;
    
    //....
}
<bean id="mybatis" class="com.zepal.spring.bean.Mybatis">
		<constructor-arg value="驅動"/>
		<constructor-arg value="連接地址"/>
		<constructor-arg value="用戶名"/>
		<constructor-arg value="密碼"/>
	</bean>
	
	<bean id="user" class="com.zepal.spring.bean.User">
		<property name="mybatis" ref="mybatis"/>
	</bean>
//這裏還可以直接使用頂級接口BeanFactory
		ApplicationContext context = new FileSystemXmlApplicationContext("classpath:applicationContext.xml");
		//按類型獲取by type
		User user = context.getBean(User.class);
		System.out.println(user.toString());
User [mybatis=Mybatis [driver=驅動, url=連接地址, userName=用戶名, passWord=密碼]]

--匿名內部類的注入形式。匿名內部類的形式可以不指定內部類的id

<!-- 在School類中定義Student類 -->
	<bean id="school" class="com.zepal.spring.bean.School">
		<property name="student" >
			<bean class="com.zepal.spring.bean.Student"/>
		</property>
	</bean>

3、自動裝配

--按bean的名稱裝配

說明:<bean>標籤的byname屬性以屬性名稱區分自動裝配。在容器中尋找與JavaBean的屬性名稱相同的bean,並將其自動裝配到JavaBean中

public class User {
	
	private Mybatis mybatis;

	//省略set方法
	
	public void printMybatisInfo() {
		System.out.println(mybatis.getDriver());
		System.out.println(mybatis.getUrl());
		System.out.println(mybatis.getUserName());
		System.out.println(mybatis.getPassWord());
	}
}
<bean autowire="byName" id="user" class="com.zepal.spring.bean.User"/>
public static void main(String[] args) {
		//這裏還可以直接使用頂級接口BeanFactory
		ApplicationContext context = new FileSystemXmlApplicationContext("classpath:applicationContext.xml");
		//按類型獲取by type
		User user = context.getBean(User.class); 
		user.printMybatisInfo();
	}
驅動
連接地址
用戶名
密碼

--按bean的類型裝配

<bean autowire="byType" id="user" class="com.zepal.spring.bean.User"/>

執行同樣的測試代碼,會有相同的結果。

在Spring中還有3種不太常用的自動裝配方式。

裝配方式 說明
no屬性 這是autowire採用的默認值,它採用自動裝配。必須使用ref直接引用其它的bean。這樣可以增加代碼的可讀性,並且不易出錯
constructor屬性 通過構造方法的參數類型自動裝配。此類型會使容器自動尋找與JavaBean的構造方法的參數類型相同的bean,並注入到需要自動裝配的bean中,它與byType類型存在相同的無法識別自動裝配的情況
autodetect屬性 首先會使用constructor方式來自動裝配,然後使用byType方式,當然此類型也存在與byType和constructor相同的異常情況。建議在使用自動裝配時把容易出現問題的bean使用手動裝配注入依賴屬性。

特別注意:a、按bean的名稱(byName)自動裝配有可能存在類型不匹配的錯誤。如果在配置文件中定了與需要自動裝配bean的屬性名稱相同而類型不同的bean,那麼它可能會錯誤的注入不同類型的Bean。

b、按類型裝配(byType),假如某一接口有多個實現類,在配置文件中,爲其每個實現類都配置了Bean,byType無法自動識別到底哪一個纔是需要的bean。要解決此問題,要麼通過手動裝配,或者使用消除歧義的方式。

public interface IPersonService {

	void work();
}
public class Teacher implements IPersonService {

	private String workTime;
	
	public String getWorkTime() {
		return workTime;
	}

	public void setWorkTime(String workTime) {
		this.workTime = workTime;
	}

	@Override
	public void work() {
		System.out.println("教師的工作是教書育人,教書"+this.workTime);
		
	}
}
public class Programmer implements IPersonService {

	private String workTime;
	
	public String getWorkTime() {
		return workTime;
	}

	public void setWorkTime(String workTime) {
		this.workTime = workTime;
	}

	@Override
	public void work() {
		System.out.println("程序員的工作是搬磚,搬磚"+this.workTime);
	}
}
<bean id="teacher" class="com.zepal.spring.bean.Teacher">
		<property name="workTime" value="7105"/>
	</bean>
	<bean id="programmer" class="com.zepal.spring.bean.Programmer">
		<property name="workTime" value="996"/>
	</bean>
//這裏還可以直接使用頂級接口BeanFactory
		ApplicationContext context = new FileSystemXmlApplicationContext("classpath:applicationContext.xml");
		IPersonService iPersonService = context.getBean(IPersonService.class);
		System.out.println("和諧社會離不開這些的辛勤工作...");
		iPersonService.work();
Exception in thread "main" org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.zepal.spring.bean.IPersonService' available: expected single matching bean but found 2: teacher,programmer

出現這個問題的原因就是,IPersonService接口有兩個實現類,將其作爲bean的時候,不知道該使用哪一個了。

<bean primary="true" id="teacher" class="com.zepal.spring.bean.Teacher">
		<property name="workTime" value="7105"/>
	</bean>

我們在teacher的bean上加上屬性primary="true",將某一個實現類標記爲主bean,再運行

和諧社會離不開這些的辛勤工作...
教師的工作是教書育人,教書7105

或者我們去掉primary="true",byName獲取,同樣的效果。

ApplicationContext context = new FileSystemXmlApplicationContext("classpath:applicationContext.xml");
		IPersonService iPersonService = (IPersonService) context.getBean("teacher");
		System.out.println("和諧社會離不開這些的辛勤工作...");
		iPersonService.work();
和諧社會離不開這些的辛勤工作...
教師的工作是教書育人,教書7105

4、bean的作用域

a.在沒有註解之前,Spring中Bean的定義和Bean相互之間的依賴關係都是通過配置XML文件中的元數據來實現的,Spring中的Bean就是由容器初始化、裝配及管理的對象,除此之外,bean和程序中的其它類並沒有什麼區別

<bean>的常用屬性

屬性名 說明
id 代表Bean的實例對象,在Bean實例化後可以通過id來引用其實例對象。
name 代表Bean的實例對象名稱
class Bean的類名,全路徑,必須屬性
scope 設置bean的作用域,有4個屬性值singleton(單例)、prototype(原型)、session(web中session的作用域)、request(request的作用域相同)
autowire 自動裝配功能
primary 接受Boolean類型,用於消除多個bean的歧義,標記主bean
lazy-init 是否延遲注入,默認情況下,容器只有在使用Bean的時候纔會加載,和Mybatis懶加載一個意思
init-method 指定bean的初始化方法
destroy-method 指定bean被回收之前調用的銷燬方法
depends-on 用於保證在depends指定的Bean被實例化之後,再實例化自身的bean

簡單測試一下作用域:

<bean scope="prototype" id="mybatis" class="com.zepal.spring.bean.Mybatis"/>
ApplicationContext context = new FileSystemXmlApplicationContext("classpath:applicationContext.xml");
		System.out.println(context.isSingleton("mybatis"));
		System.out.println(context.isPrototype("mybatis"));
false
true

說明:一旦Bean被設置爲prototype模式(非單例),每次引用Bean都會創建一個新的對象,容器則不再擁有當前返回對象的引用,容器將實例對象的生命週期交給請求方管理,所以在客戶端代碼中必須使用bean的後置處理器清楚prototype作用域的bean。一般來說,對有所狀態的bean應該設置爲prototype作用域,對無狀態的bean則應該使用singleton作用域,常見的,DAO層不會被配置成prototype,因爲一個典型的DAO不會持有任何會話狀態,像session這種的,帶有一定狀態的,則不能是singleton。

XML配置Bean的形式完結了,平時在項目中都是用@Autowired獲取Bean,那是因爲一起動項目就去加載了配置文件,上面的示例都是通過手動加載的形式。

使用註解形式實現IoC

pom依賴和XML一樣,因爲用註解,所以不再需要配置文件

1、實現一個簡單的Bean

public class User {

	private String name;
	
	private String age;
	
	private String gender;

	//省略set/get和構造方法
}
@Configuration
public class AppConfig {

	@Bean(name="user")
	public User initUser() {
		User user = new User();
		user.setName("zepal");
		user.setAge("18");
		user.setGender("男");
		return user;
	}
}
public class Test {

	ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
		//User user = context.getBean(User.class);
		User user = (User) context.getBean("user");
		System.out.println(user.toString());
		System.out.println("是否單例 :" + context.isSingleton("user"));
		System.out.println("是否原型 :" + context.isPrototype("user"));
}
User [name=zepal, age=18, gender=男]
是否單例 :true
是否原型 :false

說明:@Configuration指定當前類是一個配置類,類似於XML中的applicationContext.xml配置文件。@Bean類似於<bean>標籤,指定返回的java類裝配到IoC容器中,一般都會返回Bean的類型。name屬性指定bean的名稱,類似id屬性,如果沒有這項屬性,會默認將"initUser"方法名作爲bean的名稱,在byName獲取Bean的時候需要注意。

在註解下,很多屬性被獨立出來,比如設置作用域是單獨的註解@Scope。示例:

@Bean(name="user")
	@Scope("prototype")
	public User initUser() {
		User user = new User();
		user.setName("zepal");
		user.setAge("18");
		user.setGender("男");
		return user;
	}
System.out.println(user.toString());
System.out.println("是否單例 :" + context.isSingleton("user"));
System.out.println("是否原型 :" + context.isPrototype("user"));

---運行結果---

User [name=zepal, age=18, gender=男]
是否單例 :false
是否原型 :true

@Bean的常用屬性都和<bean>標籤中的類似,可以通過提示查看,再次不贅述了。

@Scope的作用域比起XML多了兩種,application(整個web的上下文環境,即整個生命週期)、globalSession(一個全局的session,一個Bean定義對應一個session)。

2、通過掃描裝配Bean

一般使用@Component+@ComponentScan組合。

@Component用於標記哪個類被掃描進IoC,@ComponentScan則是標明採用哪種策略去掃描裝配Bean。類似於XML中的<context:component-scan>標籤。

@Component可以指定Bean的名稱,如果不指定,則按照類型首字母小寫作爲Bean的名稱,byName獲取的時候注意。@ComponentScan默認掃描當前包或其子包,所以一般在springboot項目中,所有包都是啓動包的子包,@SpringBootApplication啓動註解,在1.5之後包含了@ComponentScan註解,所以在SpringBoot中不單獨指定。所以像我們的服務層用的@Service註解纔會被掃描註冊爲Bean,@Service裏也涵蓋了@Component註解。

@ComponentScan在指定掃麪包的時候可以採用通配符,還有一些其他功能比如過濾器,指定不掃描哪些包等等。

示例如下:

@Component("student")
public class Student {

	@Value("張三")
	private String name;
	
	@Value("18")
	private String age;

	//省略其它方法
}
@ComponentScan(basePackages= {"com.zepal.spring.bean"})
@Configuration
public class AppConfig {

	@Bean(name="user")
	public User initUser() {
		User user = new User();
		user.setName("zepal");
		user.setAge("18");
		user.setGender("男");
		return user;
	}
}
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
		Student student = context.getBean(Student.class);
		System.out.println(student.toString());

---運行結果---
Student [name=張三, age=18]

3、消除bean配置的歧義性

在下面的示例中,我們很容易利用依賴注入

public class Dog {

	private String breed;//品種
	
	private String age;
	
	//省略set/get和構造方法
}
public class Person {

	private String name;
	
	@Autowired
	private Dog dog;
	
	//省略set/get和構造方法
}
    @Bean(name="dog")
	public Dog initDog() {
		Dog dog = new Dog();
		dog.setBreed("哈士奇");
		dog.setAge("3");
		return dog;
	}
	
	@Bean(name="person")
	public Person initPerson() {
		Person p = new Person();
		p.setName("張三");
		return p;
	}
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Person person = context.getBean(Person.class);
System.out.println(person.getName() + "有一條" + person.getDog().getAge() + "歲的" + person.getDog().getBreed());

----執行結果----

張三有一條3歲的哈士奇

假如我們注入的是一個接口,且接口有兩個或以上的實現類,就會出現IoC不知道該使用哪一個Bean了。

這種場景一般出現在,在我們的service層中,假如服務接口出現多個實現類,如果不作處理,在啓動項目掃描時就會出現錯誤

public interface IAnimalService {

    //這裏還可以搞其他事情
}
public class Dog implements IAnimalService {

	private String breed;//品種
	
	private String age;
	
	//省略set/get和構造方法
}
public class Cat implements IAnimalService {

	private String breed;
	
	private String age;
	
	//省略set/get和構造方法
}
public class Person {

	private String name;
	
	@Autowired
	private IAnimalService iAnimalService;
	
	//省略set/get和其它構造方法
}
    @Bean(name="dog")
	public Dog initDog() {
		Dog dog = new Dog();
		dog.setBreed("哈士奇");
		dog.setAge("3");
		return dog;
	}
	
	@Bean(name="cat")
	public Cat initCat() {
		Cat cat = new Cat();
		cat.setBreed("波斯貓");
		cat.setAge("4");
		return cat;
	}
	
	@Bean(name="person")
	public Person initPerson() {
		Person p = new Person();
		p.setName("張三");
		return p;
	}
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Person person = context.getBean(Person.class);
System.out.println(person.toString());

----執行結果----

.....expected single matching bean but found 2: dog,cat

從控制檯提示的錯誤信息可以看到,有dog和cat兩個Bean,IoC不知道該注入哪一個了,也就是產生歧義了。

利用@Primary和@Qualifier消除這個問題。

--@Primary,這裏我們將Cat聲明爲主Bean,

    @Bean(name="cat")
	@Primary
	public Cat initCat() {
		Cat cat = new Cat();
		cat.setBreed("波斯貓");
		cat.setAge("4");
		return cat;
	}

運行結果

ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Person person = context.getBean(Person.class);
System.out.println(person.toString());

---運行結果---

Person [name=張三, iAnimalService=Cat [breed=波斯貓, age=4]]

--@Qualifier,這裏去掉@Primary,在注入的時候,聲明@Qualifier

注意:@Qualifier是按Bean的名稱(byName)去尋找Bean的,所以,在配置Bean的時候需要給定一個名稱,如果使用IoC默認生成的名稱需要注意。這個註解經常出現在service層和controller層的組合中。@Service("xxx"),然後在Controller層注入服務層接口的時候利用@Qualifier聲明服務層Bean的名稱

@Autowired
@Qualifier("cat")
private IAnimalService iAnimalService;
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Person person = context.getBean(Person.class);
System.out.println(person.toString());

---運行結果---

Person [name=張三, iAnimalService=Cat [breed=波斯貓, age=4]]

@Qualifier和@Autowired還可以用於形參當中,可根據自身需求而定,比如

public Person(String name,@Autowired @Qualifier("cat") IAnimalService iAnimalService) {
		super();
		this.name = name;
		this.iAnimalService = iAnimalService;
	}

4、生命週期和作用域

在XML形式中,修改Bean的默認加載過程,可以使用屬性lazy-init,在註解中有一個獨立的註解@Lazy(value=true),它只接受boolean類型的參數,如果使用@ComponentScan去掃面註解,它有一個屬性lazyInit可以配置。同樣的,在@Bean註解中,有initMethod和destroyMethod屬性可以指定Bean初始化的方法和銷燬的方法。

Bean的作用域在IoC註解的開頭已經說明過了,這裏就不重複累贅了。


此篇完結

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章