实现有参构造的实例化

Meteor 2023-10-30 17:04:19
Categories: Tags:

简介

在上一篇根据 Bean 的Class对象通过反射创建实例,实际上是通过调用无参构造实例化的,如果只有有参构造就会有问题,本篇先讲解两种方式实现有参构造的实例化,然后是如何将两种实例化方式融入 Bean 的生命周期中

源码:small-spring03
阅读前建议先了解一下代理模式,链接:proxy

实例化

由于不止一种实例化方法,所以可以创建一个接口用来规范方法的实现,下面是接口的代码

InstantiationStrategy

package com.meteor.factory.support;

import com.meteor.BeansException;
import com.meteor.factory.config.BeanDefinition;

import java.lang.reflect.Constructor;

public interface InstantiationStrategy {
Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException;
}

JDK

JDK依然是通过反射创建实例,不过现在要判断是否有入参,根据参数的有无调用不同的方法,代码如下:

SimpleInstantiationStrategy

package com.meteor.factory.support;

import com.meteor.BeansException;
import com.meteor.factory.config.BeanDefinition;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class SimpleInstantiationStrategy implements InstantiationStrategy{

public Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException {
Class clazz = beanDefinition.getBeanClass();
try{
if(null!=ctor){
return clazz.getDeclaredConstructor(ctor.getParameterTypes()).newInstance(args);
}
else{
return clazz.getDeclaredConstructor().newInstance();
}
} catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) {
throw new BeansException("Failed to instantiate ["+clazz.getName()+"]",e);
}
}
}

Cglib

Cglib是通过生成子类的方式实例化的,即创建从外界传入 Bean 的Class对象的子类,代码如下:

CglibSubclassingInstantiationStrategy

package com.meteor.factory.support;

import com.meteor.BeansException;
import com.meteor.factory.config.BeanDefinition;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.NoOp;

import java.lang.reflect.Constructor;

public class CglibSubclassingInstantiationStrategy implements InstantiationStrategy {

@Override
public Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException {
// 创建 Enhancer 类
Enhancer enhancer = new Enhancer();
// 设置父类
enhancer.setSuperclass(beanDefinition.getBeanClass());
// 对父类中的方法进行拦截操作,NoOp表示无操作,相当于 InvocationHandler
enhancer.setCallback(new NoOp() {
@Override
public int hashCode() {
return super.hashCode();
}
});
// 根据有无参数调用不同的方法
if (null == ctor) return enhancer.create();
return enhancer.create(ctor.getParameterTypes(), args);
}
}

融合Bean实例化

接下来就是将实例化融入到 Bean 的生命周期中,在目前存在的类中,AbstractAutowireCapableBeanFactory 类负责创建 Bean,也包括 Bean 的实例化,因此要将实例化融入到这个类中。

由于之前已经创建了一个接口 InstantiationStrategy 来管理所有的实例化策略,所以可以在 AbstractAutowireCapableBeanFactory 内添加一个 InstantiationStrategy 类型的字段,利用多态自由的切换不同的实例化策略

AbstractAutowireCapableBeanFactory

package com.meteor.factory.support;

import com.meteor.BeansException;
import com.meteor.factory.config.BeanDefinition;

import java.lang.reflect.Constructor;

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {
// 实例化策略
private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

public InstantiationStrategy getInstantiationStrategy() {
return instantiationStrategy;
}

public void setInstantiationStrategy(InstantiationStrategy instantiationStrategy) {
this.instantiationStrategy = instantiationStrategy;
}

// 创建 Bean
@Override
protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
Object bean = null;
try {
bean = createBeanInstance(beanDefinition, beanName, args);
} catch (Exception e) {
throw new BeansException("Instantiation of bean failed", e);
}
addSingleton(beanName, bean);
return bean;
}

// Bean 的实例化
protected Object createBeanInstance(BeanDefinition beanDefinition, String beanName, Object[] args) throws BeansException {
Constructor constructorToUse = null;
Class<?> beanClass = beanDefinition.getBeanClass();
Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();
for (Constructor ctor : declaredConstructors) {
// 这一步简化了操作,只判断参数的个数是否相等,实际是要判断个数和类型的
if (null != args && ctor.getParameterTypes().length == args.length) {
constructorToUse = ctor;
break;
}
}
return getInstantiationStrategy().instantiate(beanDefinition, beanName, constructorToUse, args);
}

}

测试

要测试有参构造能顺利获取到Bean,还是先创建一个UserService,而且只提供了有参构造

UserService

package com.meteor.bean;

public class UserService {
private final String name;

public UserService(String name){
this.name = name;
}
public void queryUserInfo(){
System.out.println("查询用户信息:"+name);
}

}

开始测试

package com.meteor;

import com.meteor.bean.UserService;
import com.meteor.factory.config.BeanDefinition;
import com.meteor.factory.support.DefaultListableBeanFactory;
import org.junit.Test;

public class AppTest {
@Test
public void testBeanFactory() {
//1. 初始化BeanFactory
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//2. 注入Bean
BeanDefinition beanDefinition = new BeanDefinition(UserService.class);
beanFactory.registerBeanDefinition("userService", beanDefinition);
//3. 获取Bean
UserService userService = (UserService) beanFactory.getBean("userService", "meteor");
userService.queryUserInfo();
}
}

输出结果

查询用户信息:meteor

Process finished with exit code 0

没有问题,可以顺利获得Bean

总结

本文增加了对有参构造对象的实例化处理,当然,处理的过程做了简化。不过实际使用时,在配置文件中,有时还会配置 Bean 的属性信息,因此下文将会添加属性填充功能

目录结构

相较于上一篇更新的类标注update,新增的类标注为new

src
├── main
│ └── java
│ └── com.meteor
│ ├── factory
│ │ ├── config
│ │ │ ├── BeanDefinition.java
│ │ │ └── SingletonBeanRegistry.java
│ │ ├── support
│ │ │ ├── AbstractAutowireCapableBeanFactory.java (update)
│ │ │ ├── AbstractBeanFactory.java (update)
│ │ │ ├── BeanDefinitionRegistry.java
│ │ │ ├── CglibSubclassingInstantiationStrategy.java
│ │ │ ├── DefaultListableBeanFactory.java (update)
│ │ │ └── DefaultSingletonBeanRegistry.java
│ │ │ └── InstantiationStrategy.java
│ │ │ └── SimpleInstantiationStrategy.java
│ │ └── BeanFactory.java (update)
│ └── BeansException.java
└── test
└── java
└── com.meteor
├── bean
│ └── UserService.java(update)
└── MainTest.java

依赖

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>