spring的组件注册相关注解探索
本文参考博客(基本摘自以下博客内容)
https://www.jianshu.com/p/81880251a700
https://www.jianshu.com/p/a00d2b43e160
本节主要内容介绍spring注解的组件注册,具体包括下面的几个注解:
- @ComponentScan自动扫面组件
- @Scope设置组件的作用域
- @Lazy bean组件懒加载
- @Conditional按照条件注册Bean
- @Import给容器中导入一个组件
1、@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 {};
....其他属性
//指定扫描的时候只需要包含哪些组件
Filter[] includeFilters() default {};
//指定扫描的时候按照什么规则排除那些组件
Filter[] excludeFilters() default {};
boolean lazyInit() default false;
//过滤规则类
@Retention(RetentionPolicy.RUNTIME)
@Target({})
@interface Filter {
//FilterType定义按什么过滤类型来进行过滤,
/**
FilterType.ANNOTATION:按照注解
FilterType.ASSIGNABLE_TYPE:按照给定的类型;
FilterType.ASPECTJ:使用ASPECTJ表达式
FilterType.REGEX:使用正则指定
FilterType.CUSTOM:使用自定义规则
*/
FilterType type() default FilterType.ANNOTATION;
@AliasFor("classes")
Class<?>[] value() default {};
@AliasFor("value")
Class<?>[] classes() default {};
String[] pattern() default {};
}
}
- 使用说明
1、只包含有Controller注解bean
@ComponentScan(value="com.qiu",includeFilters = {
@Filter(type=FilterType.ANNOTATION,classes={Controller.class}),}
,useDefaultFilters = false)
2、排除含有controller注解的bean
@ComponentScan(value="com.qiu",excludeFilters = {
@Filter(type=FilterType.ANNOTATION,classes={Controller.class})
})
2、@Scope设置组件的作用域
- 源码
//作用与类或者方法上
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {
@AliasFor("scopeName")
String value() default "";
@AliasFor("value")
String scopeName() default "";
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
}
- 使用
*默认是单实例的,ConfigurableBeanFactory接口含有者两个属性
*单实例和原型实例说明:prototype:多实例的:ioc容器启动并不会去调用方法创建对象放在容器中。
每次获取的时候才会调用方法创建对象;singleton:单实例的(默认值):ioc容器启动会调用方法创建
对象放到ioc容器中。 以后每次获取就是直接从容器(map.get())中拿
1、原型案例
@Scope("prototype")
@Bean()
public User user() {
System.out.println("给容器中添加user....");
return new User();
}
2、测试
ConfigurableApplicationContext context2=new
AnnotationConfigApplicationContext(ScopeConfig.class);
//测试scope:获取容器中的bean,singleton单例时候相等,prototype时候不相等
User user2=(User) context2.getBean("user");
User user1=(User) context2.getBean("user");
System.out.println(user1==user2);
3、@Lazy bean组件懒加载
- @Lazy注解用于标识bean是否需要延迟加载,默认是true。当没加注解之前主要容器启动就会实例化bean,加上@Lazy注解则必须在第一次调用的时候才会加载如下:
1、没加注解之前主要容器启动就会实例化bean
ConfigurableApplicationContext context2=new
AnnotationConfigApplicationContext(ScopeConfig.class);
2、加入@Lazy后必须第一次调用的时候才会加载
User user3=(User) context2.getBean("user1");
4、@Conditional按照条件注册Bean
- 源码
//作用于方法和类
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
//参数是:继承于Condition的类数组
Class<? extends Condition>[] value();
}
//condition接口,自定义的condition类需要实现该接口
public interface Condition {
/**
* ConditionContext:判断条件能使用的上下文(环境)
* AnnotatedTypeMetadata:注释信息
*/
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
- 案例说明
需求:按照当前操作系统来注入相应的Bean,如果系统是windows,给容器中注册("bill"),如果是linux系统,给容器中注册("linus")
1、判断是否是linux系统
public class LinuxCondition implements Condition{
/**
* ConditionContext:判断条件能使用的上下文(环境)
* AnnotatedTypeMetadata:注释信息
*/
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata)
{
//1.能获取到ioc使用的Beanfactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//2、获取到类加载器
ClassLoader classLoader = context.getClassLoader();
//3.获取到当前环境信息
Environment environment = context.getEnvironment();
//4.获取到bean定义的注册类信息
BeanDefinitionRegistry registry = context.getRegistry();
//=============这里主要是为了获取当前系统的环境变脸
String property=environment.getProperty("os.name");
if (property.contains("linux")) {
return true;//放行
}
return false;
}
}
2、判断是否是windows系统
public class WindowsCondition implements Condition{
/**
* ConditionContext:判断条件能使用的上下文(环境)
* AnnotatedTypeMetadata:注释信息
*/
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;
}
}
3、@Conditional:按照一定的逻辑进行判断,满足条件给容器注入bean
public class ConditionalConfig {
//注入windows
@Conditional(value= {WindowsCondition.class})
@Bean
public User user1() {
User user=new User();
user.setUserName("bill");
return user;
}
//注入linux
@Conditional(value= {LinuxCondition.class})
@Bean
public User user2() {
User user=new User();
user.setUserName("linus");
return user;
}
4、idea中更换操作系统方法:-Dos.name=linux
介绍完了条件注解在spring中的使用,在Springboot中条件注解的分类:
Class conditions:@ConditionalOnClass和@ConditionalOnMissingClass,表示类是否在类路径下的条件注解
Bean conditions:@ConditionalOnBean和@ConditionalOnMissingBean,表示Bean是否被定义的条件注解
Property conditions:@ConditionalOnProperty,使用prefix和name属性用来表示是否有值,默认的话,只要该属性存在值,且不为false,即可匹配
Resource conditions:@ConditionalOnResource表示是否存在指定的resouce的条件注解
Web application conditions:@ConditionalOnWebApplication和@ConditionalOnNotWebApplication,当项目是web项目,或者不是web项目的条件注解
SpEL expression conditions:@ConditionalOnExpression,根据SPEL表达式执行结果作为条件
5、@Import给容器中导入一个组件
- 源码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
//只有一个属性,类型数组
Class<?>[] value();
}
- 给容器中注册组件方式:
1、包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)[自己写的类]
2、@Bean[导入的第三方包里面的组件或者自己写的]
3、@Import[快速给容器中导入一个组件,常用于框架中]
(1)、@Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名
(2)、ImportSelector:返回需要导入的组件的全类名数组;
(3)、ImportBeanDefinitionRegistrar:手动注册bean到容器中
4、使用Spring提供的 FactoryBean(工厂Bean);
(1)默认获取到的是工厂bean调用getObject创建的对象
- @Import注入组件案例说明:
1、@Import(要导入到容器中的组件)
@Import(value= {Person.class})
2、ImportSelector:返回需要导入的组件的全类名数组;
//先自定义逻辑返回需要导入的组件
public class MyImportSelector implements ImportSelector {
//返回值,就是到导入到容器中的组件全类名
//AnnotationMetadata:当前标注@Import注解的类的所有注解信息
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//importingClassMetadata
//方法不要返回null值
return new String[]{"com.huya.qiu.model.BlackPerson",
"com.huya.qiu.model.WhitePerson"};
}
}
//然后注入
@Import(value= {MyImportSelector.class})
3、ImportBeanDefinitionRegistrar:手动注册bean到容器中
//先实现ImportBeanDefinitionRegistrar接口
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* AnnotationMetadata:当前类的注解信息
* BeanDefinitionRegistry:BeanDefinition注册类;
* 把所有需要添加到容器中的bean;调用
* BeanDefinitionRegistry.registerBeanDefinition手工注册进来
*/
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//当同时拥有下面两个注册bean时候也注册yellowPerson
boolean definition = registry.containsBeanDefinition("com.huya.qiu.model.BlackPerson");
boolean definition2 = registry.containsBeanDefinition("com.huya.qiu.model.WhitePerson");
if(definition && definition2){
//指定Bean定义信息;(Bean的类型,Bean。。。)
RootBeanDefinition beanDefinition = new RootBeanDefinition(YellowPerson.class);
//注册一个Bean,指定bean名
registry.registerBeanDefinition("yellowPerson", beanDefinition);
}
}
}
//然后注入
@Import(value= {MyImportBeanDefinitionRegistrar.class})
- 使用Spring提供的 FactoryBean注入组价案例说明
1、创建一个Spring定义的FactoryBean
public class PersonFactoryBean implements FactoryBean<Person> {
//返回一个Person对象,这个对象会添加到容器中
public Person getObject() throws Exception {
System.out.println("PersonFactoryBean...getObject...");
return new Person();
}
public Class<?> getObjectType() {
return Person.class;
}
//是单例?
public boolean isSingleton() {
return false;
}
}
2、注入相应的bean
@Bean
public PersonFactoryBean personFactoryBean(){
return new PersonFactoryBean();
}