spring-组件注册(一)

375 阅读6分钟

我们知道spring配置的方式有三种:

  • xml配置方式
  • JavaConfig方式
  • 隐式bean扫描,基于Java的注解,自动配置

本篇文章主要讲解的是通过JavaConfig的方式进行spring配置中的组件注册的内容学习(该篇内容仅是个人在学习spring的过程中的笔记,如果有误希望指出^_^)

一、容器注册组件基础

通过xml的形式进行配置时,我们会通过如下方式来进行spring 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"
       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">

    <bean id="person" class="com.xyr.model.Person">
        <property name="age" value="18"></property>
        <property name="name" value="xyr"></property>
    </bean>

</beans>

而通过JavaConfig的方式,最基础的配置如下:

//配置类==配置文件
@Configuration  //告诉spring这是一个配置类
public class SpringConfig {

    /**
     * 注册一个bean,类型为返回值的类型,默认方法名为id
     * 也可以在bean注解之后指定
     */
    @Bean
    public Person person() {
        return new Person(20, "xyr");
    }

}

其中的@Configuration标明了该类为一个配置文件,@Bean标明了需要注册的bean,bean的内容为下面方法对应的返回值,默认情况下,方法名即为bean的id。即:

public class TestBean {

    public static void main(String[] args) {

        //通过注解的方式获取对应的bean
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);

        String[] beanNames = applicationContext.getBeanDefinitionNames();
        for (String name : beanNames) {
            System.out.println(name);
        }
    }

}

得到的结果为:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory 

springConfig 

person

springConfig为配置类也属于bean对象,person即为定义的bean名称

当然我们也可以通过bean的name属性为其赋予名称。bean注解的基本构成如下:

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
    @AliasFor("name")
    String[] value() default {};

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

    /** @deprecated */
    @Deprecated
    Autowire autowire() default Autowire.NO;

    boolean autowireCandidate() default true;

    String initMethod() default "";

    String destroyMethod() default "(inferred)";
}

简单了解如下成员变量:

value: name属性的别名,在不需要其他属性时使用,也就是说value 就是默认值。

name: bean 的名称,或多个名称,主要的bean的名称加别名。如果未指定,则bean的名称是带注解方法的名称。如果指定了,方法的名称就会忽略,如果没有其他属性声明的话,bean的名称和别名可能通过value属性配置。

autowire : 此注解的方法表示自动装配的类型,返回一个

Autowired
类型的枚举,在bean注解中默认为不自动装配。

使用举栗

//配置类==配置文件
@Configuration  //告诉spring这是一个配置类
public class SpringConfig {

    /**
     * 注册一个bean,类型为返回值的类型,默认方法名为id
     * 也可以在bean注解之后指定
     */
    @Bean(value = "person")
    public Person person01() {
        return new Person(20, "xyr");
    }

}
public class TestBean {

    public static void main(String[] args) {

        //通过注解的方式获取对应的bean
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);

        String[] beanNames = applicationContext.getBeanDefinitionNames();
        for (String name : beanNames) {
            System.out.println(name);
        }
    }

}

得出结果:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory

springConfig

person

虽然方法名被设置为

person01,但由于在bean注解中定义了对应名称,所以输出的还是person

二、ComponentScan包扫描

和xml的配置方式中的包扫描一样

<!-- 表明要扫描的包 , 只要标注了@Controller、@Service、@Repository、@Component等spring对应的注解,且在对应的扫描包下就会被注入到bean容器当中 -->
<context:component-scan base-package="com.xyr" ></context:component-scan>

我们也可以通过JavaConfig中的注解@ComponentScan来注明要扫描的包,从而对包含@Controller、@Service、@Repository、@Component等spring对应的注解的类进行bean注入。如下:

//配置类==配置文件
@Configuration  //告诉spring这是一个配置类
//通过注解ComponentScan的方式指定扫描的包
@ComponentScan(basePackages = "com.xyr")
public class SpringConfig {
    /**
     * 注册一个bean,类型为返回值的类型,默认方法名为id
     * 也可以在bean注解之后指定
     * @return
     */
    @Bean(value = "person")
    public Person person01() {
        return new Person(20, "xyr");
    }

}

配置完成之后,运行之前TestBean的main方法,可输出如下结果:


带有spring相关bean注解的对象都进行了扫描注入。

点开ComponentScan注解类,可以看到如下内容:

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

在ComponentScan类中有两个属性:

  • includeFilter:指明只扫描某些内容
  • excludeFilter:指明不包含某些内容

其中过滤的方式可以有:

public enum FilterType {
    //根据注解过滤
    ANNOTATION,
    //根据指定类型
    ASSIGNABLE_TYPE,
    //使用ASPECTJ表达式
    ASPECTJ,
    //使用正则表达式
    REGEX,
    //使用自定义规则
    CUSTOM;

    private FilterType() {
    }
}

那么我们就根据includeFilter来举两个例子

1.根据注解过滤

//配置类==配置文件
@Configuration  //告诉spring这是一个配置类
//includeFilters 指定只扫描的对应bean,useDefaultFilters=false不使用默认的过滤方式
@ComponentScan(basePackages = "com.xyr", includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class)
}, useDefaultFilters = false)
public class SpringConfig {

    /**
     * 注册一个bean,类型为返回值的类型,默认方法名为id
     * 也可以在bean注解之后指定
     */
    @Bean(value = "person")
    public Person person01() {
        return new Person(20, "xyr");
    }

}

上述代码标明了要扫描的包,以及只额外注入具有Controller注解的bean(当然配置类中的bean肯定是会注入的)。

此时再去运行TestBean代码,可得到如下结果:


虽然还有其他spring bean注解的类,当此时只有Controller对应的bean是被额外扫描注入的

2.根据自定义规则过滤

定义一个实现了TypeFilter接口的过滤类,实现只有bean名称包含er字符串的符合过滤条件:

public class MyFilter implements TypeFilter {

    /**
     * 匹配规则
     * @param metadataReader 读取到当前正在扫描的类
     * @param metadataReaderFactory 获取到当前其他的类信息
     * @return
     * @throws IOException
     */
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        //获取到当前的类注解
        //AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        //获取到当前的类信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        //获取到当前的类资源(类路径)
        //Resource resource = metadataReader.getResource();

        String name = classMetadata.getClassName();
        //打印名称
        System.out.println(name);
        if (name.contains("er")) {
            //如果类名包含er字符串,则注入
            return true;
        }
        return false;
    }
}

在配置类中加上ComponentScan,注明过滤方式用自定义的MyFilter类:

//配置类==配置文件
@Configuration  //告诉spring这是一个配置类
@ComponentScan(basePackages = "com.xyr", includeFilters = {
       //通过自定义的过滤器,过滤需要的bean
        @ComponentScan.Filter(type = FilterType.CUSTOM, classes = MyFilter.class)
}, useDefaultFilters = false)
public class SpringConfig {

    /**
     * 注册一个bean,类型为返回值的类型,默认方法名为id
     * 也可以在bean注解之后指定
     */
    @Bean(value = "person")
    public Person person01() {
        return new Person(20, "xyr");
    }

}

那么此时运行TestBean的main方法:

//myFilter执行match方法时获取到的类名称

com.xyr.configure.SpringLifeConfig 

com.xyr.controller.PersonController 

com.xyr.dao.PersonDao 

com.xyr.model.Person 

com.xyr.service.PersonService 

com.xyr.test.TestBean 

//最终main方法打印出来的bean

org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory 

springConfig 

personController 

personDao 

myFilter 

person 

personService

另外spring关于包扫描注解,还有一个ComponentScans用于多个扫描注解的组合使用。