Я не знаю интерфейс Spring InitializingBean, вам следует замедлить исходный код...

Spring

Недавно я задумался над тем, как выпустить несколько протоколов через один интерфейс. Например: при публикации интерфейса dubbo, feign или Ali hsf публикуйте тот же семантический http-интерфейс.

Последняя мысль — просканировать пользовательские аннотации, а затем зарегистрировать их в mvc. Итак, я пошел посмотреть на процесс сопоставления регистрации mvc, который вызвал инициализациюBean, главного героя этой статьи.

Что такое InitializingBean?

Интерфейс InitializingBean предоставляет метод инициализации для bean-компонентов. Интерфейс включает только метод afterPropertiesSet без возвращаемого значения. Все классы, наследующие этот интерфейс, будут выполнять этот метод при инициализации bean-компонента.

/**
 * Interface to be implemented by beans that need to react once all their properties
 * have been set by a {@link BeanFactory}: e.g. to perform custom initialization,
 * or merely to check that all mandatory properties have been set.
 *
 * <p>An alternative to implementing {@code InitializingBean} is specifying a custom
 * init method, for example in an XML bean definition. For a list of all bean
 * lifecycle methods, see the {@link BeanFactory BeanFactory javadocs}.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @see DisposableBean
 * @see org.springframework.beans.factory.config.BeanDefinition#getPropertyValues()
 * @see org.springframework.beans.factory.support.AbstractBeanDefinition#getInitMethodName()
 */
public interface InitializingBean {

    /**
     * Invoked by the containing {@code BeanFactory} after it has set all bean properties
     * and satisfied {@link BeanFactoryAware}, {@code ApplicationContextAware} etc.
     * <p>This method allows the bean instance to perform validation of its overall
     * configuration and final initialization when all bean properties have been set.
     * @throws Exception in the event of misconfiguration (such as failure to set an
     * essential property) or if initialization fails for any other reason
     */
    void afterPropertiesSet() throws Exception;

}

Это все еще старое правило, и я переведу конспекты занятий для всех, кто владеет английским языком на уровне колледжа.

Bean-компоненты, реализующие этот интерфейс, должны ответить после того, как BeanFactory установит все свойства. Например: выполните пользовательскую инициализацию или проверьте, установлены ли все необходимые свойства. Вместо этого вы также можете использовать собственный метод initMethod.

Какую информацию вы получаете из аннотации класса?

  1. Должен выполняться после того, как BeanFactory установит все свойства

  2. Во-вторых, реализация этого интерфейса должна быть bean-компонентом ioc-контейнера.

  3. Эта штука не незаменимая, вместо нее можно использовать метод initMethod

Комментарии к методу AfterPropertiesSet не так много читаются, давайте сосредоточимся на них.

Этот метод вызывается со всеми установленными свойствами компонента. По сути, оно имеет тот же смысл, что и аннотация класса, ведь мальчику еще предстоит слушать Лао-цзы.

Не заглядывая в исходный код, коротко подведем итоги. Компонент реализует интерфейс InitializingBean, после чего метод afterPropertiesSet будет вызываться после того, как BeanFactory установит все свойства.

Поэкспериментируйте с InitializingBean

Как говорится в старой поговорке: просто говорите и не практикуйте фальшивые хваты, тогда давайте поиграем с этим интерфейсом.

Вы должны сделать это в проекте spring или проекте springboot, ломбок, используемый в примере кода, печатает журнал

Эта небольшая программа относительно короткая и делает две вещи.

  1. Когда проект запустится, посмотрите, будет ли напечатан лог в коде, печать означает успех

  2. Выведите количество бобов в контейнере

    @Slf4j @Компонент открытый класс InitializingBeanTest реализует InitializingBean { @Autowired частный ApplicationContext applicationContext; @Override public void afterPropertiesSet() выдает Exception { log.info("======"); log.info(" >>> Выполнение InitializingBeanTest :: afterPropertiesSet "); log.info(" >>> BeanNames :: {}", applicationContext.getBeanDefinitionCount()); log.info("======"); } /** * результат операции: * ====== * >>> Выполняется InitializingBeanTest :: afterPropertiesSet * >>>ИменаБин::128 * ====== */ }

Когда вы думаете о методе инициализации после создания экземпляра компонента, вы обязательно подумаете об атрибуте initMethod при объявлении компонента.Разница между ними будет подробно представлена ​​ниже.

На самом деле, InitializingBean может иметь большое значение.Конкретная функция будет упомянута ниже.

Разница между InitializingBean и initMethod

Метод реализации:

  1. InitializingBean — это интерфейс, класс, который должен реализовать этот интерфейс, — это bean-компонент контейнера ioc.

  2. init нужно указать в initMethod при объявлении бина, объявленного в виде xml или @Bean

Время исполнения:

  1. После того, как BeanFactory установит все свойства, он выполнит bean-компонент, реализующий интерфейс InitializingBean.

  2. Время выполнения initMethod после InitializingBean

Измените демонстрацию выше и повторите попытку.

@Slf4j
public class InitializingBeanTest implements InitializingBean {

    @Configuration
    static class BeanConfiguration {
        @Bean(initMethod = "init")
        public InitializingBeanTest getInitializingBeanTest() {
            return new InitializingBeanTest();
        }
    }

    @Autowired
    private ApplicationContext applicationContext;

    public void init() {
        log.info("  >>> bean 指定 init 执行... ");
    }

    @Override
    public void afterPropertiesSet() {
        log.info("======");
        log.info("  >>> InitializingBeanTest 执行 :: afterPropertiesSet ");
        log.info("  >>> BeanNames :: {}", applicationContext.getBeanDefinitionCount());
        log.info("======");
    }
    /**
     * 运行结果:
     * ======
     *   >>> InitializingBeanTest 执行 :: afterPropertiesSet
     *   >>> BeanNames :: 128
     * ======
     * 
     *   >>> bean 指定 init 执行... 
     */
}

