Spring注解(@Bean、@ComponentScan、自定义TypeFilter)

环境搭建



    4.0.0

    com.spring.test
    spring-annotation
    1.0-SNAPSHOT
    
    
    
    
        org.springframework
        spring-context
        4.3.12.RELEASE
    

    
        org.springframework
        spring-aspects
        4.3.12.RELEASE
    

    
        org.springframework
        spring-jdbc
        4.3.12.RELEASE
    

    
        junit
        junit
        4.12
        test
    
    
    
        javax.inject
        javax.inject
        1
    

    
    
        c3p0
        c3p0
        0.9.1.2
    

    
    
        mysql
        mysql-connector-java
        5.1.44
    
    


bean

package com.ming.beans;

public class Person {
     private String name;
    private int age;
    public Person() {
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

编写配置类
给容器中注册了一个bean,类型为返回值类型,id 默认使用方法名为id

@Configuration
public class MainConfig {
    /**
     * 给容器中注册了一个bean,类型为返回值类型,id 默认使用方法名为id
     * @return
     *
     * 修改bean的名字
     * 1.修改方法名
     * 2.在@Bean 注解中指定名字  @Bean("person02")
     */
    @Bean("person02")
    public Person person(){
        return new Person("10", 20);
    }
}

Spring 使用xml 注入bean




    
        
        
    

测试代码

public class MainTest {
    public static void main(String[] args){
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        Person bean = applicationContext.getBean(Person.class);
        System.out.println(bean);

        /**
         * 按照类型获取bean的名字(默认注入bean的方法名字)
         */
        String[] beanNamesForType = applicationContext.getBeanNamesForType(Person.class);

        for (String s : beanNamesForType) {
            System.out.println(s);
        }
    }
}

运行结果

Person [name=10, age=20, nickName=null]
person02
Process finished with exit code 0 

而在实际开发中会经常使用包扫描,只要标注了@Controller、@Service、@Repository,@Component 注解的类会自动加入到容器中

Spring xml 包扫描


Spring使用注解 包扫描 @ComponentScan

@ComponentScan("com.spring.annotation")
@Configuration
public class MainConfig {
    /**
     * 给容器中注册了一个bean,类型为返回值类型,id 默认使用方法名为id
     * @return
     *
     * 修改bean的名字
     * 1.修改方法名
     * 2.在@Bean 注解中指定名字  @Bean("person02")
     */
    @Bean("person02")
    public Person person(){
        return new Person("10", 20);
    }
}

1.添加bean

@Controller
public class BookController {
}
@Repository
public class BookDao {

}
@Service
public class BookService {
}

2.测试

@Test
    public void test(){
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        /**
         * 获取容器中所有bean定义的名字
         */
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }
    }

3.测试结果

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
bookController
bookDao
bookService
person02

Process finished with exit code 0

注意:mainConfig 配置类也是一个组件 因为@Configuration 注解中标有@Component

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    String value() default "";
}

@ComponentScan 包含过滤和排除过滤
ComponentScan.Filter[] includeFilters() default {}; 按照某些规则排除组件
ComponentScan.Filter[] excludeFilters() default {}; 指定扫描的时候只需要包含哪些组件

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

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

    Class[] basePackageClasses() default {};

    Class nameGenerator() default BeanNameGenerator.class;

    Class scopeResolver() default AnnotationScopeMetadataResolver.class;

    ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;

    String resourcePattern() default "**/*.class";
    /**/
    boolean useDefaultFilters() default true;

    ComponentScan.Filter[] includeFilters() default {};

    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 {};
    }
}

FilterType 指定不同的包含/排除 规则

package org.springframework.context.annotation;

public enum FilterType {
    ANNOTATION,
    ASSIGNABLE_TYPE,
    ASPECTJ,
    REGEX,
    CUSTOM;

    private FilterType() {
    }
}

例如:我们按照注解类型排除 Controller.class, Service.class, Repository.class

@ComponentScan(value = "com.spring.annotation", excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class, Repository.class})
})
@Configuration
public class MainConfig {
    /**
     * 给容器中注册了一个bean,类型为返回值类型,id 默认使用方法名为id
     * @return
     *
     * 修改bean的名字
     * 1.修改方法名
     * 2.在@Bean 注解中指定名字  @Bean("person02")
     */
    @Bean("person02")
    public Person person(){
        return new Person("10", 20);
    }
}

测试结果(容器中已经没有 BookController BookService BookServie这三个bean)

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
person02

Process finished with exit code 0

包含过滤 includeFilters 如果想要只包含 Controller 注解的bean,如下配置

