您的位置:首页 > 编程语言 > Java开发

spring揭秘 读书笔记 六 bean的一生

2015-08-24 16:05 701 查看
我们知道,Spring容器具有对象的BeanDefinition来保存该对象实例化时需要的数据。

对象通过container.getBean()方法是才会初始化该对象。

BeanFactory

我们知道BeanFactory默认是懒加载的,换句话说,当我们请求对象a的时候,a本身还并没有被实例化,同时如果a还依赖b,那么b也还没有被初始化。

当我们显示的在代码里调用getBean("a")的时候,容器会先初始化b,再初始化a,然后把b注入到a中,当然,如果a或b实现了某些回调接口,就根据接口装备它,最后返还a。

ApplicationContext

与BeanFactory不同,ApplicationContext并不是懒加载,当我们获得ApplicationContext的引用后,所有的bean就已经被实例化了。只不过它会在启动阶段的活动完成之后,紧接着调用注册到该容器的所有bean定义的实例化方法getBean()。

另外,我们得知道container.getBean()方法只是有可能会触发对象的实例化,我们只是说"有可能"是因为,如果对象已经实例化了,那么调用getBean的时候就会返回缓存的那个对象(当然prototype型的除外)。

实例化过程如图:



实例化Bean对象

容器在内部实现的时候,采用“策略模式(Strategy Pattern)”来决定采用何种方式初始化bean实例。通常,可以通过反射或者CGLIB动态字节码生成来初始化相应的bean实例或者动态生成其子类。

在这里,大家可以查阅一下策略模式的使用。

org.springframework.beans.factory.support.InstantiationStrategy定义是实例化策略的抽象接口,其直接子类SimpleInstantiationStrategy实现了简单的对象实例化功能,可以通过反射来实例化对象实例,但不支持方法注入方式的对象实例化。CglibSubclassingInstantiationStrategy继承了SimpleInstantiationStrategy的以反射方式实例化对象的功能,并且通过CGLIB的动态字节码生成功能,该策略实现类可以动态生成某个类的子类,进而满足了方法注入所需的对象

实例化需求。默认情况下,容器内部采用的是CglibSubclassingInstantiationStrategy。

在这一步,问题不大。

不过有个东西得说明一下,第一步实例化的结果并不是原生的bean,spring为了在第二步设置属性方便就用BeanWrapper对其做了包装。

BeanWrapper

BeanWrapper是个接口,其实现类是BeanWrapperImpl。

Object provider = Class.forName("package.name.FXNewsProvider").newInstance();
Object listener = Class.forName("package.name.DowJonesNewsListener").newInstance();
Object persister = Class.forName("package.name.DowJonesNewsPersister").newInstance();

BeanWrapper newsProvider = new BeanWrapperImpl(provider);
newsProvider.setPropertyValue("newsListener", listener);
newsProvider.setPropertyValue("newPersistener", persister);

assertTrue(newsProvider.getWrappedInstance() instanceof FXNewsProvider);
assertSame(provider, newsProvider.getWrappedInstance());
assertSame(listener, newsProvider.getPropertyValue("newsListener"));
assertSame(persister, newsProvider.getPropertyValue("newPersistener"));
这是使用BeanWrapper设置属性的例子。

如果直接用反射呢?

Object provider;
try {
provider = Class.forName("package.name.FXNewsProvider").newInstance();
Object listener = Class.forName("package.name.DowJonesNewsListener").newInstance();
Object persister = Class.forName("package.name.DowJonesNewsPersister").newInstance();

Class providerClazz = provider.getClass();
Field listenerField = providerClazz.getField("newsListener");
listenerField.set(provider, listener);
Field persisterField = providerClazz.getField("newsListener");
persisterField.set(provider, persister);
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
呵呵,操蛋的try catch。

使用BeanWrapper的好处就在于,我们可以统一对属性的操作。

各种Aware接口

还记得,我们在前几节讲的关于,从bean中获得BeanFactory的引用的例子吗?

其实这样的接口还有几个:

org.springframework.beans.factory.BeanNameAware。让bean知道自己在容器中到底叫什么名字。

org.springframework.beans.factory.BeanClassLoaderAware。让bean知道是那个classloader加载的自己。

org.springframework.beans.factory.BeanFactoryAware。让bean知道自己到底在哪个"世界"里

上面的几个是针对beanFactory的。

对应于ApplicationContext的Aware接口有

org.springframework.context.ResourceLoaderAware 。 ApplicationContext 实 现 了Spring的ResourceLoader接口(后面会提及详细信息)。当容器检测到当前对象实例实现了ResourceLoaderAware接口之后,会将当前ApplicationContext自身设置到对象实例,这样当前对象实例就拥有了其所在ApplicationContext容器的一个引用。

org.springframework.context.ApplicationEventPublisherAware。ApplicationContext作为一个容器,同时还实现了ApplicationEventPublisher接口,这样,它就可以作为Appli- cationEventPublisher来使用。所以,当前ApplicationContext容器如果检测到当前实例化的对象实例声明了ApplicationEventPublisherAware接口,则会将自身注入当前对象。

org.springframework.context.MessageSourceAware。ApplicationContext通过MessageSource接口提供国际化的信息支持,即I18n(Internationalization)。它自身就实现了MessageSource接口,所以当检测到当前对象实例实现了MessageSourceAware接口,则会将自身注入当前对象实例。

org.springframework.context.ApplicationContextAware。如果ApplicationContext容器检测到当前对象实现了ApplicationContextAware接口,则会将自身注入当前对象实例。

BeanPostProcessor

这是什么东西?这是applicationcontext检测Aware接口并设置相关依赖的东西。

BeanPostProcessor的概念容易与BeanFactoryPostProcessor的概念混淆。但只要记住BeanPostProcessor是存在于对象实例化阶段,而BeanFactoryPostProcessor则是存在于容器启动阶段,这两个概念就比较容易区分了。

BeanPostProcessor接口有两个方法,如下:

public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
两个方法

postProcessBeforeInitialization对应与图4-10中的BeanPostProcessor前置处理。

postProcessAfterInitialization对应与图4-10中的BeanPostProcessor后置处理。

BeanPostProcessor这个两个方法的参数都包含了当前要处理的bean,也就是说,在这两个方法里,我们可以对bean做任何事情。

通常比较常见的使用BeanPostProcessor的场景,是处理标记接口实现类,或者为当前对象提供代理实现。在图4-10的第三步中,ApplicationContext对应的那些Aware接口实际上就是通过BeanPostProcessor的方式进行处理的。当ApplicationContext中每个bean对象的实例化过程走到BeanPostProcessor前置处理这一步时,ApplicationContext容器会检测每一个bean是否实现了之前注册到容器的ApplicationContextAwareProcessor这个BeanPostProcessor的实现类,然后就会调用其postProcessBeforeInitialization()方法,检查并设置Aware相关依赖。

postProcessBeforeInitialization方法调用了invokeAwareInterfaces方法。

private void invokeAwareInterfaces(Object bean) {
if (bean instanceof Aware) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(
new EmbeddedValueResolver(this.applicationContext.getBeanFactory()));
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
}

注意ApplicationContextAwareProcessor这个类,实现了BeanPostProcessor,但是它并没有实现ApplicationContextAware这个接口。

*******************************************

以下为2016-04-20日更新

如果你在spring的doc文档里找ApplicationContextAwareProcessor,是找不到的,因为它并不是public的



第二,看名字也知道这个类是作用于容器时applicationcontext的时候,如果我们单纯的使用beanfactory,是不会用到它的

第三,ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)----->refresh()---->prepareBeanFactory(beanFactory);



