目录
1.3、 ApplicationContext 与 ResourceLoader
2.2、MessageSource 与 ApplicationContext
3.1.2、@Autowired 之外的选择——使用JSR250 标注依赖注入关系
1、统一资源加载策略
1.1、Spring中的 Resource
Spring框架内部使用 org.springframework.core.io.Resource 接口作为所有资源的抽象和访问接口。
Resource 接口可以根据资源的不同类型,或者资源所处的不同场合,给出相应的具体实现。Spring框架在这个理念的基础上,提供了一些实现类(可以在 org.springframework.core.io 包下找到这些实现类)。
- ByteArrayResource 。将字节(byte)数组提供的数据作为一种资源进行封装,如果通过InputStream形式访问该类型的资源,该实现会根据字节数组的数据,构造相应的 ByteArray-InputStream 并返回。
- ClassPathResource 。该实现从Java应用程序的ClassPath中加载具体资源并进行封装,可以使用指定的类加载器(ClassLoader)或者给定的类进行资源加载。
- FileSystemResource 。对 java.io.File 类型的封装,所以,我们可以以文件或者URL的形式对该类型资源进行访问,只要能跟 File 打的交道,基本上跟 FileSystemResource 也可以。
- UrlResource 。通过 java.net.URL 进行的具体资源查找定位的实现类,内部委派URL进行具体的资源操作。
- InputStreamResource 。将给定的 InputStream 视为一种资源的 Resource 实现类,较为少用。可能的情况下,以 ByteArrayResource 以及其他形式资源实现代之。
自定义资源实现
只需实现 org.springframework.core.io.Resource 接口
public interface Resource extends InputStreamSource { boolean exists(); boolean isOpen(); URL getURL() throws IOException; File getFile() throws IOException; Resource createRelative(String relativePath) throws IOException; String getFilename(); String getDescription(); } public interface InputStreamSource { InputStream getInputStream() throws IOException; }
该接口定义了7个方法,可以帮助我们查询资源状态、访问资源内容,甚至根据当前资源创建新的相对资源。不过,要真想实现自定义的 Resource , 我们可以继承org.springframework.core.io.AbstractResource 抽象类,然后根据当前具体资源特征,覆盖相应的方法就可以了。
1.2、ResourceLoader,“更广义的URL”
org.springframework.core.io.ResourceLoader 接口是资源查找定位策略的统一抽象,具体的资源查找定位策略则由相应的 ResourceLoader 实现类给出。
public interface ResourceLoader {
String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
Resource getResource(String location);
ClassLoader getClassLoader();
}
1、可用的ResourceLoader
1、DefaultResourceLoader
ResourceLoader 有一个默认的实现类,即 org.springframework.core.io.DefaultResourceLoader ,该类默认的资源查找处理逻辑如下。
(1) 首先检查资源路径是否以 classpath: 前缀打头,如果是,则尝试构造 ClassPathResource 类型资源并返回。
(2) 否则,(a) 尝试通过URL,根据资源路径来定位资源,如果没有抛出 MalformedURLException ,有则会构造 UrlResource 类型的资源并返回;(b)如果还是无法根据资源路径定位指定的资源,则委派
getResourceByPath(String) 方法来定位 , DefaultResourceLoader 的getResourceByPath(String) 方法默认实现逻辑是,构造 ClassPathResource 类型的资源并返回。DefaultResourceLoader 使用演示
ResourceLoader resourceLoader = new DefaultResourceLoader(); Resource fakeFileResource = resourceLoader.getResource("D:/spring21site/README"); assertTrue(fakeFileResource instanceof ClassPathResource); assertFalse(fakeFileResource.exists()); Resource urlResource1 = resourceLoader.getResource("file:D:/spring21site/README"); assertTrue(urlResource1 instanceof UrlResource); Resource urlResource2 = resourceLoader.getResource("http://www.spring21.cn"); assertTrue(urlResource2 instanceof UrlResource); try{ fakeFileResource.getFile(); fail("no such file with path["+fakeFileResource.getFilename()+"] exists in classpath"); }catch(FileNotFoundException e){ // } try{ urlResource1.getFile(); }catch(FileNotFoundException e){ fail(); }
2、FileSystemResourceLoader
为了避免 DefaultResourceLoader 在最后 getResourceByPath(String) 方法上的不恰当处理,我们可以使用 org.springframework.core.io.FileSystemResourceLoader ,它继承自 DefaultResourceLoader ,但覆写了 getResourceByPath(String) 方法,使之从文件系统加载资源并以FileSystemResource 类型返回。这样,我们就可以取得预想的资源类型。
使用 FileSystemResourceLoader
public void testResourceTypesWithFileSystemResourceLoader(){ ResourceLoader resourceLoader = new FileSystemResourceLoader(); Resource fileResource = resourceLoader.getResource("D:/spring21site/README"); assertTrue(fileResource instanceof FileSystemResource); assertTrue(fileResource.exists()); Resource urlResource = resourceLoader.getResource("file:D:/spring21site/README"); assertTrue(urlResource instanceof UrlResource); }
2、ResourcePatternResolver ——批量查找的 ResourceLoader
ResourcePatternResolver 是 ResourceLoader 的扩展, ResourceLoader 每次只能根据资源路径返回确定的单个 Resource 实例,而 ResourcePatternResolver 则可以根据指定的资源路径匹配模式,每次返回多个 Resource 实例。
public interface ResourcePatternResolver extends ResourceLoader {
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
Resource[] getResources(String locationPattern) throws IOException;
}
ResourcePatternResolver 在继承 ResourceLoader 原有定义的基础上,又引入了 Resource[]getResources(String) 方法定义,以支持根据路径匹配模式返回多个 Resources 的功能。它同时还引入了一种新的协议前缀 classpath*: ,针对这一点的支持,将由相应的子类实现给出。
ResourcePatternResolver 最常用的一个实现是 org.springframework.core.io.support.PathMatchingResourcePatternResolver ,该实现类支持 ResourceLoader 级别的资源加载,支持基于Ant风格的路径匹配模式(类似于 ** / * .suffix之类的路径形式),支持 ResourcePatternResolver 新增加的classpath * :前缀等,基本上集所有技能于一身。
在构造 PathMatchingResourcePatternResolver 实例的时候,可以指定一个 ResourceLoader ,如果不指定的话,则 PathMatchingResourcePatternResolver 内部会默认构造一个 DefaultResourceLoader 实例。 PathMatchingResourcePatternResolver 内部会将匹配后确定的资源路径,委派给它的 ResourceLoader 来查找和定位资源。这样,如果不指定任何 ResourceLoader 的话, Path-MatchingResourcePatternResolver 在加载资源的行为上会与 DefaultResourceLoader 基本相同,只存在返回的 Resource 数量上的差异。
3、总结
1.3、 ApplicationContext 与 ResourceLoader
ApplicationContext 继承了 ResourcePatternResolver ,当然就间接实现了 ResourceLoader 接口。所以,任何的 ApplicationContext 实现都可以看作是一个ResourceLoader 甚至 ResourcePatternResolver 。而这就是 ApplicationContext 支持Spring内统一资源加载策略的真相。
通常,所有的 ApplicationContext 实现类会直接或者间接地继承 org.springframework.context.support.AbstractApplicationContext ,从这个类上,我们就可以看到 ApplicationContext 与 ResourceLoader 之间的所有关系。 AbstractApplicationContext 继承了 DefaultResourceLoader ,那么,它的 getResource(String) 当然就直接用 DefaultResourceLoader 的了。剩下需要它“效劳”的,就是 ResourcePatternResolver的Resource[]getResources (String) ,当然, AbstractApplicationContext 也不负众望,当即拿下。 AbstractApplicationContext 类的内部声明有一个 resourcePatternResolver ,类型是 ResourcePatternResolver ,对应的实例类型为PathMatchingResourcePatternResolver 。 之 前 我 们 说 过 PathMatchingResourcePatternResolver 构造的时候会接受一个 ResourceLoader ,而 AbstractApplicationContext 本身又继承自DefaultResourceLoader ,当然就直接把自身给“贡献”了。这样,整个 ApplicationContext 的实现类就完全可以支持 ResourceLoader 或者 ResourcePatternResolver 接口,你能说 ApplicationContext 不支持Spring的统一资源加载吗?说白了, ApplicationContext 的实现类在作为 ResourceLoader 或者 ResourcePatternResolver 时候的行为,完全就是委派给了 PathMatchingResourcePatternResolver 和 DefaultResourceLoader 来做。
1、扮演 ResourceLoader 的角色
以 ResourceLoader 身份登场的 ApplicationContext
ResourceLoader resourceLoader = new ClassPathXmlApplicationContext("配置文件路径");
// 或者
// ResourceLoader resourceLoader = new FileSystemXmlApplicationContext("配置文件路径");
Resource fileResource = resourceLoader.getResource("D:/spring21site/README");
assertTrue(fileResource instanceof ClassPathResource);
assertFalse(fileResource.exists());
Resource urlResource2 = resourceLoader.getResource("http://www.spring21.cn");
assertTrue(urlResource2 instanceof UrlResource);
2、 ResourceLoader 类型的注入
public class FooBar implements ApplicationContextAware{
private ResourceLoader resourceLoader;
public void foo(String location)
{
System.out.println(getResourceLoader().getResource(location).getClass());
}
public ResourceLoader getResourceLoader() {
return resourceLoader;
}
public void setApplicationContext(ApplicationContext ctx) ➥
throws BeansException {
this.resourceLoader = ctx;
}
}
现在,容器启动的时候,就会自动将当前 ApplicationContext 容器本身注入到 FooBar 中,因为 ApplicationContext 类型容器可以自动识别 Aware 接口。
3、 Resource 类型的注入
ApplicationContext 启动伊始,会通过一个 org.springframework.beans.support.ResourceEditorRegistrar 来注册 Spring 提供的针对 Resource 类型的 PropertyEditor 实现到容器中,这个PropertyEditor 叫做 org.springframework.core.io.ResourceEditor 。这样, ApplicationContext 就可以正确地识别 Resource 类型的依赖了。
4、在特定情况下, ApplicationContext 的 Resource 加载行为
Spring扩展了协议前缀的集合。 ResourceLoader 中增加了一种新的资源路径协议—— classpath: , ResourcePatternResolver 又增加了一种—— classpath*: 。这样,我们就可以通过这些资源路径协议前缀,明确地告知Spring容器要从classpath中加载资源。
// 代码中使用协议前缀
ResourceLoader resourceLoader = new ➥
FileSystemXmlApplicationContext("classpath:conf/container-conf.xml");
// 配置中使用协议前缀
<bean id="..." class="...">
<property name="...">
<value>classpath:resource/template.vm</value>
</property>
</bean>
classpath*: 与 classpath: 的唯一区别就在于,如果能够在classpath中找到多个指定的资源,则返回多个。
当实例化相应的 ApplicationContext 时,各种实现会根据自身的特性,从不同的位置加载bean定义配置文件。当容器实例化并启动完毕,我们要用相应容器作为 ResourceLoader 来加载其他资源时,各种 ApplicationContext 容器的实现类依然会有不同的表现。对于ClassPathXmlApplicationContext 来说,如果我们不指定路径之前的前缀,它也不会像资源路径所表现的那样,从文件系统加载资源,而是像实例化时候的行为一样,从Classpath中加载这种没有路径前缀的资源。
如果当前容器类型为 FileSystemXmlApplicationContext ,事情则会像预想的那样进行,FileSystemXmlApplicationContext 将从文件系统中给我们加载该文件。但是,就跟实例化时可以通过 classpath: 前缀覆盖掉 FileSystemXmlApplicationContext 的默认加载行为一样,我们也可以在这个时候用 classpath: 前缀强制指定 FileSystemXmlApplicationContext 从Classpath中加载该文件。
2、国际化信息支持(I18n MessageSource)
2.1、Java SE 提供的国际化支持
对于Java中的国际化信息处理,主要涉及两个类,即 java.util.Locale 和 java.util.ResourceBundle 。
1、Locale
不同的 Locale 代表不同的国家和地区,每个国家和地区在 Locale 这里都有相应的简写代码表示,包括语言代码以及国家代码,这些代码是ISO标准代码。如, Locale.CHINA 代表中国,它的代码表示为zh_CN; Locale.US 代表美国地区,代码表示为en_US;而美国和英国等都属于英语地区,则可以使用 Locale.ENGLISH 来统一表示,这时代码只有语言代码,即en。
Locale 类提供了三个构造方法,它们的定义如下:
Locale(String language)
Locale(String language, String country)
Locale(String language, String country, String variant)有了 Locale ,我们的应用程序就可以通过它来判别如何为不同的国家和地区的用户提供相应的信息。
2、ResourceBundle
ResourceBundle 用来保存特定于某个 Locale 的信息(可以是String类型信息,也可以是任何类型的对象)。通常, ResourceBundle 管理一组信息序列,所有的信息序列有统一的一个 basename ,然后特定的 Locale 的信息,可以根据 basename 后追加的语言或者地区代码来区分。
我们用一组properties文件来分别保存不同国家地区的信息,可以像下面这样来命名相应的properties文件:
messages.properties messages_zh.properties messages_zh_CN.properties messages_en.properties messages_en_US.properties ...
其中,文件名中的 messages 部分称作 ResourceBundle 将加载的资源的basename,其他语言或地区的资源在 basename 的基础上追加 Locale 特定代码。每个资源文件中都有相同的键来标志具体资源条目,但每个资源内部对应相同键的资源条目内容,则根据 Locale 的不同而不同。
# messages_zh_CN.properties文件中 menu.file=文件({0}) menu.edit=编辑 ... # messages_en_US.properties文件中 menu.file=File({0}) menu.edit=Edit ...
按照规定,properties文件内容是以ISO-8859-1编码的,所以,实际上message_zh_CN.properties中各个键对应的内容是不应该以中文提供的,应该使用native2ascii或者类似的相关工具进行转码。
有了 ResourceBundle 对应的资源文件之后,我们就可以通过 ResourceBundle 的 getBundle(String baseName, Locale locale) 方法取得不同 Locale 对应的 ResourceBundle ,然后根据资源的键取得相应 Locale 的资源条目内容。
通过结合 ResourceBundle 和 Locale ,我们就能够实现应用程序的国际化信息支持。
2.2、MessageSource 与 ApplicationContext
Spring在Java SE的国际化支持的基础上,进一步抽象了国际化信息的访问接口,也就是org.springframework.context.MessageSource。
public interface MessageSource {
String getMessage(String code, Object[] args, String defaultMessage, Locale locale);
String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException;
String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
}
ApplicationContext 除了实现了 ResourceLoader 以支持统一的资源加载,它还实现了 MessageSource 接口,那么就跟 ApplicationContext 因为实现了 ResourceLoader 而可以当作ResourceLoader 来使用一样, ApplicationContext 现在也是一个 MessageSource 了。
在默认情况下, ApplicationContext 将委派容器中一个名称为 messageSource 的 MessageSource 接口实现来完成 MessageSource 应该完成的职责。如果找不到这样一个名字的 MessageSource
实现, ApplicationContext 内部会默认实例化一个不含任何内容的 StaticMessageSource 实例,以保证相应的方法调用。
ApplicationContext 容器内使用的 messageSource 的配置实例
<beans>
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>messages</value>
<value>errorcodes</value>
</list>
</property>
</bean>
...
</beans>
1、可用的 MessageSource 实现
Spring提供了三种 MessageSource 的实现,即 StaticMessageSource 、 ResourceBundleMessageSource 和 ReloadableResourceBundleMessageSource 。
- org.springframework.context.support.StaticMessageSource 。 MessageSource 接口的简单实现,可以通过编程的方式添加信息条目,多用于测试,不应该用于正式的生产环境。
- org.springframework.context.support.ResourceBundleMessageSource 。基于标准的java.util.ResourceBundle 而实现的 MessageSource ,对其父类 AbstractMessageSource的行为进行了扩展,提供对多个 ResourceBundle 的缓存以提高查询速度。同时,对于参数化的信息和非参数化信息的处理进行了优化,并对用于参数化信息格式化的 MessageFormat 实例也进行了缓存。它是最常用的、用于正式生产环境下的 MessageSource 实现。
- org.springframework.context.support.ReloadableResourceBundleMessageSource 。同样基于标准的 java.util.ResourceBundle 而构建的 MessageSource实现类 ,但通过其cacheSeconds 属性可以指定时间段,以定期刷新并检查底层的properties资源文件是否有变更。对于properties资源文件的加载方式也与 ResourceBundleMessageSource 有所不同,可以通过ResourceLoader 来加载信息资源文件。使用 ReloadableResourceBundleMessageSource 时,应该避免将信息资源文件放到classpath中,因为这无助于 ReloadableResourceBundleMessageSource 定期加载文件变更。
三种 MessageSource 实现类的简单使用演示 StaticMessageSource messageSource = new StaticMessageSource(); messageSource.addMessage("menu.file", Locale.US, "File"); messageSource.addMessage("menu.edit", Locale.US, "Edit"); assertEquals("File(F)", messageSource.getMessage("menu.file", new Object[]{"F"}, Locale.US)); assertEquals("Edit", messageSource.getMessage("menu.edit", null,"Edit", Locale.US)); ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); messageSource.setBasenames(new String[]{"conf/messages"});// 从 classpath加载资源文件 assertEquals("File(F)", messageSource.getMessage("menu.file", new Object[]{"F"}, Locale.US)); ReloadableResourceBundleMessageSource messageSource = new ➥ ReloadableResourceBundleMessageSource(); messageSource.setBasenames(new String[]{"file:conf/messages"}); // 从文件系统加载资源文件 assertEquals("File(F)", messageSource.getMessage("menu.file", new Object[]{"F"},Locale.US));
2、 MessageSourceAware 和 MessageSource 的注入
ApplicationContext 启动的时候,会自动识别容器中类型为 MessageSourceAware 的bean定义,并将自身作为 MessageSource 注入相应对象实例中。如果某个业务对象需要国际化的信息支持,那么最简单的办法就是让它实现 MessageSourceAware 接口,然后注册到 ApplicationContext 容器。不过这样一来,该业务对象对 ApplicationContext 容器的依赖性就太强了,显得容器具有较强的侵入性。
如果真的某个业务对象需要依赖于 MessageSource 的话,直接通过构造方法注入或者setter方法注入的方式声明依赖就可以了。只要配置bean定义时,将 ApplicationContext 容器内部的那个 messageSource 注入该业务对象即可。
依赖于 MessageSource 的 Validator 类定义以及相关注入配置
public class Validator
{
private MessageSource messageSource;
public ValidateResult validate(Object target){
// 执行相应验证逻辑
// 如果有错误,通过messageSource.getMessage(...)获取相应信息并放入验证结果对象中
// 返回验证结果(return result)
}
public MessageSource getMessageSource(){
return messageSource;
}
public void setMessageSource(MessageSource msgSource){
this.messageSource = msgSource;
}
// ...
}
<beans>
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>messages</value>
<value>errorcodes</value>
</list>
</property>
</bean>
<bean id="validator" class="...Validator">
<property name="messageSource" ref="messageSource"/>
</bean>
...
</beans>
2.3、容器内部事件发布
2.3.1、自定义事件发布
Java SE提供了实现自定义事件发布(Custom Event publication)功能的基础类,即 java.util.EventObject 类和 java.util.EventListener 接口。
给出自定义事件类型(define your own event object)。为了针对具体场景可以区分具体的事件类型,我们需要给出自己的事件类型的定义,通常做法是扩展 java.util.EventObject 类来实现自定义的事件类型。
实现针对自定义事件类的事件监听器接口(define custom event listener)。自定义的事件监听器需要在合适的时机监听自定义的事件。
有了自定义事件和自定义事件监听器,剩下的就是发布事件,然后让相应的监听器监听并处理事件了。通常情况下,我们会有一个事件发布者(EventPublisher),它本身作为事件源,会在合适的时点,将相应事件发布给对应的事件监听器。
2.3.2、Spring 的容器内事件发布类结构分析
Spring 的 ApplicationContext 容 器 内 部 允 许 以 org.springframework.context.Appli-cationEvent 的形式发布事件,容器内注册的 org.springframework.context.ApplicationListener 类型的bean定义会被 ApplicationContext 容器自动识别,它们负责监听容器内发布的所有ApplicationEvent 类型的事件。
1、ApplicationEvent
Spring容器内自定义事件类型,继承自 java.util.EventObject ,它是一个抽象类,需要根据情况提供相应子类以区分不同情况。默认情况下,Spring提供了三个实现。
- ContextClosedEvent: ApplicationContext 容器在即将关闭的时候发布的事件类型。
- ContextRefreshedEvent: ApplicationContext 容器在初始化或者刷新的时候发布的事件类型。
- RequestHandledEvent:Web请求处理后发布的事件,其有一子类 ServletRequestHandledEvent 提供特定于Java EE的Servlet相关事件。
2、ApplicationListener
ApplicationContext 容器内使用的自定义事件监听器接口定义,继承自 java.util.EventListener 。 ApplicationContext 容器在启动时,会自动识别并加载 EventListener 类型bean定义,一旦容器内有事件发布,将通知这些注册到容器的 EventListener 。
3、ApplicationContext
ApplicationContext 接口定义还继承了 ApplicationEventPublisher 接口,该接口提供了 void publishEvent(ApplicationEvent event) 方法定义。不难看出, ApplicationContext 容器现在担当的就是事件发布者的角色。
pplicationContext 容器的具体实现类在实现事件的发布和事件监听器的注册方面,并没事必躬亲,而是把这些活儿转包给了一个称作 org.springframework.context.event.ApplicationEventMulticaster 的接口。该接口定义了具体事件监听器的注册管理以及事件发布的方法,但接口终归是接口,还得有具体实现。 ApplicationEventMulticaster 有一抽象实现类—— org.spring-
framework.context.event.AbstractApplicationEventMulticaster ,它实现了事件监听器的管理功能。出于灵活性和扩展性考虑,事件的发布功能则委托给了其子类。 org.springframework.context.event.SimpleApplicationEventMulticaster 是 Spring 提 供 的 AbstractApplicationEventMulticaster 的一个子类实现,添加了事件发布功能的实现。不过,其默认使用了 SyncTaskExecutor 进行事件的发布,事件是同步顺序发布的。
2.3.3、Spring 容器内事件发布的应用
Spring的 ApplicationContext 容器内的事件发布机制,主要用于单一容器内的简单消息通知和处理,并不适合分布式、多进程、多容器之间的事件通知。
要让我们的业务类支持容器内的事件发布,需要它拥有 ApplicationEventPublisher 的事件发布支持。所以,需要为其注入 ApplicationEventPublisher 实例。
可以通过如下两种方式为我们的业务对象注入 ApplicationEventPublisher 的依赖
1、使用 ApplicationEventPublisherAware 接口。在 ApplicationContext 类型的容器启动时,会自动识别该类型的bean定义并将 ApplicationContext 容器本身作为 ApplicationEventPublisher 注入当前对象,而 ApplicationContext 容器本身就是一个 ApplicationEventPublisher 。
2、使用 ApplicationContextAware 接口。既然 ApplicationContext 本身就是一个 Application-EventPublisher ,那么通过 ApplicationContextAware 几乎达到第一种方式相同的效果。
3、Spring IoC容器之扩展篇
3.1、Spring2.5的基于注解的依赖注入
3.1.1、注解版的自动绑定( @Autowired )
1、从自动绑定( autowire )到 @Autowired
@Autowired 是基于注解的依赖注入的核心注解,它的存在可以让容器知道需要为当前类注入哪些依赖。
使用 @Autowired 标注后的 FXNewsProvider
public class FXNewsProvider
{
private IFXNewsListener newsListener;
private IFXNewsPersister newPersistener;
@Autowired
public FXNewsProvider(IFXNewsListener newsListner,IFXNewsPersister newsPersister)
{
this.newsListener = newsListner;
this.newPersistener = newsPersister;
}
...
}
@Autowired 是按照类型匹配进行依赖注入的 , 可以标注于类定义的多个位置,包括如下几个。
1、域(Filed)或者说属性(Property)
不管它们声明的访问限制符是 private 、 protected 还是public ,只要标注了 @Autowired ,它们所需要的依赖注入需求就都能够被满足。
2、构造方法定义(Constructor)
标注于类的构造方法之上的 @Autowired ,相当于抢夺了原有自动绑定功能中“constructor”方式的权利,它将根据构造方法参数类型,来决定将什么样的依赖对象注入给当前对象。
3、方法定义(Method)
@Autowired 不仅可以标注于传统的setter方法之上,而且还可以标注于任意名称的方法定义之上,只要该方法定义了需要被注入的参数。
虽然可以随意地在类定义的各种合适的地方标注 @Autowired ,希望这些被 @Autowired 标注的依赖能够被注入,但是,仅将 @Autowired 标注于类定义中并不能让Spring的IoC容器聪明到自己去查看这些注解,然后注入符合条件的依赖对象。容器需要某种方式来了解,哪些对象标注了@Autowired ,哪些对象可以作为可供选择的依赖对象来注入给需要的对象。
我们可以提供一个Spring的IoC容器使用的 BeanPostProcessor 自定义实现,让这个 BeanPostProcessor 在实例化bean定义的过程中,来检查当前对象是否有 @Autowired 标注的依赖需要注入。 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor 就是Spring提供的用于这一目的的 BeanPostProcessor 实现。
2、@Qualifier 的陪伴
@Autowired 是按照类型进行匹配,@Qualifier 实际上是 byName 自动绑定的注解版,既然IoC容器无法自己从多个同一类型的实例中选取我们真正想要的那个,那么我们不妨就使用 @Qualifier 直接点名要哪个好了。
3.1.2、@Autowired 之外的选择——使用JSR250 标注依赖注入关系
Spring 2.5提供的基于注解的依赖注入,除了可以使用Spring提供的 @Autowired 和 @Qualifier 来标注相应类定义之外,还可以使用JSR250的 @Resource 和 @PostConstruct 以及 @PreDestroy 对相应类进行标注,这同样可以达到依赖注入的目的。
@Resource 与 @Autowired 不同,它遵循的是 byName 自动绑定形式的行为准则,也就是说,IoC容器将根据 @Resource 所指定的名称,到容器中查找 beanName 与之对应的实例,然后将查找到的对象实例注入给 @Resource 所标注的对象。
确切地说, @PostConstruct 和 @PreDestroy 不是服务于依赖注入的,它们主要用于标注对象生命周期管理相关方法。
JSR250的这些注解也同样需要一个 BeanPostProcessor 帮助它们实现自身的价值。这个 BeanPostProcessor 就是 org.springframework.context.annotation.CommonAnnotationBeanPostProcessor ,只有将 CommonAnnotationBeanPostProcessor 添加到容器,JSR250的相关注解才能发挥作用。
3.1.3、classpath-scanning 功能介绍
使用相应的注解对组成应用程序的相关类进行标注之后,classpath-scanning功能可以从某一顶层包(base package)开始扫描。当扫描到某个类标注了相应的注解之后,就会提取该类的相关信息,构建对应的 BeanDefinition ,然后把构建完的 BeanDefinition 注册到容器。
<context:component-scan> 默认扫描的注解类型是 @Component 。不过,在 @Component 语义基础上细化后的 @Repository 、 @Service 和 @Controller 也同样可以获得 <context:component-scan>的青睐。 @Component的语义更广 、更宽泛,而 @Repository 、 @Service 和 @Controller 的语义则更具体。