注意:需要添加 useDefaultFilters = false

@ComponentScan(value = "com.spring.annotation", includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,
                classes = {Controller.class})},useDefaultFilters = false)
@Configuration
public class MainConfig {
    /**
     * 给容器中注册了一个bean,类型为返回值类型,id 默认使用方法名为id
     * @return
     *
     * 修改bean的名字
     * 1.修改方法名
     * 2.在@Bean 注解中指定名字  @Bean("person02")
     */
    @Bean("person02")
    public Person person(){
        return new Person("10", 20);
    }
}

对比Spring xml 配置

 

单元测试

@Test
    public void test(){
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        /**
         * 获取容器中所有bean定义的名字
         */
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }
    }

结果如下(可以看到只有Controller注解的bean在容器中)

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
bookController
person02

Process finished with exit code 0

我们还可以使用@ComponentScans 来指定 扫描策略
ComponentScans 注解结构 如下

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
public @interface ComponentScans {
    ComponentScan[] value();
}

可以看到其内部是一个ComponentScan[] 数组,所以我们可以在其中指定多个ComponentScan

@ComponentScans(value = {

        @ComponentScan(value = "com.spring.annotation", includeFilters = {
                @ComponentScan.Filter(type = FilterType.ANNOTATION,
                        classes = {Controller.class})},useDefaultFilters = false)

})
@Configuration
public class MainConfig {
    /**
     * 给容器中注册了一个bean,类型为返回值类型,id 默认使用方法名为id
     * @return
     *
     * 修改bean的名字
     * 1.修改方法名
     * 2.在@Bean 注解中指定名字  @Bean("person02")
     */
    @Bean("person02")
    public Person person(){
        return new Person("10", 20);
    }
}

FilterType.ASSIGNABLE_TYPE 指定不同的类型
例如,包含Controller 注解的bean 和 BookService类型的bean

@ComponentScans(value = {

        @ComponentScan(value = "com.spring.annotation", includeFilters = {
                @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class}),
                @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {BookService.class})
                
        },useDefaultFilters = false)
})
@Configuration
public class MainConfig {
    /**
     * 给容器中注册了一个bean,类型为返回值类型,id 默认使用方法名为id
     * @return
     *
     * 修改bean的名字
     * 1.修改方法名
     * 2.在@Bean 注解中指定名字  @Bean("person02")
     */
    @Bean("person02")
    public Person person(){
        return new Person("10", 20);
    }
}

FilterType.CUSTOM:使用自定义规则
1.编写MyTypeFilter 并实现 TypeFilter 接口
2.match方法中 实现自定义规则

/**
 * 自定义过滤规则
 */
public class MyTypeFilter implements TypeFilter {

    /**
     *
     * @param metadataReader
     * @param metadataReaderFactory
     * @return
     * @throws IOException
     * metadataReader:读取到的当前正在扫描的类的信息
     * metadataReaderFactory:可以获取到其他任何类信息的
     *
     *
     */

    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {

        // TODO Auto-generated method stub
        //获取当前类注解的信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        //获取当前正在扫描的类的类信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        //获取当前类资源(类的路径)
        Resource resource = metadataReader.getResource();

        String className = classMetadata.getClassName();
        System.out.println("--->"+className);
        if(className.contains("er")){
            return true;
        }
        return false;
    }
}

3.使用实例(当前扫描到的类,类名中包含er,就会注入到容器中)

@ComponentScans(value = {

        @ComponentScan(value = "com.spring.annotation", includeFilters = {
                @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class}),
                @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {BookService.class}),
                @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})

        },useDefaultFilters = false)
})
@Configuration
public class MainConfig {
    /**
     * 给容器中注册了一个bean,类型为返回值类型,id 默认使用方法名为id
     * @return
     *
     * 修改bean的名字
     * 1.修改方法名
     * 2.在@Bean 注解中指定名字  @Bean("person02")
     */
    @Bean("person02")
    public Person person(){
        return new Person("10", 20);
    }
}

小总结:
@ComponentScan value:指定要扫描的包
excludeFilters = Filter[] :指定扫描的时候按照什么规则排除那些组件
includeFilters = Filter[] :指定扫描的时候只需要包含哪些组件
FilterType.ANNOTATION:按照注解
FilterType.ASSIGNABLE_TYPE:按照给定的类型;
FilterType.ASPECTJ:使用ASPECTJ表达式
FilterType.REGEX:使用正则指定
FilterType.CUSTOM:使用自定义规则

你可能感兴趣的:(Spring注解(@Bean、@ComponentScan、自定义TypeFilter))