Spring в действии: процесс PostProcessor и его возможности

Java задняя часть
Spring в действии: процесс PostProcessor и его возможности

Прежде всего, поделитесь всеми предыдущими статьями, ставьте лайки, добавляйте в избранное и пересылайте три раза подряд. >>>>😜😜😜
Сборник статей:🎁nuggets.capable/post/694164…
Github :👉github.com/black-ant
Резервное копирование CASE:👉git ee.com/ant black/wipe…

Введение

BeanPostProcessor — один из основных компонентов Spring.Бин реализует BeanPostProcessor для выполнения многих сложных функций.

2. Структура постпроцессора

2.1 Методы интерфейса

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

public interface BeanPostProcessor {

   default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
      return bean;
   }

   default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
      return bean;
   }

}

2.2 Общие классы реализации

Здесь вы можете видеть, что АОП, синхронизация, конфигурация и т. д. достигли связанной интеграции.

system-BeanPostProcessor.png

3. Углубленный анализ исходного кода Spring

3.1 Варианты использования

В этой части рассматривается внутреннее использование Spring возможности PostProcessor на примере ScheduledAnnotationBeanPostProcessor.

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
   // 可以看到 , 前置处理并没有太多处理 , 直接返回
   return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {

   if (bean instanceof AopInfrastructureBean || bean instanceof TaskScheduler ||
         bean instanceof ScheduledExecutorService) {
      // Ignore AOP infrastructure such as scoped proxies.
      return bean;
   }

   // Step 1 : 开始特殊处理 
   Class<?> targetClass = AopProxyUtils.ultimateTargetClass(bean);
   if (!this.nonAnnotatedClasses.contains(targetClass) &&
         AnnotationUtils.isCandidateClass(targetClass, Arrays.asList(Scheduled.class, Schedules.class))) {
      // Step 2 : 获取 Method 对应 Scheduled 的集合   
      Map<Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass,
            (MethodIntrospector.MetadataLookup<Set<Scheduled>>) method -> {
               Set<Scheduled> scheduledAnnotations = AnnotatedElementUtils.getMergedRepeatableAnnotations(
                     method, Scheduled.class, Schedules.class);
               return (!scheduledAnnotations.isEmpty() ? scheduledAnnotations : null);
            });
      if (annotatedMethods.isEmpty()) {
         this.nonAnnotatedClasses.add(targetClass);
      }
      else {
         // Step 3 : 方法不为空 , 执行 Process
         annotatedMethods.forEach((method, scheduledAnnotations) ->
               scheduledAnnotations.forEach(scheduled -> processScheduled(scheduled, method, bean)));
      }
   }
   return bean;
}

PS: дальше смотреть не буду, главное запускать объект Runable через ScheduledTask

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

Этот процесс находится вПроцесс инициализации фасолейВсестороннее последующее наблюдение проводится в , вот некоторые детали

3.2 До/После вызова процесса

Запись 1: AbstractAutowireCapableBeanFactory #initializeBeanперечислить

  • applyBeanPostProcessorsBeforeInitialization
  • applyBeanPostProcessorsAfterInitialization

Процесс также относительно прост, for зацикливает все BeanPostProcessors для обработки.

3.3 Управление процессорами BeanPost

// 在 AbstractBeanFactory 中有一个集合用于管理 BeanPostProcessor
private final List<BeanPostProcessor> beanPostProcessors = new BeanPostProcessorCacheAwareList();

// 不论是哪种类型 ,都会通过以下2种方式进行调用 , 先删除, 后添加
public void addBeanPostProcessors(Collection<? extends BeanPostProcessor> beanPostProcessors) {
   this.beanPostProcessors.removeAll(beanPostProcessors);
   this.beanPostProcessors.addAll(beanPostProcessors);
}

public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
   this.beanPostProcessors.remove(beanPostProcessor);
   this.beanPostProcessors.add(beanPostProcessor);
}

Тип 1: добавленный вручную процесс

Добавление BeanPostProcessor может быть достигнуто путем добавления его вручную, обратитесь к объекту AbstractApplicationContext

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
   // .............
   // Configure the bean factory with context callbacks.
   beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
   // .............
}

Тип 2: добавлена ​​ссылка на обновление

Ха-ха, я снова пришел в это место Я заметил это место, когда читал код обновления раньше, и связанные с ним BeanPostProcessors будут зарегистрированы.

protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
   // Step 1 : 调用  PostProcessorRegistrationDelegate 进行注册
   PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}

Комментарии к исходному коду очень понятны, вот простой перевод

