Spring Filter Components From Component Scanning

When using auto component scanning, only the special marker interfaces and annotations that itself are annotated with @Component are eligible. We can modify and/or extend this behavior by applying custom filters. We can use include-filters or exclude-filters, these filters can be configured as a sub-element of the component-scan element or @ComponentScan annotation. At the moment of writing there are 5 filter types which we can use, we’ll list them below:

  • annotation com.memorynotfound.SomeAnnotation – filter by precense annotation.
  • assignable com.memorynotfound.SomeClass – filter by class/interface.
  • aspectj com.memorynotfound..*Repository+ – filter by AspectJ type expression.
  • regex com\.memorynotfound\.Default.* – filter by regex expression.
  • custom com.memorynotfound.MyTypeFilter – filter by custom implementation of TypeFilter.

Filter Components

To demonstrate the exclude filter we created a bean and annotated it with @Component, so this bean would normally be automatically picked up by spring component scanner.

package com.memorynotfound.spring.core.component;

import org.springframework.stereotype.Component;

@Component
public class ExcludeBean {

}

Next we create two beans which are not annotated with @Component or any of the other marker annotations. So this bean isn’t eligible for component scanning by the default scanner.

package com.memorynotfound.spring.core.component;

import org.springframework.beans.factory.annotation.Autowired;

public class UserService {

    @Autowired
    private UserRepository userRepository;

    public void save(){
        userRepository.save();
    }
}

Also no @Component registered.

package com.memorynotfound.spring.core.component;

public class UserRepository {

    public void save(){
        System.out.println("saving user");
    }
}

Filter Components From Component Scanning XML

The include-filter can be used to specify which beans should be included. The exclude-filter can be used to ignore beans. Both filters have a type and a expression attribute which is used to configure the filter. In this example we include all beans which have Service or Repository in their name. Note that these beans are not annotated with the @Component annotation. But they could have.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.memorynotfound">
        <context:include-filter type="regex" expression=".*Service"/>
        <context:include-filter type="regex" expression=".*Repository"/>

        <context:exclude-filter type="assignable" expression="com.memorynotfound.spring.core.component.ExcludeBean"/>
        <context:exclude-filter type="annotation" expression="org.springframework.context.annotation.Configuration"/>
    </context:component-scan>

</beans>

Filter Components From Component Scanning Java Config

Here is the same configuration as above using java configuration. We use the includeFilters and excludeFilters to add appropriate filters to component scanning. We register a filter using the @Filter.

package com.memorynotfound.spring.core.component;

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

@Configuration
@ComponentScan(basePackages = "com.memorynotfound",
        includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Service.*Repository"),
        excludeFilters = {
                @Filter(type = FilterType.ASSIGNABLE_TYPE, value = ExcludeBean.class),
                @Filter(Configuration.class)
        })
public class AppConfig {

}

First we make sure our include-filters work. We invoke the UserService#save() method to show the beans are available in the spring container. Next we try to get a reference to the ExcludeBean but this will throw a NoSuchBeanDefinitionException because no bean definition exists of type ExcludeBean in the spring container.

package com.memorynotfound.spring.core.component;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Run {

    public static void main(String... args) {
        //ApplicationContext context = new ClassPathXmlApplicationContext("app-config.xml");
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService = context.getBean(UserService.class);
        userService.save();

        // this will fail
        ExcludeBean excludeBean = context.getBean(ExcludeBean.class);
        System.out.println(excludeBean);
    }
}

Output.

saving user
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.memorynotfound.spring.core.component.ExcludeBean] is defined
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:372)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:332)
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1066)
	at com.memorynotfound.spring.core.component.Run.main(Run.java:16)

References

Download

You may also like...