spring注解之组件注册
Posted not-miss
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring注解之组件注册相关的知识,希望对你有一定的参考价值。
1.组件注册是什么?
spring组件有十几种,但核心的只有三个:Context、Core和Bean。
那么这三个组件的关系是什么呢?
-Context:容器
-Core :关系
-bean:实物
-一句话:在springIOC运行容器(Context)中,通过Core建立维护各个bean之间的关系。
我们所说的组件注册其实就是:把bean对象交给ioc容器管理
2.组件注册几种方式:
[email protected]给容器注册组件
@Configuration public class MyConfig { @Scope("prototype") @Lazy @Bean(value = "pp",name = "pp") public Person person(){ return new Person("jiajia",26); }
代码中用到的注解说明:
@Configuration:配置类 相当于配置文件,告诉spring这是一个配置类
@Scope:调整作用域 ,可取值【singleton,prototype,request,session】
-singleton:单实例(默认值):ioc容器启动会调用方法创建对象放到ioc容器中, 以后每次获取就是直接从容器中拿;
-prototype:多实例,ioc容器启动并不会去调用方法创建对象放在容器中,每次获取的时候才会调用方法创建对象;
-request:同一次请求创建一个实例
-session:同一个session创建一个实例
@Lazy :是只针对单实例。
单实例Bean:默认在容器启动的时候创建对象;
懒加载:容器启动不创建对象,第一次使用(获取)Bean创建对象,并初始化;
@Bean:如果不设置value的话默认是采用方法名 person为组件名
[email protected] 组件扫描注解
2.2.1包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)
@ComponentScan(value = "com.athome", excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Service.class,Controller.class}), @ComponentScan.Filter(type = FilterType.REGEX,pattern = "com\\\\.athome\\\\.core\\\\.message\\\\..*") }) @Configuration public class MyConfig { @Bean public Person person(){ return new Person("niHao",26); } }
分析@ComponentScan注解中内容:
@Retention(RetentionPolicy.RUNTIME) //运行时期可获得该注解,反射 @Target(ElementType.TYPE) //该注解可以表示在类中任何元素 @Documented // 可制作文档 @Repeatable(ComponentScans.class)// 可重复使用该注解 public @interface ComponentScan { @AliasFor("basePackages") String[] value() default {}; //指定要扫描的包 @AliasFor("value") String[] basePackages() default {};//表示扫描的路径是一个String[] 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 ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN; boolean useDefaultFilters() default true;//默认的装配规则,意思是扫描所有的 Filter[] includeFilters() default {}; //指定扫描那些组件,还需要禁用默认的装配规则(useDefaultFilters="false") Filter[] excludeFilters() default {}; //指定不扫描哪些组件 boolean lazyInit() default false; @Retention(RetentionPolicy.RUNTIME) @Target({}) @interface Filter {// 内注解 FilterType type() default FilterType.ANNOTATION;//过滤类型 @AliasFor("classes") Class<?>[] value() default {}; @AliasFor("value") Class<?>[] classes() default {}; //class文件 String[] pattern() default {}; } }
该注解几个重要的属性:
- basePackages/value 表示扫描的路径是一个String[]
- includeFilters 包含过滤器Filter[] ,指定扫描符合条件的组件
- excludeFilters 排除过滤器Filter[] ,不扫描不符合条件的组件
- useDefaultFilters 默认的扫描策略默认为true,如果想要用自定义的策略 该值要设为false和includeFilters一起使用
还有个内注解@Filter
- FilterType.ANNOTATION:按照注解
- FilterType.ASSIGNABLE_TYPE:按照给定的类型;
- FilterType.ASPECTJ:使用ASPECTJ表达式
- FilterType.REGEX:使用正则指定
- FilterType.CUSTOM:使用自定义规则
2.2.2自定义规则--FilterType.CUSTOM的使用案例:
//自定义过滤组件 public class MyFilterType implements TypeFilter { public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { //获取当前类注解信息 AnnotationMetadata am = metadataReader.getAnnotationMetadata(); //获取当前正在扫描的类的类信息 ClassMetadata mr = metadataReader.getClassMetadata(); //获取当前类的资源信息(类的路径) Resource resource = metadataReader.getResource(); String className = mr.getClassName(); System.out.println("--->"+className); if(className.contains("er")){ return true; } return false; } }
@ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyFilterType.class}
2.2.3 @Conditional({condition}) 按条件注册bean
在spring4中引入,用到带有@Bean注解的方法上,如果给定的条件计算结果为true,则会创建这个bean,否则这个bean就会被忽略。
案例:
/** * @Conditional({Condition}) : 按照一定的条件进行判断,满足条件给容器中注册bean * 如果系统是windows,给容器中注册("bill") * 如果是linux系统,给容器中注册("linus") */ @Conditional(WindowsCondition.class) @Bean("bill") public Person person01(){ return new Person("Bill Gates",62); } @Conditional(LinuxCondition.class) @Bean("linus") public Person person02(){ return new Person("linus", 48); }
判断Windows条件:
public class WindowsConditional implements Condition{ public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { //获取到ioc使用的beanFactory ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); //获取类的加载器 ClassLoader classLoader = context.getClassLoader(); //获取当前环境信息 Environment environment = context.getEnvironment(); //获取到bean定义的注册类 BeanDefinitionRegistry registry = context.getRegistry(); String property = environment.getProperty("os.name"); if(property.contains("windows")){ return true; } return false; } }
java中的System的getProperty方法的取值key说明:
os.name 操作系统的名称
os.arch 操作系统的架构
os.version 操作系统的版本
判断Linux条件: -Dos.name=linux
public class LinuxConditional implements Condition { 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; } }
测试:
@Test public void conditionalTest(){
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig02.class); String[] names = ac.getBeanNamesForType(Person.class); ConfigurableEnvironment environment = ac.getEnvironment(); String osName = environment.getProperty("os.name"); System.out.println(osName); }
2.2.4 @ComponentScans,装配多个@ComponentScan装配规则
在Java8以前不允许在一个类上定义两个相同的注解,可以使用@ComponentScans,装配多个@ComponentScan装配规则
@ComponentScans( value = { @ComponentScan(value="com.atguigu",includeFilters = { @Filter(type=FilterType.ANNOTATION,classes={Controller.class}), @Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class}), @Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class}) },useDefaultFilters = false) }
2.2.5 练习报错:excludeFiters遇到问题
在使用excludeFiters过滤组件时,比如我们制定排除@Controller注解,但是spring容器还是有该组件,这种情况下原因是:@ComponentScans 多个配置文件使用并且扫描包也有重复
[email protected]注解导入组件
@Import主要是用来引用第三方组件,当然也可以导入自定义的组件。这个注解分别可以导入以下注解
- 导入普通组件:
public class Color{}
public class Red{}
上面两个简单的类
---------------------------
@Configuration @Import({Color.class,Red.class}) //@Import导入组件,id默认是组件的全类名 public class MainConfig2 { }
- 导入ImportSelect的实现类
ImportSelector 接口:返回需要导入的组件的全类名数组;
public interface ImportSelector { /** * 返回值,就是到导入到容器中的组件全类名 * AnnotationMetadata:当前标注@Import注解的类的所有注解信息 */ String[] selectImports(AnnotationMetadata importingClassMetadata); }
自定义实现类:
//自定义逻辑返回需要导入的组件 public class MyImportSelector implements ImportSelector { //返回值,就是到导入到容器中的组件全类名 //AnnotationMetadata:当前标注@Import注解的类的所有注解信息 @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { // TODO Auto-generated method stub //importingClassMetadata //方法不要返回null值 return new String[]{"com.athome.bean.Blue","com.athome.bean.Yellow"}; } }
使用@Import来导入
@Configuration @Import({MyImportSelector .class}) public class MyConfing { }
- 导入ImportBeandeinitionRegistrart
ImportBeandeinitionRegistrart接口:手动注册bean到容器中
public interface ImportBeanDefinitionRegistrar { /** * AnnotationMetadata:当前类的注解信息 * BeanDefinitionRegistry:BeanDefinition注册类;
* 把所有需要添加到容器中的bean;调用
* BeanDefinitionRegistry.registerBeanDefinition手工注册进来*/ public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry); }
自定义实现类:
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { boolean definition = registry.containsBeanDefinition("com.athome.bean.Red"); boolean definition2 = registry.containsBeanDefinition("com.athome.bean.Blue"); if(definition && definition2){ //指定Bean定义信息;(Bean的类型,Bean。。。) RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class); //注册一个Bean,指定bean名 registry.registerBeanDefinition("rainBow", beanDefinition); } } }
使用@Import来导入
@Configuration @Import({MyImportBeanDefinitionRegistrar .class}) public class MyConfing { }
2.4.FactoryBean工厂注册组件
使用FactoryBean接口的实现类来给容器中注入组件,该接口里面的getObject()方法可以返回该工厂生产的Bean。
FactoryBean接口:
public interface BeanFactory { String FACTORY_BEAN_PREFIX = "&"; Object getBean(String name) throws BeansException; <T> T getBean(String name, Class<T> requiredType) throws BeansException; Object getBean(String name, Object... args) throws BeansException; <T> T getBean(Class<T> requiredType) throws BeansException; <T> T getBean(Class<T> requiredType, Object... args) throws BeansException; <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType); <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType); boolean containsBean(String name); boolean isSingleton(String name) throws NoSuchBeanDefinitionException; boolean isPrototype(String name) throws NoSuchBeanDefinitionException; boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException; boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException; @Nullable Class<?> getType(String name) throws NoSuchBeanDefinitionException; String[] getAliases(String name); }
自定义一个bean实现FactoryBean接口:
//创建一个Spring定义的FactoryBean public class ColorFactoryBean implements FactoryBean<Color> { //返回一个Color对象,这个对象会添加到容器中 public Color getObject() throws Exception { return new Color(); } public Class<?> getObjectType() { return Color.class; } //是单例? //true:这个bean是单实例,在容器中保存一份 //false:多实例,每次获取都会创建一个新的bean; public boolean isSingleton() { return false; } }
注册ColorFactoryBean :
@Bean public ColorFactoryBean colorFactoryBean(){ return new ColorFactoryBean(); }
测试:
@Test public void colorFactoryBeansTest(){ AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig02.class); String[] beansName = ac.getBeanDefinitionNames(); for (String s: beansName) { System.out.println(s); } //工厂bean获取的是调用getObject创建对象 Object bean01 = ac.getBean("colorFactoryBean"); Object bean02 = ac.getBean("colorFactoryBean"); //+前缀 & 告诉spring 拿工厂bean 本身 Object bean03 = ac.getBean("&colorFactoryBean").getClass(); System.out.println("bean01的类型: "+bean01.getClass()); //"修改isSingleton为单实例返回值 true System.out.println( bean01 == bean02); System.out.println("+前缀 & 的bean03的类型: "+bean03); }
注意:
1.默认获取到的是工厂bean调用getObject创建的对象
2.要获取工厂Bean本身,我们需要给id前面加一个&
3.小结
给容器中注册组件:
1)、包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)[自己写的类]
2)、@Bean[导入的第三方包里面的组件]
3)、@Import[快速给容器中导入一个组件]
1)、@Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名
2)、ImportSelector:返回需要导入的组件的全类名数组;
3)、ImportBeanDefinitionRegistrar:手动注册bean到容器中
4)、使用Spring提供的 FactoryBean(工厂Bean);
1)、默认获取到的是工厂bean调用getObject创建的对象
2)、要获取工厂Bean本身,我们需要给id前面加一个&
以上是关于spring注解之组件注册的主要内容,如果未能解决你的问题,请参考以下文章