Spring注解驱动——组件注册系列
1.@Configuration
从Spring3.0,@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。
用法:
@Configuration public class TestConfiguration { public TestConfiguration() { System.out.println("TestConfiguration容器启动初始化。。。"); } }
2.@Bean
给IOC容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id。 相当于<bean></bean>
用法:
Bean类:
package com.springs.entity; import org.springframework.beans.factory.annotation.Value; public class Person { private String name; private Integer age; private String nickName; public String getNickName() { return nickName; } public void setNickName(String nickName) { this.nickName = nickName; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Person(String name, Integer age) { super(); this.name = name; this.age = age; } public Person() { super(); // TODO Auto-generated constructor stub } @Override public String toString() { return "Person [name=" + name + ", age=" + age + ", nickName=" + nickName + "]"; }
Bean注入:
@Configuration//告诉Spring这是一个配置类 public class MainConfig { //给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id @Bean("person") public Person person01(){ return new Person("lisi", 20); } }
3.@ComponentScan
在配置类上添加 @ComponentScan 注解。该注解默认会扫描该类所在的包下所有的配置类,相当于之前的 <context:component-scan>。
3.1 基本使用
@Configuration//告诉Spring这是一个配置类 @ComponentScan(value = "com.springs") public class MainConfig { //给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id @Bean("person") public Person person01(){ return new Person("lisi", 20); } }
3.2 excludeFilters 和 includeFilters 的使用
- excludeFilters = Filter[] :指定扫描的时候按照什么规则排除那些组件
- includeFilters = Filter[] :指定扫描的时候只需要包含哪些组件
excludeFilters 和 includeFilters 的参数是一个 Filter[] 数组,然而每一个Filter也是一个注解,打开filter来看,我们知道需要设置一个过滤类型和使用的类
//FilterType.ANNOTATION:按照注解 //FilterType.ASSIGNABLE_TYPE:按照给定的类型; //FilterType.ASPECTJ:使用ASPECTJ表达式 //FilterType.REGEX:使用正则指定 //FilterType.CUSTOM:使用自定义规则
3.2.1 使用(一):注解过滤的使用
@Configuration//告诉Spring这是一个配置类 @ComponentScan(value = "com.springs",includeFilters = { @ComponentScan.Filter(type= FilterType.ANNOTATION,classes={Controller.class})},useDefaultFilters = false) //excludeFilters = Filter[] :指定扫描的时候按照什么规则排除那些组件 //includeFilters = Filter[] :指定扫描的时候只需要包含哪些组件 //FilterType.ANNOTATION:按照注解 //FilterType.ASSIGNABLE_TYPE:按照给定的类型; //FilterType.ASPECTJ:使用ASPECTJ表达式 //FilterType.REGEX:使用正则指定 //FilterType.CUSTOM:使用自定义规则 public class MainConfig { } }
这里涉及到 @ComponentScan 的一个 useDefaultFilters 属性的用法,该属性默认值为 true,也就是说 spring 默认会自动发现被 @Component 、@Repository、@Service 和 @Controller 标注的类,并注册进容器中。要达到只包含某些包的扫描效果,就必须将这个默认行为给禁用掉(在 @ComponentScan 中将 useDefaultFilters 设为 false 即可)
3.2.2 使用(二):自定义过滤规则
实现TypeFilter类:
public class MyTypeFilter implements TypeFilter { /** * metadataReader:读取到的当前正在扫描的类的信息 * metadataReaderFactory:可以获取到其他任何类信息的 */ @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(); if(className.contains("er")){ return true; } return false; } }
使用自动规则:
@Configuration//告诉Spring这是一个配置类 @ComponentScan(value = "com.springs",includeFilters = { @ComponentScan.Filter(type = FilterType.CUSTOM,classes = {MyTypeFilter.class}) },useDefaultFilters = false) public class MainConfig { }
3.3 添加多种扫描规则
如果使用的 jdk8,则可以直接添加多个 @ComponentScan 来添加多个扫描规则
可以使用 @ComponentScans 来添加多个 @ComponentScan,从而实现添加多个扫描规则
@Configuration//告诉Spring这是一个配置类 @ComponentScan(value = "com.springs",includeFilters = { @ComponentScan.Filter(type= FilterType.ANNOTATION,classes={Controller.class}), @ComponentScan.Filter(type =FilterType.ASSIGNABLE_TYPE,classes = {MainService.class}), @ComponentScan.Filter(type = FilterType.CUSTOM,classes = {MyTypeFilter.class}) },useDefaultFilters = false) //@ComponentScan value:指定要扫描的包 //@ComponentScans({@ComponentScan(),@ComponentScan()}) 定义多个扫描规则 //excludeFilters = Filter[] :指定扫描的时候按照什么规则排除那些组件 //includeFilters = Filter[] :指定扫描的时候只需要包含哪些组件 //FilterType.ANNOTATION:按照注解 //FilterType.ASSIGNABLE_TYPE:按照给定的类型; //FilterType.ASPECTJ:使用ASPECTJ表达式 //FilterType.REGEX:使用正则指定 //FilterType.CUSTOM:使用自定义规则 public class MainConfig { //给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id @Bean("person") public Person person01(){ return new Person("lisi", 20); } }
4.@Scope
用来调整作用域,默认是单例模式,即scope="singleton"。另外scope还有prototype、request、session、global session作用域。
- prototype:多实例的:ioc容器启动并不会去调用方法创建对象放在容器中。每次获取的时候才会调用方法创建对象;
- singleton:单实例的(默认值):ioc容器启动会调用方法创建对象放到ioc容器中。以后每次获取就是直接从容器(map.get())中拿,
- request:同一次请求创建一个实例
- session:同一个session创建一个实例
@Scope("prototype") @Bean("person") public Person person(){ System.out.println("给容器中添加Person...."); return new Person("张三", 25); }
5.@lazy
单实例bean:默认在容器启动的时候创建对象。而懒加载:容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化;
@Lazy @Bean("person") public Person person(){ System.out.println("给容器中添加Person...."); return new Person("张三", 25); }
6.@Conditional
@Conditional是Spring4新提供的注解,它的作用是根据某个条件创建特定的Bean,通过实现Condition接口,并重写matches接口来构造判断条件。
既可以用在类上(满足当前条件,这个类中配置的所有bean注册才能生效;),也可以用在方法上
用法:
linux系统条件
/** * ConditionContext:判断条件能使用的上下文(环境) * AnnotatedTypeMetadata:注释信息 */ @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { // TODO是否linux系统 //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"); //可以判断容器中的bean注册情况,也可以给容器中注册bean boolean definition = registry.containsBeanDefinition("person"); if(property.contains("linux")){ return true; } return false; }
Windows系统条件:
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; } }
配置类:
/** * @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); }
7.@import
有时没有把某个类注入到IOC容器中,但在运用的时候需要获取该类对应的bean,此时就需要用到@Import注解。id默认是组件的全类名
7.1 基本用法:
创建一个类:
package com.springs.entity; public class Color { }
使用import导入
@Import({Color.class}) //@Import导入组件,id默认是组件的全类名 public class TwoConfig { }
7.2 ImportSelector
返回需要导入的组件的全类名数组;
//自定义逻辑返回需要导入的组件 public class MyImportSelector implements ImportSelector { //返回值,就是到导入到容器中的组件全类名 //AnnotationMetadata:当前标注@Import注解的类的所有注解信息 @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { // TODO Auto-generated method stub //importingClassMetadata //方法不要返回null值 return new String[]{"com.springs.entity.Blue","com.springs.entity.Yellow"}; } }
7.3 ImportBeanDefinitionRegistrar
手动注册bean到容器中,可以指定其注入名称
ublic class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { /** * AnnotationMetadata:当前类的注解信息 * BeanDefinitionRegistry:BeanDefinition注册类; * 把所有需要添加到容器中的bean;调用 * BeanDefinitionRegistry.registerBeanDefinition手工注册进来 */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { boolean definition = registry.containsBeanDefinition("com.springs.entity.Red"); boolean definition2 = registry.containsBeanDefinition("com.springs.entity.Blue"); if(definition && definition2){ //指定Bean定义信息;(Bean的类型,Bean。。。) RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class); //注册一个Bean,指定bean名 registry.registerBeanDefinition("rainBow", beanDefinition); } } }
8.使用Spring提供的 FactoryBean(工厂Bean)注册组件
- 默认获取到的是工厂bean调用getObject创建的对象
- 要获取工厂Bean本身,我们需要给id前面加一个& :&colorFactoryBean
//创建一个Spring定义的FactoryBean public class ColorFactoryBean implements FactoryBean<Color> { //返回一个Color对象,这个对象会添加到容器中 @Override public Color getObject() throws Exception { // TODO Auto-generated method stub System.out.println("ColorFactoryBean...getObject..."); return new Color(); } @Override public Class<?> getObjectType() { // TODO Auto-generated method stub return Color.class; } //是单例? //true:这个bean是单实例,在容器中保存一份 //false:多实例,每次获取都会创建一个新的bean; @Override public boolean isSingleton() { // TODO Auto-generated method stub return false; } }
@Bean public ColorFactoryBean colorFactoryBean(){ return new ColorFactoryBean(); }
10.总结:
/** * 给容器中注册组件; * 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前面加一个& * &colorFactoryBean */