创建应用对象之间协作关系的行为通常称为装配( wiring ),这也是依赖注入( DI )的本质。
配置 Spring 容器最常见的有三种方法:
1. 在 XML 中进行显式配置;
2. 在 Java 中进行显式配置;
3. 隐式的 bean 发现机制和自动装配;
Spring 从两个角度来实现自动化装配:
- 组件扫描( component scanning ): Spring 会自动发现应用上下文中所创建的 bean 。
- 自动装配( autowiring ): Spring 自动满足 bean 之间的依赖。
组件扫描和自动装配组合在一起就能发挥出强大的威力,它们能够将你的显式配置降低到最少。
自动化装配bean的方式:
首先构建一个spring的java工程:
写一个实体类:
package main.java.sia.knights.config.testDI;
import org.springframework.stereotype.Component;
@Component
public class Student {
public void study(){
System.out.println ("学生学习中。。。。");
}
}
你需要注意的就是 Student 类上使用了 @Component 注解。这个简单的注解表明该类会作为组件类,并告知 Spring 要为这个类创建 bean 。没有必要显式配置 bean,因为这个类使用了 @Component 注解,所以 Spring 会为你把事情处理妥当。
不过,组件扫描默认是不启用的。我们还需要显式配置一下 Spring ,从而命令它去寻找带有 @Component 注解的类,并为其创建 bean 。下面的配置类展现了完成这项任务的最简洁配置:
package main.java.sia.knights.config.testDI;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan
public class ConfigScan {
}
这上面有两个注解,
- @Configuration
从Spring3.0,@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。
- @ComponentScan
ConfigScan 类并没有显式地声明任何 bean ,只不过它使用了 @ComponentScan 注解,这个注解能够在 Spring 中启用组
件扫描。
如果没有其他配置的话, @ComponentScan 默认会扫描与配置类相同的包。因为 ConfigScan 类位于 testDI包中,因此 Spring将会扫描这个包以及这个包下的所有子包,查找带有 @Component 注解的类。这样的话,就能发现 Student ,并且会在 Spring 中自动为其创建一个 bean 。
下面进行测试:
package main.java.sia.knights.config.testDI;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ConfigScan.class)
public class TestDI {
@Autowired
public Student Student;
@Test
public void testDi(){
Student.study ();
}
}
TestDI 使用了 Spring 的 SpringJUnit4ClassRunner ,以便在测试开始的时候自动创建 Spring 的应用上下文。注
解 @ContextConfiguration 会告诉它需要在 CDPlayerConfig 中加载配置。因为 CDPlayerConfig 类中包含了 @ComponentScan ,
因此最终的应用上下文中应该包含 Student 的bean。
使用 @Autowired能够从spring中获取Student 的bean,并将其注入到测试代码中,看下测试结果:
测试结果显示我们已经将student这个类交给spring管理,并使用到了它。
下面补充小知识:
为组件扫描的 bean 命名
Spring 应用上下文中所有的 bean 都会给定一个 ID 。在前面的例子中,尽管我们没有明确地为 Student设置 ID ,但 Spring 会根据类名为其指定一个 ID 。具体来讲,这个 bean 所给定的 ID 为 student,也就是将类名的第一个字母变为小写。如果想为这个 bean 设置不同的 ID ,你所要做的就是将期望的 ID 作为值传递给 @Component 注解。如下面代码所示:
@Component("teacher")
public class Student {
public void study(){
System.out.println ("学生学习中。。。。");
}
}
还有另外一种为 bean 命名的方式,这种方式不使用 @Component 注解,而是使用 Java 依赖注入规范( Java Dependency Injection )中所提供的 @Named 注解来为 bean 设置 ID :
package main.java.sia.knights.config.testDI;
import javax.inject.Named;
@Named("teacher")
public class Student {
public void study(){
System.out.println ("学生学习中。。。。");
}
}
这里你需要导入jar包javax.inject-1.jar,不然spring是找不到的,这种方式spring也是可以进行DI的;
通过 XML 启用组件扫描
创建一个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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="main.java.sia.knights.config.testDI"></context:component-scan>
</beans>
然后写一个配置类导入xml:
package main.java.sia.knights.config.testDI;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
@Configuration
@ImportResource("classpath:main/java/sia/knights/config/testDI/spring.xml")
public class ScanConfig {
}
最后进行单元测试:
package main.java.sia.knights.config.testDI;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ScanConfig.class)
public class TestDI2 {
@Autowired
public Student Student;
@Test
public void testDi(){
Student.study ();
}
}
同样,也能得到相同的结果。
通过xml注入bean
将上面的spring.xml文件加上bean的导入,并注释掉自动发现的component-scan
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--<context:component-scan base-package="main.java.sia.knights.config.testDI"></context:component-scan>-->
<bean id="student" class="main.java.sia.knights.config.testDI.Student"></bean>
</beans>
然后进行相同的测试,发现效果一样
通过java代码实现装配bean
先看下配置类怎么写:
package main.java.sia.knights.config.testDI;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class KnightConfig {
@Bean
public Student student() {
return new Student ();
}
}
测试类:
package main.java.sia.knights.config.testDI;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = KnightConfig.class)
public class TestDI3 {
@Autowired
public Student student;
@Test
public void testDi(){
student.study ();
}
}
测试结果也是相同的,大家不妨试一下。
总结
- 在 Spring 中装配 bean 的三种主要方式:自动化配置、基于 Java 的显式配置以及基于 XML 的显式配置。
- 建议尽可能使用自动化配置,以避免显式配置所带来的维护成本。但是,如果你确实需要显式配置 Spring 的话,应该优先选择基于 Java的配置,它比基于 XML 的配置更加强大、类型安全并且易于重构。
参考资料:spring in action第四版