public static void registerBeanPostProcessors(
      ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {

   // postProcessor 数组列表 
   String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);

   // processor 计数 
   int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
   //注册BeanPostProcessorChecker,当bean在BeanPostProcessor实例化期间创建时,即当bean不符合所有BeanPostProcessors处理的条件时,该checker会记录信息消息 
   beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

   // 区别 PriorityOrdered, internalPostProcessor
   List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
   List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
   
   // 有序化
   List<String> orderedPostProcessorNames = new ArrayList<>();
   List<String> nonOrderedPostProcessorNames = new ArrayList<>();
   
   for (String ppName : postProcessorNames) {
      if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
         BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
         priorityOrderedPostProcessors.add(pp);
         // 运行时用于合并bean定义的后处理器回调接口
         if (pp instanceof MergedBeanDefinitionPostProcessor) {
            internalPostProcessors.add(pp);
         }
      }
      // 如果实现了 Ordered 接口 , 则需要有序处理
      else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
         orderedPostProcessorNames.add(ppName);
      }
      else {
         nonOrderedPostProcessorNames.add(ppName);
      }
   }

   // 首先,注册实现prioritordered的BeanPostProcessors
   sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
   registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

   // 接下来,注册实现Ordered的BeanPostProcessors
   List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
   for (String ppName : orderedPostProcessorNames) {
      BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
      orderedPostProcessors.add(pp);
      if (pp instanceof MergedBeanDefinitionPostProcessor) {
         internalPostProcessors.add(pp);
      }
   }
   sortPostProcessors(orderedPostProcessors, beanFactory);
   registerBeanPostProcessors(beanFactory, orderedPostProcessors);

   // 现在,注册所有常规的BeanPostProcessors
   List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
   for (String ppName : nonOrderedPostProcessorNames) {
      BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
      nonOrderedPostProcessors.add(pp);
      if (pp instanceof MergedBeanDefinitionPostProcessor) {
         internalPostProcessors.add(pp);
      }
   }
   registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

   // 最后,重新注册所有内部BeanPostProcessors
   sortPostProcessors(internalPostProcessors, beanFactory);
   registerBeanPostProcessors(beanFactory, internalPostProcessors);

   // 将用于检测内部bean的后处理器重新注册为ApplicationListeners,将其移动到处理器链的末端
   beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}

4. Глубокая настройка

4.1 Добавить BeanPostProcessor

Способ 1: добавить вручную

PS: Конечно, этот метод слишком сложен, и применимых сценариев не так много.

@Autowired
private DefaultListableBeanFactory beanFactory;

@Bean(initMethod = "initMethod")
public CommonService getCommonService() {
    // 通过 BeanFactory 进行添加
    beanFactory.addBeanPostProcessor(new CustomizePostProcessor());
    return new CommonService();
}

Способ 2: напрямую наследовать интерфейс

Я также увидел в приведенном выше анализе, что интерфейс автоматически добавляется при реализации интерфейса.

5. Дополнительные вопросы

В чем разница между 5.1 и четырьмя распространенными методами инициализации?

Давайте рассмотрим четыре способа инициализации прогона:

  • Реализовать метод интерфейса InitializingBean после набора свойств.
  • Реализовать запуск метода интерфейса ApplicationRunner (аргументы ApplicationArguments)
  • Аннотация аннотации метода @PostConstruct
  • @Bean(initMethod = "initMethod") указывается аннотацией

разница в вызове

: ------> this is @PostConstruct <-------
: ------> this is InitializingBean  <-------
: ------> this is in @Bean(initMethod = "initMethod")  <-------
: ------> this is postProcessBeforeInitialization <-------
: ------> this is postProcessAfterInitialization <-------
: ------> this is postProcessBeforeInitialization <-------
: ------> this is postProcessAfterInitialization <-------
: ------> this is postProcessBeforeInitialization <-------
: ------> this is postProcessAfterInitialization <-------
: Started DemoApplication in 0.822 seconds (JVM running for 2.597)
: ------> this is ApplicationRunner :getCommonService <-------
  • количество звонков: четыре метода будут вызываться только один раз, а методы postProcessBeforeInitialization и postProcessAfterInitializationбудет вызываться при создании каждого компонента
  • Сроки вызова: сосредоточиться на каждом компонентеinitializeBeanсвязать вызов

6. Что мы можем с этим сделать?

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

6.1 Комбинирование шаблонов проектирования

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

В то же время шаблон декоратора также может быть реализован для улучшения обработки компонентов Bean.

Шаг 1: Подготовьте интерфейс и класс реализации

