(注:这个曾发表在某Blog上,那个马上要没了,你懂的=。=)
SSH相信很多整过J2EE的人都很熟悉,但可能很多人没有深入研究过其底层架构,我觉得其中spring的源码是记得详细研究的,如果能对其IOC,AOP的整个流程有清晰的了解,就能对其进行扩展来满足自己项目的需求,在遇到bug也能更好的解决问题。
下面分析两个spring中比较优雅的功能的底层实现来看看spring的扩展思路。
1. spring自定义属性编辑器
(1)使用
要使用spring的自定义属性编辑器,只需要让自己的属性编辑器继承java.beans.PropertyEditorSupport类,并重写其中setAsText(String text),将text装换成目标类型后,调用setValue设置目标值。
实例:
Java代码:
自定义属性编辑器:
public class CustomDatePropertyEditor extends java.beans.PropertyEditorSupport{
private String format;
public String getFormat()
{
return format;
}
public void setFormat(String format) {
this.format = format;
}
@Override
public void setAsText(String text) throws IllegalArgumentException {
SimpleDateFormat f = new SimpleDateFormat(format);
try {
this.setValue(f.parse(text));
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Spring配置文件:
<bean id="userinfo" class="cn.hibernate.UserInfo">
<property name="name">
<value>mr</value>
</property>
<property name="sign">
<value>2011-11-28</value>
</property>
</bean>
<bean id="customDatePropertyEditor" class="cn.hibernate.CustomDatePropertyEditor">
<property name="format">
<value>yyyy-MM-dd</value>
</property>
</bean>
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="java.util.Date">
<ref bean="customDatePropertyEditor"/>
</entry>
</map>
</property>
</bean>
(2)底层实现
先看看spring对自定义属性编辑器的支持:
图1 编辑器类关系
CustomEditorConfigurer实现了BeanFactoryPostProcessor接口,回头看在refresh()中的整个调用过程:
图2 refresh调用过程
从上面的时序图可以看出,在bean初始化后,调用了invokeBeanFactoryPostProcessor(),在这个方法中调用实现了BeanFactoryPostProcessor接口的postProcessBeanFactory方法,我们看看CustomEditorConfigurer类中实现的postProcessBeanFactory方法:
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
//处理系统的属性编辑器
……
//处理自定义属性编辑器
if (this.customEditors != null) {
for (Map.Entry<String, ?> entry : this.customEditors.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
Class requiredType = null;
try {
requiredType = ClassUtils.forName(key, this.beanClassLoader);
if (value instanceof PropertyEditor) {
if (logger.isWarnEnabled()) {
//处理warn信息
}
beanFactory.addPropertyEditorRegistrar(
new SharedPropertyEditorRegistrar(requiredType, (PropertyEditor) value));
}
else if (value instanceof Class) {
//value为Class类型时的处理
beanFactory.registerCustomEditor(requiredType, (Class) value);
}
else if (value instanceof String) {
Class editorClass = ClassUtils.forName((String) value, this.beanClassLoader);
Assert.isAssignable(PropertyEditor.class, editorClass);
beanFactory.registerCustomEditor(requiredType, (Class<? extends PropertyEditor>) editorClass); }
else {
//处理异常IllegalArgumentException
}
} catch (ClassNotFoundException ex) {
//处理异常
……
}
}
}
在方法postProcessBeanFactory中,首先处理spring中的属性编辑器,然后处理我们自己定义的属性编辑器,它根据value值类型的不同来分三种情况处理:(1)PropertyEditor类型;(2)Class类型(3)String类型。在online的sessionFactory配置中采用的是String类型,所以在postProcessBeanFactory中,先通过java的反射机制,通过string表示的java类路径得到java类实例,然后调用registerCustomEditor注册自定义的属性编辑器。
知道了属性编辑器是怎么进行注册的,接下来我们就要看看自定义的属性编辑器是在哪里将String转换为需要的类型。
通过一个时序图回顾一下bean对象关系的建立过程:
图3 Bean关系建立过程
上面时序图是一个简化的bean对象建立过程,其中涉及各个类的相互调用,如果想了解详细过程可看第一篇文档IOC的详细依赖注入过程。从时序图可以看到spring调用自定义属性编辑器完成类型的转换是发生在调用BeanWrapperImpl的setPropertyValues()方法时,在setPropertyValues()方法中,调用TypeConverterDelegate的convertIfNecessary方法,在此方法中spring调用自定义编辑器完成类型转换。进入convertIfNecessary,看以看到其中:
// Value not of required type?
if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue)))
{
//editor为null时,查找spring中默认的属性编辑器
if (editor == null) {
editor = findDefaultEditor(requiredType, typeDescriptor);
}
//调用属性编辑器进行类型转换
convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor); }
}
我们看下convertedValue中的代码片段:
if (sharedEditor) {
// Synchronized access to shared editor instance.
synchronized (editor) {
editor.setValue(convertedValue);
//调用PropertyEditor的getValue获得转换后的类型
newConvertedValue = editor.getValue();
}
}
结合1.2中对自定义属性编辑器的使用的讲解,相信能看的出来这里编辑具体的实现的地方。
2.spring中数据实体的加载
看一幅图:
使用过spring+hibernate的应该都比较清楚这里面包含了用来辅助进行hibernate的sessionFactory的初始化的各种FactoryBean。
这时我们应该有疑问,这些SessionFactoryBean是如何和springIOC联系起来的?
看到图2.3,bean关系的建立时序图,可以在执行了populate方法后,调用了initializeBean方法,看到initializeBean中,有:
这里调用了invokeInitMethods方法,在这个方法中又可以看到:
在这个初始化方法中调用了我们熟悉的afterPropertiesSet方法,这个方法是接口InitializingBean的方法。从图3.2中,可以看到两个sessionFactory的实现类都是实现了InitializingBean接口的,自然其afterPropertiesSet方法会被IOC容器回调,这样就可以通过afterPropertiesSet方法与springIOC联系起来。
我们可以以一个时序图看它们间的调用关系,IOC容器回调afterPropertiesSet开始:
IOC容器回调EntityAnnotationSessionFactory中的afterPropertiesSet方法,其完成了基本的操作后调用initSingleDataSource方法,在initSingleDataSource调用父类的afterPropertiesSet方法,也即AbstractSessionFactoryBean(这里不同版本的spring方法的结构可能稍有不同,但原理是一样的)。AbstractSessionFactoryBean的afterPropertiesSet方法调用buildSessionFactory,在这个方法中通过配置信息得到SessionFactory,在获得sessionFactory的过程中,buildSessionFactory通过postProcessMapping调用了scanPackage()方法,这时你应该就看到了可以通过重载这里面的各种方法来实现自己需要的功能。