Spring组件的注册方式

  • 前言
  • Bean的几种注册方式
  • 1.xml方式注册
  • 2.@Configuration ,@bean方式注册
  • 3.xml包扫描注册
  • 4.@Import注册
  • 5.使用spring提供的FactoryBean(工厂Bean)
  • 相关注解
  • 1.@Scope注解设置组件作用域
  • 2.@Lazy注解实现单实例懒加载
  • 3.@Conditional按条件注册
  • end...


前言

首先,感谢尚硅谷等平台,让我白嫖学习到了很多。毕业后一直想快速提升自己,看了很多平台的课程, 因为没有做笔记,看过的课程忘记的很快,因此,今后的学习都会在csdn上做做笔记,加深印象的同时也方便自己回顾。

Bean的几种注册方式

1.xml方式注册

注册:

<bean id="person" class="com.atgugui.bean.Person">
 	<property name="age" value="22"></property>
 	<property name="name" value="zx"></property>
</bean>

获取:

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Person person = (Person) applicationContext.getBean("person");

2.@Configuration ,@bean方式注册

注册:

@Configuration // 告诉Spring这是一个配置类
public class MainConfig {

	// @Bean注解是给IOC容器中注册一个bean,类型自然就是返回值的类型,id默认是用方法名作为id
	@Bean
	public Person person() {
		return new Person("zx", 22);
	}
	
}

获取:

// MainConfig:配置类,Person:注入类,可通过applicationContext 获取id等注入信息
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
Person person = applicationContext.getBean(Person.class);

3.xml包扫描注册

包扫描:只要是标注了@Controller、@Service、@Repository、@Component其中任意一个注解

注册:

<context:component-scan base-package="com.zx.**"></context:component-scan>

此配置可在配置类(标注@Configuration )中替换为@ComponentScan(value = “com.zx.**”) 此注解还可配置只包含、排除等,只包含需关闭自动配置(useDefaultFilters=false),例如:

@ComponentScan(value=“com.meimeixia”, excludeFilters={
 /*
 * type:指定排除的规则,有按照注解进行排除,按照给定的类型进行排除、按照正则表达式进行排除、按照自定义规则等
 * classes:例子中是排除@Controller和@Service标注的组件
 */
 @Filter(type=FilterType.ANNOTATION, classes={Controller.class, Service.class})
 // 只要是BookService类型的组件都会被加载到容器中,包括子类
 @Filter(type=FilterType.ASSIGNABLE_TYPE, classes={BookService.class})
 }, useDefaultFilters=false)
 // 自定义规则,指定实现的自定义类
 @Filter(type=FilterType.CUSTOM, classes={MyTypeFilter.class})
 }, useDefaultFilters=false) // value指定要扫描的包
 })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; // 匹配不成功,排除
	}
}


获取:

ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
String[] definitionNames =applicationContext.getBeanDefinitionNames();
for (String name : definitionNames) {
	System.out.println(name);
}
// 结果,spring自身组件+加了@controller等注解的类

4.@Import注册

@import用于快速向容器中导入一个组件,id默认是组件的全类名

例子

@Configuration
@Import(Color.class)
public class MainConfig {
}

ImportSelector接口导入bean

例子

/**
 * 自定义逻辑,返回需要导入的组件
 * @author liayun
 *
 */
public class MyImportSelector implements ImportSelector {

	// 返回值:导入到容器中的组件的全类名
	// AnnotationMetadata:当前标注@Import注解的类的所有注解信息,不仅能获取到@Import注解里面的信息,还能获取到其他注解的信息
	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		
		// 方法不能返回null值,否则会报空指针异常,可以返回一个空数组
		//return new String[]{}; 
		// 返回需要的组件
		return new String[]{"com.zx.bean.CS", "com.zx.bean.GO"}; 
	}
}
-----------------------------分隔线---------------------------------
@Configuration
@Import({Color.class, Red.class, MyImportSelector.class})
public class MainConfig {
	@Bean("***")
	···
}

