>>>> 😜😜😜 Github: 👉 github.com/black-ant CASE Backup: 👉 gitee.com/antblack/ca…

A. The preface

BeanPostProcessor is one of the core components of Spring. Bean implementation Of BeanPostProcessor can achieve many complex functions

2. Structure of PostProcessor

2.1 Interface Methods

This interface provides two main types, including pre-call and post-call. You can also see that there’s a default modifier, so it’s not mandatory to override

public interface BeanPostProcessor {

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

   default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
      returnbean; }}Copy the code

2.2 Common implementation classes

As you can see here, AOP, timing, configuration, and so on all implement relevant integrations

3. In-depth analysis of Spring source code

3.1 Use Cases

Take a look at this part of the Spring inside is how to use the PostProcessor characteristics, this part ScheduledAnnotationBeanPostProcessor, for example.

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
   // As you can see, the pre-processing does not have much processing, and returns directly
   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: Start special processingClass<? > targetClass = AopProxyUtils.ultimateTargetClass(bean);if (!this.nonAnnotatedClasses.contains(targetClass) &&
         AnnotationUtils.isCandidateClass(targetClass, Arrays.asList(Scheduled.class, Schedules.class))) {
      // Step 2: Get the collection of methods corresponding to the Scheduled changes
      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: The method is not empty, execute ProcessannotatedMethods.forEach((method, scheduledAnnotations) -> scheduledAnnotations.forEach(scheduled -> processScheduled(scheduled, method, bean))); }}return bean;
}
Copy the code

PS: Running a Runable object through a ScheduledTask

From the specific use, it is not difficult to see that he is in the creation of Bean to do supplementary operations, so let’s look at the specific processing process

This process is fully traced through the Bean initialization process, with more attention to some of the details

3.2 Before/After invokes the process

Entrance to one: # AbstractAutowireCapableBeanFactory initializeBean calls

  • applyBeanPostProcessorsBeforeInitialization
  • applyBeanPostProcessorsAfterInitialization

The for loop processes all the BeanPostProcessors

3.3 Management of BeanPostProcessors

AbstractBeanFactory there is a collection in AbstractBeanFactory that manages the BeanPostProcessor
private final List<BeanPostProcessor> beanPostProcessors = new BeanPostProcessorCacheAwareList();

// Regardless of the type, the call is made in two ways: first delete, then add
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);
}

Copy the code

Type 1: Manual adding process

Can be realized by manually add, add BeanPostProcessor, AbstractApplicationContext reference object

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

Copy the code

Type 2: Refresh segment is added

Ha ha, here we go again. I noticed this place when I looked at the Refresh code earlier, registering the associated BeanPostProcessors

protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
   / / Step 1: call PostProcessorRegistrationDelegate to register
   PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}
Copy the code

Source code comments are very clear, here is a simple translation

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

   // postProcessor arraylist
   String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true.false);

   / / processor counting
   int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
   // Register BeanPostProcessorChecker, which logs informational messages when the bean is created during BeanPostProcessor instantiation, that is, when the bean does not meet all criteria for BeanPostProcessors processing
   beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

   // distinguish PriorityOrdered, internalPostProcessor
   List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
   List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
   
   / / ordering
   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);
         // The post-processor callback interface used at runtime to merge bean definitions
         if (pp instanceofMergedBeanDefinitionPostProcessor) { internalPostProcessors.add(pp); }}// If the Ordered interface is implemented, Ordered processing is required
      else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
         orderedPostProcessorNames.add(ppName);
      }
      else{ nonOrderedPostProcessorNames.add(ppName); }}// First, register BeanPostProcessors that implement Prioritordered
   sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
   registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

   // Next, register the BeanPostProcessors that implemented Ordered
   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);

   // Now, register all regular 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);

   // Finally, re-register all internal BeanPostProcessors
   sortPostProcessors(internalPostProcessors, beanFactory);
   registerBeanPostProcessors(beanFactory, internalPostProcessors);

   // Re-register the post-handler used to detect the internal bean as ApplicationListeners, moving them to the end of the handler chain
   beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}
Copy the code

4. In-depth customization

4.1 add BeanPostProcessor

Method 1: Add it manually

PS: Of course, this method is too complicated for many scenarios

@Autowired
private DefaultListableBeanFactory beanFactory;

