Инвентаризация SpringIOC: InitializingBean, созданная Bean

Java
Инвентаризация SpringIOC: InitializingBean, созданная Bean

Общая документация:Каталог статей
Github : github.com/black-ant

Введение

Еще посмотрите на эту картинку, от @топ Java.talent/article/139…

beanSelf.jpg

Первые два шага были упомянуты ранее:

В этой статье мы поговорим о связанном с ним процессе InitializeBean на следующем шаге. Эта статья в основном включает:

  • Основной процесс initializeBean
  • Прошлое и настоящее четырех методов инициализации
  • Сводка параметров после initializeBean

2. Подробное объяснение метода InitializeBean

2.1 Метод BeanInitializeBean + функция расширения инициализации

Вышеупомянутое создание Бина в основном завершает создание Бина, внедрение атрибутов, обработку зависимостей, отAbstractAutowireCapableBeanFactory # doCreateBeanИнициировать процесс создания, после процесса сущность будет создана


/**
* 发起流程 , 创建主流程
**/
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {
    //............
    Object exposedObject = bean;
    try {
        populateBean(beanName, mbd, instanceWrapper);
        exposedObject = initializeBean(beanName, exposedObject, mbd);
    } catch (Throwable ex) {
        if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
            throw (BeanCreationException) ex;
        } else {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
        }
    } 
    //............
}           

2.1.1 Первый — это процесс загрузки InitializingBean.

C173- AbstractAutowireCapableBeanFactory
    M173_50- initializeBean
        - invokeAwareMethods(beanName, bean) :激活 Aware 方法,对特殊的 bean 处理 -> PS:M173_50_02
        - applyBeanPostProcessorsBeforeInitialization : 后处理器,before
        - invokeInitMethods : 激活用户自定义的 init 方法 
        - applyBeanPostProcessorsAfterInitialization :后处理器,after
    M173_51- invokeAwareMethods -> PS:M173_51_01 
        ?- 根据 Aware的具体类型分别设置 BeanName / BeanClassLoaderAware / BeanFactoryAware 
    M173_52- applyBeanPostProcessorsBeforeInitialization
        ?- getBeanPostProcessor 获取所有的 BeanPostProcessor 并且进行FOR 循环调用 postProcessBeforeInitialization
        ?- 注意 , 如果没有 Processor , 则直接返回原本的 Object bean , 存在则返回处理过的
        FOR- getBeanPostProcessor
            - processor.postProcessBeforeInitialization(result, beanName)
    M173_53- invokeInitMethods
        - 如果包含 afterPropertiesSet , 则调用 ((InitializingBean) bean).afterPropertiesSet()
    
// M173_50 代码
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
    // 激活 Aware 方法 , 此处会根据权限不同分别处理
    // Step 1 : 获取系统安全接口 , 如果已经为当前应用程序建立了安全管理器,则返回该安全管理器
    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
            invokeAwareMethods(beanName, bean);
            return null;
        }, getAccessControlContext());
    }else {
        invokeAwareMethods(beanName, bean);
    }

    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    try {
        invokeInitMethods(beanName, wrappedBean, mbd);
    }catch (Throwable ex) {
        .....
    }
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }
    return wrappedBean;
}   


PS: M173_50_02 Контроль разрешений

AccessController.doPrivileged :Выполняет указанное PrivilegedAction с разрешениями, включенными и ограниченными указанным AccessControlContext.
PrivilegedAction :Используемое здесь действие, поддерживающее выполнение логики с включенными привилегиями.
getAccessControlContext :Делегирование создания контекста управления доступом в SecurityContextProvider

причина :Основная причина этого заключается в механизме JVM для защиты доменов при использованииSecurityManager, и доступ к нему невозможен. В настоящее время классjava.security.AccessControllerПредоставляет механизм применения политики безопасности по умолчанию, который использует проверку стека, чтобы решить, разрешены ли потенциально небезопасные операции, в то время как субъект кода, использующий doPrivileged, является привилегированным.

инициализировать детали компонента

Вероятно, вы видите, что несколько важных вызовов

  • invokeAwareMethods
  • applyBeanPostProcessorsBeforeInitialization
  • invokeInitMethods
  • applyBeanPostProcessorsAfterInitialization

