Простой IOC голыми руками

Java

Простой IOC голыми руками

Двумя наиболее классическими в среде Spring являются IOC и AOP.Что такое IOC (инверсия управления)? Проще говоря, инверсия управления заключается в передаче действий контролирующих компонентов сущностей контейнеру Spring для управления. Проще говоря, например, если вы хотите использовать класс раньше, вы должны создать новый, но если вы используете Spring, вы можете использовать его напрямую.@AutowiredВы можете получить этот объект напрямую с помощью аннотации или конфигурации xml, и вам не нужно заботиться о его жизненном цикле и т.д. Вам не нужно создавать новый объект самостоятельно.

Если IOC ранее не использовался, то создание и присвоение этих объектов мы все делаем сами.Следующее кратко демонстрирует, что если есть зависимости от вышеуказанных четырех объектов, то без IOC мы должны создавать объекты и присваивать значения. Всего четыре объекта это так много, то раз проект большой, объектов сотни, если еще так писать, то точно будет беда.

对象A a = new 对象A();
对象B b = new 对象B();
对象C c = new 对象C();
对象D d = new 对象D();
a.setB(b);
a.setC(c);
b.setD(d);
c.setD(d);

Поэтому в Spring все объекты объединены в контейнер Spring для управления через IOC, так намного проще. Код для создания экземпляра объекта выше не требует написания.

Так много было сказано выше, на самом деле IOC очень важен, но если вы посмотрите непосредственно на исходный код Spring, он будет очень запутанным, поэтому просто напишите небольшой пример IOC, чтобы понять эту идею.

Анализировать и писать код

На этапе анализа перед написанием кода IOC Spring фактически управляет всеми bean-компонентами в унифицированном контейнере, а затем инициализирует их, когда они получены, поэтому нам нужно сохранять отмеченные классы при запуске программы.Управляется в пользовательском контейнере.

  • Фаза инициализации: будет@MyIocПохоже на Весна@ServiceОтмеченные классы помещаются в пользовательский контейнер.
  • Использование: унифицированное получение с помощью пользовательского класса сбора компонентов.

Теперь мы подробно разберем два вышеуказанных шага.

этап подготовки данных

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

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyIoc {
    
}

Затем, если вы хотите инициализировать информацию в пользовательском контейнере, какой тип контейнера вы должны использовать для хранения этой информации? Здесь вы можете подумать об использовании карты для хранения, использовании ключа в качестве имени класса и что использовать для значения? Значение — это информация о классе, которым нужно управлять в контейнере, поэтому какая информация содержится в классе, то есть из чего он состоит? Есть следующая информация

  • имя класса
  • Конструктор
  • значение атрибута
  • отец

Следовательно, согласно приведенному выше анализу, мы можем создать класс сущностей для хранения этой информации.В настоящее время мы не рассматриваем сложные конструкторы, но все инициализированные конструкторы без аргументов. Тогда свойства родительского класса не будут анализироваться и вводиться. Итак, на данный момент класс сущности класса прост.

@Data
public class BeanDefinition {
    private String className;
    private String alias;
    private String superNames;
}

фаза инициализации

С классом, который хранит информацию о классе, мы должны загрузить эту информацию в карту при запуске программы и создать класс запуска для инициализации@MyIocИнформация о отмеченном классе.

@Component
@Order(value = 1)
public class IoCInitConifg implements CommandLineRunner{

    @Override
    public void run(String... args){
        ConcurrentHashMap<String,BeanDefinition> concurrentHashMap = new ConcurrentHashMap<>();
        Reflections reflections = new Reflections();
        //获得项目中所有被MyIoc标记得类
        Set<Class<?>> typesAnnotatedWith = reflections.getTypesAnnotatedWith(MyIoc.class);
        //将其信息初始进自定义容器MyBeanFactory中
        for (Class clazz : typesAnnotatedWith){
            BeanDefinition beanDefinition = new BeanDefinition();
            String className = clazz.getName();
            String superclassName = clazz.getSuperclass().getName();
            beanDefinition.setClassName(className);
            beanDefinition.setSuperNames(superclassName);
            beanDefinition.setAlias(getClassName(className));
            concurrentHashMap.put(className,beanDefinition);
        }
        MyBeanFactoryImpl.setBeanDineMap(concurrentHashMap);
    }

    private String getClassName(String beanClassName) {
        String className = beanClassName.substring(beanClassName.lastIndexOf(".") + 1);
        className = className.substring(0, 1).toLowerCase() + className.substring(1);
        return className;
    }
}