ImportBeanDefinitionRegistrar接口导入bean

例子

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

	/**
	 * AnnotationMetadata:当前类的注解信息
	 * BeanDefinitionRegistry:BeanDefinition注册类
	 * 
	 * 我们可以通过调用BeanDefinitionRegistry接口中的registerBeanDefinition方法,手动注册需要添加到容器中的bean
	 */
	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		boolean definition1 = registry.containsBeanDefinition("com.zx.bean.Red");
		boolean definition2 = registry.containsBeanDefinition("com.zx.bean.Bule");
		if (definition1 && definition2) {
			// 指定bean的定义信息 (包括bean的类型、作用域等等)
			RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class); // bean的定义信息
			// 注册一个bean,指定bean的名称
			registry.registerBeanDefinition("rainBow", beanDefinition);
		}
	}
}
-----------------------分隔线------------------------
@Configuration
@Import({Color.class, MyImportBeanDefinitionRegistrar.class}) 
public class MainConfig {
}

5.使用spring提供的FactoryBean(工厂Bean)

通过实现FactoryBean,实现相关方法完成bean的注册

注册

public class ColorFactoryBean implements FactoryBean<Color> {
	@Override
	public Color getObject() throws Exception {
	    // 返回一个Color对象
		return new Color();
	}

	@Override
	public Class<?> getObjectType() {
		return Color.class; // 返回Class
	}
	// return true,单实例,在容器中只会保存一份;
	// return false,多实例,每次获取都会创建一个新的bean
	@Override
	public boolean isSingleton() {
		return false;
	}
}
-------------------------分隔线--------------------------
@Bean
public ColorFactoryBean colorFactoryBean() {
	return new ColorFactoryBean();
}

获取

如要在Spring容器中获取到FactoryBean对象本身,可以加上&,如 Object bean = applicationContext.getBean("&colorFactoryBean");

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
// 获取的bean是调用getObject方法创建的对象(Color)
Object bean2 = applicationContext.getBean("colorFactoryBean");
Object bean3 = applicationContext.getBean("colorFactoryBean");
// 获取FactoryBean对象本身
Object bean = applicationContext.getBean("&colorFactoryBean");

相关注解

1.@Scope注解设置组件作用域

ConfigurableBeanFactory#SCOPE_PROTOTYPE (多实例的)
ConfigurableBeanFactory#SCOPE_SINGLETON (默认值:单实例的)
org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST (同一次请求创建一个实例)
org.springframework.web.context.WebApplicationContext#SCOPE_SESSION (同一个session创建一个实例)
prototype:多实例的,IOC容器启动不会去调用方法创建对象放在容器中,每次获取的时候才去调用方法创建对象
singleton:但实例的,IOC容器启动会调用方法创建对象放到IOC容器中,以后每次获取就是直接从容器 nap.get() 中拿
例子

@Scope("prototype") 单实例 
@Bean("person")
public Person person() {
	return new Person("zx", 22);
}

2.@Lazy注解实现单实例懒加载

懒加载:又称延时加载,仅针对单实例bean生效。 单实例bean,默认在Spring容器启动的时候就加载,添加@Lazy注解后就会延迟加载,在Spring容器启动的时候并不会加载,而是在第一次使用bean的时候才会创建。

例子

@Lazy
@Bean("person")
public Person person() {
	return new Person("zx", 22);
}

3.@Conditional按条件注册

spring4.0后新增,可以标注在类上和方法上,在springBoot中大量使用。
参数:Class<? extends Condition>[] value(); 继承codition的类数组

例子

public class LinuxCondition implements Condition {

    /**
    * ConditionContext:判断条件能使用的上下文(环境)
    * AnnotatedTypeMetadata:当前标注了@Conditional注解的注释信息
    */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 判断操作系统是否是Linux系统
        // 1. 获取到bean的创建工厂(能获取到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;
    }
------------------------分隔线---------------------------
@Conditional({LinuxCondition.class})
@Bean("linus")
public Person person02() {
	return new Person("linus", 48);
}

end…