网站改版收录减少/排名
目录
- 前言
- 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 {@Beanpublic 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;}
}
测试类:
@Testpublic 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 {@Beanpublic 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;}
}
@Testpublic 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 {
}
测试结果:
@Testpublic 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 {
}
测试结果:
@Testpublic 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
)
测试结果:
@Testpublic 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*/@Overridepublic 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,就返回trueif(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 {}
@Testpublic 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,默认是单实例的@Beanpublic Tree tree(){return new Tree();}
}
测试类:(测试bean的创建和销毁)
@Testpublic 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);//trueapplicationContext.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的创建和销毁)
@Testpublic 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 {@Overridepublic 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 {@Overridepublic 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})@Beanpublic People linux(){return new People("linux", "18");}@Conditional({WindowsCondition.class})@Beanpublic People window(){return new People("window", "18");}
}test
```java
@Testpublic 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})@Beanpublic People linux(){return new People("linux", "18");}@Conditional({WindowsCondition.class})@Beanpublic People window(){return new People("window", "18");}
}
//选择放在UserService 类上
@Conditional({LinuxCondition.class})
@Service
public class UserService {
}
测试结果:可以看到,由于我们不是linux系统,所以不会出现UserService的组件
@Testpublic 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被成功导入
@Testpublic 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 {@Overridepublic 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 {}
测试结果:
@Testpublic 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来手动注册*/@Overridepublic 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 {//返回一个对象,该对象会添加到容器中@Overridepublic Object getObject() throws Exception {return new MyFactory("MyFactory被创建了");}//根据类型去容器中找bean@Overridepublic Class<?> getObjectType() {return MyFactory.class;}//是不是单例//true:单例,在容器中保存一份// false:不是,每次调用都获取@Overridepublic boolean isSingleton() {return true;}
}
2、配置类注册FactoryBean
@Configuration
@Import({People.class, MyImportSeletor.class, MyImportBeanDefinitionRegistrar.class})
public class AddBeanConfig {@Beanpublic MyFactoryBean myFactoryBean(){return new MyFactoryBean();}
}
3、测试,可以看到虽然注册的是MyFactoryBean,但是得到的是MyFactory
@Testpublic 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
如有错误,欢迎指出