Позже мы подробно рассмотрим эти методы:

2.2 Обработка invokeAwareMethods и Aware

Вот чтобы выполнить метод Aware, кстати, давайте поговорим о процессе Aware

Интерфейс Aware — это основной интерфейс контейнера Spring и суперинтерфейс с функцией идентификации.Компоненты, реализующие этот интерфейс, могут получать уведомления от контейнера Spring.

Применение :Указывает, что bean-компонент может быть уведомлен контейнером Spring о конкретном объекте фреймворка с помощью метода обратного вызова.
Структура :Обычно должен содержать только метод, возвращающий void, который принимает один параметр.
Функция:Уведомлен контейнером Spring для получения скрытых свойств

Распространенные типы BeanAware

- BeanNameAware:对该 bean 对象定义的 beanName 设置到当前对象实例中
- BeanClassLoaderAware:将当前 bean 对象相应的 ClassLoader 注入到当前对象实例中
- BeanFactoryAware:BeanFactory 容器会将自身注入到当前对象实例中,这样当前对象就会拥有一个 BeanFactory 容器的引用。

// 当然,Spring 不仅仅只是提供了上面三个 Aware 接口,而是一系列:
- LoadTimeWeaverAware:加载Spring Bean时织入第三方模块,如AspectJ
- BootstrapContextAware:资源适配器BootstrapContext,如JCA,CCI
- ResourceLoaderAware:底层访问资源的加载器
- PortletConfigAware:PortletConfig
- PortletContextAware:PortletContext
- ServletConfigAware:ServletConfig
- ServletContextAware:ServletContext
- MessageSourceAware:国际化
- ApplicationEventPublisherAware:应用事件
- NotificationPublisherAware:JMX通知

В общей структуре есть операция: проверить, активировать Aware, каковы причины и последствия этого процесса?

Система интерфейса Aware очень большая, мы возьмем только BeanNameAware в качестве примера.

Чисто конструктивно это всего лишь интерфейс

public interface BeanNameAware extends Aware {
	// 安装上文说的 , 接受单参数 ,返回 void
	void setBeanName(String name);
}

Итак, что сделал BeanNameAware?

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

Английское значение слова осведомленный:осознавать, воспринимать, замечать, обнаруживать

Другими словами, класс, реализующий соответствующий интерфейс Aware, может делать соответствующие вещи.То есть Bean реализует BeanNameAware, тогда он может воспринимать BeanName, сгенерированное для него системой.

Вот почему требуется, чтобы метод в осведомленном виде был пустым, вот для обратного вызова, чтобы получить установленное значение.

В нижней части кода запись операции Aware

C- AbstractAutowireCapableBeanFactory
	M- invokeAwareMethods(final String beanName, final Object bean)
            ?- 该方法是在 initializeBean 中被调用
            
private void invokeAwareMethods(final String beanName, final Object bean) {
    if (bean instanceof Aware) {
    
        // 根据Aware 类型的不同调用对应的方法
        if (bean instanceof BeanNameAware) {
            ((BeanNameAware) bean).setBeanName(beanName);
        }
        if (bean instanceof BeanClassLoaderAware) {
            ClassLoader bcl = getBeanClassLoader();
                if (bcl != null) {
                    ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
                }
        }
        if (bean instanceof BeanFactoryAware) {
            ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
        }
    }
}
      
            

Другими словами, после обработки различных атрибутов необходимо улучшить некоторые функции с помощью обратных вызовов после обработки связанных методов.Например, invokeAwareMethods обрабатывает три вещи:

  • BeanNameAware установить имя компонента
  • Набор BeanClassLoaderAware BeanClassLoader
  • BeanFactoryAware устанавливает фабрику компонентов

Как реализуется осведомленность

public class CommonService implements BeanNameAware {
    
    private String beanName;
    
    // Spring 创建过程中 , 在生成name 后 , 会回调该接口 , 将 BeanName 注入进来 ,让对象可感知
    @Override
    public void setBeanName(String name) {
        this.beanName = name;
    }
    
}
    

2.4 applyBeanPostProcessorsBeforeInitialization

