Add packages to component-scan

vasily picture vasily · Aug 18, 2014 · Viewed 8k times · Source

I have a project with a Spring container. Let's say this project is called my-common-library and uses the namespace my.common. It scans all components in this namespace, as specified in common-context.xml which looks like following

<beans ...>
    <context:component-scan base-package="my.common"/>
</beans>

Among other things this scan detects classes annotated with @MyComponent.

Now I would like to reuse this project as fully as possible. Let's say I start a new project my-client which uses in the namespace my.client. The my-client mostly consists of components annotated with @MyComponent.

In the ideal world I would just add the dependency my-common-library and all @MyComponents will be scanned and registered. The only problem is the new namespace is unknown to the original my-common-library.

One solution that I'm aware of is to add an updated common-context.xml to my-client which would look like

<beans ...>
    <context:component-scan base-package="my.common,my.client"/>
</beans>

That would certainly work, but seems quite fragile. Is there a more elegant solution maybe?

Answer

Ricardo Veguilla picture Ricardo Veguilla · Aug 20, 2014

Attempting to implement component registration from libraries via component-scan is always fragile, in my opinion.

If you are just reusing code, I recommend explicitly importing the my-common-library dependency. For example, using Java-based Spring configuration:

@Configuration
@ComponentScan("my.common") 
public class MyCommonLibraryConfig {


}

On 'my-client`:

@Configuration
@Import(MyCommonLibraryConfig.class)
@ComponentScan("my.client") 
public class MyClientConfig {

}

Since my-client always depends on my-library, it is best to define the dependency explicitly.

On the other hand, if what you actually want is to implement something like a plugin system, then you will need to use some package-based convention to discover dependencies since the component using the dependencies does not know what its dependencies until run-time.

In that case, I recommend defining a registration package name such as my.plugin, then the Spring configuration for the component that depends on the plugins just need to define component scan on my.plugin, and each plugin just need to define its @Components or its @Configurations beans in the same my.plugin package.

If you want more control, you can add a filter to the component-scan so that you only register the bean with a certain annotation. For example, assuming you define a @MyPlugin annotation:

@ComponentScan(
   basePackages = {"my.plugin"},
   includeFilters = @ComponentScan.Filter(
      value= MyPlugin.class, 
      type = FilterType.ANNOTATION
   )
)