The SpringBoot multi module dependency Bean cannot be injected

1. About SpringBoot automatic injection and component scanning

When Spring Boot is used at ordinary times, annotations such as @ Configuration, @ Contoller, @ Service, @ Component, etc. are often used. The classes added with these annotations will be automatically managed by the Spring container when Spring Boot is started. The function of Component scanning is to assemble the classes that meet the scanning rules into the Spring container according to the defined scanning path when SpringBoot is started.

2. @ ComponentScan in springboot

This paper briefly introduces the basic functions of @ ComponentScan. This annotation provides us with some customizable configuration properties. Let's take a look at the @ ComponentScan annotation source code:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
  	// Specifies the location of the scanned package (the same as the basePackages attribute). It can be a single path or an array of scanned paths
    @AliasFor("basePackages")
    String[] value() default {};
	// Specify the location of the scanning package (same as: value attribute)
    @AliasFor("value")
    String[] basePackages() default {};
	// Specifies the specific scanned class
    Class<?>[] basePackageClasses() default {};
	// Generator for the name of the bean	
    Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

    Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;

    ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
	// Control the class files that meet the component detection conditions. The default is * * / *. Class under package scanning
    String resourcePattern() default "**/*.class";
    // Enable detection of @ Component, @ Repository, @ Service, @ Controller classes
    boolean useDefaultFilters() default true;
    // Filter conditions included
    // 1. FilterType.ANNOTATION:  			 Filter by annotation
    // 2. FilterType.ASSIGNABLE_TYPE:  	     According to the given type
    // 3. FilterType.ASPECTJ:  				 Using AspectJ expressions
    // 4. FilterType.REGEX:  				 regular
    // 5. FilterType.CUSTOM:  				 Custom rules
    ComponentScan.Filter[] includeFilters() default {};
    // The usage of excluded filter conditions is the same as that of includeFilters
    ComponentScan.Filter[] excludeFilters() default {};

    boolean lazyInit() default false;

    @Retention(RetentionPolicy.RUNTIME)
    @Target({})
    public @interface Filter {
        FilterType type() default FilterType.ANNOTATION;
        @AliasFor("classes")
        Class<?>[] value() default {};
        @AliasFor("value")
        Class<?>[] classes() default {};
        String[] pattern() default {};
    }
}

To summarize, the common methods of @ ComponentScan are as follows:

  • Specify the scanning range by using the value and basePackages attributes;
  • Classes with @ Controller, @ Service, @ Repository, @ Component annotations under the customized scanning path are added to the Spring container;
  • Add classes without the above annotations under the scanning path through includeFilters and add them to the Spring container;
  • Filter out classes that do not need to be added to the Spring container through excludeFilters;
  • The annotation method of @ Component annotation is customized.

3. SpringBootApplication in springboot

After the SpringBoot project is created, the @ SpringBootApplication annotation will be added to the default startup class. This annotation helps us turn on some automatic configuration functions by default, such as Java based Spring configuration, component scanning, especially the automatic configuration function for enabling SpringBoot.

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration                    // Allow automatic configuration
@ComponentScan(
    excludeFilters = {@Filter(              // Define exclusion rules
    type = FilterType.CUSTOM,               // In a customized way
    classes = {TypeExcludeFilter.class}     // Custom implementation logic
), @Filter(                                 // ditto
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
    
    // Add exclude rule for @ EnableAutoConfiguration
    @AliasFor(
        annotation = EnableAutoConfiguration.class,
        attribute = "exclude"
    )
    Class<?>[] exclude() default {};
    
    // Add excludeName rule for @ EnableAutoConfiguration
    @AliasFor(
        annotation = EnableAutoConfiguration.class,
        attribute = "excludeName"
    )
    String[] excludeName() default {};
    
    // Add basePackages rule for @ ComponentScan
    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackages"
    )
    String[] scanBasePackages() default {};

    // Add basepackageclass rule for @ ComponentScan
    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackageClasses"
    )
    Class<?>[] scanBasePackageClasses() default {};
}

As can be seen from the source code above, @ SpringBootApplication is a composite annotation, which is equivalent to using a @ SpringBootApplication instead of @ SpringBootConfiguration, @ EnableAutoConfiguration, @ ComponentScan.

Note: this note is provided from SpringBoot 1.2, which means that if you are running a lower version and need these functions, you need to manually add @ Configuration, @ componentscan and @ EnableAutoConfiguration.

Then, there may be such a problem. I just used an @ SpringBootApplication annotation, but how can I customize the properties of @ ComponentScan?

Of course, the Spring team has well solved this problem by adding the @ AliasFor annotation to the attributes in the @ SpringBootApplication annotation class, so as to customize the attributes of the corresponding annotation by customizing the attributes in @ SpringBootApplication.

@AliasFor(
    annotation = ComponentScan.class,
    attribute = "basePackages"
)
String[] scanBasePackages() default {};

This code is the implementation. Through the attribute scanBasePackages of @ SpringBootApplication, you can customize the attribute basePackages in @ ComponentScan.

@AliasFor
In Spring annotations, it is often found that different attributes of many annotations play the same role, such as the value attribute and basePackages attribute of @ ComponentScan. Therefore, some basic restrictions need to be made when using. For example, the values of value and basePackages cannot conflict. For example, if you set the value or the value of basePackages attribute arbitrarily, you can obtain the value through another attribute, and so on. To handle these situations in a unified way, Spring created the @ AliasFor tag.

4. Multi module injection example 1

The project entry file is in the sub project security demo, and the package location of the entry class is: com.github.jdkong.security. That is, without any configuration, this project will only scan the files under the current package path and its sub paths, and inject qualified objects into the container for management.

Take another look at the package path location where the Configuration file is located: com.github.jdkong.browser.config. It can be seen that this package path is not within the path range of project scanning. As a result, the Configuration class defined by us will not work for our project even if @ Configuration is added.

You can simply solve this problem by slightly modifying the project annotation and formulating the scope of the scanning package. As follows:

@SpringBootApplication(scanBasePackages="com.github.jdkong")
public class SecurityApplication {

    public static void main(String[] args) {
        SpringApplication.run(SecurityApplication.class,args);
    }
}

5. Multi module injection example 2

Automatic Service injection in SpringBoot is very convenient, for example:

Service.class(Interface class)

ServiceImpl.class(Implementation class)

Controller.class((use class)

Let's use the above three classes to describe automatic injection:

Single item: @ Service is added to the ServiceImpl header and @ Autowired is added to the Service object in the Controller.

Multi module project: three (types) of classes are under three modules:

moduleA : Service.class(com.example.moduleA )

moduleB : ServiceImpl.class ( com.example.moduleB )

moduleC : Controller.class ( com.example.moduleC )

At this time, B depends on A and C depends on B. add dependencies in pom.xml.

How to automatically inject?

1. Interface, implementation and use classes, with unchanged posture, can be written in a single project mode;

2. Modify in the Application startup class of moduleC!

Because both Service and ServiceImpl are under the com.example package (C may not be present), they can be regarded as the same scan process; So the solution:

@SpringBootApplictaion(scanBasePackages="com.example")
perhaps
@ComponentScan(value = "com.example")

If you write scanbasepackages = {"com. Example. ModuleA, com. Example. Moduleb"}, it will fail;

6. Reference documents

Spring Boot multi module injection failed to access the Bean in the jar package

Solve the problem that springboot multi module injection (@ Bean, @ Service, @ Component) cannot be accessed

Keywords: Java Spring Spring Boot

Added by chrisdee on Tue, 30 Nov 2021 18:47:58 +0200