1. 程式人生 > >Spring中的Bean的配置形式

Spring中的Bean的配置形式

Spring中Bean的配置形式有兩種,基於XML檔案的方式和基於註解的方式。

1.基於XML檔案的方式配置Bean

<?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.xsd
		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd">
<bean id="address" class="com.java.spring.autowire.Address" p:city="上海" p:street="南京路"></bean>
<bean id="car" class="com.java.spring.autowire.Car" p:brand="Audi" p:price="500000.0000"></bean>
<bean id="person" class="com.java.spring.autowire.Person" p:name="Tom" autowire="byType"></bean>
</beans>

像上面這樣,在xml檔案中使用<bean...></bean>標籤設定Bean屬性。

2.基於註解的方式配置Bean

把一個Bean加上註解放到IOC容器中,首先需要了解元件掃描。元件掃描(component scanning)是指Spring 能夠從 classpath 下自動掃描, 偵測和例項化具有特定註解的元件。特定元件包括:

  • @Component: 基本註解, 標識了一個受 Spring 管理的元件
  • @Respository: 標識持久層元件
  • @Service: 標識服務層(業務層)元件
  • @Controller: 標識表現層元件

對於掃描到的元件, Spring 有預設的命名策略: 使用非限定類名, 第一個字母小寫. 也可以在註解中通過 value 屬性值標識元件的名稱。當在元件類上使用了特定的註解之後, 還需要在 Spring 的配置檔案中宣告 <context:component-scan> :

  • base-package 屬性指定一個需要掃描的基類包,Spring 容器將會掃描這個基類包裡及其子包中的所有類.當需要掃描多個包時, 可以使用逗號分隔;
  • 如果僅希望掃描特定的類而非基包下的所有類,可使用 resource-pattern 屬性過濾特定的類;
  • <context:include-filter> 子節點表示要包含的目標類;
  • <context:exclude-filter> 子節點表示要排除在外的目標類;

<context:component-scan> 下可以擁有若干個 <context:include-filter> 和 <context:exclude-filter> 子節點。

2.1 base-package屬性

如圖,建立如下的專案目錄:

分別用TestObject,UserController,UserRepositoryImpl,UserService模擬基本註解,表現層,持久層和業務層。

TestObject.java

package com.java.spring.annotation;
import org.springframework.stereotype.Component;
@Component
public class TestObject {
  
}

UserController.java

package com.java.spring.annotation.controller;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
	public void excute(){
		System.out.println("UserController's excute...");
	}
}

UserService.java

package com.java.spring.annotation.service;
import org.springframework.stereotype.Service;
@Service
public class UserService {
	public void add(){
		System.out.println("UserService's add...");
	}
}

UserRepository.java

package com.java.spring.annotation.repository;
public interface UserRepository {
	void save();
}

UserRepositoryImpl.java

package com.java.spring.annotation.repository;
import org.springframework.stereotype.Repository;
@Repository("userRepositoryImpl")
public class UserRepositoryImpl implements UserRepository {
	@Override
	public void save() {
	    System.out.println("UserRepositoryImpl's save...");
	}
}

為Bean添加註解之後還需要在 Spring 的配置檔案中宣告:

<context:component-scan base-package="com.java.spring.annotation"></context:component-scan>

 base-package="com.java.spring.annotation"意思是要掃描annotation包下的所有類。

寫一個測試類Main.java:

package com.java.spring.annotation;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.java.spring.annotation.controller.UserController;
import com.java.spring.annotation.repository.UserRepositoryImpl;
import com.java.spring.annotation.service.UserService;
public class Main {
	public static void main(String[] args){
		ApplicationContext ctx=new ClassPathXmlApplicationContext("beans-annotation.xml");
		UserRepositoryImpl uri=(UserRepositoryImpl) ctx.getBean("userRepositoryImpl");
		System.out.println(uri);	
		TestObject to=(TestObject) ctx.getBean("testObject");
		System.out.println(to);
        UserService us=(UserService) ctx.getBean("userService");
        System.out.println(us);
        UserController uc=(UserController) ctx.getBean("userController");
        System.out.println(uc);
	}
}

執行後輸出:

[email protected]b
[email protected]
[email protected]
[email protected]

2.2 <context:include-filter>子節點

子節點表示要包含的目標類。使用<context:include-filter>時要將use-default-filters的值設定為false,預設為true。

比如在beans-annotation.xml中進行配置:

 <context:component-scan base-package="com.java.spring.annotation" use-default-filters="false">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/> 
 </context:component-scan>

 則掃描到的元件只有@Repository("userRepositoryImpl")註解的持久層。

執行後輸出:

[email protected]
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'testObject' is defined
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:638)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1159)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:282)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:973)
	at com.java.spring.annotation.Main.main(Main.java:12)

其他Bean顯示未被定義。

2.3 <context:exclude-filter>子節點

子節點表示要排除在外的目標類,如下排除Repository類。

在beans-annotation.xml中進行配置:

<context:component-scan base-package="com.java.spring.annotation">
	<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/> 
</context:component-scan>

Main.java中修改為:

public class Main {
	public static void main(String[] args){
		ApplicationContext ctx=new ClassPathXmlApplicationContext("beans-annotation.xml");
		TestObject to=(TestObject) ctx.getBean("testObject");
		System.out.println(to);
        UserService us=(UserService) ctx.getBean("userService");
        System.out.println(us);
        UserController uc=(UserController) ctx.getBean("userController");
        System.out.println(uc);
        UserRepositoryImpl uri=(UserRepositoryImpl) ctx.getBean("userRepositoryImpl");
        System.out.println(uri);	
	}
}

執行後輸出:

[email protected]
[email protected]
[email protected]
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'userRepositoryImpl' is defined
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:638)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1159)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:282)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:973)
	at com.java.spring.annotation.Main.main(Main.java:16)

