Принцип и ручная реализация ИОК серии Spring

Java

содержание

Введение

Spring — это многоуровневая полнофункциональная (универсальная) облегченная среда JavaSE/EE с открытым исходным кодом. Это также одна из сред, которую должны освоить почти все, кто работает с Java.Его отличные дизайнерские идеи и искусство реализации кода — это то, что нам нужно освоить. Чтобы изучить Spring, помимо использования его в нашем проекте, нам также необходимо изучить его исходный код, но реализация Spring охватывает много знаний, и количество классов в ней тоже очень велико. Чтение кода может быть перемежено между десятками занятий, и есть большая вероятность, что вы случайно запутаетесь. Ввиду этого я сначала выполню ручную и простую реализацию нескольких важных модулей в Spring.Во-первых, я знаком с принципами этих модулей, а также имитирую структуру в Spring, чтобы заложить основу для чтения исходного кода позже .

IOC(Inversion of Control)

Инверсия управления — это инверсия управления, что означает, что объекты, созданные клиентским кодом, передаются в контейнер IOC для управления.Создание, инициализация и последующее управление объектами выполняются IOC.

Преимущества МОК

  1. Разделение: Появление IOC устраняет связь между классами и классами.В эпоху сервлетов веб-разработки, если сервлет должен зависеть от некоторых реализаций другого класса, нам нужно создать и отделить зависимые классы в текущем классе. Инициализация, если от этого класса зависят и другие классы, то его также необходимо создать и инициализировать, а если передать в управление в ИОЦ, то обращаться к ИОЦ нужно только по мере необходимости, без повторного создания и инициализации. Конечно, IOC также позволяет каждый раз воссоздавать новый объект.
  2. Удобно сотрудничать с АОП: АОП также является очень часто используемой функцией, и может быть очень удобно сотрудничать с АОП через IOC.

Шаблоны проектирования, разработанные в IOC

Заводской узор. Контейнер IOC отвечает за создание объекта экземпляра класса управления, обращение к IOC при необходимости и получение его от IOC. Таким образом, контейнер IOC также называют бобовой фабрикой.

Фабричный паттерн — это относительно простой и понятный паттерн проектирования, который мы здесь представлять не будем.заводской узор.

Ручная реализация IOC

Определение бина

Основной функцией IOC является управление бобами, включая создание, инициализацию, управление и экстази. Первый вопрос, с которым мы сталкиваемся, заключается в том, как мы позволяем IOC создавать Bean? Что нам нужно предоставить для создания bean-компонента?

Как создать бобы

Существует два объектных способа создания экземпляра класса без создания его вручную с помощью нового ключевого слова:

  1. Отражение: экземпляр класса может быть создан посредством отражения:clazz.getClass().newInstance();.
  2. Фабричный шаблон: Фабричный шаблон позволяет нам создавать экземпляры, не касаясь класса экземпляра.
public class PersonFactory{
    public Person getPerson(){
        return new Person();
    }
}
Что нам нужно предоставить для создания bean-компонента

Ответ можно легко найти, проанализировав два вышеуказанных метода.

Для метода отражения нам нужно только предоставить объект класса экземпляра.

Для фабричного метода все, что нам нужно предоставить, — это имя фабрики (factoryName) и имя метода (methodName), которые создают класс;

Что нужно сделать, кроме создания бина

IOC-контейнер управляет всем жизненным циклом бина, помимо создания, он также должен инициализировать бин и уничтожать бин, когда он не нужен (например, высвобождение ресурсов и т. д.). Поэтому нам также необходимо обеспечить такие операции, как инициализация и уничтожение.

На этом базовый анализ, необходимый для создания bean-компонента, завершен, взгляните на диаграмму классов:

BeanDefinition

Бобовая фабрика

Определение bean-компонента решено, но где должны быть размещены определение bean-компонента и созданный экземпляр bean-компонента?Нам нужно единое место для хранения этих вещей, чтобы мы могли легко брать их при использовании.