Здесь легко понять, выполнять методы, связанные с BeanPostProcessors

@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
			throws BeansException {

    Object result = existingBean;
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        Object current = processor.postProcessBeforeInitialization(result, beanName);
        if (current == null) {
            return result;
        }
        result = current;
    }
    return result;
}


// applyBeanPostProcessorsAfterInitialization 类似 , 其区别就是调用 postProcessAfterInitialization

2.5 Основной процесс invokeInitMethods

На этом этапе свойство было установлено, проверьте, реализует ли компонент InitializingBean или определяет собственный метод инициализации, и если да, то вызывает необходимые обратные вызовы.

// PS:M173_51_01  详情处理
    protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
            throws Throwable {
        boolean isInitializingBean = (bean instanceof InitializingBean);
        if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
            // 同理 , 对存在安全域  SecurityManager 的方法 , 通过 AccessController 进行授权调用
            if (System.getSecurityManager() != null) {
                try {
                    AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
                        ((InitializingBean) bean).afterPropertiesSet();
                        return null;
                    }, getAccessControlContext());
                }
                catch (PrivilegedActionException pae) {
                    throw pae.getException();
                }
            }
            else {
                ((InitializingBean) bean).afterPropertiesSet();
            }
        }
        
        /
        if (mbd != null && bean.getClass() != NullBean.class) {
            String initMethodName = mbd.getInitMethodName();
            if (StringUtils.hasLength(initMethodName) &&
                    // 取反 , 避免反复调用
                    !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                    !mbd.isExternallyManagedInitMethod(initMethodName)) {
                // 在给定bean上调用指定的自定义init方法    
                invokeCustomInitMethod(beanName, bean, mbd);
            }
        }
    }
   

Дополнение 1: Метод InitializingBean

> InitializingBean 是一个接口 , 
    M- afterPropertiesSet() : 
        |- 在 bean 的初始化进程中会判断当前 bean 是否实现了 InitializingBean
        |- 如果实现了则用 #afterPropertiesSet() 方法,进行初始化工作
            |- 属性初始化的处理
        |- 然后再检查是否也指定了 init-method 
            |- 如果指定了则通过反射机制调用指定的 init-method 方法
            |- 利用反射机制执行, 激活用户自定义的初始化方法

Дополнение 2: метод invokeCustomInitMethod

Обычно этим занимается@Bean(initMethod = "initMethod")Обрабатывается с указанием аннотации

  • Получите initMethod для настройки
  • Получить объекты метода через отражение
  • Выполнение метода методом отражения
protected void invokeCustomInitMethod(String beanName, final Object bean, RootBeanDefinition mbd)
            throws Throwable {
        
        // 获取配置的 initMethod
        String initMethodName = mbd.getInitMethodName();
        
        // 通过反射获取方法对象
        Method initMethod = (mbd.isNonPublicAccessAllowed() ?
                BeanUtils.findMethod(bean.getClass(), initMethodName) :
                ClassUtils.getMethodIfAvailable(bean.getClass(), initMethodName));

        // 如果方法不存在 , 抛出异常或返回
        if (initMethod == null) {
           // 指示配置的init方法是否为默认方法
            if (mbd.isEnforceInitMethod()) {
                throw new BeanDefinitionValidationException(.....);
            }
            else {
                return;
            }
        }
        
        // 如果可能,为给定的方法句柄确定相应的接口方法
        Method methodToInvoke = ClassUtils.getInterfaceMethodIfPossible(initMethod);

        // 方法反射的方式执行 method , 同理 , 会获取权限管理
        if (System.getSecurityManager() != null) {
            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                ReflectionUtils.makeAccessible(methodToInvoke);
                return null;
            });
            try {
                AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () ->
                        methodToInvoke.invoke(bean), getAccessControlContext());
            }
            catch (PrivilegedActionException pae) {
                InvocationTargetException ex = (InvocationTargetException) pae.getException();
                throw ex.getTargetException();
            }
        }
        else {
            try {
                // 获取许可后反射获取
                ReflectionUtils.makeAccessible(methodToInvoke);
                methodToInvoke.invoke(bean);
            }
            catch (InvocationTargetException ex) {
                throw ex.getTargetException();
            }
        }
    }

