Spring-几种组件注册方式
一只不正经的程序猿。。。
IOC(Inversion of Control)控制反转,spring中的核心,有了ioc容器,在程序开发过程中可以将bean对象托管给Spring容器进行管理(管理对象的生命周期,对象之间的依赖关系等),从而更好的专注于业务开发,减少代码工作量。
下面介绍几种组件注册到容器中的方式
!注意以下Spring版本不同,实现方式可能会到受影响
1.配置文件注册方式
引入spring-context maven依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.17.RELEASE</version>
</dependency>
bean测试类
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
创建spring-bean.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--基于配置方式 id,name 要保证唯一性-->
<bean class="org.happybean.bean.Person" name="person1" id="person1-id" >
<property name="name" value="spring1"/>
<property name="age" value="20"/>
</bean>
<bean class="org.happybean.bean.Person" name="person2" id="person2-id" >
<property name="name" value="spring2"/>
<property name="age" value="21"/>
</bean>
</beans>
测试使用
public static void main(String[] args) {
//加载配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-bean.xml");
//根据 bean name 获取,需要类型强制转换
Person person1 = (Person) applicationContext.getBean("person1");
System.out.println("person1:"+person1);
//根据bean id 获取,需要类型强制转换
Person person2 = (Person) applicationContext.getBean("person2-id");
System.out.println("person2:"+person2);
//根据bean类型获取,无需要类型强制转换,
//使用此种方式时要保证改类型bean只配置一个,否则会抛出异常 org.springframework.beans.factory.NoUniqueBeanDefinitionException
Person person3 = applicationContext.getBean(Person.class);
System.out.println("person3:"+person3);
//根据bean name和类型获取,无需要类型强制转换
//多个相同类型bean时,可以使用该方法获取
Person person4 = applicationContext.getBean("person1", Person.class);
System.out.println("person4:"+person4);
}
2.配置类注册方式
配置类
@Configuration
public class SpringConfig {
//@Configuration 告诉spring这是一个配置类
@Bean
public Person person1(){
//@Bean 将对象注册到容器 name默认为方法名
return new Person("spring1",20);
}
@Bean(name = "person2-name")
public Person person2(){
//@Bean 将对象注册到容器 id默认为方法名
return new Person("spring2",21);
}
}
测试使用
public static void main(String[] args) {
//加载配置类 此时的配置类等同于xml配置文件
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
//根据bean 默认name(方法名) 获取,需要类型强制转换
Person person1 = (Person) applicationContext.getBean("person1");
System.out.println("person1:" + person1);
//根据 bean name 获取,需要类型强制转换
Person person2 = (Person) applicationContext.getBean("person2-name");
System.out.println("person2:" + person2);
//根据bean name和类型获取,无需要类型强制转换
//多个相同类型bean时,可以使用该方法获取
Person person4 = applicationContext.getBean("person1", Person.class);
System.out.println("person4:" + person4);
//获取bean 在容器中的 name
String[] beanNames = applicationContext.getBeanDefinitionNames();
for (String name : beanNames) {
System.out.println(name);
}
}
3.Xml配置文件包扫描方式
配置文件 spring-scan.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--配置文件方式扫描,@Controller @Service @Repository等注解的类会以bean方式注册到容器中-->
<context:component-scan base-package="org.happybean.controller,org.happybean.service,org.happybean.repository"/>
</beans>
注册bean时默认使用单例模式注入,或者可以在bean标签中定义 scope="prototype"定义为多实例模式
4.配置类注解包扫描方式
配置类
@Configuration
@ComponentScan(value = {"org.happybean.controller", "org.happybean.service", "org.happybean.repository"})
public class SpringConfig {
}
包扫描方式会默认以类首字母小写作为 bean name,或者也可以用@Controller("SpringController")方式自定义name
当然你也可以使用 @ComponentScan中的excludeFilters排除不需要注册到容器中的类
或用以下配置类方式实现多实例模式
@Bean
@Scope("prototype")
public Person person1() {
return new Person("spring1", 20);
}
通过测试我们可以得知,在使用默认的单例模式注册时,spring会使用饿汉模式进行注册,如果想改为懒加载模式(懒汉模式),可以在xml配置文件bean标签中加入lazy-init="true”,或者以如下方式实现
@Lazy
@Bean(name = "person2-name")
public Person person2() {
//@Bean 将对象注册到容器 id默认为方法名
//使用@Lazy将单例模式下的bean改为懒加载模式(懒汉模式)
return new Person("spring2", 21);
}
5.条件注册方式
如果你想满足某种条件下才注册bean,可以使用如下方式
创建一个注册条件类 必须实现 org.springframework.context.annotation.Condition
public class OsCondition implements Condition {
/**
* 满足操作系统是mac时才会创建 name为乔布斯的bean
*
* @param conditionContext 上下文(环境)
* @param annotatedTypeMetadata 注解信息
*/
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
//获取环境信息
Environment environment = conditionContext.getEnvironment();
//获取操作系统信息
String osName = environment.getProperty("os.name");
if (osName.equals("Mac OS X")) {
return true;
}
return false;
}
}
bean配置
@Bean
@Conditional(OsCondition.class)
public Person steveJobs() {
//@Conditional 满足某种条件下才会注册bean
return new Person("steveJobs", 56);
}
测试使用
public static void main(String[] args) {
//加载配置类 此时的配置类等同于xml配置文件
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
//获取bean 在容器中的 name
String[] beanNames = applicationContext.getBeanDefinitionNames();
for (String name : beanNames) {
System.out.println(name);
}
}
这种方式在springboot中也大量的使用
6.手动注册方式
也许你还想自己手动注册一个bean,可以尝试如下写法
public static void main(String[] args) {
//加载配置类 此时的配置类等同于xml配置文件
ApplicationContext applicationContext = new AnnotationConfigApplicationContext();
//获取bean工厂
ConfigurableApplicationContext configurableContext = (ConfigurableApplicationContext) applicationContext;
BeanDefinitionRegistry registry = (DefaultListableBeanFactory) configurableContext.getBeanFactory();
//bean builder
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(Person.class);
BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
//手动注册bean
registry.registerBeanDefinition("manual", beanDefinition);
//获取bean 在容器中的 name
String[] beanNames = applicationContext.getBeanDefinitionNames();
for (String name : beanNames) {
System.out.println(name);
}
}
7.快速注册方式
如果你想快速将一个类注册到容器中,可以使用@Import
bean 默认name是类全名 例如:org.happybean.bean.Person
@Configuration
@Import({Person.class})
public class SpringConfig {
}
@Import中可以写多个class
8.批量注册方式
可以在@Import注解中添加一个ImportSelector类以实现批量注册的功能
自定义注册选择器
public class SpringImportSelector implements ImportSelector {
/**
* 注册的所有类
*
* @param annotationMetadata 当前类注解的信息 例如:@Import
* @return 所有需要注册到容器的全类名
*/
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
String[] className = {"org.happybean.bean.Animal"};
return className;
}
}
配置类
@Configuration
@Import(SpringImportSelector.class)
public class SpringConfig {
}
9.@Import注解 手动注册方式
此时你需要编写一个org.springframework.context.annotation.ImportBeanDefinitionRegistrar
实现类
public class SpringImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* @param annotationMetadata 当前类注解信息
* @param beanDefinitionRegistry beanDefinition 注册类
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
//判断是否存在 name为animal 的bean
if (!beanDefinitionRegistry.containsBeanDefinition("animal")) {
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(Animal.class);
BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
beanDefinitionRegistry.registerBeanDefinition("animal", beanDefinition);
}
}
}
使用方式同上
@Configuration
@Import(SpringImportBeanDefinitionRegistrar.class)
public class SpringConfig {
}