Recently, I was thinking about how to issue multiple protocols through a single interface. For example, publishing dubbo, Feign, or Ali HSF interfaces at the same time as publishing HTTP interfaces with the same semantics

The last thing that came to mind was scanning custom annotations and registering them in MVC. So I took a look at the MVC registration mapping process, which led to InitializingBean, the main character of this article

What is an InitializingBean?

The InitializingBean interface provides a way for a bean to initialize a method. The interface consists of only an afterPropertiesSet method that returns no value. Classes that inherit this interface execute this method when initializing the 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;

}
Copy the code

Or the old rules, with the author of the university level of English to translate the class notes

The bean implementing this interface that needs to respond after all the properties have been set by the BeanFactory. For example, perform custom initialization, or check that all required properties are set. You can also use the custom initMethod method instead

What do you get from the class annotation

  1. The BeanFactory must have set all the attributes before executing

  2. Second, the bean that implements this interface must bean ioc container

  3. This is not irreplaceable, you can use initMethod instead

AfterPropertiesSet (); afterPropertiesSet ()

This method is called when all bean properties are set. It’s the same as a class comment, because you still have to listen to me

Without looking at the source code, let’s summarize briefly. The Bean implements the InitializingBean interface, so after the BeanFactory has set all the properties, the afterPropertiesSet method is called

Play a trick InitializingBean

As the old saying goes: all talk, no practice; So let’s play around with this interface

You’ll want to do this in a Spring project or a SpringBoot project, and the sample code uses Lombok to print logs

This little program is relatively brief and does two things

  1. When the project starts, see if the logs in the code are printed, and if they are printed, they are successful

  2. Print the number of beans in the container

    @Slf4j @Component public class InitializingBeanTest implements InitializingBean { @Autowired private ApplicationContext applicationContext; @Override public void afterPropertiesSet() throws Exception { log.info(“======”); Log.info (” >>> InitializingBeanTest execute :: afterPropertiesSet “); log.info(” >>> BeanNames :: {}”, applicationContext.getBeanDefinitionCount()); log.info(“======”); } / run results: * * * * = = = = = = * > > > InitializingBeanTest execution: : afterPropertiesSet * > > > BeanNames: : * = = = = = = 128 * /}

When you think of the instantiated bean initialization method, you must think of the initMethod property when declaring the bean. We’ll explain the difference in detail below

In fact, the InitializingBean has a lot going for it, which will be covered below

The difference between InitializingBean and initMethod

Implementation method:

  1. InitializingBean is an interface, and the class that needs to implement this interface is an IOC container bean

  2. Init needs to be specified in initMethod when declaring beans, either as XML or as @beans

Execution time:

  1. After the BeanFactory has set all the properties, it executes the bean that implements the InitializingBean interface

  2. InitMethod is executed after InitializingBean

Modify the demo above and try again

@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 specifies init to execute... "); } @Override public void afterPropertiesSet() { log.info("======"); Log.info (" >>> InitializingBeanTest execute :: afterPropertiesSet "); log.info(" >>> BeanNames :: {}", applicationContext.getBeanDefinitionCount()); log.info("======"); } /** * Result: * ====== * >>> InitializingBeanTest Execute :: afterPropertiesSet * >>> BeanNames :: 128 * ====== * * >>> bean specifies init to execute... * /}Copy the code

InitMethod runs after InitializingBean. Why is this?

I sorted out some information, and friends in need can click to get it directly.

25 Java Interview Topics

Learning routes and materials from 0 to 1Java

Java core knowledge set

Left program cloud algorithm

Spring buckets

How is InitializingBean loaded?

By the spring loaded class AbstractAutowiredCapableBeanFactory bean source code

Looking at the invocation logic, the invokeInitMethods method is responsible for calling the bean that implements the InitializingBean interface and specifies the initMethod method

protected void invokeInitMethods(String beanName, Object bean, @nullable RootBeanDefinition MBD) throws Throwable {// Check whether the bean implements the InitializingBean interface Boolean isInitializingBean = (bean instanceof InitializingBean); if (isInitializingBean && (mbd == null || ! mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { if (logger.isTraceEnabled()) { logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'"); } / / System security processor is empty, directly executed else process, call the afterPropertiesSet method / / default is empty, so the else directly process the if (System. GetSecurityManager ()! = null) { try { AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> { ((InitializingBean) bean).afterPropertiesSet(); return null; }, getAccessControlContext()); } catch (PrivilegedActionException pae) { throw pae.getException(); }} else {// Call afterPropertiesSet((InitializingBean) bean).afterPropertiesSet(); } } if (mbd ! = null && bean.getClass() ! String initMethodName = mbd.getinitMethodName (); String mbd.getinitMethodName (); If (stringutils. hasLength(initMethodName) &&! If (stringutils. hasLength(initMethodName) &&! (isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && ! MBD. IsExternallyManagedInitMethod (initMethodName)) {/ / call initMethod specified method through reflection invokeCustomInitMethod (beanName, bean, mbd); }}}Copy the code

Spring checks to see if the bean implements the InitializingBean interface when dependency injection is complete, and calls the afterPropertiesSet method

In addition, summarize three small knowledge points for your reference:

  1. See why the InitializingBean interface precedes the initMethod method

  2. If the bean object implements the InitializingBean interface and declares initMethod with the name “afterPropertiesSet”, this call is not repeated

  3. The initMethod method is executed by reflection, whereas the InitializingBean is called directly, and you can choose how to initialize it

How to use MVC source code?

The source code for SpringMVC will not be explained much, but will be explained in the next MVC source code analysis

How does MVC use the InitializingBean initialization method feature to complete the mapping

@RestController @RequestMapping("/test") public class MvcController { @GetMapping("/say/hello/{name}") public String sayHello(@PathVariable("name") String name) { return "Hello World " + name; }}Copy the code

In order to avoid any confusion, it is used to register url routing information related to Mapping

For springmvc source is very complex, and mainly how AbstractHandlerMethodMapping converts urls HandlerMethod used

The SpringMVC HandlerMethod is springMVC exclusive, so don’t worry about it

Let’s start with the most critical wave, which is the implementation of the InitializingBean interface and the invocation of initialization logic in the afterPropertiesSet method

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

This method iterates through all the beans in the Spring IOC container, and then maintains the MAPPING of urls by qualifying beans with @Controller, @RequestMapping, and so on

protected void initHandlerMethods() { if (logger.isDebugEnabled()) { logger.debug("Looking for request mappings in application context: " + getApplicationContext()); } / / for all beans in the ioc container name String [] beanNames = (enclosing detectHandlerMethodsInAncestorContexts? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) : obtainApplicationContext().getBeanNamesForType(Object.class)); 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); @requestMapping if (beanType! = null && isHandler(beanType)) {detectHandlerMethods(beanName); } } } handlerMethodsInitialized(getHandlerMethods()); }Copy the code

“Said

The article explains in detail what an InitializingBean is and how it differs from the initMethod specified when declaring a bean through practical examples and source code

Finally, the MVC framework is used to describe how to bind url and custom Handler entity to each other through the InitializingBean interface, because this can better help us understand the actual use of the project

Due to the limited level of the author, you are welcome to give feedback and correct the wrong places in the article, thank you