在对 Spring 进行显示配置的时候,有两种可选方案:Java 和 XML。Java 配置是更好的选择,因为它更强大,类型安全且对重构友好。因为它就是 Java 代码,就像程序中的其他 Java 代码一样。
同时,JavaConfig与其他的Java代码又有所区别,尽管它与其他的组件一样都使用相同的语言进行表述,但JavaConfig是 配置代码。这意味着它不应该包含任何业务逻辑,JavaConfig也不应该侵入到业务逻辑代码 之中。
接下来,让我们看一下如何通过JavaConfig显式配置Spring
基本概念:@Configuration 和 @Bean
@Configuration 被用于类上,指明这是一个配置类,该类应该包含在Spring应用上下文中如何创建bean的细节。@Bean 用于方法上,这个方法返回一个被装配的对象,方法体中包含了最终产生bean实例的逻辑。
看一个例子:
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
上面的 AppConfig 类将和下面的 XML 配置有一样的效果
<beans>
<bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>
默认情况下,bean 的 ID 与带有 @Bean 注解的方法名是一样的。在本例中,bean的名字将会是 myService。如果你想为其设置成一个不同的名字的话,那么可以重命名该方法,也可以通过 name 属性指定一个不同的名字:
@Bean(name = "hisService")
public MyService myService() {
return new MyServiceImpl();
}
开启自动装配也是很简单的
@Configuration
@ComponentScan(basePackages = "com.acme")
public class AppConfig {
...
}
使用 @ComponentScan 注解即可,它等同于如下 XML 配置
<beans>
<context:component-scan base-package="com.acme"/>
</beans>
借助 JavaConfig 实现注入
我们前面所声明的 myService bean 是非常简单的,它自身没有其他的依赖。但现在,我 们需要声明 MyApp bean,它依赖于 myService 。在 JavaConfig 中,要如何将它们装配在一起呢?
在 JavaConfig 中装配 bean 的最简单方式就是引用创建 bean 的方法。例如,下面就是一种声明 myApp 的可行方案:
@Bean
public MyApp myApp() {
return new MyApp(myService());
}
可以看到,通过调用方法来引用 bean 的方式有点令人困惑。其实还有一种理解起来更为简单的方式:
@Bean
public MyApp myApp(MyService myService) {
return new MyApp(myService);
}
在这里,myApp() 方法请求一个 MyService 作为参数。当 Spring 调用myApp() 创建 MyApp bean 的时候,它会自动装配一个 MyService 到配置方法之中。然后,方法体就可以按照合适的方式来使用它。
通过这种方式引用其他的 bean 通常是最佳的选择,因为它不会要求将MyService 声明到 同一个配置类之中。在这里甚至没有要求MyService 必须要在 JavaConfig 中声明,实际上 它可以通过组件扫描功能自动发现或者通过 XML 来进行配置。
使用 JavaConfig 配置 Java Web
配置DispatcherServlet
DispatcherServlet 是 Spring MVC的核心。在这里请求会第一次接触到框架,它要负责将请求路由到其他的组件之中。
按照传统的方式,像 DispatcherServlet 这样的 Servlet 会配置在 web.xml 文件中,这个文件会放到应用的 WAR 包里面。当然,这是配置 DispatcherServlet 的方法之一。但是,借助于 Servlet 3 规范和 Spring 3.1的功能增强,这种方式已经不是唯一的方案了。
我们会使用 Java 将 DispatcherServlet 配置在 Servlet 容器中,而不会再使用 web.xml 文 件。如下的程序清单展示了所需的Java类。
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] {AppConfig.class};
}
//指定配置类
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] {WebConfig.class};
}
//将 DispatcherServlet 映射到 "/"
@Override
protected String[] getServletMappings() {
return new String[] {"/"};
}
}
要理解上面的程序清单是如何工作的,我们可能只需要知道扩 展AbstractAnnotationConfigDispatcherServletInitializer的任意类都会自 动地配置DispatcherServlet和Spring应用上下文,Spring的应用上下文会位于应用程序 的Servlet上下文之中。
尽管它的名字很长,但是 AbstractAnnotationConfigDispatcherServletInitializer 使用起来很简 便。在程序清单中,WebAppInitializer 重写了三个方法。
第一个方法是 getServletMappings(),它会将一个或多个路径映射 到DispatcherServlet上。在本例中,它映射的是“/”,这表示它会是应用的默认 Servlet。它会处理进入应用的所有请求。
为了理解其他的两个方法,我们首先要理解DispatcherServlet和一个Servlet监听器(也 就是ContextLoaderListener)的关系。
当 DispatcherServlet 启动的时候,它会创建 Spring 应用上下文,并加载配置文件或配置 类中所声明的bean。在程序清单的 getServletConfigClasses() 方法中,我们要求 DispatcherServlet 加载应用上下文时,使用定义在 WebConfig 配置类(使用Java配置)中的 bean。
但是在Spring Web应用中,通常还会有另外一个应用上下文。另外的这个应用上下文是 由ContextLoaderListener创建的。
getServletConfigClasses() 方法返回的带有 @Configuration 注解的类将会用来定义 DispatcherServlet 应用上下文中的 bean。getRootConfigClasses() 方法返回的带有 @Configuration 注解的类将会用来配置 ContextLoaderListener 创建的应用上下文中的bean。
在本例中,根配置定义在 AppConfig 中,DispatcherServlet 的配置声明在 WebConfig 中。
启用Spring MVC
我们有多种方式来配置 DispatcherServlet,与之类似,启用Spring MVC组件的方法也不仅一种。以前,Spring 是使用 XML 进行配置的,你可以使用 <mvc:annotationdriven>
启用注解驱动的Spring MVC。
我们所能创建的最简单的 Java Spring MVC 配置就是一个带有 @EnableWebMvc 注解的类
@Configuration
@EnableWebMvc
public class WebConfig {
}
这可以运行起来,它的确能够启用Spring MVC,但还有不少问题要解决:
- 没有配置视图解析器。如果这样的话,Spring 默认会使用 BeanNameViewResolver,这个视图解析器会查找 ID 与视图名称匹配的 bean,并且查找的 bean 要实现 View 接口,它以这样的方式来解析视图。
- 没有启用组件扫描。这样的结果就是,Spring只能找到显式声明在配置类中的控制器。
- 这样配置的话,DispatcherServlet 会映射为应用的默认 Servlet,所以它会处理所有的请求,包括对静态资源的请求,如图片和样式表(在大多数情况下,这可能并不是你想要的效果)。
因此,我们需要在 WebConfig 这个最小的 Spring MVC 配置上再加一些内容,从而让它变得真正有用。如下程序清单中的 WebConfig 解决了上面所述的问题。
@Configuration
@EnableWebMvc //启用 Spring MVC
@ComponentScan("com.web") //启用组件扫描
public class WebConfig extends WebMvcConfigurerAdapter {
//配置视图解析器
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
resolver.setExposeContextBeansAsAttributes(true);
return resolver;
}
//配置静态资源处理
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}