Implementation of a custom bean register from 0 to 1

Time:2020-7-31

Implementation of a custom bean register from 0 to 1

Implementation of a custom bean register from 0 to 1


We know that in spring, we can use the@Component@Service, @RepositoryDecorate a class and register it as a bean through automatic scanning; you can also use the@BeanIn addition to these methods, are there any other ways to declare a class as a bean?

Can we customize an annotation, and then actively declare the annotation decorated class as a bean to register with the spring container, so as to achieve the similar@ComponentWhat about the effect?

Next, this article will introduce how to use theImportBeanDefinitionRegistrarThe main knowledge points are as follows:

  • ImportBeanDefinitionRegistrarThe core class of bean registration
  • @ImportImport configuration
  • ClassPathBeanDefinitionScanner

<!– more –>

1. Custom bean registry

Although our goal is relatively clear, but suddenly let us achieve such a thing, it is really a bit at a loss. Where should we start?

0. Looking for a “salute” object

If you have read my previous blog about spring boot combined with Java Web three swordsmen (filter, servlet, listener), you should remember an important knowledge point:

  • @WebListener, @WebServlet, @WebFilterThese three annotations belong to the servlet 3 + specification
  • In the springboot project, if you need the above annotation to take effect, you need to add annotation on the startup class@ServletComponentScan

When I saw the above one, I was inspired (when I wrote the above blog, I took a special look at the logic of the notes at the back). Haha, I feel that I have found a way to success

since@WebXxxAnnotations are not native spring support annotations, so annotations that make them work@ServletComponentScanIt’s very important. Obviously, it serves as a bridge, and then we can pay homage to it

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ServletComponentScanRegistrar.class)
public @interface ServletComponentScan {
    @AliasFor("basePackages")
    String[] value() default {};

    @AliasFor("value")
    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};
}

The definition of annotation is relatively simple. Needless to say, it must beServletComponentScanRegistrarNow, take another look

Implementation of a custom bean register from 0 to 1

(different versions of springboot may have different implementation classes. The above source code is intercepted from the package of spring boot 2.1.2.release)

1. Preparation

We have found the object of homage. Next, we will start some preparations before the formal realization. First, we will exemplify the target

  • All classes have custom annotations@MetaClass, will be registered with the spring container as a normal bean object

Then there is the key case to test and verify whether it works

  • Without external dependence@MetaCan the class be recognized by spring
  • @MetaClass can be used by otherbean or @MetaClass through@Autowiredintroduce
  • @MetaCan classes normally rely on normalbean@Metaclass

2. Start to implement

a. @ meta annotation definition

similar@ComponentWe can simplify the function of annotation

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Meta {
}

b. @ metacomponentscan annotation

This note and@ServletComponentScanThe function is similar, mainly used to loadImportBeanDefinitionRegistrarImplementation class, which is the core class that defines beans

The implementation is as follows

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({MetaAutoConfigureRegistrar.class})
public @interface MetaComponentScan {
    @AliasFor("basePackages") String[] value() default {};

    @AliasFor("value") String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};
}

First, ignore the value of import for the time being, and take a look at the annotationbasePackagesandbasePackageClasses

We know@ComponentScanIs used to specify which classes in the package path can enable annotation scanning;MetaComponentScanThe main functions of several members are the same as above;

  • When a value is specified, it is mainly loaded under these package paths@MetaThe class of annotation;
  • If all are the default values (i.e. empty), all the contents in the package path corresponding to the class of this annotation are scanned@MetaClass of

c. MetaAutoConfigureRegistrar

Let’s move on to our core class, which mainly inherits fromImportBeanDefinitionRegistrar, bean defines the register, and its core method is

void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}

There are two parameters. The first parameter, annotation metadata, is mostly used to obtain annotation properties; the second is bean definition register. When we learn dynamic registration of beans (for details, please refer to: – 181013 springboot basic chapter bean dynamic registration), we know that beandefinitionregistry can be used to register beans, because our goal here is to register all the@MetaAnnotated classes

Natural thoughts

  • Scan all classes to see if there are any@MetaNote, if any, register it manually through registry

However, before we do it, we should stop a little bit; scan all classes to see if there is any annotation. This operation should belong to the more common case (why?) in spring, and there should be some auxiliary classes for us to use

Continue to show respect to the people,ServletComponentScanRegistrarClass is mainly registrationservletComponentRegisteringPostProcessorSo we move to the details of the latterorg.springframework.boot.web.servlet.ServletComponentRegisteringPostProcessor#createComponentProvider)

