作为J2EE核心框架的 Spring,其最基本的一项职能是管理bean,即管理Java对象。在 Spring 的概念中,这个管理所有bean的大管家就是Container。有了Container这个概念,每一个Java类便可以更加独立地存在。创建出来的类A,可以将自己注册进这个container,成为一个bean,供它管理。在此之后,所有用到这个类A的地方,便可以直接向container索取。如此,每个类只需要关心自己的业务逻辑,而不必再费心去构建自己依赖的其它类。每一个类都可以把其它类当做是一个独立的服务,随时可以供自己索取。从而实现整体上各个类之间的解耦。
如上所述,对这个container来讲最重要的有两个工作:一个是提供一种方式,能够让一个Java类把自己注册进container成为一个bean;另一个是提供一种方式,能够让一个Java类A,从container中获取到自己需要的依赖类B。
完成这两个工作,有三种基本的形式:
- automatic configuration
- explicit Java-based configuration
- explicit XML-based configuration
Automatic Configuration
这应该是最为推荐、也作为好用的模式。无论是一个Java类将自己注册进Spring container成为bean,还是从container那里取出自己需要的bean依赖,都非常方便。这两个过程对应的概念为:
- Component Scanning: tell Spring what classes need to be created as beans.
- Autowiring: get the matched bean from Spring.
package terence.xie.service;
import org.springframework.stereotype.Component;
@Component
public class TerryCD implements CompactDisc {
private String title = "Your Spring";
private String artist = "Terry";
public void play() {
System.out.println(String.format("Title = '%s', artist = '%s'", title, artist));
}
}
这里最重要的是 @Component
这个Java注解(annotation)。这个annotation就向Spring container表征了,我需要被创建为bean。
另一方面,对于container来讲,只是有这个 @Component
还不够。虽然遇到这个annotation就知道要把它创建为bean,但是,我从哪里去搜索这一堆堆的类呢?也即是,对container来讲,它首先需要知道从哪个地方去搜索出一堆备选的Java类,然后再从这堆Java类中一个个地看它们是否被打上了 @Component
annotation,进而将其注册为bean。
需要一个单独的类,来说明container应该从什么地方搜索备选类,这就是 XXXConfig.java
的作用。
package terence.xie.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ComponentScan;
@Configuration
@ComponentScan(basePackages = "terence.xie")
public class CDConfig {
}
对这个类来讲,annotation @Configuration
用于告知container这个类将被当做配置文件处理。虽然它依旧是以Java文件的方式来呈现,但在逻辑上应当只用作提供bean的配置文件,而不应该做其它多余的事情。
在这里,整个 CDConfig
类表面上没有做任何事,但annotation @ComponentScan
告知了Spring container需要从package terence.xie
去寻找是否需要创建为bean的Java类,即寻找被标注为 @Component
的类。
可以看到,无论是 “告知Spring应该被注册为bean” 还是 “从Spring获取依赖的bean” 似乎都没有什么明显的方法。这就是 Autowired 的灵活和方便之处。仅仅依靠 “注解的发现” ,将其他繁琐的发现机制逻辑隐藏于Spring之后,实在是非常优雅。这也是目前最受推荐的注册bean、获取bean的方式。
Explicit Java-based configuration
使用 Automatic Configuration 的机制虽然方便,但在某些特定情况还无法满足开发的需求。例如,你想创建一个第三方的类作为bean,但你又没有权限修改第三方的源码,此时你便无法将 @Component
这个annotation加入到这个第三方类的源码上。这样 Automatic Configuration 的机制便无法使用了。
对这个问题的解决方案是,可以继续使用上面创建的 XXXConfig.java
类。上面的 CDConfig.java
内部并没有任何代码,仅使用了类级别的注解来指定container发现bean的路径。通过增加 @Bean
注解在内部,可以更为灵活地将bean注册进container。
package terence.xie.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ComponentScan;
@Configuration
@ComponentScan(basePackages = "terence.xie")
public class CDConfig {
@Bean
public CompactDisc terryCd() {
return new TerryCD();
}
}
可以看到,这里将 TerryCD
注册进container成为bean,并不需要改动 TerryCD
的源码,也不需要 TerryCD
内部必须有 @Component
存在。这种方式,即是手动、显式地将一个Java类注册进container。
这里 @Bean
的意思是,将返回的类作为一个bean注册进container。至于其返回类的方法 terryCD()
是不重要的,重要的是这个方法的返回值。所以,当你需要将一个类A注册为bean时,你只需要在 XXXConfig.java
中提供一个方法,标记好annotation @Bean
,将其返回类型设定为类型A即可。
Explicit XML-based configuration
同 Explicit Java-based configuration 很相似的另一种显式声明bean的方式是使用 XML 文件。这里提供它的说明,更多的是处于对 legacy code 的理解。很多第三方的、创建于很早之前的项目,都会以这种方式来注册bean。如果是从零开始,那一定会选择 automatic configuration 或者 explicit java-based configuration 的方式。
<?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
http://www.springframework.org/schema/context">
<!-- configuration details go here -->
</beans>
对, xml-based configuration 会显得有些复杂。上面这个xml给出了一个声明bean的pattern,但其实里面没有任何一个实际的bean声明。也即是说,只要你想要声明bean,你至少得先写下上面这么多的xml代码。
如果要声明一个bean,只需要在其中添加标签 <bean id="compactDisc" class="terence.xie.service.TerryCD" />
即可:
<?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
http://www.springframework.org/schema/context">
<!-- configuration details go here -->
<bean id="compactDisc" class="terence.xie.service.TerryCD" />
</beans>
这里的 id
将作为整个bean的名称注册进container,而 class
指定了需要被注册成为bean的类。