// 统一接口
public interface Sourceable {
    void method();
}


// 对应实现类 (实际业务类)
@Service
public class Source implements Sourceable {

    @Override
    public void method() {
        System.out.println("the original method!");
    }
}

Шаг 2: Подготовьте промежуточный класс

public class AopProxyImpl implements Sourceable {

    private Sourceable source;

    public AopProxyImpl(Sourceable source) {
        super();
        // 注意 ,代理模式是在代理类中创建一个对象
        // this.source = new Source();

        // 修饰模式是传入对应的对象
        this.source = source;
    }

    @Override
    public void method() {
        before();
        source.method();
        atfer();
    }

    private void atfer() {
        System.out.println("after proxy!");
    }

    private void before() {
        System.out.println("before proxy!");
    }
}

Шаг 3: BeanPostProcessor для обработки

@Service
public class AopPostProcessor implements BeanPostProcessor {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        logger.info("------> AopPostProcessor postProcessBeforeInitialization <-------");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        logger.info("------> AopPostProcessor postProcessAfterInitialization <-------");

        if (bean instanceof Sourceable) {
            logger.info("------> AopPostProcessor build Aop <-------");
            AopProxyImpl proxy = new AopProxyImpl((Sourceable)bean);
            return proxy;
        }

        return bean;
    }
}

Пополнить:

  • Если здесь нет, будет выброшено BeanNotOfRequiredTypeException, потому что Spring будет настаивать на том, соответствует ли класс
  • Здесь мы относимся к статическому прокси, на самом деле его можно использовать более гибкоcglib динамический прокси
  • Существует также множество способов интеграции шаблонов проектирования, которые можно гибко использовать.

6.2 Менеджер управляет объектами указанного типа

Этот способ состоит в том, чтобы сделать запись управления для указанного bean-компонента в postProcessor.

Общий способ его использования - подготовить BeanManager, а затем управлять им в постпроцессоре.

@Component
public class BeanManager {
    
    // 对特殊的Bean 进行分组
    private static Map<String, TypeBean> typeOne = new ConcurrentHashMap();
    private static Map<String, TypeBean> typeTwo = new ConcurrentHashMap();
    
    // PS : 这种方式适合处理更复杂的场景 , 简单场景可以考虑通过 ApplicationContext.getBean 获取对应的类
}


public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    logger.info("------> ManagerPostProcessor postProcessAfterInitialization <-------");

    if (bean instanceof TypeBean) {
        logger.info("------> AopPostProcessor build Aop <-------");
        beanManager.getTypeOne().put(beanName, (TypeBean) bean);
    }

    return bean;
}

// PS : 实际上和注入的是同一个对象
------> typeOne.get("typeBeanImpl") == (typeBean) :true <-------

Пополнить:

Это самый простой способ его использования.Для более сложных вы также можете интегрировать аннотации, интегрировать интерфейсы или родительские классы или просто записывать информацию о классе для достижения собственных бизнес-результатов.

6.3 Внедрение специальных свойств

Как мы видим из рисунка, BeanPostProcessor обрабатывается после ссылки PopulateBean, затем мы можем изменить свойства в Bean через эту ссылку.Общие идеи использования включают:

  • Установить динамический прокси для определенного свойства
  • Получите свойства от Remote и установите

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

// 作用一 : 
使用 JDBC (JPA 应该也已经加载可用) , 从数据库获取配置 , 判断当前类是否有特定注解 , 刷新注解的配置

// 作用二 : 
调用远端配置中心 , 自定义刷新配置

// 作用三 : 
刷新特殊的属性或者对象等等

** Обновление специальных объектов имеет множество произвольных применений, которые можно гибко использовать в соответствии с вашим бизнесом **

6.4 Другие идеи

Это всего лишь предложение, и вы можете придумать свои собственные идеи.

  • свойства рефакторинга
  • Переопределить классы во время настройки

Суммировать

image.png

будь осторожен :

  • applyBeanPostProcessorsBeforeInitialization в основном вызывается в ссылке initializeBean
  • applyBeanPostProcessorsAfterInitialization в дополнение к initializeBeanТакже называется в нескольких ссылках, в том числе GetsingletonFactoryBeanFortyPecheck и несколько других фабрик в процессе создания присмотра
  • Избегайте циклов, ManagerPostProcessor не будет обрабатывать сам себя
  • BeanPostProcessor вызывается при каждом создании компонента.Слишком много повлияет на эффективность запуска
  • BeanPostProcessor в основном после populateBean, обратите внимание на порядок до и после