Implementation of a custom bean register from 0 to 1

At this point, our thinking has opened up again, and we can use the help ofClassPathScanningCandidateComponentProviderTo implement bean registration


The above paragraph belongs to foreplay, put it in your mind and quickly pass it. Next, enter the text;

The first is to create aClassPathScanningCandidateComponentProviderTo register aAnnotationTypeFilterTo ensure that the filter gets all@MetaAnnotated classes

private static class MetaBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {
    public MetaBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
            Environment environment, ResourceLoader resourceLoader) {
        super(registry, useDefaultFilters, environment, resourceLoader);
        registerFilters();
    }

    protected void registerFilters() {
        addIncludeFilter(new AnnotationTypeFilter(Meta.class));
    }
}

Then get the scanned package path by parsing the previously definedMetaComponentScanTo get the

private Set<String> getPackagesToScan(AnnotationMetadata metadata) {
    AnnotationAttributes attributes =
            AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(MetaComponentScan.class.getName()));
    String[] basePackages = attributes.getStringArray("basePackages");
    Class<?>[] basePackageClasses = attributes.getClassArray("basePackageClasses");

    Set<String> packagesToScan = new LinkedHashSet<>(Arrays.asList(basePackages));
    for (Class clz : basePackageClasses) {
        packagesToScan.add(ClassUtils.getPackageName(clz));
    }

    if (packagesToScan.isEmpty()) {
        packagesToScan.add(ClassUtils.getPackageName(metadata.getClassName()));
    }

    return packagesToScan;
}

So the complete implementation of metaautoconfigureregistrator is available

public class MetaAutoConfigureRegistrar
        implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {

    private ResourceLoader resourceLoader;

    private Environment environment;

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        MetaBeanDefinitionScanner scanner =
                new MetaBeanDefinitionScanner(registry, this.environment, this.resourceLoader);
        Set<String> packagesToScan = this.getPackagesToScan(importingClassMetadata);
        scanner.scan(packagesToScan.toArray(new String[]{}));
    }

    private static class MetaBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {
      //I'll leave it out
    }

    private Set<String> getPackagesToScan(AnnotationMetadata metadata) {
      //Reference to the preceding, this is omitted
    }
}

2. Test and summary

The above implementation seems very simple now (two annotation definitions, one core class, and no more complicated); next, we need to verify whether this works

1. Case0 meta annotation class

If it is recognized as a bean by spring, the constructor is called

@Meta
public class DemoBean1 {
    public  DemoBean1() {
        System.out.println("DemoBean1 register!");
    }
}

2. Case1 meat annotation class, depending on bean

Define a normal bean object

@Component
public class NormalBean {
    public NormalBean() {
        System.out.println("normal bean");
    }
}

Then define a meta decorated class that depends on normalbean

@Meta
public class DependBean {
    public DependBean(NormalBean normalBean) {
        System.out.println("depend bean! " + normalBean);
    }
}

3. Case2 bean depends on meta annotation class

@Component
public class ABean {
    public ABean(DemoBean1 demoBean1) {
        System.out.println("a bean : " + demoBean1);
    }
}

4. Testing

Start the class. Note that we need to add our custom@MetaComponentScanannotation

@SpringBootApplication
@MetaComponentScan
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}

Execute output results

Implementation of a custom bean register from 0 to 1

5. Summary

This paper mainly introduces how to pass theImportBeanDefinitionRegistrarTo achieve the whole process of custom bean register, including how to “salute” the existing code logic for novice to achieve our goal “cleverly”

2. Others

0. Project

  • 181013 springboot basic article bean dynamic registration
  • Project: https://github.com/liuyueyi/spring-boot-demo
  • Project: https://github.com/liuyueyi/spring-boot-demo/tree/master/spring-case/006-importbean

1. A gray blog

It’s not as good as a letter. The above contents are all from one family. Due to limited personal ability, there are inevitably omissions and mistakes. If you find a bug or have better suggestions, you are welcome to criticize and correct, and thank you

The following is a gray personal blog, recording all the blog articles in study and work. Welcome to visit

  • Personal blog https://blog.hhui.top
  • A grey blog spring blog http://spring.hhui.top

Implementation of a custom bean register from 0 to 1

Recommended Today

The use of springboot Ajax

Ajax overview What is Ajax? data Ajax application scenarios? project Commodity system. Evaluation system. Map system. ….. Ajax can only send and retrieve the necessary data to the server, and use JavaScript to process the response from the server on the client side. data But Ajax technology also has disadvantages, the biggest disadvantage is that […]