將Repository排除,則掃描不到被@Repository("userRepositoryImpl")註解的Bean userRepositoryImpl,顯示該Bean未被定義。

3.元件裝配

<context:component-scan> 元素還會自動註冊 AutowiredAnnotationBeanPostProcessor 例項, 該例項可以自動裝配具有 @Autowired 和 @Resource 、@Inject註解的屬性.

3.1 使用@Autowired自動裝配所有Bean

@Autowired 註解自動裝配具有相容型別的單個 Bean屬性。

  • 構造器, 普通欄位(即使是非 public), 一切具有引數的方法都可以應用@Authwired 註解
  • 預設情況下, 所有使用 @Authwired 註解的屬性都需要被設定. 當 Spring 找不到匹配的 Bean 裝配屬性時, 會丟擲異常, 若某一屬性允許不被設定, 可以設定 @Authwired 註解的 required 屬性為 false
  • 預設情況下, 當 IOC 容器裡存在多個型別相容的 Bean 時, 通過型別的自動裝配將無法工作. 此時可以在 @Qualifier 註解裡提供 Bean 的名稱. Spring 允許對方法的入參標註 @Qualifiter 已指定注入 Bean 的名稱
  • @Authwired 註解也可以應用在陣列型別的屬性上, 此時 Spring 將會把所有匹配的 Bean 進行自動裝配.
  • @Authwired 註解也可以應用在集合屬性上, 此時 Spring 讀取該集合的型別資訊, 然後自動裝配所有與之相容的 Bean.
  • @Authwired 註解用在 java.util.Map 上時, 若該 Map 的鍵值為 String, 那麼 Spring 將自動裝配與之 Map 值型別相容的 Bean, 此時 Bean 的名稱作為鍵值.

示例程式碼:使用Autowired註解

UserController.java

package com.java.spring.annotation.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import com.java.spring.annotation.service.UserService;
@Controller
public class UserController {
	@Autowired
	private UserService userservice;
	public void excute(){
		System.out.println("UserController's excute...");
		userservice.add();
	}
}

 UserService.java

package com.java.spring.annotation.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.java.spring.annotation.repository.UserRepository;
@Service
public class UserService {
	@Autowired
	private UserRepository userrepository;
	public void add(){
		System.out.println("UserService's add...");
		userrepository.save();
	}
}

UserRepositoryImpl.java

package com.java.spring.annotation.repository;
import org.springframework.stereotype.Repository;
@Repository("userRepository")
public class UserRepositoryImpl implements UserRepository {
	@Override
	public void save() {
	    System.out.println("UserRepository's save...");
	}
}

在主方法中測試:

public class Main {
	public static void main(String[] args){
		ApplicationContext ctx=new ClassPathXmlApplicationContext("beans-annotation.xml");
        UserController uc=(UserController) ctx.getBean("userController");
        System.out.println(uc);
        uc.excute();
	}

主方法中呼叫uc.excute();指向 UserController.java 中的excute()方法,先執行System.out.println("UserController's excute...");,再呼叫userservice.add();方法,此時需要使用Autowired註解UserService這個Bean,否則相當於UserService這個Bean未配置到IOC容器中去,出錯;userservice.add();方法指向 UserService.java中的add()方法,先執行System.out.println("UserService's add...");,再呼叫userrepository.save();方法,同理需要註解Bean,可以看到在UserRepositoryImpl.java中使用註解@Repository("userRepository")。

3.2 @Resource 和 @Inject

Spring 還支援 @Resource 和 @Inject 註解,這兩個註解和 @Autowired 註解的功用類似。@Resource 註解要求提供一個 Bean 名稱的屬性,若該屬性為空,則自動採用標註處的變數或方法名作為 Bean 的名稱。@Inject 和 @Autowired 註解一樣也是按型別匹配注入的 Bean, 但沒有 reqired 屬性。建議使用 @Autowired 註解。

4.泛型依賴注入

Spring 4.x 中可以為子類注入子類對應的泛型型別的成員變數的引用。

BaseService<T>中引用了BaseRepository<T>,UserService為BaseService<T>的實現類,UserRepository為BaseRepository<T>的實現類,那麼UserService和BaseService之間會自動地建立引用關係。

示例程式碼:

BaseService.java

package com.java.spring.generic.di;

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

public class BaseService<T>{
	@Autowired
    private BaseRepository<T> repository;
    public void add(){
    	System.out.println("add...");
    	System.out.println(repository);
    }
}

BaseRepository.java

package com.java.spring.generic.di;

public class BaseRepository<T> {

}

User.java

package com.java.spring.generic.di;
public class User {
	public User() {
	}
}

UserService.java

package com.java.spring.generic.di;
import org.springframework.stereotype.Service;
@Service
public class UserService extends BaseService<User> {
	
}

UserRepository.java

package com.java.spring.generic.di;
import org.springframework.stereotype.Repository;
@Repository
public class UserRepository extends BaseRepository{
	public UserRepository() {
		
	}
}

在beans-generic-di.xml中進行配置:

<context:component-scan base-package="com.java.spring.generic.di"></context:component-scan>

在主方法中進行測試:

public static void main(String[] args){
		ApplicationContext ctx=new ClassPathXmlApplicationContext("beans-generic-di.xml");
		UserService userService=(UserService) ctx.getBean("userService");
		System.out.println(userService);
		userService.add();
	}

執行輸出:

[email protected]
add...
[email protected]

在以上的程式碼中,BaseService中引用了BaseReponsitory,並且在BaseService的add方法中呼叫了BaseReponsitory的add方法。在他們的子類中,繼承了這種關係。因此我們在測試方法中呼叫userService.add(); 也是可以成功地呼叫UserReponsitory中的add方法。