目录
- 前言
- 1. @Configuration和@Bean
- 1.1 大概介绍
- 1.2 测试
- 2. @ComponentScan+@Component
- 2.1 一般用法
- 2.2 排除扫描一些类
- 2.3 只扫描某些类
- 2.4 @ComponentScans
- 2.5 自定义扫描规则
- 2.5.1 ASSIGNABLE_TYPE
- 2.5.2 CUSTOM,自定义类型扫描
- 3. Scope注解
- 3.1 测试单实例bean
- 3.2 测试多实例bean(懒加载)
- 4. @Conditional
- 4.1 放在方法上
- 4.2 放在类上
- 5. Import相关
- 5.1 import直接导入
- 5.2 importSelector
- 5.3 ImportBeanDefinitionRegistrar
- 6. FactoryBean
前言
最近学了一些spring注解的用法,记录一下
1. @Configuration和@Bean
1.1 大概介绍
@Configuration: 告诉spring当前类是一个配置类
@Bean:在配置类中使用添加组件
使用@Bean添加组件的时候,id默认是方法名。我们也可以自己在注解中设置id,比如@Bean(“person01”)
1.2 测试
配置类:
@Configuration
public class AddBeanConfig {
@Bean
public People people01(){
People people = new People();
people.setAge("18");
people.setName("aaa");
return people;
}
@Bean("people")
public People people02(){
People people = new People();
people.setAge("19");
people.setName("bbb");
return people;
}
}
测试类:
@Test
public void test(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AddBeanConfig.class);
People people01 = (People)applicationContext.getBean("people01");
People people = (People)applicationContext.getBean("people");
System.out.println(people01);
System.out.println(people);
//Person{name='aaa', age='18'}
//Person{name='bbb', age='19'}
}
2. @ComponentScan+@Component
2.1 一般用法
@ComponentScan(“要扫描的包名”),比如@ComponentScan(“com.test.bean”)就是扫面com.test.bean包,被扫描的类要加上@Component
@Component
public class Cat {
}
@Component
public class Dog {
}
@ComponentScan("com.test.bean")
@Configuration
public class AddBeanConfig {
@Bean
public People people01(){
People people = new People();
people.setAge("18");
people.setName("aaa");
return people;
}
@Bean("people")
public People people02(){
People people = new People();
people.setAge("19");
people.setName("bbb");
return people;
}
}
@Test
public void test02(){
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
//包含了下面两个
//cat
//dog
}
}
2.2 排除扫描一些类
在@ComponentScan注解内部有下面一段源码,提供了一个FIlter可以给我们用来包含或者排除某些组件,我们可以在Filter中写上自己要排除扫描的注解的类型,比如Controller,Service等等
/**
* Declares the type filter to be used as an {@linkplain ComponentScan#includeFilters
* include filter} or {@linkplain ComponentScan#excludeFilters exclude filter}.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({})
@interface Filter {
/**
* The type of filter to use.
* <p>Default is {@link FilterType#ANNOTATION}.
* @see #classes
* @see #pattern
*/
FilterType type() default FilterType.ANNOTATION;
@AliasFor("classes")
Class<?>[] value() default {};
@AliasFor("value")
Class<?>[] classes() default {};
String[] pattern() default {};
}
//测试:排除扫描Controller和Service
//配置类
@ComponentScan(value = "com.test",excludeFilters =
{@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})}
)
@Configuration
public class AddBeanConfig {
}
//其他类
@Controller
public class UserController {
}
@Service
public class UserService {
}
@Repository
public class UserDao {
}
测试结果:
@Test
public void test02(){
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
//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
//addBeanConfig
//userDao
}
2.3 只扫描某些类
includeFilters = Filter[],指定按照某些规则只包含哪些包,注意一定要加上useDefaultFilters = false,不使用默认的过滤规则
//测试只扫描Controller注解
//配置类
@ComponentScan(value = "com.test",includeFilters =
{@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})}
,useDefaultFilters = false
)
@Configuration
public class AddBeanConfig {
}
//其他类
@Controller
public class UserController {
}
@Service
public class UserService {
}
@Repository
public class UserDao {
}
测试结果:
@Test
public void test02(){
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
//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
//addBeanConfig
//userController
}
2.4 @ComponentScans
ComponentScans:可以在里面指定多个ComponentScan的规则
在ComponentScan的源码中,可以看到有一个@Repeatable(ComponentScans.class),也就是说,我们可以使用@ComponentScans注解重复添加ComponentScan
@Repeatable(ComponentScans.class)
public @interface ComponentScan {}
示例:
@ComponentScans(value = {
@ComponentScan(value = "com.test", includeFilters = {
@ComponentScan.Filter(type= FilterType.ANNOTATION, classes={Controller.class})
}, useDefaultFilters = false)
})
2.5 自定义扫描规则
type= FilterType.ANNOTATION只是其中的一个,意思就是根据注解类型进行过滤。同时,spring还为我们提供了其他的一些规则。在这里我们测试ASSIGNABLE_TYPE和CUSTOM。
public enum FilterType {
//注解类型
ANNOTATION,
//按照给定的类型
ASSIGNABLE_TYPE,
//使用ASPECTJ表达式
ASPECTJ,
//使用正则表达式
REGEX,
//自定义规则
CUSTOM
}
2.5.1 ASSIGNABLE_TYPE
按照给定的类的类型进行过滤,比如我要单独扫描某一个类或者单独排除某一个类,就可以用这种方法
//这次把注解换成type = FilterType.ASSIGNABLE_TYPE,class就填我们要包含的类的类型,比如我们要只想扫描UserDao.class
@ComponentScan(value = "com.test",includeFilters =
{@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {UserDao.class})}
,useDefaultFilters = false
)
测试结果:
@Test
public void test02(){
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
//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
//addBeanConfig
//userDao
}
2.5.2 CUSTOM,自定义类型扫描
使用@ComponentScan.Filter(type = FilterType.CUSTOM),我们可以写一个类实现TypeFilter 接口,在里面编写我们的自定义扫描代码逻辑。
//这里写逻辑,默认过滤dao的组件
public class MyFilter implements TypeFilter {
/**
* @param metadataReader 读取当前正在扫描的类的信息
* @param metadataReaderFactory 可以获取到其他任何类的信息
* @return
* @throws IOException
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//获取当前类注解信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//获取当前正在扫描的类信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//获取当前类的资源信息
Resource resource = metadataReader.getResource();
//获取当前类的类名
String className = classMetadata.getClassName();
//如果包含dao,就返回true
if(className.contains("Dao")){
return true;
}
return false;
}
}
//这里根据我们的需求,只扫描MyFilter中的逻辑指定的组件类型,就是只扫描类名含有Dao的
@ComponentScan(value = "com.test",includeFilters =
{@ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyFilter.class})}
,useDefaultFilters = false
)
@Configuration
public class AddBeanConfig {
}
@Test
public void test02(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AddBeanConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
//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
//addBeanConfig
//userDao
}
3. Scope注解
这其实不算是一种添加bean的方法,而是指定bean的类型的方法,比如指定bean是单实例还是多实例…
Scope的取值默认有四种,默认是单实例
/**
--- prototype(多实例,IOC容器并不会在启动就创建,而是等getBean后才创建)
--- singleton(单实例,默认这个,在ioc容器启动的时候会调用方法创建对象在IOC容器中,以后每次获取就回到IOC容器中拿)
--- request(同一个请求创建一个实例,同一次请求只有一个实例bean)
--- session(同一个session创建一个实例,同一个session只有一个实例bean)
*/
@AliasFor("value")
String scopeName() default "";
我们当前默认是单实例bean的,在IOC容器启动的时候就会调用方法创建bean,以后每次获取都是直接从IOC容器中拿。
3.1 测试单实例bean
@ComponentScan
public class Tree {
public Tree() {
System.out.println("Tree 被创建了");
}
}
@Configuration
public class AddBeanConfig {
//此时没有加Scope,默认是单实例的
@Bean
public Tree tree(){
return new Tree();
}
}
测试类:(测试bean的创建和销毁)
@Test
public void test03(){
//创建启动IOC容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AddBeanConfig.class);
System.out.println("容器启动完成");
Tree tree1 = (Tree)applicationContext.getBean("tree");
Tree tree2 = (Tree)applicationContext.getBean("tree");
System.out.println(tree1 == tree2);
//true
applicationContext.close();
}
结果:
结论: 可以看到,我们在容器创建完成之前,就已经完成bean的创建了,在多次getBean的时候,获取到的是同一个bean。
3.2 测试多实例bean(懒加载)
@ComponentScan
public class Tree {
public Tree() {
System.out.println("Tree 被创建了");
}
}
@Configuration
public class AddBeanConfig {
//加了prototype,表示多实例
@Bean
@Scope("prototype")
public Tree tree(){
return new Tree();
}
}
测试类:(测试bean的创建和销毁)
@Test
public void test03(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AddBeanConfig.class);
System.out.println("容器创建完成");
Tree tree1 = (Tree)applicationContext.getBean("tree");
Tree tree2 = (Tree)applicationContext.getBean("tree");
System.out.println(tree1 == tree2);
applicationContext.close();
}
结论:Tree被创建了2次,而且每次都是在容器创建完成后才创建tree的,最终也能看出来这两个tree不是同一个tree。
4. @Conditional
按照条件给容器添加bean,按照一定条件给容器中添加条件,满足需求才添加。
- 放在类上表示满足当前条件类中的bean才会加载
- 放在方法上表示满足当前条件方法才会生效
- 用法:实现Condition接口,重写matches方法
4.1 放在方法上
测试:根据不同的系统注册不同的bean
Condition 接口:
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//获取到当前的环境
Environment environment = context.getEnvironment();
//获取操作系统的名字
String property = environment.getProperty("os.name");
if(property.contains("linux")){
return true;
}
return false;
}
}
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//获取到当前的环境
Environment environment = context.getEnvironment();
//获取操作系统的名字
String property = environment.getProperty("os.name");
if(property.contains("Windows")){
return true;
}
return false;
}
}
Config:
@Configuration
public class AddBeanConfig {
@Conditional({LinuxCondition.class})
@Bean
public People linux(){
return new People("linux", "18");
}
@Conditional({WindowsCondition.class})
@Bean
public People window(){
return new People("window", "18");
}
}
test
```java
@Test
public void test05(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AddBeanConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
//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
//addBeanConfig
//window
}
当前系统是window,所以注册了window组件,没有注册linux组件
4.2 放在类上
@Configuration
@ComponentScan("com.test")
public class AddBeanConfig {
@Conditional({LinuxCondition.class})
@Bean
public People linux(){
return new People("linux", "18");
}
@Conditional({WindowsCondition.class})
@Bean
public People window(){
return new People("window", "18");
}
}
//选择放在UserService 类上
@Conditional({LinuxCondition.class})
@Service
public class UserService {
}
测试结果:可以看到,由于我们不是linux系统,所以不会出现UserService的组件
@Test
public void test05(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AddBeanConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
//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
//addBeanConfig
//userController
//userDao
//window
}
5. Import相关
源码:其实就是要导入哪一个类就写哪一个
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
Class<?>[] value();
}
5.1 import直接导入
直接在config类上面加上Import哪一个类就行,注意一点,import导入的bean的id是全类名
@Configuration
@Import({People.class})
public class AddBeanConfig {
}
测试:
//People被成功导入
@Test
public void test06(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AddBeanConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
//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
//addBeanConfig
//com.test.bean.People
}
5.2 importSelector
除了第一种直接导入,import还为我们提供了其他的导入方法
用法就是:继承ImportSelector 接口,在selectImports里面返回全类名数组,注意要返回全类名
public class MyImportSeletor implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.test.bean.Cat", "com.test.bean.Dog"};
}
}
接着在config类上面加上自己的importSelector类就可以了
@Configuration
@Import({People.class, MyImportSeletor.class})
public class AddBeanConfig {
}
测试结果:
@Test
public void test06(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AddBeanConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
//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
//addBeanConfig
//com.test.bean.People
//com.test.bean.Cat
//com.test.bean.Dog
}
5.3 ImportBeanDefinitionRegistrar
在这个方法中,我们可以直接注册bean,比如满足一定条件就注册哪个bean等等用途。使用的方法是继承ImportBeanDefinitionRegistrar 接口,在registerBeanDefinitions里面注册bean
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* @param importingClassMetadata 当前类的信息
* @param registry 注册类
* 把所有需要添加到容器中的bean,调用registry.registerBeanDefinition来手动注册
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//指定bean信息
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Tree.class);
//注册bean
//参数1:bean名字 参数2:bean信息
registry.registerBeanDefinition("myTree", rootBeanDefinition);
}
}
加入到import中
@Configuration
@Import({People.class, MyImportSeletor.class, MyImportBeanDefinitionRegistrar.class})
public class AddBeanConfig {
}
测试结果:
6. FactoryBean
- 使用spring提供的FactoryBean(工厂Bean)
1、继承FactoryBean,在getObject添加组件,getObjectType指定组件类型
public class MyFactory {
private String call;
public MyFactory() {
}
public MyFactory(String call) {
this.call = call;
}
}
//创建一个spring定义的工厂bean
public class MyFactoryBean implements FactoryBean {
//返回一个对象,该对象会添加到容器中
@Override
public Object getObject() throws Exception {
return new MyFactory("MyFactory被创建了");
}
//根据类型去容器中找bean
@Override
public Class<?> getObjectType() {
return MyFactory.class;
}
//是不是单例
//true:单例,在容器中保存一份
// false:不是,每次调用都获取
@Override
public boolean isSingleton() {
return true;
}
}
2、配置类注册FactoryBean
@Configuration
@Import({People.class, MyImportSeletor.class, MyImportBeanDefinitionRegistrar.class})
public class AddBeanConfig {
@Bean
public MyFactoryBean myFactoryBean(){
return new MyFactoryBean();
}
}
3、测试,可以看到虽然注册的是MyFactoryBean,但是得到的是MyFactory
@Test
public void test07(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AddBeanConfig.class);
//提供FactoryBean获取组件
MyFactory myFactory = (MyFactory)applicationContext.getBean("myFactoryBean");
System.out.println(myFactory);
//Tree 被创建了
//com.jianglianghao.bean.MyFactory@1817d444
}
4、获取MyFactoryBean本身,在BeanName前面加一个&就可以了
MyFactoryBean myFactoryBean = (MyFactoryBean)applicationContext.getBean("&myFactoryBean");
//com.jianglianghao.config.MyFactoryBean@1817d444