@Bean(initMethod = "initMethod")
public CommonService getCommonService(a) {
    // Add via BeanFactory
    beanFactory.addBeanPostProcessor(new CustomizePostProcessor());
    return new CommonService();
}
Copy the code

Method 2: Inherit the interface directly

As you can see from the above analysis, the interface is automatically added once implemented

5. Supplementary questions

5.1 What are the differences from the four common initialization methods?

Review the four ways to initialize a run:

  • Implement the InitializingBean interface method afterPropertiesSet
  • Implement ApplicationRunner interface method Run (ApplicationArguments args)
  • Method annotation @postconstruct
  • @bean (initMethod = “initMethod”) is specified by an annotation

Call difference

: -- -- -- -- -- - >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 <-------
Copy the code
  • Call number: four methods will be called only once, and postProcessBeforeInitialization and postProcessAfterInitialization will be called when each Bean created
  • Call timing: Calls are focused on each Bean initializeBean link

Six. What can we do with it?

There is no discussion here about whether there is a real scene to use, just divergent thinking, to think about how to use its characteristics to do something

6.1 Combine design patterns

Proxying is one of the most appropriate uses of postProcess. AOP is proxying using its features. We can simulate AOP to do a more business-oriented static proxy pattern.

The decorator pattern can also be implemented to enhance Bean processing.

Step 1: Prepare interfaces and implementation classes

// Unified interface
public interface Sourceable {
    void method(a);
}


// Corresponding implementation class (actual business class)
@Service
public class Source implements Sourceable {

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

Step 2: Prepare intermediate classes

public class AopProxyImpl implements Sourceable {

    private Sourceable source;

    public AopProxyImpl(Sourceable source) {
        super(a);// Note that the proxy mode creates an object in the proxy class
        // this.source = new Source();

        // The decorator mode is passed in the corresponding object
        this.source = source;
    }

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

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

    private void before(a) {
        System.out.println("before proxy!"); }}Copy the code

Step 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;
        }

        returnbean; }}Copy the code

Supplement:

  • Here if not, it will throw BeanNotOfRequiredTypeException, because Spring will go to stick to match
  • Here we are static proxies, but we can use cglib dynamic proxies more flexibly
  • There are many other ways to integrate design patterns that can be used flexibly

6.2 Manager Manages objects of a specified type

This is done by placing an administrative record on the specified Bean in the postProcessor

The approximate way to use it is to prepare a BeanManager and manage it in the postProcessor

@Component
public class BeanManager {
    
    // Group special beans
    private static Map<String, TypeBean> typeOne = new ConcurrentHashMap();
    private static Map<String, TypeBean> typeTwo = new ConcurrentHashMap();
    
    / / PS: this way is suitable for processing more complex scenarios, may consider the simple scenario by ApplicationContext. GetBean to obtain the corresponding class
}


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: actually the same object as the injected object
------> typeOne.get("typeBeanImpl") == (typeBean) :true <-------

Copy the code

Supplement:

This is the simplest way to use it, but more complex ways can be to integrate annotations, integrate interfaces or superclasses, or just record class information to achieve their own business effects

6.3 Injecting special Attributes

We can see from the picture that BeanPostProcessor is processed after the PopulateBean link, so we can modify the attributes in the Bean through this link. Common ideas include:

  • Sets the dynamic proxy for a specific property
  • Get the properties from the Remote and set them

This makes a lot of sense. In fact, at this point RestTemplate is loaded, JDBC is available, and you can get configuration information from the remote end

// Function 1:Using JDBC (JPA should also be loaded and available), get the configuration from the database, determine if the current class has a specific annotation, and refresh the annotation configuration// Function 2:Call the remote configuration center to customize the refresh configuration//Refresh special properties or objects and so onCopy the code

** Special object refresh has a variety of arbitrary use, can be used flexibly according to their own business

6.4 Other Ideas

This is just a piece of cake, welcome to put forward their own ideas

  • Refactoring properties
  • Classes are overridden during customization

conclusion

Note:

  • ApplyBeanPostProcessorsBeforeInitialization mainly in initializeBean link calls
  • ApplyBeanPostProcessorsAfterInitialization besides initializeBean also in multiple link is called, Including getSingletonFactoryBeanForTypeCheck, etc in the process of a few Factory to instantiate
  • Avoid loops; ManagerPostProcessor does not process itself
  • BeanPostProcessor is called every time a bean is created, and too much can affect startup efficiency
  • BeanPostProcessor is mainly after populateBean, note the order