spring揭秘 读书笔记 六 bean的一生
我们知道,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
spring揭秘 读书笔记 六 bean的一生的更多相关文章
- Spring揭秘 读书笔记 三 bean的scope与FactoryBean
本书可作为王富强所著<<Spring揭秘>>一书的读书笔记 第四章 BeanFactory的xml之旅 bean的scope scope有时被翻译为"作用域&quo ...
- spring揭秘 读书笔记 二 BeanFactory的对象注册与依赖绑定
本文是王福强所著<<spring揭秘>>一书的读书笔记 我们前面就说过,Spring的IoC容器时一个IoC Service Provider,而且IoC Service Pr ...
- spring揭秘 读书笔记 一 IoC初探
本文是王福强所著<<spring揭秘>>一书的读书笔记 ioc的基本概念 一个例子 我们看下面这个类,getAndPersistNews方法干了四件事 1 通过newsList ...
- spring揭秘 读书笔记 二 BeanFactory的对象注冊与依赖绑定
本文是王福强所著<<spring揭秘>>一书的读书笔记 我们前面就说过,Spring的IoC容器时一个IoC Service Provider,并且IoC Service Pr ...
- Spring揭秘读书笔记 八 数据访问异常体系
这篇博客 来自spring揭秘一书的第十三章 为什么要有访问异常都有一个体系,这个我们得从DAO模式说起. DAO模式 任何一个系统,不管是一个最简单的小系统,还是大规模的系统,都得跟数据打交道,说白 ...
- Spring揭秘 读书笔记 七 BeanFactory的启动分析
首先,先看我自己画的BeanFactory启动时的时序图. 第一次接触时序图,可能有些地方画的不是很符合时序图的规则,大家只关注调用顺序即可. public static void main(Stri ...
- Spring揭秘 读书笔记 五 容器的启动
Spring的IoC容器所起的作用,就是生产bean,并维持bean间的依赖关系.它会以某种方式加载Configuration Metadata(通常也就是XML格式的配置信息),然后根据这些信息绑定 ...
- Spring揭秘 读书笔记 四----方法注入
我们知道,拥有prototype类型scope的bean,在请求方每次向容器请求该类型对象的时候,容器都会返回一个全新的该对象实例. 我们看下面的例子: public class MockNewsPe ...
- spring揭秘读书笔记----spring的ioc容器之BeanFactory
spring的ioc容器是一种特殊的Ioc Service Provider(ioc服务提供者),如果把普通的ioc容器认为是工厂模式(其实很相似),那spring的ioc容器只是让这个工厂的功能更强 ...
随机推荐
- iOS开源加密相册Agony的实现(二)
简介 虽然目前市面上有一些不错的加密相册App,但不是内置广告,就是对上传的张数有所限制.本文介绍了一个加密相册的制作过程,该加密相册将包括多密码(输入不同的密码即可访问不同的空间,可掩人耳目).Wi ...
- 从0到1:制作你的苹果podcast(播客)
注意:本文不是教你如何录音.如何做后期的文章.而是聚焦于如何搭建播客(podcast)需要的环境. 本文科普类文章,干货少,湿货多. 先选一个主机吧 这步的初衷和你自己建站是一样的.你可以购买一个独立 ...
- Tomcat怎么实现异步Servlet
有时Servlet在生成响应报文前必须等待某些耗时的操作,比如在等待一个可用的JDBC连接或等待一个远程Web服务的响应.对于这种情况servlet规范中定义了异步处理方式,由于Servlet中等待阻 ...
- 针对于Python的OpenCV环境搭建
OpenCV 依赖 下载OpenCV 配置 总结 给Python搭建opencv的环境还真是略嫌麻烦,于是做下笔记,以备不时之需. OpenCV 依赖 opencv有些依赖,我们必须安装一下,否则接下 ...
- static 变量(静态变量)
在C++的面向对象编程中,static还可以加在类的数据成员或成员函数之前.这样定义的数据成员或成员函数就被类所拥有,而不再属于类的对象. #include <iostream> usin ...
- linux源码编译安装OpenCV
为了尽可能保证OpenCV的特性,使用OpenCV源码编译安装在linux上.先从安装其依赖项开始,以ubuntu 14.04.X为例讲解在Linux上源码编译安装OpenCV,其他linux版本可以 ...
- 10 GridView 样式属性
GridView 样式属性: 1,android:numColumns="auto_fit" 显示的列数 如果android:numColumns不设置那么自动每行1列 如下图 2 ...
- JAVA面向对象-----main方法详解
JVM看不懂的可以跳过,这里不做过多解释,(^__^) 嘻嘻-- 主函数是静态的 public static void main(String[] args){ } 主函数是什么:主函数是一个特殊的函 ...
- Struts 2 之文件上传
如果要获得上传文件的原始名称,需要定义一个String类型的属性,属性名必须为***FileName,其中***为File属性的名称:同理,如果要获取该文件的MIME类型,需要定义一个***Conte ...
- Android简易实战教程--第二十三话《绚丽的菜单项》
转载本博客请注明出处:点击打开链接 http://blog.csdn.net/qq_32059827/article/details/52327456 今天这篇稍微增强点代码量,可能要多花上5分钟喽 ...