Согласно результатам выполнения, initMethod запускается после InitializingBean.Далее объясним, почему с точки зрения исходного кода?

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

25 лучших тем для интервью по Java (с анализом)

От 0 до 1Обучающие маршруты и материалы по Java

Базовый набор знаний Java

Алгоритм Цзо Чэнъюня

Весеннее семейное ведро

Как загружается InitializingBean?

Исходный класс AbstractAutowiredCapableBeanFactory, который может загружать bean-компоненты через spring

Просмотрите конкретную логику вызова.Метод invokeInitMethods отвечает за вызов bean-компонента, который реализует интерфейс InitializingBean и указывает метод initMethod.

protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
        throws Throwable {
        // 判断 bean 是否实现了 InitializingBean 接口
    boolean isInitializingBean = (bean instanceof InitializingBean);
    if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
        if (logger.isTraceEnabled()) {
            logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
        }
        // 系统安全处理器为空则直接执行 else 流程, 调用 afterPropertiesSet 方法
        // 默认为空,所以直接 else 流程
        if (System.getSecurityManager() != null) {
            try {
                AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
                    ((InitializingBean) bean).afterPropertiesSet();
                    return null;
                }, getAccessControlContext());
            }
            catch (PrivilegedActionException pae) {
                throw pae.getException();
            }
        }
        else {
            // 直接调用 afterPropertiesSet
            ((InitializingBean) bean).afterPropertiesSet();
        }
    }

    if (mbd != null && bean.getClass() != NullBean.class) {
        // 判断是否指定了 initMethod 方法, 如果指定会进行调用
        String initMethodName = mbd.getInitMethodName();
        // 如果 initMethod 方法名称为 “afterPropertiesSet”, 则不尽兴调用
        if (StringUtils.hasLength(initMethodName) &&
                !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                !mbd.isExternallyManagedInitMethod(initMethodName)) {
            // 通过反射调用 initMethod 指定方法
            invokeCustomInitMethod(beanName, bean, mbd);
        }
    }
}

Мы уже поняли из исходного кода, на самом деле, когда внедрение зависимостей будет завершено, Spring проверит, реализует ли бин интерфейс InitializingBean, и если он был реализован, то вызовет метод afterPropertiesSet этого класса.

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

  1. Узнайте, почему интерфейс InitializingBean предшествует методу initMethod.

  2. Если объект компонента реализует интерфейс InitializingBean и объявляет метод initMethod с именем «afterPropertiesSet», он не будет вызываться повторно.

  3. Метод initMethod выполняется через отражение, а InitializingBean вызывается напрямую, метод инициализации вы можете выбрать сами

Как использовать его в исходном коде mvc?

Исходный код springmvc не будет слишком подробно объясняться, он будет объяснен в следующем анализе исходного кода mvc.

Эта статья является метательным камнем, чтобы спросить направления.Давайте сначала посмотрим, как mvc использует функцию метода инициализации InitializingBean для завершения отношения сопоставления.

@RestController
@RequestMapping("/test")
public class MvcController {

    @GetMapping("/say/hello/{name}")
    public String sayHello(@PathVariable("name") String name) {
        return "Hello World " + name;
    }
}

Чтобы у всех не сложилось впечатление об этой части, давайте поговорим об этом: она будет регистрировать информацию о маршрутизации URL-адресов, связанную с сопоставлением.

Исходный код springmvc по-прежнему очень сложен, в основном объясняя, как AbstractHandlerMethodMapping преобразует URL-адрес в HandlerMethod для использования.

springmvc HandlerMethod принадлежит эксклюзивному Springmvc, не нужно обращать внимание

Давайте сначала рассмотрим наиболее важную волну, которая заключается в реализации интерфейса InitializingBean и вызове логики инициализации в методе afterPropertiesSet.

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
   @Override
      public void afterPropertiesSet() {
            initHandlerMethods();
      }
}

Этот метод проходит через все bean-компоненты в контейнере ioc spring, а затем поддерживает сопоставление URL-адресов с квалифицированными bean-компонентами, аннотированными с помощью @Controller, @RequestMapping и т. д.

protected void initHandlerMethods() {
    if (logger.isDebugEnabled()) {
        logger.debug("Looking for request mappings in application context: " + getApplicationContext());
    }
    // 获取 ioc 容器中所有 bean 名称
    String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
            BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
            obtainApplicationContext().getBeanNamesForType(Object.class));
        // 循环 bean 名称数组, 找到符合条件的进行关系维护
    for (String beanName : beanNames) {
        if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
            Class<?> beanType = null;
            try {
                beanType = obtainApplicationContext().getType(beanName);
            }
            catch (Throwable ex) {
                // An unresolvable bean type, probably from a lazy bean - let's ignore it.
                if (logger.isDebugEnabled()) {
                    logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
                }
            }
            // isHandler 即判断是否拥有 @Controller、@RequestMapping
            if (beanType != null && isHandler(beanType)) {
                // 符合条件, 进行关系维护
                detectHandlerMethods(beanName);
            }
        }
    }
    handlerMethodsInitialized(getHandlerMethods());
}

Вывод

В статье подробно объясняется, что такое InitializingBean, и в форме реальных примеров и объяснений исходного кода описывается отличие от initMethod, указанного при объявлении bean-компонента.

Наконец, фактический пример платформы mvc описывает, как связать URL-адрес и пользовательский объект Handler друг с другом через интерфейс InitializingBean, потому что это может помочь нам лучше понять фактическое использование проекта.

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