Мы определяем фабрику бинов для хранения бинов, и мы можем получить их от фабрики бинов, когда они нам понадобятся. То, что фабрика бинов предоставляет извне, — это только метод для получения бинов. Поскольку тип бина неизвестен, возвращаемое значение определяет местонахождение Object .

BeanFactory

Зарегистрировать определение компонента

Теперь, когда у нас есть определение bean-компонента для создания bean-компонентов и bean-фабрика для хранения bean-компонентов и управления ими, нам нужно подумать о том, как соединить эти два класса.Нам также нужен еще один интерфейс.Функция интерфейса состоит в том, чтобы позволить нам регистрировать и Получите определение bean-компонента, здесь мы различаем разные bean-компоненты по beanName.

BeanDefinitionRegistry

Код

На данный момент мы в основном завершили то, что нам нужно для реализации простого контейнера IOC.Давайте посмотрим на базовую диаграмму классов:

类关系

Базовая реализация кода:

DefaultBeanDefinition:

public class DefaultBeanDefinition implements BeanDefinition{

    private Class<?> clazz;

    private String beanFactoryName;

    private String createBeanMethodName;

    private String staticCreateBeanMethodName;

    private String beanInitMethodName;

    private String beanDestoryMethodName;

    private boolean isSingleton;

    // setter
    

    public void setSingleton(boolean singleton) {
        isSingleton = singleton;
    }

    @Override
    public Class<?> getBeanClass() {
        return this.clazz;
    }

    @Override
    public String getBeanFactory() {
        return this.beanFactoryName;
    }

    @Override
    public String getCreateBeanMethod() {
        return this.createBeanMethodName;
    }

    @Override
    public String getStaticCreateBeanMethod() {
        return this.staticCreateBeanMethodName;
    }

    @Override
    public String getBeanInitMethodName() {
        return this.beanInitMethodName;
    }

    @Override
    public String getBeanDestoryMethodName() {
        return this.beanDestoryMethodName;
    }

    @Override
    public String getScope() {
        return this.isSingleton?BeanDefinition.SINGLETION :BeanDefinition.PROTOTYPE;
    }

    @Override
    public boolean isSingleton() {
        return this.isSingleton;
    }

    @Override
    public boolean isPrototype() {
        return !this.isSingleton;
    }
}

DefaultBeanFactory:

public class DefaultBeanFactory implements BeanFactory, BeanDefinitionRegistry, Closeable {

    private Log log = LogFactory.getLog(this.getClass());

    //ConcurrentHashMap应对并发环境
    private Map<String, BeanDefinition> bdMap = new ConcurrentHashMap<>();

    private Map<String, Object> beanMap = new ConcurrentHashMap<>();

    @Override
    public void register(BeanDefinition bd, String beanName) {

        Assert.assertNotNull("beanName不能为空 beanName", beanName);
        Assert.assertNotNull("BeanDefinition不能为空", bd);

        if(bdMap.containsKey(beanName)){
            log.info("[" + beanName + "]已经存在");
        }

        if(!bd.validate()){
            log.info("BeanDefinition不合法");
        }

        if(!bdMap.containsKey(beanName)){
            bdMap.put(beanName, bd);
        }
    }

    @Override
    public boolean containsBeanDefinition(String beanName) {
        return bdMap.containsKey(beanName);
    }

    @Override
    public BeanDefinition getBeanDefinition(String beanName) {
        if(!bdMap.containsKey(beanName)){
            log.info("[" + beanName + "]不存在");
        }
        return bdMap.get(beanName);
    }