看到了吧,在这里手动注入的

那么我们自己写的beanpostprocessor如何加入到容器里呢?

ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)----->refresh()-->registerBeanPostProcessors()



第四 和beanfactory的区别

applicationcontext是专门注入一个ApplicationContextAwareProcessor,来处理xxxaware接口的问题

beanfactory并没有一个单独的类来处理它,当然也就不需要注入这个类了

看AbstractAutowireCapableBeanFactory:





以上为2016-04-20日更新

*******************************************

自定义BeanPostProcessor

为了演示BeanPostProcessor的强大功能,我们自定义一个BeanPostProcessor

假设系统中所有的IFXNewsListener实现类需要从某个位置取得相应的服务器连接密码,而且系统中保存的密码是加密的,那么在IFXNewsListener发送这个密码给新闻服务器进行连接验证的时候,首先需要对系统中取得的密码进行解密,然后才能发送。我们将采用BeanPostProcessor技术,对所有的IFXNewsListener的实现类进行统一的解密操作。

到底哪些类需要加密解密呢?我们用PasswordDecodable标记

public interface PasswordDecodable {
String getEncodedPassword();   //从系统某处获得加密后的密码
void setDecodedPassword(String password);  //将解密后的密码写入对象中
}

public class DowJonesNewsListener implements IFXNewsListener,PasswordDecodable {
private String password;           //解密后的密码

public String[] getAvailableNewsIds() {
// 省略
}

public FXNewsBean getNewsByPK(String newsId) {
// 省略
}

public void postProcessIfNecessary(String newsId) {
// 省略
}

public String getEncodedPassword() {
return this.password;
}

public void setDecodedPassword(String password) {
this.password = password;
}

}
然后实现我们自己的BeanPostProcessor。

public class PasswordDecodePostProcessor implements BeanPostProcessor {

public Object postProcessAfterInitialization(Object object, String beanName)
throws BeansException {
return object;
}

public Object postProcessBeforeInitialization(Object object, String beanName)
throws BeansException {
if(object instanceof PasswordDecodable) {
String encodedPassword = ((PasswordDecodable)object).getEncodedPassword();
String decodedPassword = decodePassword(encodedPassword);
((PasswordDecodable)object).setDecodedPassword(decodedPassword);
}
return object;
}

private String decodePassword(String encodedPassword) {
if(encodedPassword.length()<5)    //解码是 去掉前5个字符就是真正的密码..
return "";
encodedPassword=encodedPassword.subString(5);
return encodedPassword;
}
}
下来就是注册这个PasswordDecodePostProcessor了。

对于BeanFactory类型的容器来说,我们需要通过手工编码的方式将相应的BeanPostProcessor注册到容器,也就是调用ConfigurableBeanFactory的addBeanPostProcessor()方法,见如下代码:

ConfigurableBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource(...));
beanFactory.addBeanPostProcessor(new PasswordDecodePostProcessor());
...
getBean();
如果是applicationcontext那就直接在xml注明就OK

<beans>
<bean id="passwordDecodePostProcessor" class="package.name.PasswordDecodePostProcessor">
<!--如果需要,注入必要的依赖-->
</bean>
...
</beans>

http://guoliangqi.iteye.com/blog/635826 里面包含了一个自定义BeanPostProcessor实现动态代理的例子

感谢glt

参考资料

http://guoliangqi.iteye.com/blog/635826

http://langgufu.iteye.com/blog/1499966

http://blog.csdn.net/wyabc1986/article/details/9415463
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: