The background,

We developed a third-party JAR package ourselves that we want to integrate with Spring and inject into the Spring container. Add a custom annotation @customImport (beanName=””) to all classes that need to be added to the Spring container. The value of the beanName attribute represents the name to be injected into the Spring container.

Second, implementation scheme

1, based on @ComponentScan annotation implementation

Using this scheme is relatively simple, To add our custom annotations to the Spring container, use @ComponentScan(includeFilters = {@ComponentScans.filter (value = customImport.class)}) directly. This scheme is omitted.

2, based on ImportBeanDefinitionRegistrar and FactoryBean implementation

1, implement this interface (ImportBeanDefinitionRegistrar), can be our own BeanDefinition object added to BeanDefinitionRegistry, waiting for the following Spring initializes the object.

Note:

1. We can get some properties from custom annotations and then personalize what the Bean we want to initialize looks like.

2. Implement ImportBeanDefinitionRegistrar class with @ Configuration and @ Import annotations, can when start to load the class.

2. Implementing this interface (FactoryBean) allows us to instantiate the Bean we want to build.

3, pay attention to

Maybe some people will say, I can fix things with @ComponentScan, why choose to implement based on the second method, this is not redundant? In fact, I mainly record such a train of thought. Mybatis integrated with Spring, the @mapper application is automatically added to the MapperScannerRegistrar. It should be similar to plan 2 above.

Three, implementation steps

1. Define a custom annotation, CustomImport, that indicates the class to be added to the Spring container.

  1. The CustomImport annotation can add additional attributes, such as beanName for the bean name when it is injected into the Spring container.

2. Write a class that implements the CustomImportFactoryBean and shows how to instantiate the class marked by the CustomImport annotation.

  1. Construct an object.
  2. The constructed object needs to be initialized by itself. If you need an object from Spring, you can get the ApplicationContext and get the injection.

3, write a class implements ImportBeanDefinitionRegistrar, scan all CustomImport class, and then constructs the BeanDefinition, join the Spring container.

  1. The BeanClass attribute when constructing a BeanDefinition needs to specify the class of the previous CustomImportFactoryBean.
  2. ClassPathBeanDefinitionScanner can scan the package.

4, a custom annotations EnableCustomImport, start after class to join the annotations, realize ImportBeanDefinitionRegistrar calls.

  1. Needed on this annotation@Importmark

1. Custom annotationsCustomImport

/** * Classes annotated by this annotation are automatically added to Spring management. * *@authorHuan. Fu 2021 1/4/14-9:23 am */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CustomImport {
    /** * The name of the bean injected into the Spring container */
    String beanName(a);
}
Copy the code

2, implementation,CustomImportFactoryBeanBuild the object

package com.huan.study.framewrok;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/** * factory Bean, used to build classes for CustomImport annotation annotation, how to instantiate **@authorHuan. Fu 2021 1/4/14-9:44 am */
public class CustomImportFactoryBean implements FactoryBean<Object>, ApplicationContextAware {

    privateClass<? > type;private String beanName;
    private ApplicationContext applicationContext;

    /** * Objects built here, if you need to rely on Spring beans, need to be built in by yourself, not automatically injected by default, i.e. by default@AutowiredComments are not valid */
    @Override
    public Object getObject(a) throws Exception {
        Object instance = this.type.getDeclaredConstructor().newInstance();
        applicationContext.getAutowireCapableBeanFactory().autowireBean(instance);
        return instance;
    }

    @Override
    publicClass<? > getObjectType() {return type;
    }

    publicClass<? > getType() {return type;
    }

    public void setType(Class
        type) {
        this.type = type;
    }

    public String getBeanName(a) {
        return beanName;
    }

    public void setBeanName(String beanName) {
        this.beanName = beanName;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext; }}Copy the code

3, write,ImportBeanDefinitionRegistrar

package com.huan.study.framewrok;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;

import java.util.Map;
import java.util.Optional;
import java.util.Set;

/ * * *@authorHuan. Fu 2021 1/4/14-9:25 am */
public class CustomImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar.ResourceLoaderAware.EnvironmentAware {

    private static final Logger log = LoggerFactory.getLogger(CustomImportBeanDefinitionRegistrar.class);

    private Environment environment;
    private ResourceLoader resourceLoader;

    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
        if(! annotationMetadata.hasAnnotation(EnableCustomImport.class.getName())) {return;
        }
        Map<String, Object> annotationAttributesMap = annotationMetadata.getAnnotationAttributes(EnableCustomImport.class.getName());
        AnnotationAttributes annotationAttributes = Optional.ofNullable(AnnotationAttributes.fromMap(annotationAttributesMap)).orElseGet(AnnotationAttributes::new);
        // Get the packets to scan
        String[] packages = retrievePackagesName(annotationMetadata, annotationAttributes);
        UseDefaultFilters = false, i.e. the second parameter indicates that classes annotated by @Component, @ManagedBean, and @named are not scanned
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, false, environment, resourceLoader);
        // Add a scan of our custom annotations
        scanner.addIncludeFilter(new AnnotationTypeFilter(CustomImport.class));
        / / scan packages
        for (String needScanPackage : packages) {
            Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(needScanPackage);
            try {
                registerCandidateComponents(registry, candidateComponents);
            } catch(ClassNotFoundException e) { log.error(e.getMessage(), e); }}}/** * Get the package to scan */
    private String[] retrievePackagesName(AnnotationMetadata annotationMetadata, AnnotationAttributes annotationAttributes) {
        String[] packages = annotationAttributes.getStringArray("packages");
        if (packages.length > 0) {
            return packages;
        }
        String className = annotationMetadata.getClassName();
        int lastDot = className.lastIndexOf('. ');
        return new String[]{className.substring(0, lastDot)};
    }

    /** * Register BeanDefinition */
    private void registerCandidateComponents(BeanDefinitionRegistry registry, Set<BeanDefinition> candidateComponents) throws ClassNotFoundException {
        for (BeanDefinition candidateComponent : candidateComponents) {
            if (candidateComponent instanceof AnnotatedBeanDefinition) {
                AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) candidateComponent;
                AnnotationMetadata annotationMetadata = annotatedBeanDefinition.getMetadata();
                Map<String, Object> customImportAnnotationAttributesMap = annotationMetadata.getAnnotationAttributes(CustomImport.class.getName());
                AnnotationAttributes customImportAnnotationAttributes = Optional.ofNullable(AnnotationAttributes.fromMap(customImportAnnotationAttributesMap)).orElseGet(AnnotationAttributes::new);
                String beanName = customImportAnnotationAttributes.getString("beanName"); String className = annotationMetadata.getClassName(); Class<? > clazzName = Class.forName(className); AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(CustomImportFactoryBean.class) .addPropertyValue("type", clazzName)
                        .addPropertyValue("beanName". beanName) .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE) .setRole(BeanDefinition.ROLE_INFRASTRUCTURE) .getBeanDefinition(); registry.registerBeanDefinition(beanName, beanDefinition); }}}@Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

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

4. Write @enablecustomImport

/** * Enable automatic import **@authorHuan. Fu 2021 1/4/14-9:29 am */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CustomImportBeanDefinitionRegistrar.class)
public @interface EnableCustomImport {

    String[] packages() default {};
}
Copy the code

5. Run a small case to get test results

Finish the code

1, gitee.com/huan1993/sp…

5. Reference links

1, stackoverflow.com/questions/4…