A preface.

Played a National Day, feel so tired ~~ must write an article to have a rest! Through the previous several source code series after learning, it is necessary to carry out a stage summary. Based on the previous study, we have been very clear how Spring based on Spring BeanFactoryPostProcessor and BeanDefinitionRegistoryPostProcessor to intervene in the BeanFactory extension principle; @Configuration/ @import /@ComponentScan/@Bean annotation parsing principle. Along the way, you also got a glimpse into how Spring implements AOP based on dynamic proxy technology. Anyone who has used Spring technology should know that there are many features in Spring that use @enablexxx to implement specific capabilities, such as @enablecache. These amazing technologies are based on Spring’s basic capabilities. This article will be based on the current grasp of these knowledge, their own hand to a simple @enableleon to consolidate the source code learning results and application.

2. Implement

Design goals

Make all methods of a specified class print Hello Leon! By enabling the @enableleon annotation on the configuration class. .

Annotations to realize

package com.leon.funddatahouse.spring;

import org.springframework.context.annotation.Import;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/ * * *@author created by leon on 2020-10-06
 * @sinceV1.0 * /
@Retention(RetentionPolicy.RUNTIME)
@Import(MyImportSeletor.class)
public @interface EnableLeon {

}
Copy the code

The application of @ Import

@import (myImportseletor.class) MyImportSeletor class:

package com.leon.funddatahouse.spring;

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

import java.util.Set;

/ * * *@author created by leon on 2020-10-06
 * @sinceV1.0 * /
public class MyImportSeletor implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
    	// Get all the current annotation names
        Set<String> annotationTypes = importingClassMetadata.getAnnotationTypes();
        // Determine whether the @enableleon annotation is included
        boolean contains = annotationTypes.contains("com.leon.funddatahouse.spring.EnableLeon");
        // Returns an array of fully qualified names for the specified class if it does. Here I just specify MyDao.
        if (contains) {
            return new String[]{MyDao.class.getName()};
        } else {
            return null; }}}Copy the code

Application of rear processor

The backend handler can intervene in the Bean instantiation process, so we define a custom backend handler to enhance the Bean we specify (that is, the MyDao class specified above). The enhanced approach is, of course, based on dynamic proxies.

package com.leon.funddatahouse.spring;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

import java.lang.reflect.Proxy;

/ * * *@author created by leon on 2020-10-06
 * @sinceThe version number * /
@Component
public class HelloLeonBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    	// Determine whether the current bean is MyDao class. If so, use JDK dynamic proxy to enhance the bean (CGLIB dynamic proxy is also possible).
        if (bean.getClass().getName().equals(MyDao.class.getName())) {
            return Proxy.newProxyInstance(bean.getClass().getClassLoader(), new Class[]{Dao.class}, new HelloLeonInvokeHandler(bean));
        }
        returnbean; }}Copy the code

The JDK dynamic proxy HelloLeonInvokeHandler code is as follows:

package com.leon.funddatahouse.spring;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/ * * *@author created by leon on 2020-10-06
 * @sinceThe version number * /
public class HelloLeonInvokeHandler implements InvocationHandler {

    private Object obj;

    public HelloLeonInvokeHandler(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    	// Print Hello Leon before the target method executes
        System.out.println("Hello Leon!");
        returnmethod.invoke(obj, args); }}Copy the code

MyDao code

Since we are based on JDK dynamic proxies, we have to implement interfaces. The specific code is as follows:

package com.leon.funddatahouse.spring;

/ * * *@author created by leon on 2020-10-06
 * @sinceThe version number * /
public interface Dao {
    void test(a);
}
Copy the code
package com.leon.funddatahouse.spring;

/ * * *@author created by leon on 2020-10-06
 * @sinceThe version number * /
public class MyDao implements Dao {

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

The configuration class

Enable the @enableleon annotation on the configuration class.

package com.leon.funddatahouse.spring;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/ * * *@author created by leon on 2020-10-06
 * @sinceThe version number * /
@Configuration
@ComponentScan("com.leon.funddatahouse.spring")
@EnableLeon
public class Config {}Copy the code

Start the class

package com.leon.funddatahouse.spring;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/ * * *@author created by leon on 2020-10-06
 * @sinceThe version number * /
public class MyApplication {
    public static void main(String[] args) {
    	// Start the Spring container based on the configuration class
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        // Get the bean. Since the @enableleon annotation is enabled, this must be a proxy classDao indexDao = context.getBean(Dao.class); indexDao.test(); }}Copy the code

The execution result

As you can see, the @enableleon annotation works.

3. Technical summary

This example is simple, but it uses a post-processor, the application of @Configuration and @import Configuration annotations, and the application of dynamic proxies. Behind each of these application points is a powerful and complex source code. Being able to look at the dots and see the principles behind them is the goal of our study. This is an example, but if you can understand this example, you will find that this example and their implementation ideas are very similar when you study other components such as AOP, Mybatis-Spring, etc. In real engineering, we can even implement our own components and integrate them into the Spring framework.