Spring注解详解之组件注册(@Configuration,@Bean,@ComponentScan)
在spring源码中,会有很多注解的运用,而且,在spring boot和spring could运用和学习中,很多时候也需要查看源码,对spring的注解的了解也是不可或缺的,就准备做个spring注解的系列,记录一些重要注解的使用,以便日后查看和学习
1.@Configuration:
@Target({ElementType.TYPE})//使用在类上
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component//是一个组件,
public @interface Configuration {
}
从上面的源码,可以看出该注解用于类上,并且是IOC容器的一个组件,注明该注解标注的类是一个配置类,类似于下面的xml配置文件的作用,该注解类的内部包含一个或多个@Bean注解的方法,这些方法会被 AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器
注意:
1. @Configuration不可以是final类型;
2. @Configuration不可以是匿名类;
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="person" class="com.ming.bean.Person">
<property name="id" value="123"></property>
<property name="name" value="zhangsan"></property>
</bean>
</beans>
2.@Bean
从下面源码,该注解使用在注解方法和注解的定义,标记在方法上,方法的返回值相当于向IOC容器中注入一个bean,返回值相当于上面xml文件中bean标签的class属性,方法的名称相当于bean标签的id属性,@bean注解中有一个name属性来指定bean的id;
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documentedpublic
@interface Bean {
}
3.举例
首先定义一个配置类
@Configuration//告知spring是一个配置类
public class MyConfig {
//向IOC容器中注册一个bean,类型为返回值的类型,id默认使用方法名子
@Bean("Person")//通过name属性来指定bean的id
public Person person(){
return new Person(12,"lisi");
}
}
测试方法
@Test
public void testAnno(){
//AnnotationConfigApplicationContext扫描配置类,指定配置类
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
//从IOC容器中通过类型获取bean的名字
String[] names = applicationContext.getBeanNamesForType(Person.class);
for (String name : names) {
System.out.println(name);
}
}
//输出结果
Person{id=12, name='lisi'}
Person
4.@ComponentScan:自动扫描和指定扫描规则
该注解的作用就是,根据定义的扫描路径, 把符合扫描规则的类装配到spring容器中 , 相当于之前xml配置文件中的 context:component-scan标签,
@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 {};
}
}
basePackages与value: 用于指定包的路径,进行扫描
basePackageClasses: 用于指定某个类的包的路径进行扫描
nameGenerator: bean的名称的生成器
useDefaultFilters: 是否开启对@Component,@Repository,@Service,@Controller的类进行检测
includeFilters: 包含的过滤条件 (注意:在使用这个时,需要将useDefaultFilters属性设置为false,才能生效默认是true)
excludeFilters: 排除的过滤条件,用法和includeFilters一样
FilterType.ANNOTATION:按照注解过滤
FilterType.ASSIGNABLE_TYPE:按照给定的类型
FilterType.ASPECTJ:使用ASPECTJ表达式
FilterType.REGEX:正则
FilterType.CUSTOM:自定义规则
创建下面的测试工程,controller,service,dao都分别加注解@Repository,@Service,@Controller,
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-76LUa35Y-1583330609745)(D:\soft\typora\笔记\spring注解详解\images\1583307278841.png)]
修改配置类,通过value属性指定扫描的包, includeFilters属性指定过滤条件(包括@Controller和@Service)
//告知spring是一个配置类
@Configuration
/*
value:指定扫描的包,
excludeFilters:Filter[]扫描时按指定的规则进行排除
includeFilters:Filter[]扫描时按指定的规则进行包含(注意:在使用这个时,需要将useDefaultFilters属性设置为false,默认是true才能生效)
*/
@ComponentScan(value ="com.ming",
useDefaultFilters=false,
//按照注解类型指定controller,按照给定类型指service
includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, Service.class},ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = {UserServiceImpl.class})))
public class MyConfig {
//向IOC容器中注册一个bean,类型为返回值的类型,id默认使用方法名子
@Bean("Person")//通过name属性来指定bean的id
public Person person(){
return new Person(12,"lisi");
}
}
测试方法
@Test
public void testAnno(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
//获取容器中bean的名字
String[] names = applicationContext.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
}
//输出结果,上面的4个是spring自己注入的组件
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
myConfig //@Configuration注解包含@Component,也是容器中的一个组件
userController
userServiceImpl
Person //controller,service,是配置类中指定包含的,person是@Bean("Person")注入的
增加自定义的过滤类MyTypeFilter,来验证 FilterType.CUSTOM:自定义规则
//自定义类型过滤器,需要继承TypeFilter接口
public class MyTypeFilter implements TypeFilter {
/*
MetadataReader:读取到正在扫描的类的信息
MetadataReaderFactory:可以获取到其他类的信息
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//获取当前类的注解信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//获取当前类的资源(类路径)
Resource resource = metadataReader.getResource();
//获取当前正在扫描类的类的信息(实现接口等)
ClassMetadata classMetadata = metadataReader.getClassMetadata();
String className = classMetadata.getClassName();
//输出扫描类的名字
System.out.println("---->"+className);
if(className.contains("service")){
return true;//不过滤类名中包含service的组件
}
return false;
}
}
验证测试类:
@Test
public void testAnno(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
String[] names = applicationContext.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
//输出结果,指定的扫描路径是com.ming,该包路径下的每一个类都会被MyTypeFilter过滤,可以看出扫描的这些类中只有service类没有被过滤掉,其他的都被过滤掉了,从IOC容器中获取不到
---->com.ming.SpringTest
---->com.ming.bean.Person
---->com.ming.config.MyTypeFilter
---->com.ming.controller.UserController
---->com.ming.dao.UserRepositoryImpl
---->com.ming.service.UserServiceImpl
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
myConfig
userServiceImpl
person