Spring注解(@Bean、@ComponentScan、自定

2019-03-21  本文已影响0人  Godlike_4029

环境搭建

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.spring.test</groupId>
    <artifactId>spring-annotation</artifactId>
    <version>1.0-SNAPSHOT</version>
    
    <dependencies>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.3.12.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>4.3.12.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>4.3.12.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/javax.inject/javax.inject -->
    <dependency>
        <groupId>javax.inject</groupId>
        <artifactId>javax.inject</artifactId>
        <version>1</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/c3p0/c3p0 -->
    <dependency>
        <groupId>c3p0</groupId>
        <artifactId>c3p0</artifactId>
        <version>0.9.1.2</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.44</version>
    </dependency>
    </dependencies>

</project>

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

<?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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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-4.3.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">

    <bean id="person" class="com.spring.annotation.bean.Person">
        <property name="name" value="zhangsan"></property>
        <property name="age" value="20"></property>
    </bean>
</beans>

测试代码

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 包扫描

<context:component-scan base-package="com.ming" use-default-filters="false"></context:component-scan>

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<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

    Class<? extends ScopeMetadataResolver> 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 配置

 <context:component-scan base-package="com.test.ming" use-default-filters="false"></context:componentscan>

单元测试

@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:使用自定义规则

上一篇下一篇

猜你喜欢

热点阅读