I code it

Code and Life

Spring的注入示例

spring在3.0之后分离成了差不多20个分离的jar包,又对JDBC/JMS等的支持,对AOP的支持,WEB开发的支持,以及核心的依赖注入部分(几乎所有的spring应用都或多或少的会涉及到spring的依赖注入特性)。下图是一个3.0的依赖注入子模块需要的包结构:

spring-core

这篇文章中,我将介绍使用spring的DI来完成一个简单的示例:

exporter

对于Exporter来说,它面向Component这个接口,而不关注其实际的实现,比如Componet的一个实现为Label:

   1: public class Label implements Component {
   2:     private String text;
   3:  
   4:     public Label(String text) {
   5:         this.text = text;
   6:     }
   7:  
   8:     @Override
   9:     public String render() {
  10:         return "<label>" + text + "</label>";
  11:     }
  12: }

而使用Exporter的时候,可以为其传递一个Component的实例(如Label):

   1: public class Main {
   2:     public static void main(String[] args) {
   3:         Exporter exporter = new Exporter(new Label("This ia a label"));
   4:         System.err.println(exporter.export());
   5:     }
   6: }

可以看到,Main这个类中需要知道Label的存在,因为我们需要new一个Component的实例。而使用spring,开发人员很少自己实例化一个具体的类,而将对象的生命周期托管给了spring框架。使用spring的DI,一般流程为:开发人员定义很多的bean,然后在配置文件中将这些bean关联起来,然后将剩下的一切交给spring框架。

比如上面的例子中,我们可以创建一个di.xml文件:

   1: <?xml version="1.0" encoding="UTF-8" ?>
   2: <beans xmlns="http://www.springframework.org/schema/beans"
   3:         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   4:         xsi:schemaLocation="http://www.springframework.org/schema/beans
   5:         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
   6:  
   7:     <bean id="label" class="org.icodeit.di.component.Label">
   8:         <constructor-arg value="This is a label" />
   9:     </bean>
  10:  
  11: </beans>

在这个xml文件中,我们定义了一个id为label的bean,这个bean对应的Java类为org.icodeit.di.component.Label。然后在Main中,使用spring的ApplicationContext(用以根据bean的id来查找实例)对象及ClassPathXmlApplicationContext(用以加载配置文件):

   1:  
   2: public class Main {
   3:     public static void main(String[] args) {
   4:         ApplicationContext context = new ClassPathXmlApplicationContext(
   5:                 "org/icodeit/di/di.xml");
   6:  
   7:         Component component = (Component) context.getBean("label");
   8:         Exporter exporter = new Exporter(component);
   9:         System.err.println(exporter.export());
  10:     }
  11: }

这样,在使用Exporter的地方(Main中),我们就无需知道Label(接口Component的实现)的存在了,这样更容易我们关注接口,而不是实现。

更进一步,我们甚至可以将Exporter的初始化交给spring:

   1: <bean id="label" class="org.icodeit.di.component.Label">
   2:     <constructor-arg value="This is a label" />
   3: </bean>
   4:  
   5: <bean id="exporter" class="org.icodeit.di.exporter.Exporter">
   6:     <constructor-arg ref="label" />
   7: </bean>

id为“exporter”的bean引用了id为“label”的bean,这个动作在spring中叫做wiring。就是将配置文件中的bean连接起来。最后,Main将会非常简单:

   1:  
   2: public class Main {
   3:     public static void main(String[] args) {
   4:         ApplicationContext context = new ClassPathXmlApplicationContext(
   5:                 "org/icodeit/di/di.xml");
   6:  
   7:         Exporter exporter = (Exporter)context.getBean("exporter");
   8:         System.err.println(exporter.export());
   9:     }
  10: }

Exporter现在完全面向Component接口,而项目中其他地方也均通过id来引用接口的实例,与具体的实现完全分离。

   1: public class Exporter {
   2:     private Component component;
   3:  
   4:     public Exporter(Component component) {
   5:         this.component = component;
   6:     }
   7:  
   8:     public String export() {
   9:         return component.render();
  10:     }
  11: }

Comments