新日撸java三百行新手小白java学习记录
Day5 Spring之IOC篇(二)
前言
树欲静而风不止
—— 古人
一 、对于有参构造的处理
上一天写到了把创建对象的权利交出去,当时的实现是很暴躁的直接类对象new, 忽略了有参的初始化情况,这里进行改进:
1、使用策略模式,构建不同的初始化器,JDK的类对象初始化与Cglib的初始化方案:
public interface InstantiationStrategy {
Object instantiate(Class beanClass , Constructor constructor , Object... args);
}
public class CglibInstantiationStrategy implements InstantiationStrategy{
@Override
public Object instantiate(Class beanClass, Constructor constructor, Object... args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(beanClass);
enhancer.setCallback(new NoOp() {
@Override
public int hashCode() {
return super.hashCode();
}
});
if(null == constructor)return enhancer.create();
else return enhancer.create(constructor.getParameterTypes() , args);
}
}
public class JDKInstantiationStrategy implements InstantiationStrategy{
@Override
public Object instantiate(Class beanClass, Constructor constructor, Object... args) {
if(constructor == null) {
try {
return beanClass.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
else {
try {
return constructor.newInstance(args);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
解析:最上层表明了需要的基本参数有:需要被实例化的类对象 、对应的构造器 、对应的构造方法的参数 ,有这几个参数便可以构造对象,使用JDK的只需要直接使用构造器创建对象即可 , 还有一个使用Cglib来创建,对于这个我也不是很熟练,以后在学习。
至于这些参数怎么来的:
abstract public class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory{
@Override
public Object createBean(String name, BeanDefinition beanDefinition , Object[] args) {
Object bean;
try {
bean = createBeanInstance(beanDefinition , args);
addSingleton(name , bean);
} catch (Exception e) {
throw new RuntimeException(e);
}
return bean;
}
protected Object createBeanInstance(BeanDefinition beanDefinition, Object... args){
Constructor constructor = null;
Class<?> clazz = beanDefinition.getBeanClass();
for (Constructor c: clazz.getConstructors()
) {
if(args != null && args.length == c.getParameterTypes().length){
constructor = c;
break;
}
}
return getInstantiationStrategy().instantiate(clazz ,constructor ,args);
}
protected InstantiationStrategy getInstantiationStrategy (){
//return new JDKInstantiationStrategy();
return new CglibInstantiationStrategy();
}
}
class可以从bean定义里面得到,因为bean定义里面目前存的就是这个,剩下的唯一操作就是根据参数判断走哪个构造器,只需要用参数的长度比一下构造器的长度,哪个相等就用哪个。
然后就是改动的别的地方的代码:
abstract public class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory {
@Override
public Object getBean(String name) {
Object bean = getSingleton(name);
if(bean != null) return bean;
return doGetBean(name ,null);
}
@Override
public Object getBean(String name , Object... args){
Object bean = getSingleton(name);
if(bean != null)return bean;
return doGetBean(name , args);
}
public Object doGetBean(String name , Object... args){
BeanDefinition beanDefinition = getBeanDefinition(name);
return createBean(name , beanDefinition , args);
}
abstract public BeanDefinition getBeanDefinition(String name);
abstract public Object createBean(String name , BeanDefinition beanDefinition , Object[] args);
}
public interface BeanFactory {
Object getBean(String name);
Object getBean(String name , Object[] args);
}
这点内容是在太简单,主要还是学习他的设计模式,学习每个类如何甩锅。
二、实现依赖注入
1、像HashMap一样定义一个捆绑包 , 包含属性的名字与值。
public class PropertyValue {
private final String name;
private final Object value;
public PropertyValue(String name, Object value) {
this.name = name;
this.value = value;
}
public String getName(){
return name;
}
public Object getValue(){
return value;
}
}
2、由于一个类不只有一个属性,所以有需要一个捆绑包链:
public class PropertyValues {
private final List<PropertyValue> propertyValueList = new ArrayList<>();
public void addPropertyValue(PropertyValue propertyValue){
propertyValueList.add(propertyValue);
}
public PropertyValue[] getPropertyValues(){
return propertyValueList.toArray(new PropertyValue[0]);
}
public PropertyValue getPropertyValue(String name){
for (PropertyValue pv:propertyValueList
) {
if(pv.getName().equals(name))return pv;
}
return null;
}
}
3、当bean对象被创建好了,就可以往里面填充属性:
public Object createBean(String name, BeanDefinition beanDefinition , Object[] args) {
Object bean;
try {
bean = createBeanInstance(beanDefinition , args);
applyPropertyValues(bean, beanDefinition);
addSingleton(name , bean);
} catch (Exception e) {
throw new RuntimeException(e);
}
return bean;
}
private void applyPropertyValues(Object bean, BeanDefinition beanDefinition) {
PropertyValues propertyValues = beanDefinition.getPropertyValues();
for (PropertyValue p : propertyValues.getPropertyValues()
) {
String name = p.getName();
Object value = p.getValue();
if(value instanceof BeanReference){
String beanName = ((BeanReference) value).getBeanName();
value = getBean(beanName);
}
BeanUtils.setFieldValue(bean, name ,value);
//BeanUtil.setFieldValue(bean , name ,value); 调用 cn.hutool.core.bean.BeanUtil包;
}
}
4、 这里的BeanReference是属性里面的对象类型,非基本类型,主要也就是记录一个名字,好方便getBean:
public class BeanReference {
private final String beanName;
public BeanReference(String beanName) {
this.beanName = beanName;
}
public String getBeanName(){
return beanName;
}
}
5、最关键的地方就是如何填充bean对象的属性,有两种办法实现BeanUtil,一种就是掉包简单省事还稳妥 ,第二就是自己搓一个简单的BeanUtils,优点是锻炼自己,缺点是健壮性太差:
public class BeanUtils {
public static void setFieldValue(Object bean , String name , Object value){
Class clazz = bean.getClass().getName().contains("$$EnhancerByCGLIB$$") ? bean.getClass().getSuperclass() : bean.getClass();
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
long offset = unsafe.objectFieldOffset(clazz.getDeclaredField(name));
setFile(bean , offset ,value ,unsafe);
} catch (Exception e) {
throw new RuntimeException("偏移量错误");
}
}
public static void setFile(Object bean , long offset , Object value , Unsafe unsafe){
if(value instanceof Integer)unsafe.putInt(bean ,offset , (int) value);
else if(value instanceof String)unsafe.putObject(bean ,offset ,value);
else unsafe.putObject(bean , offset , value);
}
}
大概思路就是:使用Unsafe直接往对应的地址位置写值,需要注意的是,因为对象可能是Cglib生成的,所以需要单独判断确保拿到真正的Class对象,这种写法没什么营养,主要是帅。
6、贴一个运行结果:
public class Main {
public static void main(String[] args) {
BeanDefinition beanDefinitionDog = new BeanDefinition(Dog.class);
DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
defaultListableBeanFactory.registerBeanDefinition("dog" , beanDefinitionDog);
PropertyValues propertyValues = new PropertyValues();
propertyValues.addPropertyValue(new PropertyValue("age" , 18));
propertyValues.addPropertyValue(new PropertyValue("name" , "花花"));
propertyValues.addPropertyValue(new PropertyValue("dog" , new BeanReference("dog")));
BeanDefinition beanDefinitionCat = new BeanDefinition(Cat.class, propertyValues);
defaultListableBeanFactory.registerBeanDefinition("Cat" , beanDefinitionCat);
Cat cat = (Cat)defaultListableBeanFactory.getBean("Cat");
// Dog dog = (Dog)defaultListableBeanFactory.getBean("Dog");
cat.name();
cat.age();
cat.useDog();
}
}