Spring注解驱动开发学习总结1:组件注册之@Configuration、@Bean、@ComponentScan详解
1、使用@Configuration、@Bean给容器添加组件
可以使用@Configuration、@Bean给容器添加组件,具体步骤如下:
1.1 新建maven工程:spring-annotation
如下,新建maven工程spring-annotation:
1.2 pom文件添加依赖spring核心容器依赖
Pom文件添加spring核心容器的依赖spring-context,这里选择的是4.3.12版本
1.3 添加Person类
新建com.example.bean包,包下新建一个Person类:
1.4 添加配置类MainConfig
添加配置类MainConfig。
1)MainConfig类上添加@Configuration注解,spring会将标注该注解的类自动注入到容器中。
2)给配置类中的方法添加@Bean注解。@Bean注解会给容器注册一个bean,类型为返回值的类型;id默认使用方法名作为id,也可以用name指定,比如@Bean(“person”)。
package com.example.config;
import com.example.bean.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
// 配置类就等同于以前的配置文件beans.xml
@Configuration
public class MainConfig {
// @Bean注解会给容器注册一个bean。
// 类型为返回值的类型;id默认使用方法名作为id,也可以用name指定,比如@Bean("person")
@Bean
public Person person() {
return new Person("lisi", 20);
}
}
1.5 运行main方法进行测试
添加main方法,送注解配置容器中获取Person类型的bean,以及获取它的名字:
package com.example;
import com.example.bean.Person;
import com.example.config.MainConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
Person bean = applicationContext.getBean(Person.class);
System.out.println("获取Person类型的bean为: " + bean);
String[] beanNamesForType = applicationContext.getBeanNamesForType(Person.class);
for (String name : beanNamesForType) {
System.out.println("Person bean的名字: " + name);
}
}
}
运行MainTest,可以得到结果:
2、使用@ComponentScan自动扫描组件
@ComponentScan可以标注包扫描的位置,包扫描的路径下,所有标注了@Controller, @Service, @Repository, @Component 注解的类都会被自动注册到spring容器中。
2.1 新建BookController, BookService, BookDao
新建
com/example/controller/BookController.java,
com/example/service/BookService.java,
com/example/dao/BookDao.java,
并添加对应的注解:
2.2 配置文件新增包扫描路径
配置文件新增包扫描路径:com.example,这样com.example路径下,所有标注了@Controller, @Service, @Repository, @Component 注解的类都会被自动注册到spring容器中。
2.3 测试@ComponentScan自动扫描组件功能
1)首先在pom文件中添加junit依赖,选择的版本是4.12
2)新建测试类src/test/java/com/example/test/IocTest.java,添加测试testComponentScan方法,看看刚刚添加的BookController,BookService, BookDao组件是否自动导入了容器中:
package com.example.test;
import com.example.config.MainConfig;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class IocTest {
@SuppressWarnings("resource")
@Test
public void testComponentScan() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName:beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
}
返回结果如下:
可以看到,刚刚标注了注解的BookController,BookService, BookDao, 这3个都已经自动导入了容器。
同时标注了@Configuration注解的配置类mainConfig,也自动导入了容器。
配置类mainConfig标注了@Bean方法的返回类型为Person,id为person的bean页自动导入了容器。
3、使用@ComponentScan指定扫描规则
1、使用@ComponentScan可以自动注入默认组件外,还可以自定义扫描规则。
@ComponentScan中有以下3个参数:
boolean useDefaultFilters() default true;
ComponentScan.Filter[] includeFilters() default {};
ComponentScan.Filter[] excludeFilters() default {};
1)配置excludeFilters参数,可以排除指定的组件,不导入容器;
2)配置includeFilters参数,可以只导入指定的组件到容器中,要求同时要将useDefaultFilters改为false;
2、当配置includeFilters参数时,需要填写的是ComponentScan.Filter注解类型的数组。
而ComponentScan.Filter中有一个参数type,表示要指定的组件的类型,它的参数类型是FIlterType。
@interface Filter {
FilterType type() default FilterType.ANNOTATION;
}
FIlterType可填以下5种参数。其中ANNOTATION表示指定注解,ASSIGNABLE_TYPE表示指定类型。
public enum FilterType {
ANNOTATION,
ASSIGNABLE_TYPE,
ASPECTJ,
REGEX,
CUSTOM
}
以下以只导入bookController, bookService组件进行举例,其中的type参数,选择了常用的:ANNOTATION和ASSIGNABLE_TYPE进行举例。
3.1 包扫描注解中指定扫描组件
在@ComponentScan包扫描注解中,新增includeFilters参数,该参数是ComponentScan.Filter的数组格式。
1)新增一个类型为ANNOTATION的Filter,表示指定扫描的类型是注解类型;参数value,填的是Controller.class,表示只扫描controller注解的组件。
2)新增一个类型为ASSIGNABLE_TYPE的Filter,表示类型是指定类名;参数value,填的是BookService.class,表示只扫描BookService类的组件。
同时,配置includeFilters参数时,需要将ComponentScan默认的参数useDefaultFilters改为false。
package com.example.config;
import com.example.bean.Person;
import com.example.service.BookService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
// 配置类就等同于以前的配置文件beans.xml
@Configuration
@ComponentScan(value = "com.example",
useDefaultFilters = false,
includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = BookService.class)
}
)
public class MainConfig {
// @Bean注解会给容器注册一个bean。
// 类型为返回值的类型;id默认使用方法名作为id,也可以用name指定,比如@Bean("person")
@Bean
public Person person() {
return new Person("lisi", 20);
}
}
3.2 测试@ComponentScan指定扫描规则功能
此时再运行testComponentScan,得到下图结果:
此时可以看到:
bookDao组件没有被注入容器,只有bookController,bookService注入了容器。
而mainConfig依然是通过@Configuration注解自动导入了容器;同理,person也还是通过@Bean注解自动导入了容器。
4、自定义TypeFilter指定过滤规则
1、上面提到了,FIlterType可填以下5种参数。
上一节中用到了,ANNOTATION和ASSIGNABLE_TYPE。本小节使用CUSTOM来进行自定义过滤规则。
public enum FilterType {
ANNOTATION,
ASSIGNABLE_TYPE,
ASPECTJ,
REGEX,
// Filter candidates using a given custom org.springframework.core.type.filter.TypeFilter implementation.
CUSTOM
}
通过注释可以看到,使用CUSTOM,需要提供一个自定义的org.springframework.core.type.filter.TypeFilter实现类。
因此我们首先需要构建一个自定义的TypeFIlter实现类。
2、可以先查看下TypeFIlter类,看下我们要实现什么方法?
可以看到该接口只有一个方法,这个方法就是用来判断该类是否满足过滤条件。
1)metadataReader:当前类的元信息(比如注解、类变量、类方法、类路径等)
2)metadataReaderFactory:当前类的元信息工厂,还包含父类及接口信息
public interface TypeFilter {
/**
* Determine whether this filter matches for the class described by the given metadata.
* @param metadataReader the metadata reader for the target class
* @param metadataReaderFactory a factory for obtaining metadata readers for other classes (such as superclasses and interfaces)
* @return whether this filter matches
*/
boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
throws IOException;
}
现在逻辑应该就很清楚了,我们构建一个自定义的TypeFilter实现类,然后加入到@ComponentScan.Filter参数中即可。
4.1 新建自定义的TypeFilter实现类
新建一个自定义的org.springframework.core.type.filter.TypeFilter实现类:com/example/filter/MyTypeFilter.java。
由于bookController, bookService, bookDao中,bookController和bookService的名字中都包含字符串"er",因此下面举例的过滤规则就是,如果该类的名字中包含字符串"er",那么该类就可以注入到容器中。
同时,还添加了日志,显示所有需要进入该方法进行过滤的bean的名称。
package com.example.filter;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import java.io.IOException;
public class MyTypeFilter implements TypeFilter {
@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("er")) {
System.out.println(" 当前类满足自定义过滤规则!!!");
return true;
}
return false;
}
}
4.1 配置类改为自定义过滤规则
配置类改为自定义过滤规则:@ComponentScan.Filter(type = FilterType.CUSTOM, value = MyTypeFilter.class)
package com.example.config;
import com.example.bean.Person;
import com.example.filter.MyTypeFilter;
import com.example.service.BookService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
// 配置类就等同于以前的配置文件beans.xml
@Configuration
@ComponentScan(value = "com.example",
useDefaultFilters = false,
includeFilters = {
// @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class),
// @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = BookService.class)
@ComponentScan.Filter(type = FilterType.CUSTOM, value = MyTypeFilter.class)
}
)
public class MainConfig {
// @Bean注解会给容器注册一个bean。
// 类型为返回值的类型;id默认使用方法名作为id,也可以用name指定,比如@Bean("person")
@Bean
public Person person() {
return new Person("lisi", 20);
}
}
4.3 测试自定义TypeFilter指定过滤规则的功能
测试结果如下图,可以看到bookController和bookService确实都注入了容器,而bookDao由于字符串中未包含“er”,因此不满足自定义过滤规则。
5、本篇小结
1、spring会将标注了 @Configuration 注解的类自动注入到容器中,该类就会成为一个配置类。
1.1 给配置类中的方法添加@Bean注解。@Bean注解会给容器注册一个bean,类型为返回值的类型;id默认使用方法名作为id,也可以用name指定,比如@Bean(“id名称”)。
1.2 配置类上可以添加@ComponentScan注解,该注解可以指出包扫描的位置。只要是在包扫描的路径下,所有标注了@Controller, @Service, @Repository, @Component 注解的类都会被自动注册到spring容器中。
1.2.1 @ComponentScan注解可以配置excludeFilters参数,该参数可以排除指定的组件,这些组件不导入容器;
1.2.2 @ComponentScan注解可以配置includeFilters参数,该参数只导入指定的组件到容器中,要求同时要将useDefaultFilters改为false;
1.2.3 具体自定义规律规则可以参考上面详细的步骤介绍。