1. 程式人生 > >Spring中Bean和配置和注入詳解

Spring中Bean和配置和注入詳解

1 bean與spring容器的關係

Bean配置資訊定義了Bean的實現及依賴關係,Spring容器根據各種形式的Bean配置資訊在容器內部建立Bean定義登錄檔,然後根據登錄檔載入、例項化Bean,並建立Bean和Bean的依賴關係,最後將這些準備就緒的Bean放到Bean快取池中,以供外層的應用程式進行呼叫。

1 bean配置

bean配置有三種方法:

  • 基於xml配置Bean
  • 使用註解定義Bean
  • 基於java類提供Bean定義資訊

1.1 基於xml配置Bean

 對於基於XML的配置,Spring 2.0以後使用Schema的格式,使得不同型別的配置擁有了自己的名稱空間,是配置檔案更具擴充套件性。

①預設名稱空間:它沒有空間名,用於Spring Bean的定義;

②xsi名稱空間:這個名稱空間用於為每個文件中名稱空間指定相應的Schema樣式檔案,是標準組織定義的標準名稱空間;

③aop名稱空間:這個名稱空間是Spring配置AOP的名稱空間,是使用者自定義的名稱空間。

名稱空間的定義分為兩個步驟:第一步指定名稱空間的名稱;第二步指定名稱空間的Schema文件樣式檔案的位置,用空格或回車換行進行分分隔。

1.1.1 Bean基本配置

在Spring容器的配置檔案中定義一個簡要Bean的配置片段如下所示:

一般情況下,Spring IOC容器中的一個Bean即對應配置檔案中的一個<bean>,這種映象對應關係應該容易理解。其中id為這個Bean的名稱,通過容器的getBean("foo")即可獲取對應的Bean,在容器中起到定位查詢的作用,是外部程式和Spring IOC容器進行互動的橋樑。class屬性指定了Bean對應的實現類。

下面是基於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"
         xsi:schemaLocation="http://www.springframework.org/schema/beans 
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
     <bean id="car" name="#car1" class="com.baobaotao.simple.Car"></bean>  
     <bean id="boss" class="com.baobaotao.simple.Boss"></bean>
</beans>

複製程式碼

1.1.2 依賴注入

  1. 屬性注入
  2. 建構函式注入
  3. 工廠方式注入

1.2 使用註解定義Bean

我們知道,Spring容器成功啟動的三大要件分別是:Bean定義資訊、Bean實現類以及Spring本身。如果採用基於XML的配置,Bean定義資訊和Bean實現類本身是分離的,而採用基於註解的配置方式時,Bean定義資訊即通過在Bean實現類上標註註解實現。

下面是使用註解定義一個DAO的Bean:

複製程式碼

package com.baobaotao.anno;

import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
//①通過Repository定義一個DAO的Bean

@Component("userDao")
public class UserDao {

}

複製程式碼

在①處,我們使用@Component註解在UserDao類宣告處對類進行標註,它可以被Spring容器識別,Spring容器自動將POJO轉換為容器管理的Bean。

它和以下的XML配置是等效的:

<bean id="userDao" class="com.baobaotao.anno.UserDao"/>