    public Object doGetBean(String beanName) throws InstantiationException, IllegalAccessException {
        if(!beanMap.containsKey(beanName)){
            log.info("[" + beanName + "]不存在");
        }

        Object instance = beanMap.get(beanName);

        if(instance != null){
            return instance;
        }

        //不存在则进行创建
        if(!this.bdMap.containsKey(beanName)){
            log.info("不存在名为:[" + beanName + "]的bean定义");
        }

        BeanDefinition bd = this.bdMap.get(beanName);

        Class<?> beanClass = bd.getBeanClass();

        if(beanClass != null){
            instance = createBeanByConstruct(beanClass);
            if(instance == null){
                instance = createBeanByStaticFactoryMethod(bd);
            }
        }else if(instance == null && StringUtils.isNotBlank(bd.getStaticCreateBeanMethod())){
            instance = createBeanByFactoryMethod(bd);
        }

        this.doInit(bd, instance);

        if(instance != null && bd.isSingleton()){
            beanMap.put(beanName, instance);
        }

        return instance;
    }

    private void doInit(BeanDefinition bd, Object instance) {
        Class<?> beanClass = instance.getClass();
        if(StringUtils.isNotBlank(bd.getBeanInitMethodName())){
            try {
                Method method = beanClass.getMethod(bd.getBeanInitMethodName(), null);
                method.invoke(instance, null);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 构造方法创建实例
     * @param beanClass
     * @return
     */
    private Object createBeanByConstruct(Class<?> beanClass) {
        Object instance = null;
        try {
            instance = beanClass.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }

    /**
     * 普通工厂方法创建实例
     * @param bd
     * @return
     */
    private Object createBeanByFactoryMethod(BeanDefinition bd) {
        Object instance = null;
        try {
            //获取工厂类
            Object factory = doGetBean(bd.getBeanFactory());
            //获取创建实例的方法
            Method method = factory.getClass().getMethod(bd.getCreateBeanMethod());
            //执行方法
            instance = method.invoke(factory, null);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }

    /**
     * 静态方法创建实例
     * @param bd
     * @return
     */
    private Object createBeanByStaticFactoryMethod(BeanDefinition bd) {
        Object instance = null;
        try {
            Class<?> beanClass = bd.getBeanClass();
            //获取创建实例的方法
            Method method = beanClass.getMethod(bd.getStaticCreateBeanMethod());
            instance = method.invoke(beanClass, null);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }

    @Override
    public Object getBean(String beanName) {
        if(!beanMap.containsKey(beanName)){
            log.info("[" + beanName + "]不存在");
        }
        return beanMap.get(beanName);
    }

    @Override
    public void close() throws IOException {
        Set<Map.Entry<String, BeanDefinition>> entries = bdMap.entrySet();
        for(Map.Entry<String, BeanDefinition>  entry: entries){
            BeanDefinition value = entry.getValue();
            String destoryMethodName = value.getBeanDestoryMethodName();
            try {
                Method method = value.getBeanClass().getMethod(destoryMethodName, null);
                method.invoke(value.getBeanClass(), null);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

Просто проверьте это: Экземплярный компонент:

public class User {

    private String name;

    private int age;

    //getter setter

    public void init(){
        System.out.println("init...");
    }

    public void destory(){
        System.out.println("destory...");
    }

}

Заводской класс:

public class TestFactory {
    public Object createMethod(){
        return new User();
    }

    public static Object staticCreateMethod(){
        return new User();
    }
}

Тестовый класс:

public class MySpringTest {

    static DefaultBeanFactory factory = new DefaultBeanFactory();

    @Test
    public void test() throws IllegalAccessException, InstantiationException {
        DefaultBeanDefinition bd = new DefaultBeanDefinition();
        bd.setClazz(User.class);
        bd.setSingleton(true);
        bd.setBeanFactoryName("TestFactory");
        bd.setCreateBeanMethodName("createMethod");
        bd.setStaticCreateBeanMethodName("staticCreateMethod");

        bd.setBeanInitMethodName("init");

        factory.register(bd, "user");

        System.out.println(factory.doGetBean("user"));
    }
}

резюме

Простой контейнер реализован таким образом, конечно, здесь у нас есть только базовые функции, но по факту он еще далеко позади, например, инстанцирование бинов с параметрами. Но основной принцип МОК выражен, и на его основе нужно только добавить новые функции.