本文轉載自:https://www.cnblogs.com/wuchanming/p/5426746.html
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 使用注解定義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
1.@Autowired注入是按照類型注入的,只要配置文件中的bean類型和需要的bean類型是一致的,這時候注入就沒問題。但是如果相同類型的bean不止一個,此時注入就會出現問題,Spring容器無法啟動。
2.@Resourced標簽是按照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); } }; } }