• 作者:老汪软件技巧
  • 发表时间:2024-10-01 17:01
  • 浏览量:

这一章节的目标主要是为了解决上一章节我们埋下的坑,那是什么坑呢?其实就是一个关于 Bean 对象在含有构造函数进行实例化的坑。

1.具体实现a.拥有含参构造的class对象

public class UserService{
    private String name;
    public UserService(String name){//这里因为含参,所以要重写getBean
        this.name = name;
    }
}

此时会产生报错

java.lang.InstantiationException: cn.bugstack.springframework.test.bean.UserService
​
    at java.lang.Class.newInstance(Class.java:427)
    at cn.bugstack.springframework.test.ApiTest.test_newInstance(ApiTest.java:51)
    ...
​

分析原因:beanDefinition.getBeanClass().newInstance(); 实例化方式并没有考虑构造函数的入参,所以就这个坑就在这等着你了!那么我们的目标就很明显了,来把这个坑填平

b.getBean优化-增加含参情况的getBean

public  interface BeanFactory{
    Object getBean(String name) throws BeansException;
    Object getBean(String name,Object...args) throws BeansException;//这就是优化所在,我们为含参数的构造函数提供了新的实例化方案.
}

这里我们对...这个语法现象进行分析:

在 Java 中,... 被称为可变参数(Varargs)语法。它允许方法接收不定数量的参数。这种语法使得调用方法时可以传入零个、一个或多个参数,而不需要事先定义参数的数量。

具体分析

可变参数的定义:

使用方式:

方法内部处理:

总结

...args 允许 getBean 方法接收可变数量的 Object 类型参数,使得该方法更加灵活和易于使用。使用可变参数的好处是简化了方法的调用方式,避免了重载大量不同参数数量的方法。

c.定义实例化策略接口(策略模式)

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

d.策略模式之使用JDK实例化(实现策略接口)

public class SimpleInstantiationStrategy implements InstantiationStrategy {
​
    @Override
    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);
                //这里就是关键所在,我们使用JDK提供的反射,从传入的构造函数中,取出参数的类型,并且使用Objects[] args中的参数去newInstance(); 
                //从而实现了含参构造的创建
            } else {
                return clazz.getDeclaredConstructor().newInstance();
                //在上一个版本,仅仅有此部分,也就是仅有无参构造的新对象创建
            }
        } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
            throw new BeansException("Failed to instantiate [" + clazz.getName() + "]", e);
        }
    }
​
}
​

e.策略模式之使用CgLib完成实例化

public class CglibSubclassingInstantiationStrategy implements InstantiationStrategy {
​
    @Override
    public Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(beanDefinition.getBeanClass());//设置Cglib要代理的类
        enhancer.setCallback(new NoOp() {//设置方法拦截器
            @Override
            public int hashCode() {
                return super.hashCode();
            }
        });
        if (null == ctor) return enhancer.create();//此部分为默认的构造无参对象
        return enhancer.create(ctor.getParameterTypes(), args); //这部分是通过ctor拿到参数类型,再用参数数组创建调用了含参构造的对象
    }
​
}
​

看到上面的内容有点懵?兴许是你不了解CgLib,我们做一个简单介绍

CGLIB 是一个强大的字节码生成库,主要用于在运行时创建类的子类。它广泛用于动态代理和 AOP(面向切面编程)。下面是一个简单的 CGLIB 使用示例,帮助你了解如何使用它。

1. 添加依赖

如果你使用 Maven,可以在 pom.xml 中添加以下依赖:

xmlCopy Code<dependency>
    <groupId>cglibgroupId>
    <artifactId>cglibartifactId>
    <version>3.3.0version> 
dependency>

创建被代理的类

假设我们有一个简单的类 UserService:

构造函数实例化__构造函数应用案例

javaCopy Codepublic class UserService {
    public void addUser(String user) {
        System.out.println("User " + user + " added.");
    }
​
    public void deleteUser(String user) {
        System.out.println("User " + user + " deleted.");
    }
}

创建 CGLIB 代理类

接下来,我们使用 CGLIB 创建 UserService 的代理类:

javaCopy Codeimport net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
​
public class CglibExample {
    public static void main(String[] args) {
        // 创建 Enhancer 对象
        Enhancer enhancer = new Enhancer();
        
        // 设置要代理的类
        enhancer.setSuperclass(UserService.class);
        
        // 设置方法拦截器
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable {
                // 在方法调用前执行的逻辑
                System.out.println("Before method: " + method.getName());
                
                // 调用原始方法
                Object result = proxy.invokeSuper(obj, args);
                
                // 在方法调用后执行的逻辑
                System.out.println("After method: " + method.getName());
                
                return result;
            }
        });
        
        // 创建代理对象
        UserService proxy = (UserService) enhancer.create();
        
        // 使用代理对象
        proxy.addUser("Alice");
        proxy.deleteUser("Bob");
    }
}

运行示例

当你运行上面的 CglibExample 类时,你会看到输出如下:

Copy CodeBefore method: addUser
User Alice added.
After method: addUser
Before method: deleteUser
User Bob deleted.
After method: deleteUser

总结

上面的代码展示了如何使用 CGLIB 创建一个简单的代理:

添加依赖:确保项目中包含 CGLIB 的依赖。

创建目标类:定义需要被代理的类。

创建代理

调用代理方法:通过代理对象调用方法,观察拦截器的行为。

这样,你就成功地使用 CGLIB 创建了一个动态代理!

f.使用Cglib做为策略的模版BeanFactory

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {
​
    private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();
​
    @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;
    }
​
    protected Object createBeanInstance(BeanDefinition beanDefinition, String beanName, Object[] args) {
        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);
    }
​
}
​

1. createBean 和 createBeanInstance 的区别

createBeanInstance:

2. addSingleton 的来源

addSingleton(beanName, bean) 方法没有在代码中实现,这表明它是在 AbstractBeanFactory 类或其父类中定义的。通常,这个方法的作用是将创建的 bean 存储在一个单例集合中,以便后续请求可以直接返回同一个实例。这种设计可以提高效率,避免重复创建相同的 bean 实例。

设计模式

在这个代码片段中,可以看到以下几种设计模式的体现:

策略模式 (Strategy Pattern) :

模板方法模式 (Template Method Pattern) :

总结

g.测试用例

public class UserService {
​
    private String name;
​
    public UserService(String name) {
        this.name = name;
    }
​
    public void queryUserInfo() {
        System.out.println("查询用户信息:" + name);
    }
​
    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("");
        sb.append("").append(name);
        return sb.toString();
    }
}
​

@Test
public void test_BeanFactory() {
    // 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", "测试内容");
    userService.queryUserInfo();
}
​

查询用户信息:测试内容
​
Process finished with exit code 0