指定该注解的类是个配置类,对应之前的配置文件
指定该方法作为一个Bean组件,它会往容器中注册一个Bean组件;
其中类型为返回值,id默认为方法名;
也可以使用@Bean注解的name属性,来自定义id。
可以配置多个@ComponentScan。
3.1、@ComponentScan:
指定要扫描的包路径
excludeFilters=Filter[]: 指定扫描的时候要排除的组件。
useDefaultFilters: 过滤策略,默认为true,即会自动扫描带@Component注解(包括@Component,@Controller,@Service,@Repository注解的类)。
includeFilters=Filter[]: 指定扫描符合规则的组件,如果只是想扫描某些指定组件,需要将useDefaultFilters设为false,禁用默认的扫描策略。
3.1.1、Filter类型:
@Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
使用@Controller注解的类符合以上规则。
@Filter(type = FilterType.ASSIGNABLE_TYPE, classes = xxx.class)
符合xxx.java的类符合以上规则。
要使用该策略,类必须实现org.springframework.core.type.filter.TypeFilter接口。
public class MyTypeFilter implements TypeFilter {
/**
* metadataReader: 读取到的当前正在扫描的类信息
* metadataReaderFactory: 可以获取到其他任何类信息的工厂类
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
throws IOException {
// TODO Auto-generated method stub
//获取当前类注解的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//获取当前正在扫描的类信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//获取当前类的资源(类路径)
Resource resource = metadataReader.getResource();
String className = classMetadata.getClassName();
System.out.println(">>>>>" + className);
//简单测试,如果扫描的类名中包含了er,则匹配
if(className.contains("er")){
return true;
}
return false;
}
}
同时标注:
@Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})
这样,在扫描的时候会将包含"er"字母的类加入到容器中。
指定组件的作用域,默认为单例:singleton
singleton:单实例,当ioc容器启动会调用方法创建对象并放到ioc容器中,而以后每次获取对象的时候直接从ioc容器中获取。
prototype:多实例,ioc容器启动并不会去调用方法创建对象,而是在每次获取的时候才会去调用方法创建对象,所以每次获取的对象都是不一样的。
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Bean
public Book book(){
System.out.println("创建book实例...");
return new Book();
}
@Bean
public Person person(){//默认单实例
System.out.println("创建person实例...");
return new Person("zhangsan", 20);
}
测试:
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig2.class);
//单实例
Object obj1 = applicationContext.getBean("person");
Object obj2 = applicationContext.getBean("person");
System.out.println(obj1 == obj2);//输出true
//多实例
Object obj3 = applicationContext.getBean("book");
Object obj4 = applicationContext.getBean("book");
System.out.println(obj3 == obj4);//输出false
懒加载:只针对于单实例对象
默认加载Bean是在容器启动的时候就会去创建对象,并将其放入ioc容器中;而懒加载则相反,容器启动时不创建对象,而是在第一次使用(获取)对象的时候,才会去创建对象,再将其加入到ioc容器中,之后每次再获取该对象的时候,直接从ioc容器中获取,不再重新创建。
@Lazy
@Bean
public Book book2(){
System.out.println("创建book2实例...");
return new Book();
}
该注解可以标注在类上,也可以标注在方法上
方法级别:根据条件创建bean,只有满足条件才会创建bean;
类级别:根据条件创建配置类,只有满足条件才会去创建配置类,如果不满足,则不会创建该配置类,同时配置类里的bean也就不会被创建。
配置类:
@Configuration
public class MyConfig3 {
@Conditional(WindowsCondition.class)
@Bean("bill")
public Person person1(){
return new Person("bill gates", 60);
}
@Conditional(LinuxCondition.class)
@Bean("linus")
public Person person2(){
return new Person("linus", 50);
}
}
LinuxCondition条件类:
//判断是否是Linux系统
public class LinuxCondition implements Condition{
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// TODO Auto-generated method stub
//获取到ioc使用的BeanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//获取类加载器
ClassLoader classLoader = context.getClassLoader();
//获取当前环境变量
Environment environment = context.getEnvironment();
//获取到bean定义的注册类
BeanDefinitionRegistry registry = context.getRegistry();
//获取系统环境
String systemName = environment.getProperty("os.name");
if(systemName.contains("Linux")){
return true;
}
return false;
}
}
WindowsCondition条件类:
//判断是否是Windows系统
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// TODO Auto-generated method stub
// 获取当前环境变量
Environment environment = context.getEnvironment();
// 获取系统环境
String systemName = environment.getProperty("os.name");
if (systemName.contains("Windows")) {
return true;
}
return false;
}
}
这样,使用不同的操作系统,创建的Bean也就不一样。
Spring导入组件的方式:
1、使用注解@Component、@Controller、@Service、@Repository
2、使用@Bean导入第三方组件
3、使用@Import快速导入一个或多个第三方组件
3.1、指定类
3.2、使用ImportSelector接口
3.3、使用ImportBeanDefinitionRegistrar接口
4、 FactoryBean接口
7.1、@Import
7.1.1、指定类
假设有两个颜色类:Red.java和Green.class,可以在配置类上使用@Import注解注册该两个类。
@Import({Red.class, Green.class})
这样在Bean定义的注册表里有其两个组件,其组件的id为全类名。
7.1.2、ImportSelector接口
需要实现ImportSelector接口
public class MyImportSelector implements ImportSelector {
//返回值就是要导入到容器中的组件全类名
//AnnotationMetadata:当前标注@Import注解的类的所有注解信息
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// TODO Auto-generated method stub
//方法不要返回null
return new String[]{"org.com.cay.spring.annotation.color.Blue", "org.com.cay.spring.annotation.color.Yellow"};
}
}
导入:
@Import({Red.class, Green.class, MyImportSelector.class})
这样不仅会将@Import注解中指定的类加入到容器中,同样会将MyImportSelector#selectImports中方法返回的全类名进行注册到容器中。
7.1.3、ImportBeanDefinitionRegistrar接口
实现ImportBeanDefinitionRegistrar接口,实现registerBeanDefinitions方法,手动注册bean。
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* AnnotationMetadata:当前类的注解信息
*
* BeanDefinitionRegistry:BeanDefinition注册类
* 把所有需要添加到容器中的bean,手动通过{@link BeanDefinitionRegistry#registerBeanDefinition(String, BeanDefinition)}进行注册
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// TODO Auto-generated method stub
//因为Yellow这个Bean是通过@Import导入的,所以bean的id为全类名,所以这里需要判断全类名才行
boolean hasBean = registry.containsBeanDefinition("org.com.cay.spring.annotation.color.Yellow");
System.out.println(">>>>>>hasBean: " + hasBean);
if(hasBean){
BeanDefinition beanDefinition = new RootBeanDefinition(Tiger.class);
registry.registerBeanDefinition("tiger", beanDefinition);
}
}
}
添加该实现类到@Import属性中:
@Import({ Red.class, Green.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class })
7.2、FactoryBean接口
实现FactoryBean接口
public class ColorFactory implements FactoryBean{
//返回一个Color对象,这个对象会添加到容器中
@Override
public Color getObject() throws Exception {
// TODO Auto-generated method stub
return new Color();
}
@Override
public Class> getObjectType() {
// TODO Auto-generated method stub
return Color.class;
}
@Override
public boolean isSingleton() {
// TODO Auto-generated method stub
return true;
}
}
并将该factoryBean注册到容器中:
@Configuration
@Import({ Red.class, Green.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class })
public class MyConfig4 {
@Bean
public ColorFactory colorFactory(){
return new ColorFactory();
}
}
测试:
@Test
public void testFactoryBean(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig4.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
Arrays.asList(beanDefinitionNames).stream().forEach(System.out::println);
//获取到的是实现FactoryBean接口后通过getObject返回的实际bean
Object bean = applicationContext.getBean("colorFactory");
System.out.println("bean的类型: " + bean.getClass());
//获取factorybean自身bean
//org.springframework.beans.factory.BeanFactory.FACTORY_BEAN_PREFIX = "&";
Object bean2 = applicationContext.getBean("&colorFactory");
System.out.println("bean2的类型: " + bean2.getClass());
}
为何使用"&"?
package org.springframework.beans.factory;
public interface BeanFactory {
/**
* Used to dereference a {@link FactoryBean} instance and distinguish it from
* beans created by the FactoryBean. For example, if the bean named
* {@code myJndiObject} is a FactoryBean, getting {@code &myJndiObject}
* will return the factory, not the instance returned by the factory.
*/
String FACTORY_BEAN_PREFIX = "&";
//other...
}
QQ: |
412425870 |
微信公众号:Cay课堂 |
|
csdn博客: |
http://blog.csdn.net/caychen |
码云: |
https://gitee.com/caychen/ |
github: |
https://github.com/caychen |
点击群号或者扫描二维码即可加入QQ群:328243383(1群) |
|
点击群号或者扫描二维码即可加入QQ群: |
|
--------------------- 作者:caychen 来源:CSDN 原文:https://blog.csdn.net/caychen/article/details/82887926?utm_source=copy 版权声明:本文为博主原创文章,转载请附上博文链接!