Spring是從兩個角度來實現自動化裝配:
- 組件掃描(component scanning) : Spring會自動發現應用上下文中所創建的bean
- 自動裝配(autowiring) : Spring自動滿足bean之間的依賴
文章目錄
1.組件掃描
1.1Java配置類方式-配置組件掃描
問題: 這個類Spring是如何發現該類的?--> 參考使用Java配置類創建Spring容器(上下文)
在 Spring| 上下文和Bean的生命週期 一文中已經介紹了Spring基於配置類生成上下文的例子,這裏同樣是使用基於配置類來構建上下文,與之前不同的是,我們的Bean配置是通過掃描包的方式自動生成,而不是使用Bean註解來配置了.
A.配置組件掃描包
這裏我討論的是Spring強推的使用Java配置類來配置組件掃描的方式:類似如下代碼
// @Named
@Component
public class Role {
private String id;
private String name;
private String desc;
//省略get,set方法
}
package com.yveshe.chapter2.bean;
public interface ScanPackageInfoInterface {
}
@Configuration
@ComponentScan(basePackageClasses = com.yveshe.chapter2.bean.ScanPackageInfoInterface.class)
public class SpringContextConf {
}
public class JavaConfComponetScanTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(com.yveshe.chapter2.contextconf.javaconf.SpringContextConf.class);
com.yveshe.chapter2.bean.Role role = context.getBean(com.yveshe.chapter2.bean.Role.class);
context.close();
System.out.println(JsonUtil.toJson(role));
}
}
/**輸出結果:
{
"age":18,
"name":"yveshe-JavaConfComponetScanTest",
"role":{
"desc":"擁有整個系統的管理權限",
"id":"4d53a9ab-e858-4369-8240-46dd497ace3e",
"name":"系統管理員"
}
}
**/
@Component
申明該類爲組件類,允許Spring管理
@Configuration
申明該類是配置類
@ComponentScan
配置掃描路徑(支持String類型的包名配置和Class類型的類所在包下)
這裏主要講解的是@ComponentScan註解的使用,默認的方式是可以配置String類型的basePackages,但是考慮到代碼的重構,建議大家考慮在包中創建一個用來進行掃描的空標記接口(markerinterface) ,使用basePackageClasses參數的方式來配置掃描包.,比如我這裏配置的ScanPackageInfoInterface就是一個空的接口
ComponentScan註解相關參數如下,我們可以發現常用的支持String類型的包名配置,和Class類型的配置,也支持配置多個包:
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
boolean useDefaultFilters() default true;
Filter[] includeFilters() default {};
Filter[] excludeFilters() default {};
boolean lazyInit() default false;
B.配置生成Bean的Id
Spring應用上下文中所有的bean都會給定一個ID。當沒有指定ID的時候,默認的是類名將首字母小寫後的結果,比如 Role 類的Id默認爲 role
// @Named("role")
@Component("role")
public class Role {
private String id;
private String name;
private String desc;
// 省略 get,set
}
如果用戶需要指定Bean的Id,可以通過主機@Component註解的value來設置,也可以使用Java依賴注入規範(Java Dependency Injection) 中所提供的@Named註解來爲bean設置ID.Spring支持將@Named作爲@Component註解的替代方案。 兩者之間有一些細微的差異, 但是在大多數場景中, 它們是可以互相替換的。話雖如此, 但是我更加強烈地喜歡@Component註解,因爲@Component簡單的表達了組件的含義.這兩個註解中都只有一個屬性value.
Java依賴注入規範(Java Dependency Injection)
Java依賴注入規範(Java Dependency Injection) 中所提供的@Named註解來生成Bean,@Inject註解來注入-> 大部分時候等價Spring中的@Autowriter註解
參考: http://tool.oschina.net/uploads/apidocs/javaEE6/javax/inject/package-summary.html
更多詳細可以參考: Spring| Java註解規範與Spring註解對比
1.2XML方式-配置組件掃描
使用XML來啓用組件掃描的話, 那麼可以使用Spring context命名空間的<context:component-scan>
元素,<context:component-scan>
元素會有與@ComponentScan註解相對應的屬性和子元素。
XML配置掃描方式,不需要Java配置類只需要在Spring上下文配置XMl中配置掃描標籤既可.同樣在需要被掃描註冊的Bean也是需要使用@Component之類的標籤說明的,下面是流程代碼示例:
// @Named
@Component
public class Role {
private String id;
private String name;
private String desc;
//省略get,set方法
}
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config />
<context:component-scan base-package="com.yveshe.chapter2.bean" />
</beans>
public class XmlComponetScanTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/com/yveshe/chapter2/contextconf/xml/spring-context.xml");
com.yveshe.chapter2.bean.Role role = context.getBean(com.yveshe.chapter2.bean.Role.class);
context.close();
System.out.println(JsonUtil.toJson(role, true));
}
}
<context:annotation-config />
:
是用於激活那些已經在spring容器裏註冊過的bean(無論是通過xml的方式還是通過Java註解方式),這樣在自動注入依賴的時候,Spring纔會將已經註冊的Bean自動裝載進其他需要該依賴的Bean中.
<context:component-scan />
:
除了具有<context:annotation-config>
的功能之外,<context:component-scan />
還可以在指定的package下掃描以及註冊javabean 。
參考: context:annotation-config 和 context:component-scan的區別
2.自動裝配
如果我們的應用程序所有對象都是獨立的, 彼此之間沒有任何依賴,那麼僅僅使用Spring的掃描功能就算完事了,它會自動的幫我們把Java對象創建.但是如果對象與對象之間如果存在相互依賴的聯繫時,我們就需要用的Spring給我們提供的自動裝配了.
2.1自動裝配關鍵註解
- @Autowired (Spring定義)
- @Inject (Java依賴注入規範)
關於Java依賴規範中定義瞭如下註解: (Named 大部分情況下等價Spring中Component註解,Spring對該規範做了支持)
Annotation Types Summary | |
---|---|
Inject | Identifies injectable constructors, methods, and fields. |
Named | String-based qualifier. |
Qualifier | Identifies qualifier annotations. |
Scope | Identifies scope annotations. |
Singleton | Identifies a type that the injector only instantiates once. |
可以參看: http://tool.oschina.net/uploads/apidocs/javaEE6/javax/inject/package-summary.html
@Autowired的替代品:
如果你不願意在代碼中到處使用Spring的特定註解@Autowired來完成自動裝配任務的話,那麼你可以考慮將其替換爲@Inject註解,不過在Spring的項目中還是建議使用@Autowired註解
2.2自動裝配原理 (主要是@Autowired註解的使用)
什麼是自動裝配?
簡單來說, 自動裝配就是讓Spring自動滿足bean依賴的一種方法, 在滿足依賴的過程中, 會在Spring應用上下文中尋找匹配某個bean需求的其他bean。
如何使用Spring的自動裝配功能?
在Spring中爲了聲明要進行自動裝配, 我們可以藉助Spring的@Autowired註解來實現.
2.2.1@Autowired的簡單例子
代碼結果如下:
@Component
public class Role {
private String id;
private String name;
private String desc;
// 省略get,set方法
}
沒有使用自動裝配的User類
@Component
public class User {
private String name;
private int age;
private Role role;
//省略get,set
}
沒有使用自動裝配的User類
@Component
public class User2 {
private String name;
private int age;
private Role role;
/**
* 不管是構造方法、 Setter方法還是其他的有參方法上所聲明的依賴都是可以進行自動裝配的
*
* 也就是說: 與這個方法名稱沒有關係setRole於yveshe是一樣的效果,不過在Java中,通常我們還是用這些默認的名稱
*
* @param role
*/
@Autowired
public void aaa(Role role) {
this.role = role;
}
//省略其他get,set方法
}
測試結果:
public class AutowiredTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("com/yveshe/chapter2/autowired/contextconf/spring-context-4autowired.xml");
com.yveshe.chapter2.autowired.bean.User user = context.getBean(com.yveshe.chapter2.autowired.bean.User.class);
com.yveshe.chapter2.autowired.bean.User2 user2 = context.getBean(com.yveshe.chapter2.autowired.bean.User2.class);
context.close();
}
}
我們可以發現role實例對象注入進了user2中,而user中卻沒有背注入
2.2.2@Autowired具體使用介紹
註解Autowired源碼如下:
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
/**
* Declares whether the annotated dependency is required.
* <p>Defaults to {@code true}.
*/
boolean required() default true;
}
- 如果
沒有匹配的bean
, 那麼在應用上下文創建的時候, Spring會拋出一個異常。爲了避免異常的出現, 你可以將@Autowired的required屬性設置爲false,將required屬性設置爲false時, Spring會嘗試執行自動裝配, 但是如果沒有匹配的bean的話, Spring將會讓這個bean處於未裝配的狀態,(也就相當於沒有進行賦值,引用類型就是Null了)如果在你的代碼中沒有進行null檢查的話, 這個處於未裝配狀態的屬性有可能會出現NullPointerException。 - 如果
匹配出現多個
滿足條件的, Spring將會拋出一個異常,表明沒有明確指定要選擇哪個bean進行自動裝配。( 在第3章中, 我們會進一步討論自動裝配中的歧義性。)
總結自動裝配本質:
不管是構造方法、 Setter方法還是其他的有參方法上所聲明的依賴都是可以進行自動裝配,說明與方法名稱無關
.