Spring框架中支持了两种不同形式的IOC容器初始化方式,一种是基于XML的容器初始化,另外一种是基于注解的初始化方式,初始化容器时使用的具体初始化类不同。
基于xml的方式:
ClasspathXmlApplicationContext
基于annotation的方式:
AnnotationConfigApplicationContext
他们都是AbstractApplicationContext的子类,在该类中定义了基于xml和annotation初始化容器及注册bean的公共流程。
基于xml的方式
- 示例Bean对应的类Person
public class Person {
private String uuid;
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
@Override
public String toString() {
return "Person{" +
"uuid='" + uuid + '\'' +
'}';
}
}
- xml配置文件beans.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"
default-autowire="byName" default-lazy-init="false">
<!-- 指定Bean的ID和Bean实例对应的类型 -->
<bean id="person" class="com.wb.spring.beans.Person">
<!-- 指定Bean的属性名称及属性值,此处只是一个基础属性,
还可以指定引用类型属性,用来引用另外一个bean -->
<property name="uuid" value="123"/>
</bean>
</beans>
- 基于xml方式的容器初始化测试类
public class TestMain {
// 正常测试应该使用Junit或者spring的测试框架
public static void main(String[] args) {
// 加载beans.xml
// 这一步中,大过程为:加载及解析xml->生成bean定义->将bean定义注册到ioc容器中
// -> 按照bean定义实例化容器中的bean,但该步骤中详细的有12大步操作,后面会一一介绍
AbstractApplicationContext acx =
new ClassPathXmlApplicationContext("beans.xml");
Person person = (Person) acx.getBean("person");
System.out.println(person);
acx.close();
}
}
// 输出结果:Person{uuid='123'}
基于annotation的方式
-
Spring提供的注册Bean的基础注解
1、@Configuration注解
@Configuration用来标注一个具体的类,标注之后,这个类如果被Spring容器扫描到之后,会将该类作为一个component注册到ioc容器中,该注解其实本身就是一个component,点开注解的源码可以看到是使用了@Component注解标注的。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 本身自带了@Component注解,所以配置类会被作为一个bean注册到容器中
@Component
public @interface Configuration {
// 用来指定配置类的名称
@AliasFor(annotation = Component.class)
String value() default "";
}
2、@Bean注解
@Bean注解是一个用来标注某一个配置类中的非private final方法的,标注之后,在容器启动时,扫描该配置类之后,会按照方法中的逻辑去创建和初始Bean,并注册到容器中。@Bean中的属性比较多,本篇文章不做介绍,下篇文章具体介绍每个属性的含义和具体作用。
- 使用annotation方式创建Bean配置类PersonConfig
@Configuration
public class PersonConfig {
// 注意几点:
//(1)此处的方法不能是private或者final修饰的,否则会提示异常
//(2) 使用该方法创建完成的bean,默认的bean的名称为方法名,即:person
//(3) 如果需要在创建的过程中,加入一些逻辑,可以直接在方法中完成
//(4) 方法的返回值类型为bean对应的类型
@Bean
public Person person() {
Person person = new Person();
person.setUuid(UUID.randomUUID().toString());
return person;
}
}
测试类TestMain
public class TestMain {
public static void main(String[] args) {
// 按照指定的配置类初始化Spring容器,有多个配置类可以传入多个,构造函数中支持了可变参数的形式
AnnotationConfigApplicationContext acx =
new AnnotationConfigApplicationContext(PersonConfig.class);
// 根据配置类中的方法名称获取容器中的Bean实例
Person person = (Person) acx.getBean("person");
System.out.println(person);
// 关闭容器,如果指定配置类时使用ApplicationContext去接收,则不能直接调用该方法
acx.close();
}
}
// 输出结果:
Person{uuid='03c9ec72-94f3-4134-b890-3831a3f2c202'}
XML方式和Annotation方式的对比
1.1、优劣区别
(1)灵活度上:xml方式是具有一定局限性的,比如:在创建bean的时候,需要加入一些定制化的逻辑,当满足什么条件时在bean中加入什么样的属性,使用xml的方式就会比较麻烦,但是使用JavaConfig,即Annotation的方式更加灵活,可以很轻松地加入这些创建逻辑,而且代码更加清晰。
(2)安全性上:使用JavaConfig方式,属于类型安全的一种方式,通常在集成开发工具中,如果某一个类名写错,直接会编译提示错误,提早发现错误,而使用xml的方式,在某些编译器中是检测不出来的,类名写错之后,只有在启动或者运行的时候才会提示错误,存在一定的安全隐患。
(3)方便程度上:使用JavaConfig的方式如果在线上需要修改某一个配置,只能重新编译,然后替换配置类对应的class。而使用xml的方式,不需要重新编译,直接修改xml,重启即可生效。
(4)可读性上:对于Java程序猿,通常还是比较喜欢阅读java代码的,而对xml是有一定排斥性的,我本身就是这样感觉。xml的可阅读性是比JavaConfig差很多的。
1.2、使用建议 (1)如果对代码没什么要求,两种都可以,熟悉哪个用哪个;
(2)如果希望后期去学习一些Spring生态圈中的其他框架,建议尽早使用JavaConfig这种方式。因为在一些Spring生态圈中的微服务框架,例如:Spring-Cloud和Spring-Boot中,大量使用了基于JavaConfig的这种方式。