Aware是什么
spring框架提供了多個(gè)*Aware接口骤铃,用于輔助Spring Bean編程訪問(wèn)Spring容器应役。
通過(guò)實(shí)現(xiàn)這些接口荒给,可以增強(qiáng)Spring Bean的功能秋柄,將一些特殊的spring內(nèi)部bean暴露給業(yè)務(wù)應(yīng)用庸队。
- ApplicationContextAware
將ApplicationContext暴露出來(lái)积蜻,使用最頻繁的api為getBean方法,動(dòng)態(tài)獲取spring中的bean彻消。spring2.5+之后可以不實(shí)現(xiàn)aware接口竿拆,直接@autowired注入到業(yè)務(wù)bean中直接使用。
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver
- BeanFactoryAware
將BeanFactory暴露給用戶宾尚,也可以直接@autowired注入丙笋。BeanFactory只聲明了一些基本方法,如getBean,containsBean不见,isPrototype澳化,isSingleton等,ApplicationContext繼承了BeanFactory稳吮,它有自己的很多擴(kuò)展方法(容器啟動(dòng)日期缎谷,容器id,他還實(shí)現(xiàn)了事件接口灶似,可以發(fā)布事件等等)列林。
- ApplicationEventPublisherAware
ApplicationEventPublisher可以發(fā)布事件,ApplicationContext也繼承了該接口酪惭,可以直接使用ApplicationContext來(lái)發(fā)布希痴。spring使用示例:
在使用Spring的事件支持時(shí),我們需要關(guān)注以下幾個(gè)對(duì)象:
1. ApplicationEvent:繼承自EventObject春感,同時(shí)是spring的application中事件的父類砌创,需要被自定義的事件繼承。
2. ApplicationListener:繼承自EventListener鲫懒,spring的application中的監(jiān)聽(tīng)器必須實(shí)現(xiàn)的接口嫩实,需要被自定義的監(jiān)聽(tīng)器實(shí)現(xiàn)其onApplicationEvent方法
3. ApplicationEventPublisherAware:在spring的context中希望能發(fā)布事件的類必須實(shí)現(xiàn)的接口,該接口中定義了設(shè)置ApplicationEventPublisher的方法窥岩,由ApplicationContext調(diào)用并設(shè)置甲献。在自己實(shí)現(xiàn)的ApplicationEventPublisherAware子類中,需要有ApplicationEventPublisher屬性的定義颂翼。
4. ApplicationEventPublisher:spring的事件發(fā)布者接口晃洒,定義了發(fā)布事件的接口方法publishEvent。因?yàn)锳pplicationContext實(shí)現(xiàn)了該接口朦乏,因此spring的ApplicationContext實(shí)例具有發(fā)布事件的功能(publishEvent方法在AbstractApplicationContext中有實(shí)現(xiàn))球及。在使用的時(shí)候,只需要把ApplicationEventPublisher的引用定義到ApplicationEventPublisherAware的實(shí)現(xiàn)中呻疹,spring容器會(huì)完成對(duì)ApplicationEventPublisher的注入桶略。
在spring的bean配置中,因?yàn)槭录怯墒录窗l(fā)出的诲宇,不需要注冊(cè)為bean由spring容器管理。所以在spring的配置中惶翻,只需配置自定義的ApplicationEventListener和publisherAware(即實(shí)現(xiàn)了ApplicationEventPublisherAware接口的發(fā)布類)姑蓝,而對(duì)于ApplicationEventPublisher的管理和注入都由容器來(lái)完成。
基于spring的事件簡(jiǎn)單實(shí)現(xiàn)如下:
定義ApplicationEvent
import org.springframework.context.ApplicationEvent;
/**
* 定義Spring容器中的事件吕粗,與java普通的事件定義相比纺荧,只是繼承的父類不同而已,在
* 在定義上并未有太大的區(qū)別,畢竟ApplicationEvent也是繼承自EventObject的宙暇。
*/
public class MethodExecutionEvent extends ApplicationEvent {
private static final long serialVersionUID = 2565706247851725694L;
private String methodName;
private MethodExecutionStatus methodExecutionStatus;
public MethodExecutionEvent(Object source) {
super(source);
}
public MethodExecutionEvent(Object source, String methodName, MethodExecutionStatus methodExecutionStatus) {
super(source);
this.methodName = methodName;
this.methodExecutionStatus = methodExecutionStatus;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public MethodExecutionStatus getMethodExecutionStatus() {
return methodExecutionStatus;
}
public void setMethodExecutionStatus(MethodExecutionStatus methodExecutionStatus) {
this.methodExecutionStatus = methodExecutionStatus;
}
}
定義ApplicationEventListener
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import com.nuc.event.MethodExecutionEvent;
import com.nuc.event.MethodExecutionStatus;
/**
* Spring容器中的事件監(jiān)聽(tīng)器输枯,與java中基本的事件監(jiān)聽(tīng)器的定義相比,這里需要實(shí)現(xiàn)ApplicationListener接口
* ApplicationListener接口雖然繼承自EventListener占贫,但擴(kuò)展了EventListener
* 它在接口聲明中定義了onApplicationEvent的接口方法桃熄,而不像EventListener只作為標(biāo)記性接口。
*/
public class MethodExecutionEventListener implements ApplicationListener {
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof MethodExecutionEvent) {
if (MethodExecutionStatus.BEGIN
.equals(((MethodExecutionEvent) event)
.getMethodExecutionStatus())) {
System.out.println("It's beginning");
}
if (MethodExecutionStatus.END.equals(((MethodExecutionEvent) event).getMethodExecutionStatus())) {
System.out.println("It's ending");
}
}
}
}
定義ApplicationEventPublisherAware
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import com.nuc.event.MethodExecutionEvent;
import com.nuc.event.MethodExecutionStatus;
public class MethodExecutionEventPublisher implements
ApplicationEventPublisherAware {
private ApplicationEventPublisher eventPublisher;
public void methodToMonitor() {
MethodExecutionEvent beginEvent = new MethodExecutionEvent(this, "methodToMonitor", MethodExecutionStatus.BEGIN);
this.eventPublisher.publishEvent(beginEvent);
//TODO
MethodExecutionEvent endEvent = new MethodExecutionEvent(this, "methodToMonitor", MethodExecutionStatus.END);
this.eventPublisher.publishEvent(endEvent);
}
public void setApplicationEventPublisher(
ApplicationEventPublisher applicationEventPublisher) {
this.eventPublisher = applicationEventPublisher;
}
}
定義bean配置
<?xml version="1.0" encoding="GBK"?>
<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-2.0.xsd"
default-autowire="byName">
<bean id="methodExecListener" class="com.nuc.listener.MethodExecutionEventListener"></bean>
<bean id="evtPublisher" class="com.nuc.publisher.MethodExecutionEventPublisher"></bean>
</beans>
使用事件
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.nuc.publisher.MethodExecutionEventPublisher;
public class App
{
public static void main( String[] args )
{
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
MethodExecutionEventPublisher publisher = (MethodExecutionEventPublisher)context.getBean("evtPublisher");
publisher.methodToMonitor();
}
}
- BeanClassLoaderAware
實(shí)現(xiàn)該aware后spring會(huì)把加載業(yè)務(wù)bean類時(shí)使用的類加載器暴露出來(lái)型奥。接口聲明如下:
public interface BeanClassLoaderAware extends Aware {
/**
* Callback that supplies the bean {@link ClassLoader class loader} to
* a bean instance.
* <p>Invoked <i>after</i> the population of normal bean properties but
* <i>before</i> an initialization callback such as
* {@link InitializingBean InitializingBean's}
* {@link InitializingBean#afterPropertiesSet()}
* method or a custom init-method.
* @param classLoader the owning class loader; may be {@code null} in
* which case a default {@code ClassLoader} must be used, for example
* the {@code ClassLoader} obtained via
* {@link org.springframework.util.ClassUtils#getDefaultClassLoader()}
*/
void setBeanClassLoader(ClassLoader classLoader);
}
- BeanNameAware
將當(dāng)前bean在spring中的beanname暴露出來(lái)瞳收。
lic interface BeanNameAware extends Aware {
/**
* Set the name of the bean in the bean factory that created this bean.
* <p>Invoked after population of normal bean properties but before an
* init callback such as {@link InitializingBean#afterPropertiesSet()}
* or a custom init-method.
* @param name the name of the bean in the factory.
* Note that this name is the actual bean name used in the factory, which may
* differ from the originally specified name: in particular for inner bean
* names, the actual bean name might have been made unique through appending
* "#..." suffixes. Use the {@link BeanFactoryUtils#originalBeanName(String)}
* method to extract the original bean name (without suffix), if desired.
*/
void setBeanName(String name);
}
Spring 自動(dòng)調(diào)用。并且會(huì)在Spring自身完成Bean配置之后厢汹,且在調(diào)用任何Bean生命周期回調(diào)(初始化或者銷毀)方法之前就調(diào)用這個(gè)方法螟深。換言之,在程序中使用BeanFactory.getBean(String beanName)之前烫葬,Bean的名字就已經(jīng)設(shè)定好了界弧。所以,程序中可以盡情的使用BeanName而不用擔(dān)心它沒(méi)有被初始化搭综。
- BootstrapContextAware,資源適配器,不知道怎么用
- EmbeddedValueResolverAware
應(yīng)用配置文件讀取輔助月匣。他的讀取方式需要加${xx.xx}
這樣的方式,使用示例:
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.stereotype.Component;
import org.springframework.util.StringValueResolver;
@Component("propertiesUtils")
public class PropertiesUtils implements EmbeddedValueResolverAware {
private StringValueResolver resolver = null;
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.resolver = resolver;
}
public String getPropertiesValue(String name) {
return resolver.resolveStringValue(name);
}
}
測(cè)試:
import com.google.common.collect.Maps;
import com.jd.caiyu.common.utils.PropertiesUtils;
import com.jd.caiyu.match.domain.agent.enums.AgentTypeEnum;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.Map;
import static com.jd.caiyu.match.domain.agent.enums.AgentTypeEnum.PRIMARY;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/spring-config.xml")
public class PropertiesUtilsTest {
@Autowired
private PropertiesUtils propertiesUtils;
@Test
public void testGetValue(){
String value = propertiesUtils.getPropertiesValue("${jmq.address}"); //注意訪問(wèn)方式
Assert.assertEquals("查詢結(jié)果與期待的不一致7茏恕3!", "192.168.179.66:50088", value);
Map<AgentTypeEnum, String> map = Maps.newHashMap();
map.put(PRIMARY, "xxx");
}
}
- EnvironmentAware
Environment可以獲取所有環(huán)境變量称诗,包括應(yīng)用的properties萍悴,使用示例:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
/**
* 主要是@Configuration癣诱,實(shí)現(xiàn)接口:EnvironmentAware就能獲取到系統(tǒng)環(huán)境信息
*/
@Configuration
public class MyEnvironmentAware implements EnvironmentAware{
//注入application.properties的屬性到指定變量中.
@Value("${spring.datasource.url}")
private String myUrl;
/**
*注意重寫(xiě)的方法 setEnvironment 是在系統(tǒng)啟動(dòng)的時(shí)候被執(zhí)行。
*/
@Override
public void setEnvironment(Environment environment) {
//打印注入的屬性信息.
System.out.println("myUrl="+myUrl);
//通過(guò) environment 獲取到系統(tǒng)屬性.
System.out.println(environment.getProperty("JAVA_HOME"));
//通過(guò) environment 同樣能獲取到application.properties配置的屬性.
System.out.println(environment.getProperty("spring.datasource.url"));
//獲取到前綴是"spring.datasource." 的屬性列表值.
RelaxedPropertyResolver relaxedPropertyResolver = new RelaxedPropertyResolver(environment, "spring.datasource.");
System.out.println("spring.datasource.url="+relaxedPropertyResolver.getProperty("url"));
System.out.println("spring.datasource.driverClassName="+relaxedPropertyResolver.getProperty("driverClassName"));
}
}
ImportAware -- 待研究
LoadTimeWeaverAware袜香,切面植入实抡,后續(xù)單獨(dú)研究欠母,http://www.cnblogs.com/wade-luffy/p/6078213.html
MessageSourceAware,消息國(guó)際化
ApplicationContext也繼承了該接口吆寨,所以可以直接使用ApplicationContext赏淌。
國(guó)際化使用示例:
首先定義一個(gè)messageSource:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- 資源國(guó)際化測(cè)試 -->
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <property name="basenames">
<list>
<value>org/rjstudio/spring/properties/messages</value>
</list>
</property>
</bean>
</beans>
這個(gè)Bean的id是定死的,只能為“messageSource”啄清。這里的Class需要填入MessageSource接口的實(shí)現(xiàn)六水。其中,在我看的書(shū)中只提及了兩個(gè)類盒延,一個(gè)是:ResourceBundleMessageSource缩擂,另一個(gè)則是ReloadableResourceBundleMessageSource。其中添寺,后者提供了無(wú)需重啟就可重新加載新配置的特性胯盯。
list節(jié)點(diǎn)的value子節(jié)點(diǎn)中的body值“org/rjstudio/spring/properties/messages”,是指org.rjstudio.spring.proerties包下的以messages為主要名稱的properties文件计露。比如說(shuō)博脑,以Locale為zh_CN為例,Spring會(huì)自動(dòng)在類路徑中在org.rjstudio.spring.properties包下按照如下順序搜尋配置文件并進(jìn)行加載票罐。
接下來(lái)叉趣,讓我們?cè)趏rg.rjstudio.spring.properties下,建立兩個(gè)messages的屬性文件该押。一個(gè)名為messages_zh_CN.properties疗杉,另一個(gè)為messages_en_US.properties,分別對(duì)應(yīng)國(guó)際化中的中國(guó)和美國(guó)蚕礼。
在這兩個(gè)屬性文件中分別建立一個(gè)userinfo屬性烟具。
中國(guó)為:userinfo=當(dāng)前登陸用戶[{0}] 登陸時(shí)間[{1}]
美國(guó)為:userinfo=current login user:[{0}] login time:[{1}]
好了,一切就緒奠蹬,接下來(lái)可以寫(xiě)段代碼來(lái)測(cè)試了朝聋。。建個(gè)類囤躁,寫(xiě)個(gè)測(cè)試Main方法冀痕。
public class MessageTest {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("messages.xml");
Object[] arg = new Object[] { "Erica", Calendar.getInstance().getTime() };
String msg = ctx.getMessage("userinfo", arg,Locale.CHINA); //使用中文資源配置
System.out.println("Message is ===> " + msg);
}
}
//最后輸出的結(jié)果是:Message is ===> 當(dāng)前登錄用戶:[Erica] 登錄時(shí)間:[07-6-8 上午10:20]
ctx.getMessage("userinfo", arg,Locale.US);
/*這個(gè)方法,傳入的三個(gè)參數(shù)狸演,第一個(gè)是properties文件中對(duì)應(yīng)的名言蛇。arg為一個(gè)對(duì)象數(shù)組,我們?cè)趐roperties里面放置了兩個(gè)變量宵距,[{0}]和[{1}]猜极,Spring會(huì)為我們給它們賦值。而最后則需要傳入一個(gè)Local消玄。這里用 Locale.CHINA代表中國(guó)跟伏。如果我們用Locale.US,則輸出會(huì)變?yōu)椋?
Message is ===> current login user:[Erica] login time:[6/8/07 10:59 AM]
*/
- NotificationPublisherAware翩瓜,jmx相關(guān)受扳,后續(xù)深究
- ResourceLoaderAware
資源加載器注入,ApplicationContext也實(shí)現(xiàn)了ResourceLoader.
在Spring里面還定義有一個(gè)ResourceLoader接口兔跌,該接口中只定義了一個(gè)用于獲取Resource的getResource(String location)方法勘高。它的實(shí)現(xiàn)類有很多,這里我們先挑一個(gè)DefaultResourceLoader來(lái)講坟桅。DefaultResourceLoader在獲取Resource時(shí)采用的是這樣的策略:首先判斷指定的location是否含有“classpath:”前綴华望,如果有則把location去掉“classpath:”前綴返回對(duì)應(yīng)的ClassPathResource;否則就把它當(dāng)做一個(gè)URL來(lái)處理仅乓,封裝成一個(gè)UrlResource進(jìn)行返回赖舟;如果當(dāng)成URL處理也失敗的話就把location對(duì)應(yīng)的資源當(dāng)成是一個(gè)ClassPathResource進(jìn)行返回。
@Test
public void testResourceLoader() {
ResourceLoader loader = new DefaultResourceLoader();
Resource resource = loader.getResource("http://www.google.com.hk");
System.out.println(resource instanceof UrlResource); //true
//注意這里前綴不能使用“classpath*:”夸楣,這樣不能真正訪問(wèn)到對(duì)應(yīng)的資源宾抓,exists()返回false
resource = loader.getResource("classpath:test.txt");
System.out.println(resource instanceof ClassPathResource); //true
resource = loader.getResource("test.txt");
System.out.println(resource instanceof ClassPathResource); //true
}
ApplicationContext接口也繼承了ResourceLoader接口,所以它的所有實(shí)現(xiàn)類都實(shí)現(xiàn)了ResourceLoader接口豫喧,都可以用來(lái)獲取Resource石洗。
對(duì)于ClassPathXmlApplicationContext而言,它在獲取Resource時(shí)繼承的是它的父類DefaultResourceLoader的策略紧显。
FileSystemXmlApplicationContext也繼承了DefaultResourceLoader讲衫,但是它重寫(xiě)了DefaultResourceLoader的getResourceByPath(String path)方法。所以它在獲取資源文件時(shí)首先也是判斷指定的location是否包含“classpath:”前綴孵班,如果包含涉兽,則把location中“classpath:”前綴后的資源從類路徑下獲取出來(lái),當(dāng)做一個(gè)ClassPathResource重父;否則花椭,繼續(xù)嘗試把location封裝成一個(gè)URL,返回對(duì)應(yīng)的UrlResource房午;如果還是失敗矿辽,則把location指定位置的資源當(dāng)做一個(gè)FileSystemResource進(jìn)行返回。
- ServletConfigAware
public interface ServletConfig {
/**
* Returns the name of this servlet instance.
* The name may be provided via server administration, assigned in the
* web application deployment descriptor, or for an unregistered (and thus
* unnamed) servlet instance it will be the servlet's class name.
*
* @return the name of the servlet instance
*
*
*
*/
public String getServletName();
/**
* Returns a reference to the {@link ServletContext} in which the caller
* is executing.
*
*
* @return a {@link ServletContext} object, used
* by the caller to interact with its servlet
* container
*
* @see ServletContext
*
*/
public ServletContext getServletContext();
/**
* Returns a <code>String</code> containing the value of the
* named initialization parameter, or <code>null</code> if
* the parameter does not exist.
*
* @param name a <code>String</code> specifying the name
* of the initialization parameter
*
* @return a <code>String</code> containing the value
* of the initialization parameter
*
*/
public String getInitParameter(String name);
/**
* Returns the names of the servlet's initialization parameters
* as an <code>Enumeration</code> of <code>String</code> objects,
* or an empty <code>Enumeration</code> if the servlet has
* no initialization parameters.
*
* @return an <code>Enumeration</code> of <code>String</code>
* objects containing the names of the servlet's
* initialization parameters
*
*
*
*/
public Enumeration getInitParameterNames();
}
- ServletContextAware,注入ServletContext
【ServletContext的5大作用】
ServletContext,是一個(gè)全局的儲(chǔ)存信息的空間郭厌,服務(wù)器開(kāi)始袋倔,其就存在,服務(wù)器關(guān)閉折柠,其才釋放宾娜。request,一個(gè)用戶可有多個(gè)扇售;session前塔,一個(gè)用戶一個(gè)嚣艇;而servletContext,所有用戶共用一個(gè)华弓。所以食零,為了節(jié)省空間,提高效率寂屏,ServletContext中贰谣,要放必須的、重要的迁霎、所有用戶需要共享的線程又是安全的一些信息吱抚。
1.獲取web的上下文路徑
String getContextPath();
2.獲取全局的參數(shù)
String getInitParameter(String name);
Enumeration getInitParameterNames();
3.和域?qū)ο笙嚓P(guān)的
void setAttribute(String name,Onject object);
Object getAttribute(String name);
void removeAttribute(String name);
域?qū)ο螅ㄓ驅(qū)ο缶褪窃诓煌Y源之前來(lái)共享數(shù)據(jù),保存數(shù)據(jù)考廉,獲取數(shù)據(jù))
ServletContext是我們學(xué)習(xí)的第一個(gè)域?qū)ο?Servlet共有三個(gè)域?qū)ο骃ervletContext秘豹、HttpServletRequest、HttpSession)
- 請(qǐng)求轉(zhuǎn)發(fā)的
RequestDispatcher getRequestDispatcher(String path);
在Servlet跳轉(zhuǎn)頁(yè)面:
4.1請(qǐng)求重定向(你找我借錢(qián)芝此,我沒(méi)有憋肖,你自己去找他借錢(qián))
1.地址欄會(huì)改變,變成重定向到的地址
2.可以跳轉(zhuǎn)到項(xiàng)目?jī)?nèi)的資源婚苹,也可以跳轉(zhuǎn)項(xiàng)目外的資源
3.瀏覽器向服務(wù)器發(fā)出兩次請(qǐng)求岸更,那么不能使用請(qǐng)求來(lái)作為域?qū)ο髞?lái)共享數(shù)據(jù)。
4.2請(qǐng)求轉(zhuǎn)發(fā)(你找我借錢(qián)膊升,我沒(méi)有怎炊,我?guī)湍闳ハ蛩桢X(qián))
1.地址欄不會(huì)改變
2.只能跳轉(zhuǎn)到項(xiàng)目?jī)?nèi)的資源,不能跳轉(zhuǎn)項(xiàng)目外的資源廓译。
3.瀏覽器向服務(wù)器發(fā)出一次請(qǐng)求评肆,那么可以使用請(qǐng)求作為域?qū)ο蠊蚕頂?shù)據(jù)。
5.讀取web項(xiàng)目的資源文件
String getRealPath(String path);
InputStream getResourceAsStream(String path);
URL getResource(String path);