Spring| Spring 組件掃描和自動裝配5

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方法還是其他的有參方法上所聲明的依賴都是可以進行自動裝配,說明與方法名稱無關.

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