Spring IOC容器初始化:XML和注解

124 阅读5分钟

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的这种方式。