2.5 Как вызвать метод инициализации

Есть несколько способов инициализировать bean-компоненты при запуске приложения:

  • Реализовать метод интерфейса InitializingBean после набора свойств.
  • Реализовать запуск метода интерфейса ApplicationRunner (аргументы ApplicationArguments)
  • Аннотация аннотации метода @PostConstruct
  • @Bean(initMethod = "initMethod") указывается аннотацией
// 直观的从 log 上面看 , 顺序为 
- @PostConstruct
- InitializingBean 
- @Bean(initMethod = "initMethod")    
- ApplicationRunner

Процесс загрузки @PostConstruct

@PostConstruct принадлежитjavax.annotation, является одной из собственных аннотаций Java, используемых в методах, которым необходимо выполнить любую инициализацию после завершения внедрения зависимостей.Этот метод должен быть вызван до того, как класс будет введен в эксплуатацию. Все классы, поддерживающие внедрение зависимостей, должны поддерживать эту аннотацию.. Методы, аннотированные с помощью PostConstruct, должны вызываться, даже если класс не запрашивает внедрение каких-либо ресурсов.

    
C- DefaultInstanceManager # newInstance
C- DefaultInstanceManager # populateAnnotationsCache
C- DefaultInstanceManager # findPostConstruct

@Bean(initMethod = "initMethod")

Как упоминалось выше, в конечном итоге он будет выполнен в initializeBean -> invokeInitMethods и, наконец, будет выполнен путем отражения.

String initMethodName = mbd.getInitMethodName();

C- AbstractBeanDefinition
    F- private String initMethodName;
    
// 对应的初始化流程
C- ConfigurationClassBeanDefinitionReader
    M- loadBeanDefinitionsForBeanMethod

// 这里获取 @Bean 中的属性 initMethod
// @Bean(initMethod = "initMethod", autowire = Autowire.BY_TYPE)
String initMethodName = bean.getString("initMethod");
if (StringUtils.hasText(initMethodName)) {
    beanDef.setInitMethodName(initMethodName);
}   

// 最后在  AbstractAutowireCapableBeanFactory 中调用
C- AbstractAutowireCapableBeanFactory
    M- initializeBean
    M- invokeInitMethods 

afterPropertiesSet

afterPropertiesSet также выполняется в invokeInitMethods


    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
            invokeAwareMethods(beanName, bean);
            return null;
        }, getAccessControlContext());
    }else {
        invokeAwareMethods(beanName, bean);
    }

ApplicationRunner # запустить процесс запуска

Выполнение метода запуска относительно просто. ApplicationRunner является эксклюзивным методом SpringBoot. Когда SpringApplication вызывает метод запуска, он будет выполнен.

public ConfigurableApplicationContext run(String... args) {
        //..............
        try {
            //..............
            // 执行 run 方法
            callRunners(context, applicationArguments);
        } catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }
         //..............
        return context;
}


private void callRunners(ApplicationContext context, ApplicationArguments args) {
    List<Object> runners = new ArrayList<>();
    // 添加  ApplicationRunner 类
    runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
    // 添加  CommandLineRunner 类
    runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
    AnnotationAwareOrderComparator.sort(runners);
    // 对所有的 runner 进行处理
    for (Object runner : new LinkedHashSet<>(runners)) {
        if (runner instanceof ApplicationRunner) {
            callRunner((ApplicationRunner) runner, args);
        }
        if (runner instanceof CommandLineRunner) {
            callRunner((CommandLineRunner) runner, args);
        }
    }
}

// 最后通过 (runner).run(args); 调用

Суммировать

Эта статья относительно проста, но общий эффект лучше, чем у предыдущей Я чувствую, что каждая статья не должна быть слишком длинной, и ее нелегко понять, если задействовано слишком много пунктов.

Приложение:

исходный объект: image.png

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd)

Объект перед входом initializeBean

На самом деле, инъекция свойства здесь, autowired, была обработана.

image.png

После обработки он не будет отправлен, т.к. не выполняются операции осведомленности и постобработки, что по сути одно и то же