На этом этапе я должен поговорить о пользовательском унифицированном классе управления контейнерами.MyBeanFactoryЭтот класс используется как способ получить класс единообразно

public interface MyBeanFactory {

    Object getBeanByName(String name) throws Exception;
}

В это время также существует его класс реализации

@Log4j
public class MyBeanFactoryImpl implements MyBeanFactory{
    //存储对象名称和已经实例化的对象映射
    private static ConcurrentHashMap<String,Object> beanMap = new ConcurrentHashMap<>();
    //存储对象名称和对应对象信息的映射
    private static ConcurrentHashMap<String,BeanDefinition> beanDefineMap= new ConcurrentHashMap<>();
    //存储存储在容器中对象的名称
    private static Set<String> beanNameSet = Collections.synchronizedSet(new HashSet<>());

    @Override
    public Object getBeanByName(String name) throws Exception {
        //看有没有已经实例化的对象,有的话就直接返回
        Object object = beanMap.get(name);
        if (object != null){
            return object;
        }
        //没有的话就实例化一个对象
        object = getObject(beanDefineMap.get(name));
        if (object != null){
            //对实例化中对象的注入需要的参数
            setFild(object);
            //将实例化的对象放入Map中,便于下次使用
            beanMap.put(name,object);
        }
        return object;
    }

    public void setFild(Object bean) throws Exception {
        Field[] declaredFields = bean.getClass().getDeclaredFields();
        for (Field field: declaredFields){
            String filedAllName = field.getType().getName();
            if (beanNameSet.contains(filedAllName)){
                Object findBean = getBeanByName(filedAllName);
                //为对象中的属性赋值
                field.setAccessible(true);
                field.set(bean,findBean);
            }
        }
    }

    public Object getObject(BeanDefinition beanDefinition) throws Exception {
        String className = beanDefinition.getClassName();
        Class<?> clazz = null;
        try {
            clazz = Class.forName(className);
        } catch (ClassNotFoundException e) {
            log.info("can not find bean by beanName: "+className);
            throw new Exception("can not find bean by beanName: "+className);
        }
        return clazz;
    }

    public static void setBeanDineMap(ConcurrentHashMap<String,BeanDefinition> beanDefineMap){
        MyBeanFactoryImpl.beanDefineMap = beanDefineMap;
    }

    public static void setBeanNameSet(Set<String> beanNameSet){
        MyBeanFactoryImpl.beanNameSet = beanNameSet;
    }

}

На этом этап инициализации завершен, т. е. все@MyIocВсе отмеченные классы были сохранены в пользовательском контейнере. По сути, здесь мы уже можем использовать собственный кастомный контейнер для получения бинов.

@MyIoc
@Data
public class User {
    private Student student;
}

@MyIoc
public class Student {
    public String play(){
        return "student"+ this.toString();
    }
}

На данный момент мы пишем следующее в классе запуска

		User user1 = (User)beanFactory.getBeanByName("com.example.ioc.domain.User");
		User user2 = (User)beanFactory.getBeanByName("com.example.ioc.domain.User");
		Student student1 = user1.getStudent();
		Student student2 = user1.getStudent();
		Student student3 = (Student)beanFactory.getBeanByName("com.example.ioc.domain.Student");
		System.out.println(user1);
		System.out.println(user2);
		System.out.println(student1);
		System.out.println(student2);
		System.out.println(student3);

Обнаружено, что все объекты, выводимые в консоль, являются одним и тем же объектом, а объект Student также автоматически внедряется в User. На этом простой IOC завершен.

User(student=com.example.ioc.domain.Student@705e7b93)
User(student=com.example.ioc.domain.Student@705e7b93)
com.example.ioc.domain.Student@705e7b93
com.example.ioc.domain.Student@705e7b93
com.example.ioc.domain.Student@705e7b93

Суммировать

Первоначальная идея заключалась в том, чтобы написать что-то вроде@AutowiredПользовательские аннотации для аннотаций, но столкнулся с трудностью в процессе кодирования, например, в следующем коде легко создать экземпляр B, но как внедрить B в каждый экземпляр A, эта проблема преследует меня в течение нескольких дней, я также искал много информации, но решения до сих пор нет.По оценкам, только изучая исходный код Spring, мы можем понять, как это сделать.

@MyIoc
public class A{

@MyIocUse
private B b;

}

полный адрес проекта

Справочная статья