第一章 Spring之最熟悉的陌生人——IOC
第二章 Spring之假如让你来写IOC容器——加载资源篇
第三章 Spring之假如让你来写IOC容器——解析配置文件篇
第四章 Spring之假如让你来写IOC容器——XML配置文件篇
第五章 Spring之假如让你来写IOC容器——BeanFactory和FactoryBean
第六章 Spring之假如让你来写IOC容器——Scope和属性填充
第七章 Spring之假如让你来写IOC容器——属性填充特别篇:SpEL表达式
第八章 Spring之假如让你来写IOC容器——拓展篇
第九章 Spring之源码阅读——环境搭建篇
第十章 Spring之源码阅读——IOC篇
对于Spring一直都是既熟悉又陌生,说对它熟悉吧,平时用用没啥问题,但面试的时候被问的一脸懵逼,就很尴尬,都不好意思在简历上写着熟悉Spring了
所以决定花点时间研究研究Spring的源码。主要参考的书籍是:《Spring源码深度解析(第2版)》、《Spring揭秘》、《Spring技术内幕:深入解析Spring架构与设计原理(第2版)》
书接上回,在上篇 第五章 Spring之假如让你来写IOC容器——BeanFactory和FactoryBean 已经提取出BeanFactory接口,还添加了FactoryBean作为拓展,给用户添加自己创建bean的方式。接下来看看A君又会面临什么样的挑战吧
出场人物:A君(苦逼的开发)、老大(项目经理)
背景:老大要求A君在一周内开发个简单的IOC容器
前情提要:在用户的要求下,A君 新增了FactoryBean接口作为拓展,让用户自己能自由的创建对象。。。
随着FactoryBean的改造完毕,A君暗自的祷告:别再提新需求,别再提新需求。A君是真的被折腾怕了,一提新需求,加班指定是跑不掉的
屋漏偏逢连夜雨,船迟又遇打头风。还没过多久,老大又找到A君了,不过这回倒不是新需求,而是性能相关的问题。一时间,A君不知道是该高兴还是该骂娘。用户提到:对象创建过于频繁,重复创建导致内存占用大,GC频率高
听到这个问题,A君一下子想到了很多:spring默认使用单例,单例可以提高效率,对象重用。凡此种种,不都是为了减少内存开销,提高效率吗?前人的智慧已经摆在那里了,没有比这更好的解决方案了
有了方案后,A君心里大定,不怕没代码,就怕没思路。不过还是得考虑下拓展,碰到这么难缠的客户,鬼知道之后还会提什么奇奇怪怪的需求。基于这般考虑,A君定义了Scope接口,作为bean的作用域,其代码如下:
import com.hqd.ch03.v10.factory.ObjectFactory;
/**
* bean对象作用域
*/
public interface Scope {
/**
* 获取对象
*
* @param name
* @param factory
* @return
*/
Object get(String name, ObjectFactory<?> factory);
/**
* 删除对象
*
* @param name
* @return
*/
Object remove(String name);
}
这里会涉及到ObjectFactory接口,这个接口只是单纯为了bean对象延迟创建,可以通过ObjectFactory接口获取到bean对象,毕竟创建对象还是得容器来做,至于放哪里嘛?交由用户决定,ObjectFactory接口代码如下:
@FunctionalInterface
public interface ObjectFactory<T> {
/**
* 获取对象
*
* @return
*/
T getObject();
}
定义完Scope接口和ObjectFactory接口之后,A君又遇到一个问题:要怎么注册用户自定义的作用域?当然可以在BeanFactory接口中添加个注册作用域的方法,但是这又违反了接口单一性原则,BeanFactory接口只规定了IOC容器最基本的功能,作用域这些属于拓展的内容了,放在BeanFactory接口里显然不合适
既然没办法直接写进BeanFactory接口中,那就新加个接口吧!于是,A君定义了ConfigurableBeanFactory接口,对BeanFactory进行了拓展,负责注册各种用户自定义的内容,代码如下:
import com.hqd.ch03.v10.config.Scope;
public interface ConfigurableBeanFactory extends BeanFactory {
/**
* 单例
*/
String SCOPE_SINGLETON = "singleton";
/**
* 多利
*/
String SCOPE_PROTOTYPE = "prototype";
/**
* 注册作用域
*
* @param scopeName
* @param scope
*/
void registerScope(String scopeName, Scope scope);
}
解决完注册的问题后,接下来就是单例的问题了。想要实现单例,就需要把创建好的bean对象缓存起来,保证bean对象只创建一次,后边都从缓存里边取。老样子,A君定了单例缓存接口SingletonBeanRegistry,代码如下:
/**
* 单例缓存接口
*/
public interface SingletonBeanRegistry {
/**
* 获取单例对象
*
* @param beanName
* @return
*/
Object getSingleton(String beanName);
/**
* 是否包含对象
*
* @param beanName
* @return
*/
boolean containsSingleton(String beanName);
/**
* 添加单例对象
*
* @param beanName
* @param obj
*/
void registerSingleton(String beanName, Object obj);
}
按照以往惯例,有了接口后,就是提供默认实现了。不过此时,A君却遇到一个面试经常遇到的问题:循环依赖
循环依赖产生的原因比较简单,无非就是A引用了B,而B又引用了A,造成了闭环,导致出现死循环。这种情况在实际开发中没办法完全杜绝,也许人家真的有这种需要呢?
在讨论解决循环依赖之前,先明确不能解决的情况
构造器中包含循环依赖,创建对象时,你猜是先栈溢出还是堆溢出。对象都没法创建出来,更别说解决了
对象不是单例对象,这点也好说,不是单例对象,意味着无法对对象进行缓存,每次都是新的对象,还是个死结
明确完不能解决的情况,能解决的情况就呼之欲出了,必须是构造器中不包含循环依赖的单例对象。如果只是单纯的解决,A君心里已经有了答案,无非就是先创建一个空对象,再把这个空对象放进缓存里就行了,这样只要一级缓存就够了。这样解决虽然方便,但是却会破坏原有的流程,原本容器是:创建对象=>填充属性=>放入缓存,如今却要因为一个特殊情况,而改变整个bean对象的创建流程,变成:创建对象=>放入缓存=>填充属性,这不是A君所愿意看到,破坏bean创建流程,意味着到时候在流程上做拓展会很麻烦,A君希望只有真正存在循环依赖的情况才进行特殊处理
为此,A君想到了多级缓存
一级缓存存放初始化完成的bean对象
二级缓存存放为初始化的对象用来解决循环依赖,并且保证单例
三级缓存用以存放工厂延迟加载
理清思绪后,就能给SingletonBeanRegistry接口提供默认实现了。A君定义了DefaultSingletonBeanRegistry类,代码如下:
import com.hqd.ch03.v10.factory.ObjectFactory;
import com.hqd.ch03.v10.registry.SingletonBeanRegistry;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {
/**
* 三级缓存,只有Bean存在循环依赖时触发,做特殊处理,其他Bean按照正常初始化流程
*/
private final Map<String, ObjectFactory<?>> singletonFactories = new ConcurrentHashMap<>();
/**
* 二级缓存,保证三级缓存使用ObjectFactory创建出来的对象唯一
*/
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();
/**
* 一级缓存,存储初始化完的单例对象
*/
private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
@Override
public Object getSingleton(String beanName) {
Object single = singletonObjects.get(beanName);
if (single == null) {
single = earlySingletonObjects.get(beanName);
if (single == null) {
ObjectFactory<?> objectFactory = this.singletonFactories.get(beanName);
if (objectFactory != null) {
single = objectFactory.getObject();
this.earlySingletonObjects.put(beanName, single);
this.singletonFactories.remove(beanName);
}
}
}
return single;
}
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
/**
* 只有bean未初始化完成才能加入三级缓存
*/
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
}
}
protected void addSingleton(String beanName, Object obj) {
this.singletonObjects.put(beanName, obj);
this.earlySingletonObjects.remove(beanName);
this.singletonFactories.remove(beanName);
}
@Override
public boolean containsSingleton(String beanName) {
return singletonObjects.containsKey(beanName);
}
@Override
public void registerSingleton(String beanName, Object obj) {
singletonObjects.put(beanName, obj);
}
}
有了单例缓存的实现类,接着需要对SpringImitation进行一番改造。SpringImitation需要继承DefaultSingletonBeanRegistry类,并且实现ConfigurableBeanFactory接口,改造也不多,就是提供注册作用域Scope的实现,根据不同的作用域存放对象,比如:单例存放在缓存中,多例不缓存。另外的改造点就是获取对象时,先去缓存获取即可,改造如下:
注册Scope作用域,如下:
完整代码:
import com.hqd.ch03.v10.config.BeanDefinition;
import com.hqd.ch03.v10.config.MutablePropertyValues;
import com.hqd.ch03.v10.config.Scope;
import com.hqd.ch03.v10.factory.BeanFactory;
import com.hqd.ch03.v10.factory.ConfigurableBeanFactory;
import com.hqd.ch03.v10.factory.FactoryBean;
import com.hqd.ch03.v10.io.ResourceLoader;
import com.hqd.ch03.v10.io.support.DefaultResourceLoader;
import com.hqd.ch03.v10.registry.BeanDefinitionRegistry;
import com.hqd.ch03.v10.registry.support.DefaultSingletonBeanRegistry;
import com.hqd.ch03.v10.registry.support.SimpleBeanDefinitionRegistry;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
import java.util.Map;
public abstract class SpringImitationV10 extends DefaultSingletonBeanRegistry implements ConfigurableBeanFactory {
protected BeanDefinitionRegistry beanDefinitionRegistry = new SimpleBeanDefinitionRegistry();
/**
* 单例缓存
*/
protected Map<String, Scope> scopeCache = new HashMap<>();
/**
* 资源加载器
*/
protected ResourceLoader resourceLoader = new DefaultResourceLoader();
public static boolean isFactoryDereference(String name) {
return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
}
/**
* 处理传入的name,去除&开头
*
* @param name
* @return
*/
protected String transformedBeanName(String name) {
if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
return name;
}
String beanName = name;
do {
beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
}
while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
return beanName;
}
/**
* 区分正常bean还是FactoryBean
*
* @param obj
* @param name
* @param beanName
* @param bd
* @return
*/
protected Object getObjectForBeanInstance(Object obj, String name, String beanName, BeanDefinition bd) {
if (isFactoryDereference(name)) {
if (!(obj instanceof FactoryBean)) {
throw new RuntimeException(String.format("%s 未实现FactoryBean接口", beanName));
}
return obj;
}
if (obj instanceof FactoryBean) {
return ((FactoryBean) obj).getObject();
}
return obj;
}
private void initBean(Object bean, BeanDefinition bd) {
try {
MutablePropertyValues properties = bd.getProperties();
properties.getProperties().forEach(propertyValue -> {
boolean isRef = propertyValue.isRef();
Object value = propertyValue.getValue();
if (isRef) {
value = getBean((String) value);
}
try {
BeanUtils.setProperty(bean, propertyValue.getName(), value);
} catch (Exception e) {
throw new RuntimeException(e);
}
});
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private Object createBean(BeanDefinition bd, String beanName) {
try {
Object instance = null;
if (bd.isSingleton()) {//单例
instance = getSingleton(beanName);
if (instance == null) {
instance = doCreate(bd, beanName);
}
addSingleton(beanName, instance);
} else if (bd.isPrototype()) {//多例
instance = doCreate(bd, beanName);
} else {//自定义作用域
String scopeName = bd.getScope();
Scope scope = scopeCache.get(scopeName);
if (scope == null) {
throw new RuntimeException(String.format("[%s]未注册scope", scopeName));
}
instance = scope.get(beanName, () -> doCreate(bd, beanName));
}
return instance;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected Object doCreate(BeanDefinition bd, String beanName) {
String beanClass = bd.getBeanClass();
try {
Object instance = Class.forName(beanClass).getConstructor().newInstance();
addSingletonFactory(beanName, () -> instance);
//doSomething
initBean(instance, bd);
return instance;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void registerScope(String scopeName, Scope scope) {
if (StringUtils.isNotBlank(scopeName) && scope != null) {
scopeCache.put(scopeName, scope);
}
}
@Override
public <T> T getBean(String name, Class<T> clazz) {
return (T) doGetBean(name);
}
@Override
public Object getBean(String name) {
return doGetBean(name);
}
protected Object doGetBean(String name) {
String beanName = transformedBeanName(name);
BeanDefinition bd = beanDefinitionRegistry.getBeanDefinition(beanName);
Object bean = createBean(bd, beanName);
bean = getObjectForBeanInstance(bean, name, beanName, bd);
return bean;
}
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}
都做好后,又到了愉快地测试环节,这次测试需要测试两部分内容,一个是自定义作用域,另外一个是循环依赖。先从循环依赖开始吧,先添加一个IdCard类,如下:
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class IdCard {
private String id;
private User user;
}
接着修改下User类,形成循环依赖,如下:
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class User {
private String name;
private int age;
private IdCard idCard;
public User(String name, IdCard idCard) {
this.name = name;
this.idCard = idCard;
}
}
然后是自定义作用域ThreadScope类,bean和线程绑定,相同的线程才能获取到相同的bean对象。代码如下:
import com.hqd.ch03.v10.config.Scope;
import com.hqd.ch03.v10.factory.ObjectFactory;
import java.util.HashMap;
import java.util.Map;
/**
* 线程作用域
*/
public class ThreadScope implements Scope {
private final ThreadLocal<Map<String, Object>> threadLocal = new ThreadLocal() {
@Override
protected Object initialValue() {
return new HashMap<>();
}
};
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
Map scope = threadLocal.get();
Object obj = scope.get(name);
if (obj == null) {
obj = objectFactory.getObject();
scope.put(name, obj);
}
return obj;
}
@Override
public Object remove(String name) {
return threadLocal.get().remove(name);
}
}
接下再修改下配置文件,如下:
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="idCard" class="com.hqd.ch03.bean.IdCard">
<property name="id" value="123456"/>
<property name="user" ref="user0"/>
bean>
<bean id="user0" class="com.hqd.ch03.bean.User" scope="singleton">
<property name="idCard" ref="idCard"/>
<property name="name" value="法外狂徒-张三"/>
<property name="age" value="14"/>
bean>
<bean id="user1" class="com.hqd.ch03.bean.User" scope="prototype">
<property name="name" value="法外狂徒-张三"/>
<property name="age" value="14"/>
bean>
<bean id="user2" class="com.hqd.ch03.bean.User" scope="thread">
<property name="name" value="法外狂徒-张三"/>
<property name="age" value="14"/>
bean>
<bean id="user3" class="com.hqd.ch03.bean.User" scope="singleton">
<constructor-arg index="0" value="ikun"/>
<constructor-arg index="1" value="15"/>
<constructor-arg index="2" ref="idCard"/>
bean>
<bean id="user4" class="com.hqd.ch03.bean.User" scope="singleton">
<constructor-arg index="0" value="ikun"/>
<constructor-arg index="1" ref="idCard"/>
<property name="age" value="15"/>
bean>
beans>
都准备好后,编写测试方法,如下:
@Test
public void v10() {
System.out.println("############# 第十版:scope作用域 #############");
SpringImitationV10 beanFactory = new SpringImitationV10Xml("classpath:v10/bean.xml");
Scope threadScope = new ThreadScope();
//注册自定义作用域
beanFactory.registerScope("thread", threadScope);
for (int i = 0; i < 5; i++) {
String beanName = "user";
for (int j = 0; j < 2; j++) {
User bean = beanFactory.getBean(beanName + j, User.class);
System.out.println(beanName + j + ":" + bean);
}
}
//使用容器获取bean
try {
for (int i = 0; i < 2; i++) { //@2
new Thread(() -> {
System.out.println(Thread.currentThread() + "," + beanFactory.getBean("user2"));
System.out.println(Thread.currentThread() + "," + beanFactory.getBean("user2"));
}).start();
TimeUnit.SECONDS.sleep(1);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
打印结果如下:
可以看到,IdCard和User都有值了,并且分别引用了对方
根据打印结果可以看出,user0是单例模式,所以每次获取到的对象hashCode都一样。user1是多例,每次获取的对象都不一样。user2是自定义线程作用域,所以同一个线程内获取到的对象一样
“好嘞,总算搞定了”。虽然被折腾的很难受,A君还是有所收获的,从之前的懵懵懂懂,到现在越来越清晰起来了。或许,以后可以开始看spring的源码了
“他来了,他又来了”,A君看到老大过来都要崩溃了。老大当然不是来给A君升职加薪,而是带着用户的新需求来的。
“这次又是啥需求”,A君都绝望了,屁股还没坐热,新需求就过来了
“用户希望可以自定义类属性解析,现在的类属性解析不符合他们的要求,又没法拓展”,老大留下几句话后就飘然离去。只剩下A君在原地凌乱了:之前图省事,直接用 BeanUtils 实现了,哪知道现在又玩这出
不过,A君这次到不怎么慌乱,毕竟不能白被虐这么多次。说是类属性解析,其实就是再设置类属性时候,回调用户自定义的实现,相当于类型转换:把String类型转成各个不同的类型。基于之前的经验,无非就是定义一个接口给用户做定制化类型转换,再定义个注册类型转换的接口就完事了
这次转换接口不需要A君自己定义,jdk有现成的 PropertyEditor 接口,还有做好封装的 PropertyEditorSupport 类,A君只需要定义了接口 PropertyEditorRegistry 来注册自定义实现即可,代码如下:
import java.beans.PropertyEditor;
/**
* 属性转换器注册接口
*/
public interface PropertyEditorRegistry {
/**
* 注册转换器
*
* @param requiredType
* @param propertyEditor
*/
void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor);
/**
* 查找类型转化器
*
* @param requiredType
* @return
*/
PropertyEditor findCustomEditor(Class<?> requiredType);
}
有了注册接口后,老样子,提供个默认实现,代码如下:
import com.hqd.ch03.v11.beans.property.PropertyEditorRegistry;
import com.hqd.ch03.v11.beans.propertyeditors.CustomBooleanEditor;
import com.hqd.ch03.v11.beans.propertyeditors.CustomNumberEditor;
import java.beans.PropertyEditor;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 类型转化器注册类
*/
public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {
/**
* 用户注册编辑器
*/
private Map<Class<?>, PropertyEditor> customEditors = new LinkedHashMap<>();
/**
* 默认编辑器
*/
private Map<Class<?>, PropertyEditor> defaultEditors = new LinkedHashMap<>();
public PropertyEditorRegistrySupport() {
this.createDefaultEditors();
}
public PropertyEditor getDefaultEditor(Class<?> requiredType) {
return this.defaultEditors.get(requiredType);
}
public PropertyEditor findCustomEditor(Class<?> requiredType) {
return this.customEditors.get(requiredType);
}
/**
* 注册系统默认转换器
*/
private void createDefaultEditors() {
this.defaultEditors.put(boolean.class, new CustomBooleanEditor());
this.defaultEditors.put(Boolean.class, new CustomBooleanEditor());
this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class));
this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class));
this.defaultEditors.put(double.class, new CustomNumberEditor(Double.class));
this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class));
}
@Override
public void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor) {
this.customEditors.put(requiredType, propertyEditor);
}
}
有了注册接口默认实现后,还有个问题:怎么寻找到对应的类型转换器(PropertyEditor),匹配规则,优先级要怎么定义?
原本A君打算直接在 PropertyEditorRegistry 接口中定义方法就完事了。“接口单一,接口单一”,A君默认了几遍,硬生生的忍了下来。不然回头又要被老大叼了。基于这般想法,A君定义了 TypeConverter 接口,负责从注册器中(PropertyEditorRegistry)中寻找到对应的类型转换器(PropertyEditor),代码如下:
import com.hqd.ch03.v11.beans.property.PropertyEditorRegistry;
/**
* 类型转换接口,通常与{@link PropertyEditorRegistry}连用
* {@link PropertyEditorRegistry}负责具体转换器的注册,删除
* TypeConverter负责寻找具体的转换器进行转换
*/
public interface TypeConverter {
<T> Object convertIfNecessary(Object value, Class<T> requiredType);
}
定义好接口后,A君思考了下,觉得:用户毕竟是大哥,用户说了算。匹配规则就先匹配用户自定义的类型转换器(PropertyEditor),找不到,在找系统默认的转换器。就有了 TypeConverterSupport 实现,代码如下:
import com.hqd.ch03.v11.beans.TypeConverter;
import com.hqd.ch03.v11.beans.property.support.PropertyEditorRegistrySupport;
import java.beans.PropertyEditor;
public class TypeConverterSupport extends PropertyEditorRegistrySupport implements TypeConverter {
@Override
public <T> T convertIfNecessary(Object value, Class<T> requiredType) {
PropertyEditor editor = findCustomEditor(requiredType);
if (editor == null) {
editor = getDefaultEditor(requiredType);
}
if (editor == null) {
return (T) value;
}
if (value instanceof String) {
editor.setAsText((String) value);
} else {
editor.setValue(value);
}
return (T) editor.getValue();
}
}
做好这些后,接下来就是想办法把 TypeConverterSupport 和bean关联起来。为此,A君先定义了个接口:PropertyAccessor,用来操作bean属性信息,代码如下:
import com.hqd.ch03.v11.config.PropertyValue;
/**
* bean操作属性接口
*/
public interface PropertyAccessor {
/**
* 根据属性名获取属性类型
*
* @param propertyName 属性名
* @return
*/
Class<?> getPropertyType(String propertyName);
/**
* 根据属性名获取属性值
*
* @param propertyName 属性名
* @return
*/
Object getPropertyValue(String propertyName);
/**
* 设置属性值
*
* @param propertyName 属性名
* @param value 属性值
* @param isRef 是否引用类型
*/
void setPropertyValue(String propertyName, Object value, boolean isRef);
/**
* 通过{@link PropertyValue}设置属性值
*
* @param pv
*/
void setPropertyValue(PropertyValue pv);
}
再包装个 AbstractPropertyAccessor 抽象类,抽取公共实现,如下:
import com.hqd.ch03.v11.beans.property.PropertyAccessor;
import com.hqd.ch03.v11.beans.support.TypeConverterSupport;
import com.hqd.ch03.v11.config.PropertyValue;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
public abstract class AbstractPropertyAccessor extends TypeConverterSupport implements PropertyAccessor {
/**
* bean对象
*/
protected Object bean;
protected Class<?> beanClass;
public AbstractPropertyAccessor(Object bean) {
this.bean = bean;
this.beanClass = bean.getClass();
}
@Override
public void setPropertyValue(PropertyValue pv) {
setPropertyValue(pv.getName(), pv.getValue(), pv.isRef());
}
protected boolean checkType(String propertyName, Object value) {
if (value == null) {
return true;
}
Class<?> targetClass = value.getClass();
Class<?> propertyType = getPropertyType(propertyName);
return propertyType.isAssignableFrom(targetClass);
}
@Override
public void setPropertyValue(String propertyName, Object value, boolean isRef) {
Object newVal = value;
Class<?> propertyType = getPropertyType(propertyName);
if (isRef) {
if (!checkType(propertyName, value)) {
throw new RuntimeException(String.format("%s is not subclass of %s", value, propertyName));
}
} else {
newVal = convertIfNecessary(value, propertyType);
}
try {
PropertyDescriptor pd = new PropertyDescriptor(propertyName, beanClass);
Method methodName = pd.getWriteMethod();
methodName.invoke(bean, newVal);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public Class<?> getPropertyType(String propertyName) {
try {
PropertyDescriptor pd = new PropertyDescriptor(propertyName, beanClass);
return pd.getPropertyType();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public Object getPropertyValue(String propertyName) {
try {
PropertyDescriptor pd = new PropertyDescriptor(propertyName, beanClass);
return pd.getReadMethod().invoke(bean);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
还需要个接口 BeanWrapper 用来操作bean,并且包含类型转换。代码如下:
import com.hqd.ch03.v11.beans.property.PropertyAccessor;
import com.hqd.ch03.v11.beans.property.PropertyEditorRegistry;
public interface BeanWrapper extends PropertyEditorRegistry, PropertyAccessor, TypeConverter {
/**
* 获取bean的实例对象
*
* @return
*/
Object getWrappedInstance();
Class<?> getWrappedClass();
}
默认实现 BeanWrapperImpl ,代码如下:
import com.hqd.ch03.v11.beans.property.support.AbstractPropertyAccessor;
public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWrapper {
public BeanWrapperImpl(Object bean) {
super(bean);
}
@Override
public Object getWrappedInstance() {
return bean;
}
@Override
public Class<?> getWrappedClass() {
return beanClass;
}
}
现在什么都准备好了,提供个方法给用户注册自定义类型转换器即可。为此,A君 修改了 ConfigurableBeanFactory 接口,如下:
import com.hqd.ch03.v11.config.Scope;
import java.beans.PropertyEditor;
public interface ConfigurableBeanFactory extends BeanFactory {
/**
* 单例
*/
String SCOPE_SINGLETON = "singleton";
/**
* 多利
*/
String SCOPE_PROTOTYPE = "prototype";
void registerScope(String scopeName, Scope scope);
void registerCustomEditor(Class<?> requiredType, Class<? extends PropertyEditor> propertyEditorClass);
}
接着就是 ConfigurableBeanFactory 的实现 SpringImitation, SpringImitation改动并不多,只需在初始化bean对象时,调用 BeanWrapper 的方法即可,如下:
完整代码
import com.hqd.ch03.v11.beans.BeanWrapper;
import com.hqd.ch03.v11.beans.BeanWrapperImpl;
import com.hqd.ch03.v11.config.BeanDefinition;
import com.hqd.ch03.v11.config.MutablePropertyValues;
import com.hqd.ch03.v11.config.Scope;
import com.hqd.ch03.v11.factory.BeanFactory;
import com.hqd.ch03.v11.factory.ConfigurableBeanFactory;
import com.hqd.ch03.v11.factory.FactoryBean;
import com.hqd.ch03.v11.io.ResourceLoader;
import com.hqd.ch03.v11.io.support.DefaultResourceLoader;
import com.hqd.ch03.v11.registry.BeanDefinitionRegistry;
import com.hqd.ch03.v11.registry.support.DefaultSingletonBeanRegistry;
import com.hqd.ch03.v11.registry.support.SimpleBeanDefinitionRegistry;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import java.beans.PropertyEditor;
import java.util.HashMap;
import java.util.Map;
public abstract class SpringImitationV11 extends DefaultSingletonBeanRegistry implements ConfigurableBeanFactory {
private final Map<Class<?>, Class<? extends PropertyEditor>> customEditors = new HashMap<>();
protected BeanDefinitionRegistry beanDefinitionRegistry = new SimpleBeanDefinitionRegistry();
/**
* 单例缓存
*/
protected Map<String, Scope> scopeCache = new HashMap<>();
/**
* 资源加载器
*/
protected ResourceLoader resourceLoader = new DefaultResourceLoader();
public static boolean isFactoryDereference(String name) {
return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
}
@Override
public void registerCustomEditor(Class<?> requiredType, Class<? extends PropertyEditor> propertyEditorClass) {
customEditors.put(requiredType, propertyEditorClass);
}
/**
* 处理传入的name,去除&开头
*
* @param name
* @return
*/
protected String transformedBeanName(String name) {
if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
return name;
}
String beanName = name;
do {
beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
}
while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
return beanName;
}
/**
* 区分正常bean还是FactoryBean
*
* @param obj
* @param name
* @param beanName
* @param bd
* @return
*/
protected Object getObjectForBeanInstance(Object obj, String name, String beanName, BeanDefinition bd) {
if (isFactoryDereference(name)) {
if (!(obj instanceof FactoryBean)) {
throw new RuntimeException(String.format("%s 未实现FactoryBean接口", beanName));
}
return obj;
}
if (obj instanceof FactoryBean) {
return ((FactoryBean) obj).getObject();
}
return obj;
}
private void initBean(BeanWrapper beanWrapper, BeanDefinition bd) {
try {
MutablePropertyValues properties = bd.getProperties();
properties.getProperties().forEach(propertyValue -> {
boolean isRef = propertyValue.isRef();
Object value = propertyValue.getValue();
if (isRef) {
value = getBean((String) value);
}
try {
beanWrapper.setPropertyValue(propertyValue.getName(), value, isRef);
//BeanUtils.setProperty(bean, propertyValue.getName(), value);
} catch (Exception e) {
throw new RuntimeException(e);
}
});
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private Object createBean(BeanDefinition bd, String beanName) {
try {
Object instance = null;
if (bd.isSingleton()) {//单例
instance = getSingleton(beanName);
if (instance == null) {
instance = doCreate(bd, beanName);
}
addSingleton(beanName, instance);
} else if (bd.isPrototype()) {//多例
instance = doCreate(bd, beanName);
} else {//自定义作用域
String scopeName = bd.getScope();
Scope scope = scopeCache.get(scopeName);
if (scope == null) {
throw new RuntimeException(String.format("[%s]未注册scope", scopeName));
}
instance = scope.get(beanName, () -> doCreate(bd, beanName));
}
return instance;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected Object doCreate(BeanDefinition bd, String beanName) {
String beanClass = bd.getBeanClass();
try {
Object instance = Class.forName(beanClass).getConstructor().newInstance();
BeanWrapper beanWrapper = new BeanWrapperImpl(instance);
initBeanWrapper(beanWrapper);
addSingletonFactory(beanName, () -> beanWrapper.getWrappedInstance());
//doSomething
initBean(beanWrapper, bd);
return instance;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected void initBeanWrapper(BeanWrapper bw) {
/**
* 注册自定义类型转换器
*
*/
registerCustomEditors(bw);
}
/**
* 是否应该区分全局转换器和局部转换器
* 全局为Factory级别,局部为Bean级别
*
* @param bw
*/
protected void registerCustomEditors(BeanWrapper bw) {
if (MapUtils.isNotEmpty(customEditors)) {
customEditors.forEach((key, val) -> {
try {
bw.registerCustomEditor(key, val.getConstructor().newInstance());
} catch (Exception e) {
e.printStackTrace();
}
});
}
}
@Override
public void registerScope(String scopeName, Scope scope) {
if (StringUtils.isNotBlank(scopeName) && scope != null) {
scopeCache.put(scopeName, scope);
}
}
@Override
public <T> T getBean(String name, Class<T> clazz) {
return (T) doGetBean(name);
}
@Override
public Object getBean(String name) {
return doGetBean(name);
}
protected Object doGetBean(String name) {
String beanName = transformedBeanName(name);
BeanDefinition bd = beanDefinitionRegistry.getBeanDefinition(beanName);
Object bean = createBean(bd, beanName);
bean = getObjectForBeanInstance(bean, name, beanName, bd);
return bean;
}
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}
改造完毕后,又是测试的时间了。先准备个IKun对象,如下:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class IKun extends User {
private boolean handsome;
private double deposit;
private String[] hobbies;
}
xml配置如下:
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="idCard" class="com.hqd.ch03.bean.IdCard">
<property name="id" value="123456"/>
<property name="user" ref="ikun"/>
bean>
<bean id="ikun" class="com.hqd.ch03.bean.IKun" scope="singleton">
<property name="idCard" ref="idCard"/>
<property name="name" value="法外狂徒-张三"/>
<property name="age" value="14"/>
<property name="handsome" value="true"/>
<property name="deposit" value="6.66"/>
<property name="hobbies" value="唱,跳,rap"/>
bean>
beans>
IKun主要爱好是:唱,跳,rap,以逗号作为分割符,设置属性时候,按逗号进行切割。所以自定义类型转换器 CustomArrayEditor,代码如下:
import java.beans.PropertyEditorSupport;
public class CustomArrayEditor extends PropertyEditorSupport {
@Override
public void setAsText(String text) throws IllegalArgumentException {
setValue(text.split(","));
}
}
测试类需要注册一下 CustomArrayEditor 即可,代码如下:
@Test
public void v11() {
System.out.println("############# 第十一版:属性填充 #############");
SpringImitationV11 beanFactory = new SpringImitationV11Xml("classpath:v11/bean.xml");
beanFactory.registerCustomEditor(String[].class, CustomArrayEditor.class);
IKun iKun = beanFactory.getBean("ikun", IKun.class);
System.out.println("加载标签对象:" + iKun);
}
打印结果,如下:
Ikun的兴趣爱好已经成功的设置进去了
“这是在帮我了解spring吗?”,A君 不禁感慨。客户虽然难缠额一批,也是因为他们难缠,让 A君 对spring的理解比以往深入了不少
随着A君 代码上板,这次的自己实现IOC容器已经进入了收尾阶段。按照老大的话说:还需要留些拓展功能,就能结束了
正所谓树欲静而风不止,欲知后事如何,请看下回分解(✪ω✪)