除了@Component以外,Spring提供了3個功能基本和@Component等效的註解,它們分別用於對DAO、Service及Web層的Controller進行註解,所以也稱這些註解為Bean的衍型註解:(類似於xml檔案中定義Bean<bean id=" " class=" "/>

  • @Repository:用於對DAO實現類進行標註;
  • @Service:用於對Service實現類進行標註;
  • @Controller:用於對Controller實現類進行標註;

之所以要在@Component之外提供這三個特殊的註解,是為了讓註解類本身的用途清晰化,此外Spring將賦予它們一些特殊的功能。

1.2.1 使用註解配置資訊啟動spring容器

Spring提供了一個context的名稱空間,它提供了通過掃描類包以應用註解定義Bean的方式:

複製程式碼

<?xml version="1.0" encoding="UTF-8" ?>
<!--①宣告context的名稱空間-->
<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-3.0.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context-3.0.xsd"
         >
    <!--②掃描類包以應用註解定義的Bean-->
   <context:component-scan base-package="com.baobaotao.anno"/>
   <bean class="com.baobaotao.anno.LogonService"></bean>
   <!-- context:component-scan base-package="com.baobaotao" resource-pattern="anno/*.class"/ -->
   <!-- context:component-scan base-package="com.baobaotao">
       <context:include-filter type="regex" expression="com\.baobaotao\.anno.*Dao"/>
       <context:include-filter type="regex" expression="com\.baobaotao\.anno.*Service"/>
       <context:exclude-filter type="aspectj" expression="com.baobaotao..*Controller+"/>
   </context:component-scan -->
</beans>

複製程式碼

在①處宣告context名稱空間,在②處即可通過context名稱空間的component-scan的base-package屬性指定一個需要掃描的基類包,Spring容器將會掃描這個基類包裡的所有類,並從類的註解資訊中獲取Bean的定義資訊。

如果僅希望掃描特定的類而非基包下的所有類,你們可以使用resource-pattern屬性過濾特定的類,如下所示:

< context:component-scan base-package="com.baobaotao" resource-pattern="anno/*.class"/ >

這裡我們將基類包設定為com.baobaotao,預設情況下resource-pattern屬性的值為"**/*.class",即基類包裡的所有類。這裡我們設定為"anno/*.class",則Spring僅會掃描基包裡anno子包中的類。

1.3 基於java類提供Bean定義

在普通的POJO類中只要標註@Configuration註解,就可以為spring容器提供Bean定義的資訊了,每個標註了@Bean的類方法都相當於提供了一個Bean的定義資訊。

複製程式碼

package com.baobaotao.conf;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//①將一個POJO標註為定義Bean的配置類
@Configuration
public class AppConf {
        //②以下兩個方法定義了兩個Bean,以提供了Bean的例項化邏輯
    @Bean
    public UserDao userDao(){
       return new UserDao();    
    }
    
    @Bean
    public LogDao logDao(){
        return new LogDao();
    }
    //③定義了logonService的Bean
    @Bean
    public LogonService logonService(){
        LogonService logonService = new LogonService();
                //④將②和③處定義的Bean注入到LogonService Bean中
        logonService.setLogDao(logDao());
        logonService.setUserDao(userDao());
        return logonService;
    }
}

複製程式碼

①處在APPConf類的定義處標註了@Configuration註解,說明這個類可用於為Spring提供Bean的定義資訊。類的方法處可以標註@Bean註解,Bean的型別由方法返回值型別決定,名稱預設和方法名相同,也可以通過入參顯示指定Bean名稱,如@Bean(name="userDao").直接在@Bean所標註的方法中提供Bean的例項化邏輯。

在②處userDao()和logDao()方法定義了一個UserDao和一個LogDao的Bean,它們的Bean名稱分別是userDao和logDao。在③處,又定義了一個logonService Bean,並且在④處注入②處所定義的兩個Bean。

因此,以上的配置和以下XML配置時等效的:

<bean id="userDao" class="com.baobaotao.anno.UserDao"/>
<bean id="logDao" class="com.baobaotao.anno.LogDao"/>
<bean id="logService" class="com.baobaotao.conf.LogonService"
    p:logDao-ref="logDao" p:userDao-ref="userDao"/>

基於java類的配置方式和基於XML或基於註解的配置方式相比,前者通過程式碼的方式更加靈活地實現了Bean的例項化及Bean之間的裝配,但後面兩者都是通過配置宣告的方式,在靈活性上要稍遜一些,但是配置上要更簡單一些。

 2 Bean注入

Bean注入的方式有兩種,一種是在XML中配置,此時分別有屬性注入、建構函式注入和工廠方法注入;另一種則是使用註解的方式注入 @Autowired,@Resource,@Required

2.1 在xml檔案中配置依賴注入

2.1.1 屬性注入

屬性注入即通過setXxx()方法注入Bean的屬性值或依賴物件,由於屬性注入方式具有可選擇性和靈活性高的優點,因此屬性注入是實際應用中最常採用的注入方式。

屬性注入要求Bean提供一個預設的建構函式,併為需要注入的屬性提供對應的Setter方法。Spring先呼叫Bean的預設建構函式例項化Bean物件,然後通過反射的方式呼叫Setter方法注入屬性值。

複製程式碼

package com.baobaotao.anno;

import org.springframework.beans.factory.BeanNameAware;

public class LogonService implements BeanNameAware{

    private LogDao logDao;

    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void setLogDao(LogDao logDao) {
        this.logDao = logDao;
    }
    
    public LogDao getLogDao() {
        return logDao;
    }
    public UserDao getUserDao() {
        return userDao;
    }
    
    public void setBeanName(String beanName) {
        System.out.println("beanName:"+beanName);        
    }
    
    public void initMethod1(){
        System.out.println("initMethod1");
    }
    public void initMethod2(){
        System.out.println("initMethod2");
    }
    
}

複製程式碼

bean.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: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"
       default-autowire="byName"
         >
    <bean id="logDao" class="com.baobaotao.anno.LogDao"/>
    <bean id="userDao" class="com.baobaotao.anno.UserDao"/>
   <bean class="com.baobaotao.anno.LogonService">
       <property name="logDao" ref="logDao"></property>
       <property name="userDao" ref="userDao"></property>
   </bean>
</beans>

複製程式碼

2.1.2 構造方法注入

使用建構函式注入的前提是Bean必須提供帶引數的建構函式。例如

複製程式碼

package com.baobaotao.anno;

import org.springframework.beans.factory.BeanNameAware;

public class LogonService implements BeanNameAware{

    public LogonService(){}

    public LogonService(LogDao logDao, UserDao userDao) {
        this.logDao = logDao;
        this.userDao = userDao;
    }

    private LogDao logDao;

    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void setLogDao(LogDao logDao) {
        this.logDao = logDao;
    }
    
    public LogDao getLogDao() {
        return logDao;
    }
    public UserDao getUserDao() {
        return userDao;
    }
    
    public void setBeanName(String beanName) {
        System.out.println("beanName:"+beanName);        
    }
    
    public void initMethod1(){
        System.out.println("initMethod1");
    }
    public void initMethod2(){
        System.out.println("initMethod2");
    }
    
}

複製程式碼

bean.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: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"
       default-autowire="byName">

    <bean id="logDao" class="com.baobaotao.anno.LogDao"/>
    <bean id="userDao" class="com.baobaotao.anno.UserDao"/>
   <bean class="com.baobaotao.anno.LogonService">
      <constructor-arg  ref="logDao"></constructor-arg>
       <constructor-arg ref="userDao"></constructor-arg>
   </bean>

</beans>

複製程式碼

2.1.3 工廠方法注入

非靜態工廠方法:

有些工廠方法是非靜態的,即必須例項化工廠類後才能呼叫工廠放。

複製程式碼

package com.baobaotao.ditype;

public class CarFactory {
   public Car createHongQiCar(){
       Car car = new Car();
       car.setBrand("紅旗CA72");
       return car;
   }
   
   public static Car createCar(){
       Car car = new Car();
       return car;
   }
}

複製程式碼

工廠類負責建立一個或多個目標類例項,工廠類方法一般以介面或抽象類變數的形式返回目標類例項,工廠類對外遮蔽了目標類的例項化步驟,呼叫者甚至不用知道具體的目標類是什麼。

複製程式碼

<?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="carFactory" class="com.baobaotao.ditype.CarFactory" />
    <bean id="car5" factory-bean="carFactory" factory-method="createHongQiCar">
    </bean>


</beans>

複製程式碼

靜態工廠方法:

很多工廠類都是靜態的,這意味著使用者在無須建立工廠類例項的情況下就可以呼叫工廠類方法,因此,靜態工廠方法比非靜態工廠方法的呼叫更加方便。

複製程式碼

<?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="car6" class="com.baobaotao.ditype.CarFactory"
        factory-method="createCar"></bean>

</beans>

複製程式碼

2.2 使用註解的方式注入

2.2.1 使用@Autowired進行自動注入

Spring通過@Autowired註解實現Bean的依賴注入,下面是一個例子:

複製程式碼

package com.baobaotao.anno;

import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
//① 定義一個Service的Bean(不需要在XML中定義Bean)
@Service
public class LogonService implements BeanNameAware{
        //② 分別注入LogDao及UserDao的Bean(不需要在XML中定義property屬性注入)
    @Autowired(required=false)
    private LogDao logDao;
    @Autowired
    @Qualifier("userDao")
    private UserDao userDao;
    
    public LogDao getLogDao() {
        return logDao;
    }
    public UserDao getUserDao() {
        return userDao;
    }
    
    public void setBeanName(String beanName) {
        System.out.println("beanName:"+beanName);        
    }
    
    public void initMethod1(){
        System.out.println("initMethod1");
    }
    public void initMethod2(){
        System.out.println("initMethod2");
    }
    
}
 

複製程式碼

在①處,我們使用@Service將LogonService標註為一個Bean,在②處,通過@Autowired注入LogDao及UserDao的Bean。@Autowired預設按型別匹配的方式,在容器查詢匹配的Bean,當有且僅有一個匹配的Bean時,Spring將其注入到@Autowired標註的變數中。

2.2.2 使用@Autowired的required屬性

如果容器中沒有一個和標註變數型別匹配的Bean,Spring容器啟動時將報NoSuchBeanDefinitionException的異常。如果希望Spring即使找不到匹配的Bean完成注入也不用丟擲異常,那麼可以使用@Autowired(required=false)進行標註:

@Service
public class LogonService implements BeanNameAware{
    @Autowired(required=false)
    private LogDao logDao;
        ...
}

預設情況下,@Autowired的required屬性的值為true,即要求一定要找到匹配的Bean,否則將報異常。

2.2.3 使用@Qualifier指定注入Bean的名稱

如果容器中有一個以上匹配的Bean時,則可以通過@Qualifier註解限定Bean的名稱,如下所示:

複製程式碼

@Service
public class LogonService implements BeanNameAware{
    @Autowired(required=false)
    private LogDao logDao;
    //①注入名為UserDao,型別為UserDao的Bean
    @Autowired
    @Qualifier("userDao")
    private UserDao userDao;
}

複製程式碼

這裡假設容器有兩個型別為UserDao的Bean,一個名為userDao,另一個名為otherUserDao,則①處會注入名為userDao的Bean。

2.2.4 對類方法進行標註

@Autowired可以對類成員變數及方法的入參進行標註,下面我們在類的方法上使用@Autowired註解:

複製程式碼

package com.baobaotao.anno;

import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class LogonService implements BeanNameAware{
    
    private LogDao logDao;
    private UserDao userDao;
    
    
    @Autowired
    public void setLogDao(LogDao logDao) {
        this.logDao = logDao;
    }
    
    @Autowired
    @Qualifier("userDao")
    public void setUserDao(UserDao userDao) {
        System.out.println("auto inject");
        this.userDao = userDao;
    }
    
}

複製程式碼

如果一個方法擁有多個入參,在預設情況下,Spring自動選擇匹配入參型別的Bean進行注入。Spring允許對方法入參標註@Qualifier以指定注入Bean的名稱,如下所示:

    @Autowired
    public void init(@Qualifier("userDao")UserDao userDao,LogDao logDao){
        System.out.println("multi param inject");
        this.userDao = userDao;
        this.logDao =logDao;
    }

在以上例子中,UserDao的入參注入名為userDao的Bean,而LogDao的入參注入LogDao型別的Bean。

一般情況下,在Spring容器中大部分的Bean都是單例項的,所以我們一般都無須通過@Repository、@Service等註解的value屬性為Bean指定名稱,也無須使用@Qualifier按名稱進行注入。

2.2.5 對標準註解的支援

此外,Spring還支援@Resource和@Inject註解,這兩個標準註解和@Autowired註解的功能型別,都是對類變數及方法入參提供自動注入的功能。@Resource要求提供一個Bean名稱的屬性,如果屬性為空,則自動採用標註處的變數名或方法名作為Bean的名稱。

複製程式碼

package com.baobaotao.anno;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;

import org.springframework.stereotype.Component;

@Component
public class Boss {
    
    private Car car;
    
    public Boss(){
        System.out.println("construct...");
    }

//    @Autowired
//    private void setCar(Car car){
//        System.out.println("execute in setCar");
//        this.car = car;
//    }
    
    @Resource("car")
    private void setCar(Car car){
        System.out.println("execute in setCar");
        this.car = car;
    }
    
    @PostConstruct
    private void init1(){
        System.out.println("execute in init1");
    }
    
    @PostConstruct
    private void init2(){
        System.out.println("execute in init1");
    }
    
    @PreDestroy
    private void destory1(){
        System.out.println("execute in destory1");
    }
    
    @PreDestroy
    private void destory2(){
        System.out.println("execute in destory2");
    }

}

複製程式碼

這時,如果@Resource未指定"car"屬性,則也可以根據屬性方法得到需要注入的Bean名稱。可見@Autowired預設按型別匹配注入Bean,@Resource則按名稱匹配注入Bean。而@Inject和@Autowired一樣也是按型別匹配注入的Bean的,只不過它沒有required屬性。可見不管是@Resource還是@Inject註解,其功能都沒有@Autowired豐富,因此除非必須,大可不必在乎這兩個註解。(類似於Xml中使用<constructor-arg ref="logDao"></constructor-arg>或者<property name="logDao" ref="logDao"></property>進行注入,如果使用了@Autowired或者Resource等,這不需要在定義Bean時使用屬性注入和構造方法注入了)

2.2.6 關於Autowired和@Resource

[email protected]注入是按照型別注入的,只要配置檔案中的bean型別和需要的bean型別是一致的,這時候注入就沒問題。但是如果相同型別的bean不止一個,此時注入就會出現問題,Spring容器無法啟動。  [email protected]標籤是按照bean的名字來進行注入的,如果我們沒有在使用@Resource時指定bean的名字,同時Spring容器中又沒有該名字的bean,這時候@Resource就會退化為@Autowired即按照型別注入,這樣就有可能違背了使用@Resource的初衷。所以建議在使用@Resource時都顯示指定一下bean的名字@Resource(name="xxx") 

2.2.7 讓@Resource和@Autowired生效的幾種方式

1.在xml配置檔案中顯式指定 

<!-- 為了使用Autowired標籤,我們必須在這裡配置一個bean的後置處理器 -->  
    <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />   
      
    <!-- 為了使用@Resource標籤,這裡必須配置一個後置處理器 -->  
    <bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />   

2.在xml配置檔案中使用context:annotation-config 

<context:annotation-config />

3.在xml配置檔案中使用context:component-scan 

<context:component-scan base-package="com.baobaotao.anno"/>

4.重寫Spring容器的Context,在自定義BeanFactory時呼叫AnnotationConfigUtils.registerAnnotationConfigProcessors()把這兩個註解處理器增加到容器中。 

一開始使用公司框架的時候發現可以在web層使用@Resource以及@Autowired來注入一些bean,首先這個註解是Spring提供的,自己把這部分程式碼抽出來寫了小例子,發現要想使用Spring的這兩註解,必須直接或者間接的引入AutowiredAnnotationBeanPostProcesso以及CommonAnnotationBeanPostProcessor這兩個註解處理器引入到BeanDefinitions中,否則不會實現注入的,但是仔細閱讀公司框架程式碼後發現沒有地方直接或間接引入這兩個註解處理器,發現一個細節,公司框架所依賴的Spring版本是2.5.6而我使用的Spring版本是2.5.5,當初的結論是高版本的Spring在容器啟動的時候,自動把這兩個註解處理器加入到BeanDefinitions中,這幾天仔細看了看Spring的原始碼,發現Spring2.5.6並沒有這樣做。然後子寫DEBUG了一下公司框架的原始碼,最後發現原來公司框架有一個自己的XmlWebApplicationContext,在這個context中重寫customizeBeanFactory(),在這個方法中呼叫了AnnotationConfigUtils.registerAnnotationConfigProcessors()方法把這兩自動註解處理器加入到BeanDefinitions中,這樣公司框架在web層就支援@Resource和@Autowired進行自動注入啦 

複製程式碼

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.alibaba.citrus.springext.support.context;

import com.alibaba.citrus.springext.ResourceLoadingExtendable;
import com.alibaba.citrus.springext.ResourceLoadingExtender;
import com.alibaba.citrus.springext.support.context.InheritableListableBeanFactory;
import com.alibaba.citrus.springext.support.resolver.XmlBeanDefinitionReaderProcessor;
import java.io.IOException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;

public class XmlWebApplicationContext extends org.springframework.web.context.support.XmlWebApplicationContext implements ResourceLoadingExtendable {
    private ResourceLoadingExtender resourceLoadingExtender;
    private boolean parentResolvableDependenciesAccessible = true;

    public XmlWebApplicationContext() {
    }

    public boolean isParentResolvableDependenciesAccessible() {
        return this.parentResolvableDependenciesAccessible;
    }

    public void setParentResolvableDependenciesAccessible(boolean parentResolvableDependenciesAccessible) {
        this.parentResolvableDependenciesAccessible = parentResolvableDependenciesAccessible;
    }

    public void setResourceLoadingExtender(ResourceLoadingExtender resourceLoadingExtender) {
        if(this.resourceLoadingExtender != null) {
            this.getApplicationListeners().remove(this.resourceLoadingExtender);
        }

        this.resourceLoadingExtender = resourceLoadingExtender;
        if(resourceLoadingExtender instanceof ApplicationListener) {
            this.addApplicationListener((ApplicationListener)resourceLoadingExtender);
        }

    }

    protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {
        (new XmlBeanDefinitionReaderProcessor(beanDefinitionReader)).addConfigurationPointsSupport();
    }

    protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
        super.customizeBeanFactory(beanFactory);
    //AnnotationConfigUtils.registerAnnotationConfigProcessors()方法把這兩自動註解處理器加入到BeanDefinitions中,這樣公司框架在web層就支援@Resource和@Autowired進行自動注入
        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory, (Object)null);
    }

    protected DefaultListableBeanFactory createBeanFactory() {
        return (DefaultListableBeanFactory)(this.isParentResolvableDependenciesAccessible()?new InheritableListableBeanFactory(this.getInternalParentBeanFactory()):super.createBeanFactory());
    }

    protected Resource getResourceByPath(String path) {
        Resource resource = null;
        if(this.resourceLoadingExtender != null) {
            resource = this.resourceLoadingExtender.getResourceByPath(path);
        }

        if(resource == null) {
            resource = super.getResourceByPath(path);
        }

        return resource;
    }

    protected ResourcePatternResolver getResourcePatternResolver() {
        final ResourcePatternResolver defaultResolver = super.getResourcePatternResolver();
        return new ResourcePatternResolver() {
            public Resource[] getResources(String locationPattern) throws IOException {
                ResourcePatternResolver resolver = null;
                if(XmlWebApplicationContext.this.resourceLoadingExtender != null) {
                    resolver = XmlWebApplicationContext.this.resourceLoadingExtender.getResourcePatternResolver();
                }

                if(resolver == null) {
                    resolver = defaultResolver;
                }

                return resolver.getResources(locationPattern);
            }

            public ClassLoader getClassLoader() {
                return defaultResolver.getClassLoader();
            }

            public Resource getResource(String location) {
                return defaultResolver.getResource(location);
            }
        };
    }
}