JavaConfig

JavaConfig的历史

  Spring在最初的时候,一直是采用XML作为容器配置的手段,但随着现代项目的复杂性越来越高,Spring项目大量的烂用XML反而变成一个灾难,遭受了非常严励的一个批评。由于Spring会把几乎所有的业务类都以Bean的形式配置在XML文件中,造成了大量的XML文件。使用XML来配置Bean失去了编译时的类型安全检查。大量的XML配置使得整个项目变得更加复杂。Rod Johnson也注意到了这个非常严重的问题。当随着JAVA EE 5.0的发布,其中引入了一个非常重要的特性——Annotations(注解)。注解是源代码的标签,这些标签可以在源代码层进行处理或通过编译器把它融入到class文件中。在JAVA EE 5以后的版本中,注解成为了一个主要的配置选项。

  相对于传统的XML配置方式,JavaConfig有如下的优势:

  1. 它是纯Java的配置方式,这意味着你可以利用所有面向对象的优势,重用、继承、多态以及类型检查等。
  2. 拥有对于实例及其依赖更完全的控制权。
  3. 拥有更好的IDE支持。
  4. 配置与业务逻辑是在一起的,而不是分散成代码和配置。
  5. 当然,它仍然是和 XML 兼容的,你可以同时在项目中使用 XML 和 JavaConfig 进行配置。

常见的JavaConfig用法

​ 我想大部分同学都已经使用JavaConfig来注解我们的SpringBean了,比如我们常用的:

@Component 通用组件,可以标注在任意的SprinbBean上
@Repository 通常作为存储层的Bean定义,派生自 @Component
@Service 通常作为服务层的Bean定义,派生自 @Component
@Controller 通常作为控制层的Bean定义,派生自 @Component

​ 当然,以上所有的注解都必须配合ComponentScan才能生效,比如我们会在XML配置中配置如下的节点:<component-scan base-package="packages.to.scan"或者更加 JavaConfig 的做法是通过Java代码来声明,如下所示:

1
2
3
4
@Configuration
@ComponentScan("packages.to.scan")
public class SpringConfig {
}

@Configuration 的用法

@Component@Repository@Service@Controller等这些注解就不讲解了,实在太过于简单和常见,我们主要讲解一下@Configuration的用法,习惯于用XML配置的同学可能对这个注解了解得比较少,也很少会使用。

​ 在上面的代码中我们看到一个注解@Configuration,这个注解是Spring基于JavaConfig配置的核心,这个注解的说明如下:

Indicates that a class declares one or more {@link Bean @Bean} methods and may be processed by the Spring container to generate bean definitions and service requests for those beans at runtime

​ Spring会去解析标注这个注解的类,将它作为Spring容器的配置入口,它将会通过一系列的方法定义SpringBean。比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Configuration
public class SpringConfig {

@Bean
public ExternalObjectA externalObjectA() {
return new ExternalObjectA();
}

@Bean
public ExternalObjectB externalObjectB1() {
return new ExternalObjectB(externalObjectA());
}

@Bean
public ExternalObjectB externalObjectB2() {
return new ExternalObjectB(externalObjectA());
}
}

​ 这段代码就是通过JavaConfig定义了三个SpringBean,需要注意的是:

  1. ExternalObjectB类型的Bean注册了2个,分别来自于externalObjectB1()方法的调用和externalObjectB2()方法的调用,它们的名字分别来自于@Bean对应的方法名。

  2. ExternalObjectAexternalObjectB1externalObjectB2以参数的方式依赖。

  3. 由于ExternalObjectA也被注解为@Bean,同时,默认的SpringBean的Scope是Singleton,所以虽然被2个Bean依赖以参数方式调用了2次,但实际上externalObjectA()只会执行一次。

通过以下的代码测试:

1
2


@Import的用法

​ 在现实的编码过程中,我们通常会将所需要的SpringBean分门别类的在不同的@Configuration类中进行声明配置,而不是写一个超级大的配置类来管理所有的SpringBean,有点类似于我们在XML配置时写好几个文件比如application-context-dao.xmlapplication-context-service.xml之类的。

​ 在这种情况下,我们就需要在不同的场景组合不同的配置类,这个组合的注解就是@Import,该注解可以将其它的@Configuration导入到当前的配置中。比如:

1
2
3
4
5
@Configuration
@Import({ SpringConfig1.class, SpringConfig2.class })
public class JavaConfigImport {

}

​ 同时,如果其它的配置是在XML文件中,也可以通过@ImportResource来导入,也是非常方便的,比如:

1
2
3
4
5
@Configuration
@ImportResource("classptah: /application-context-*.xml")
public class JavaConfigImportResource {

}