7.6 Spring提供的Java配置管理

7.6 Spring提供的Java配置管理

Spring为不喜欢XML的人提供了一种选择:如果不喜欢使用XML来管理Bean,以及Bean之间的依赖关系, Spring允许开发者使用Java类进行配置管理
假如有如下Person接口的实现类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package org.crazyit.app.service.impl;

import org.crazyit.app.service.Axe;
import org.crazyit.app.service.Person;

public class Chinese implements Person
{
private Axe axe;
private String name;
// axe的setter方法
public void setAxe(Axe axe)
{
this.axe = axe;
}
// name的setter方法
public void setName(String name)
{
this.name = name;
}
// 实现Person接口的useAxe()方法
public void useAxe()
{
// 调用axe的chop()方法,表明Person对象依赖于axe对象
System.out.println("我是:" + name
+ axe.chop());
}
}

上面的Chinese类需要注入两个属性:nameaxe,本示例当然也为Axe提供了两个实现类StoneAxeSteelAxe。如果采用XML配置,相应的配置文件如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="GBK"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置chinese实例,其实现类是Chinese -->
<bean id="chinese" class="org.crazyit.app.service.impl.Chinese">
<!-- 驱动Spring执行setAxe()方法,以容器中id为stoneAxe的Bean为参数 -->
<property name="axe" ref="stoneAxe"/>
<!-- 驱动Spring执行setName()方法,以字符串"孙悟空"为参数 -->
<property name="name" value="孙悟空"/>
</bean>
<!-- 配置stoneAxe实例,其实现类是StoneAxe -->
<bean id="stoneAxe" class="org.crazyit.app.service.impl.StoneAxe"/>
<!-- 配置steelAxe实例,其实现类是SteelAxe -->
<bean id="steelAxe" class="org.crazyit.app.service.impl.SteelAxe"/>
</beans>

如果开发者不喜欢使用XML配置文件, Spring允许开发者使用Java类进行配置上面的XML配置文件可以替换为如下的Java配置类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package org.crazyit.app.config;

import org.springframework.context.annotation.*;
import org.springframework.beans.factory.annotation.*;

import org.crazyit.app.service.*;
import org.crazyit.app.service.impl.*;

@Configuration
public class AppConfig
{
// 相当于定义一个名为personName的变量,其值为"孙悟空"
@Value("孙悟空") String personName;
// 配置一个Bean:chinese
@Bean(name="chinese")
public Person person()
{
Chinese p = new Chinese();
p.setAxe(stoneAxe());
p.setName(personName);
return p;
}
// 配置Bean:stoneAxe
@Bean(name="stoneAxe")
public Axe stoneAxe()
{
return new StoneAxe();
}
// 配置Bean:steelAxe
@Bean(name="steelAxe")
public Axe steelAxe()
{
return new SteelAxe();
}
}

上面的配置文件中使用了Java配置类的三个常用注解。

注解 描述
@Configuration 用于修饰一个Java配置
@Bean 用于修饰一个方法,将该方法的返回值定义成容器中的一个Bean
@Value 用于修饰一个Field,用于为该Field配置一个值,相当于配置一个变量。

一旦使用了Java配置类来管理Spring容器中的Bean及其依赖关系,此时就需要使用如下方式来创建Spring容器:

1
2
ApplicationContext ctx = new
AnnotationConfigApplicationContext(AppConfig.class);

上面的AnnotationConfigApplicationContext类会根据**Java配置类**来创建Spring容器。不仅如此,该类还提供了一个register(Class)方法用于添加Java配置类。
使用Java配置类时,还有如下常用的注解。

注解 描述
@Import 修饰一个Java配置,用于向当前Java配置类中导入其他Java配置类。
@Scope 用于修饰一个方法,指定该方法对应的Bean的生命域。
@Lazy 用于修饰一个方法,指定该方法对应的Bean是否需要延迟初始化。
@DependsOn 用于修饰一个方法,指定在初始化该方法对应的Bean之前初始化所依赖的Bean

使用XML配置文件管理Bean和使用配置类管理Bean的对比

就普通用户习惯来看,还是使用XML配置文件管理Bean及其依赖关系更为方便,毕竟使用XML文件来管理Bean及其依赖关系是为了解耦。但这种Java配置类的方式又退回到Java代码耦合层次,只是将这种耦合集中到一个或多个Java配置类中。

为什么引入配置类来管理Bean

实际上, Spring提供@Configuration@Bean并不是为了完全取代XML配置,只是希望将它作为XML配置的一种补充。对于Spring框架的用户来说, Spring配置文件的”急剧膨胀”是一个让人头痛的点,因此Spring框架从2.0版本开始就不断地寻找各种对配置文件”减肥”的方法。
后面所介绍的各种注解也都是为了简化Spring配置文件而出现的,但由于注解引入时间较晚,因此在一些特殊功能的支持上,注解还不如XML强大。

使用XML还是使用注解

因此,在目前的多数项目中,

  • 要么**完全使用XML**配置方式管理Bean的配置,
  • 要么使用以注解为主、XML为辅的配置方式管理Bean的配置,想要完全放弃XML配置还是比较难的。

为什么会混用XML和注解

之所以会出现两者共存的情况,主要归结为三个原因:

  • 其一,目前绝大多数采用Spring进行开发的项目,几乎都是基于XML配置方式的, Spring在引入注解的同时,必须保证注解能够与XML和谐共存,这是前提;
  • 其二,由于注解引入较晚,因此功能也没有发展多年的XML强大,对于复杂的配置,注解还很难独当一面,在一段时间内仍然需要XML的配合才能解决问题;
  • 其三, SpringBean的配置方式与Spring核心模块之间是解耦的,因此,改变配置方式对Spring的框架自身是透明的。 Spring可以通过使用Bean后处理器( BeanPostProcessor)非常方便地增加对于注解的支持。

    以XML配置为主还是以Java类配置为主

    因此,在实际项目中可能会混合使用XML配置和Java类配置,在这种混合下存在一个问题:项目到底以XML配置为主,还是以Java类配置为主呢?

    以XML配置为主时

    如果以XML配置为主,就需要让XML配置能加载Java类配置。这并不难,只要在XML配置中增加如下代码即可:
    1
    2
    3
    4
    5
    <?xml version="1.0" encoding="GBK"?>
    <beans ....>
    ....
    <bean class="org.crazyit.app.config.AppConfig"/>
    </beans>
    由于以XML配置为主,因此应用创建Spring容器时,还是以这份XML配置文件为参数来创建ApplicationContext对象。那么Spring会先加载这份XML配置文件,再根据这份XML配置文件的指示去加载指定的Java配置类。

    如果以Java类配置为主

    如果以Java类配置为主,就需要让Java配置类能加载XML配置。这就需要借助于@)ImportResource注解,这个注解可修饰Java配置类,用于导入指定的XML配置文件。也就是在Java配置类上增加如下注解:
    1
    2
    3
    4
    5
    6
    7
    @Configuration
    // 导入XML配置
    @ImportResource("classpath:/beans.xml")
    public class AppConfig
    {
    ......
    }
    由于以Java类配置为主,因此应用创建Spring容器时,应以Java配置类为参数,通过创建AnnotationConfigApplicationContext对象作为Spring容器。那么Spring会先加载这个Java配置类,再根据这个Java配置类的指示去加载指定的XML配置文件。