最近在工作用到了Spring框架,由于以前没有接触过Spring,就从Spring的官方文档开始学起。在“Quick Start”介绍了一个使用Spring做依赖性注入(dependency injection)的例子,该例子使用Maven或GRADlE进行管理的。作者以前没有接触过这两个项目管理工具,由于时间紧迫,就直接使用了Eclipse编译了这个例子。在Eclipse下的文件结构如下:
(由于没有使用项目管理工具,刚开始只加入了spring-context.jar和spring-core.jar,又根据异常信息导入了commouns-logging.jar、spring-expression.jar和spring-beans.jar。这就是不是项目管理工具的麻烦所在。)
hello/MessageService.java的代码如下:
package hello; public interface MessageService { String getMessage(); }
hello/MessagePrinter.java的代码如下:
package hello; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class MessagePrinter { final private MessageService service; @Autowired public MessagePrinter(MessageService service) { this.service = service; } public void printMessage() { System.out.println(this.service.getMessage()); } }
hello/App.java的代码如下:
package hello; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.*; @Configuration @ComponentScan public class App { @Bean MessageService mockMessageService() { return new MessageService() { public String getMessage() { return "Hello World!"; } }; } public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(App.class); MessagePrinter printer = context.getBean(MessagePrinter.class); printer.printMessage(); } }
编译运行该项目,会有以下输出:
“Quick Start”最后总结说这个充分体现了依赖性注入(dependency injection)的概念,至于如何体现的作者尝试做一下解析,权当对Spring Framework的一个初步认识。
项目的入口是App.java的main函数,在这个函数中首先声明了一个ApplicationContext对象context,并向下转型为AnnotationConfigApplicationContext对象。ApplicationContext是对一个应用(Application)提供配置的核心接口,AnnotationConfigApplicationContext是它的一个实现,可用来处理用@Configuration,@Component和JSR-330标注的类。由于App.java使用@Configuration进行了标注,故App.class可以作为AnnotationConfigApplicationContext构造器的的参数。
MessagePrinter printer = context.getBean(MessagePrinter.class);
这句代码通过context的getBean(Class<T>)方法得到了MessagePrinter的一个实例。为什么呢得到MessagePrinter类的一个实例呢?看getBean(Class<T>)的API解释:
Return the bean instance that uniquely matches the given object type, if any.
(如果给定的对象唯一匹配,就返回一个bean实例。)
printer.printMessage();
调用MessagePrinter的printMessage()方法,该方法如下实现:
public void printMessage() { System.out.println(this.service.getMessage()); }
其实是调用了接口MessageService的getMessage()方法,由于App.java中的mockMessageService()方法中的匿名内部类实现了MessageService接口,并在getMessage()方法中返回了“hello,world!”字符串,顾能在结果中输出"hello,world!"。
令人迷惑的是在mockMessageService()方法并没有被显式调用,为何其匿名内部类能被实例化并输出信息呢?原因就在于程序中使用的以下Spring标注。
@Configuration 用于标注一个类,表示bean定义的源文件(source);
@Bean 用于标注一个方法,表示一个方法实例化、配置或初始化一个新的对象(Object),这个对象被Spring的控制反转(IoC)容器管理,相当于Spring <bean />XML配置文件中<bean />元素。(详见官方文档)
@ComponentScan 用于标注一个类,表示扫描指定包中的@Component标注的类,并将这些类注册为Spring IoC容器的bean,亦相当于一个<bean />元素;
@Autowired 用于标注setter方法,构造器,包含多个参数的方法,集合等等,用于自动绑定;
@Component 用于标注一个类
由于MessagePrinter中有如下构造器:
@Autowired public MessagePrinter(MessageService service) { this.service = service; }
从这个构造器中可以到MessagePrinter依赖MessgeServie,并用@Autowired标注。这就表示在通过contexnt.getBean(MessagePrinter.class)得到MessagePrinter的一个实例时,会将对MessageService的依赖自动绑定到MessageService类,将查找所有能作为<bean />元素的类(@Component @Configuration标注)或方法(@Bean标注),而在App.java中有如下方法:
@Bean MessageService mockMessageService() { return new MessageService() { public String getMessage() { return "Hello World!"; } }; }
查找到这个方法后会将这个MessageService实现注入到MessagePrinter实例中,从而实现“hello,world!”的输出。
以上就是作者对Spring Framework文档“Quick Start”例子的解析,有不当之处=请多多指教。