1概述
applicationContext 除了拥有BeanFactory支持的所有功能。其还进一步扩展其功能主要有统一资源加载策略。国际化信息支持,容器内事件发布。
主要实现
-
FileSystemXmlApplicationContext
从文件系统加载bean定义,以及相关资源的applicationContext实现 -
ClassPathXmlApplicationContext
从classpath加载bean定义以及相关资源的applicationContext实现 -
XmlWebApplicatonContext
用于web程序的applicationcontext实现。
2 统一资源加载策略
除**file:,http:,ftp:协议前缀外,spring还扩展了其他协议,classPath:和classPath:其区别在于classPath:如果能在classPath路径中找到多个则返回多个。
我们可以使用classpath:前缀让FileSystemXmlApplicationContext从Classpath中加载配置,而不是从文件系统中加载。
2.1 Resource
spring中提供resource接口作为所有资源的抽象和访问接口。
resource接口根据资源的不同类型,给出了具体的实现。
-
ByteArrayResource
将字节提供的数据进行封装,通过InputStream进行访问类型的资源,构造出byteArray-inputStream并返回。 -
ClassPathResource
从java中的ClassPath中加载具体资源并进行封装,可以使用指定的类加载器或类对其加载 -
FileSystemResource
对file类型的封装,可以通过文件或者url对该类型进行访问 -
UrlResource
通过url进行具体资源查找定位的实现类 -
InputStreamResource
将InputStream视为一种资源的Resource的实现类
2.2 ResourceLoader
ResourceLoader接口是用于资源查找和定位的接口
public interface ResourceLoader {
String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
//根据指定资源位置,定位到具体的资源实例
Resource getResource(String location);
@Nullable
ClassLoader getClassLoader();
}
2.2.1 DefaultResourceLoader
是ResourceLoader默认实现类其处理逻辑为
-
首先检查是否以/为前缀,是,则委派给getResourceByPath来定位,其内部是构造classPathResource类型资源并返回。
-
然后检查资源路径是否以classpath为前缀,是,则尝试构造classpathresource资源并返回
-
否则 则会尝试通过url,根据资源路径进行定位资源,如果没有则抛出MalFormedURLException则会构造Url类型资源并返回。还是无法根据资源路径定位指定的资源则会委派给getResourceByPath来定位,其内部是构造classPathResource类型资源并返回。
其主要逻辑
if (location.startsWith("/")) {
return getResourceByPath(location);
}
else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// Try to parse the location as a URL...
URL url = new URL(location);
return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
}
catch (MalformedURLException ex) {
// No URL -> resolve as resource path.
return getResourceByPath(location);
}
2.2.2 FileSystemResourceLoader
为了避免DefaultResourceLoader的getResourceBypath方法上的不正当处理,其FileSystemResourceLoader覆写了其方法,使之从**文件系统加载资源并以fileSystemResource **类型返回
其重写的方法
protected Resource getResourceByPath(String path) {
if (path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemContextResource(path);
}
2.2.3 ResourcePatternResolver
ResourcePatternResolver是resourceloader的扩展,其可以根据指定的资源路径匹配模式,每次返回多个resource实例。
public interface ResourcePatternResolver extends ResourceLoader {
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
//又定义了getResources用于批量查找
Resource[] getResources(String locationPattern) throws IOException;
}
其实现类为PathMatchingResourcePatternResolver,需要指定一个ResourceLoader,否则其内部默认使用的是defaultresourceloader。
2.3 applicationcontext 与 resource
由图可知applicationContext本身就是ResourceLoader,
2.3.1 ResourceLoader类型的注入
我们可以通过实现ResourceLoaderAware和ApplicationContextAware接口,来让自身applicationContext(ResourceLoader)注入进去。
public class TestAware implements ResourceLoaderAware {
//实现其相应的Aware会将自身给注入进去
private ResourceLoader resourceLoader;
public ResourceLoader getResourceLoader() {
return resourceLoader;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader=resourceLoader;
}
}
xml中bean的注入
<bean id ="testAware" class="org.example.Day03.TestAware"/>
3 国际化信息支持(l18n)
对于java中的国际化信息处理,主要涉及了Locale和ResourceBundle
-
Locale
不同的Locale代表不同的国家和地区,如Locale.CHINA代表中国,代码表示为zh_CN;zh表示语言代码,CN表示国家代码 -
ResourceBundle
ResourceBundle用于保存特定于某个Locale的信息。,ResourceBundle管理一组信息序列,其序列有统一的basename,可以根据basename后追加语言或者地区代码进行区分
message_zh.properties
message_zh_CN.properties
我们可以通过ResourceBundle的getBundle(String basename,Loacale locale)方法取得不同的Locale对应的ResourceBundle,然后根据资源建获取相应的locale资源条目内容。
3.1 MessageSource与Applicationcontext
spring进一步抽象了国际化信息的访问接口 messageSource
public interface MessageSource {
//根据资源条目的键,信息参数和locale进行查找,未找到则返回默认的defaultMessage
String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);
//根据资源条目的键,信息参数和locale进行查找,未找到则抛出异常
String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;
//将资源条目的键,信息参数封装成MessageSourceResolvable
String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
}
配置文件
# message_en_US.properties中
menu.file=file({0})
menu.age=18
bean容器中需要注册 messageResource
<bean id ="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename">
<value>message</value>
</property>
</bean>
执行
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
String a= applicationContext.getMessage("menu.file",new Object[]{"ww"}, Locale.US);
System.out.println(a);
4 事件发布
javase 提供了事件发布功能的基础类,eventobject类和EventListenner接口
4.1 自定义事件发布流程
4.2 spring容器内的事件发布
以applicationEvent形式发布事件,而容器内注册的applicationListener会被容器自动识别,负责监听容器内applicationEvent类型的事件。
4.2.1 ApplicationEvent
一个抽象类,spring提供了三个实现
- ContextClosedEvent: 容器在即将关闭是发布的事件类型
- ContextRefreshedEvent: 容器在初始化或刷新时发布的事件类型
- RequestHandledEvent: web请求处理后发布的事件
4.2.2 ApplicationListener
容器内使用的自定义事件监听器接口定义,applicationcontext容器在启动时,会自动识别eventlistener类型bean定义,一旦容器内有事件发布,则将通知这些注册到容器的eventlistener
4.2.3 ApplicationContext
applicationContext除了resourceLoader接口和messageSource接口其还实现了ApplicationEventPublisher接口,担当发布者角色。
但是其将事件发布功能全部委托给了ApplicationEventMulticaster来做,因此容器在启动时会先检查是否存在,没有的话则默认初始化SimpleApplicationEventMulticaster.
4.2.4 自定义事件
创造事件本身
public class TestEvent extends ApplicationEvent {
private String name;
/**
* Create a new ApplicationEvent.
*
* @param source the object on which the event initially occurred (never {@code null})
*/
public TestEvent(Object source) {
super(source);
}
public TestEvent(Object source,String name) {
super(source);
this.name=name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
创建监听器
public class SimpleTestListen implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if(event instanceof TestEvent){
System.out.println("tytt"+((TestEvent) event).getName());
}
}
}
创建发布者
public class TestPublisher implements ApplicationContextAware {
//通过实现 ApplicationContextAware 将publisher给注入进去
private ApplicationEventPublisher publisher;
private List<TestListen> lists = new ArrayList<>();
public void monitor(){
TestEvent testEvent = new TestEvent(this,"ws");
//重点在于发布事件
this.publisher.publishEvent(testEvent);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.publisher=applicationContext;
}
}
将发布者与监听器给注册到bean容器
<bean id="listener" class="org.example.Day03.SimpleTestListen"></bean>
<bean id = "publisher" class="org.example.Day03.TestPublisher"></bean>
4.3 基于注解
@Autowired
通过类型(byType)进行自动注入,@Qualifier
可以对依赖注入做进一步的限定。
@Resource
通过名字(byName)进行自动注入
其注解的原理都是实现了相应的BeanPostProcessor,因此我们需要在容器中加载相应的BeanPostProcessor,而我们可以使用<context:annotation-config>
配置自动加载以上的BeanPostProcessor.<context:component-scan base-package="org.example"/>
是扫描到某个类标注了相应的注解后,提取该类的信息,构成相应的beanDefinition,然后把构建完的BeanDefinition注册到容器中其默认命名为首字母小写。这样就可以免于配置bean的灾难