Spring MVC是Spring框架中用于Web应用快速开发的一个模块,基于Servlet API构建的原始WEB框架,正式名称是"Spring Web MVC",来自于它的源模块(spring-webmvc),不过我们通常称之为Spring MVC。其中MVC是Model-View-Controller的缩写。主要是DispatcherServlet。
客户端向服务端发送请求,请求首先发送到DispatcherServlet(前端控制器)
DispatcherServlet接收到请求后,会使用HandlerMapping类(映射处理器)遍历查找具体的Handler(也就是Controller)。根据request请求的URL等信息查找能够处理的Handler。
handlerMapping找到对应的Handler,并不是直接返回一个Handler原始对象,而是一个Handler执行链。这个执行链中包含了拦截器和处理请求的Handler。
即如果配置了拦截器intercrptor,会构造HandlerExecutionChain类,HandlerMapping类会将这个类的对象返回给DispatcherServlet
DispatcherServlet拿到返回的Handler后会调用HandlerAdapter类去执行Handler
HandlerAdapter类执行完成Handler后返回一个ModelAndView对象(封装了处理结果数据和视图名称信息)给DispatcherServlet
DispatcherServlet接收到ModelAndView后,会根据视图名来调用viewResolver类进行视图解析
viewResolver类根据逻辑视图解析成一个真正的view视图返回给DispatcherServlet
DispatcherServlet接收到view视图后,根据model进行视图中数据的填充,即视图渲染,完成后返回给客户端。
源码只有一个jar包。包含了aop,beans,context,core,jdbc,orm等。仅支持xml配置。
1.增加了对于注解的配置。提供了XML命名空间和AspectJ支持。
@Repository 将数据访问层的类标识为Bean。
2.applicationContext.xml文件的头文件由1.2的.dtd规则变成了.xsd规则。
Spring2.5之前,通过实现Controller接口来定义处理器类。
1.提供了若干新特性用于简化web应用开发部署,支持jdk5。
2.添加了可扩展的xml配置,新增了注解驱动配置,引入了完整的Annotation,无需使用Controller继承接口的方式,而是基于@Controller和@RequestMapping等注解配置MVC应用 。
需要通过处理器映射DefaultAnnotationHandlerMapping和处理器适配器AnnotationMethodHandlerAdapter开启支持@Controller和@RequestMapping注解的处理器。
3.增加了注释驱动annotation-driven的配置支持。
4.提供了对JSR-250的支持
JSR-250规范定义了两个用于指定声明周期方法的注解:
@PostConstruct:初始化之后执行的回调方法 ,替代init-method
@PreDestory:销毁之前的回调方法,替代destory-method
使用时还需要激活Bean的后处理器。在xml文件中添加:context:annotation-config
5.Spring web mvc不再在spring.jar中,发布在lib/modules下。包是spring-webmvc.jar和spring-webmvc-protler.jra。
随着java EE规范一起发布,支持jdk6。分成了多个jar包进行管理。
通过@PathVariable注解和一些其他特性支持。
用于支持mvc配置。
mvc:annotation-driven:自动注册基于注解风格需要的DefaultAnnotationHandlerMapping,AnnotationMethodHandlerAdapter
mvc:interceptors:注册自定义的处理器拦截器;
mvc:view-controller:和ParameterizableViewController类似,收到相应请求后直接选择相应的视图;
mvc:resources:逻辑静态资源路径到物理静态资源路径的支持;
mvc:default-servlet-handler:当在web.xml中DispatcherServlet使用/映射时,能映射静态资源(当Spring Web MVC框架没有处理请求对应的控制器时(如一些静态资源),转交给默认的Servlet来响应静态文件,否则报404找不到资源错误)等等。
1.添加了@Configuration,将该注解标注在类上,将该类作为spring.xml文件中的 ,保证bean的作用域。配置spring容器,等价于applicationContext.xml。
2.新增了mvc XML的命名空间用于支持mvc配置,简化了Spring MVC的配置。添加了@CookieValue,@RequestHeaders等注解对mvc进行增强。web部分主要分为:web,Servlet,Portlet,Strtus四个模块。
3.web.xml的顶层标签有一个metadata-complete属性,用来指定当前的部署描述文件是否是完全的,为true表示容器只依赖web.xml,将会忽略所有的注解(同时会跳过web-fragment.xml的扫描),也叫禁用可插性支持。为false则表示启动注解支持。默认为false。
Servlet3.0新增了若干个注解,@WebServlet,@WebFilter,@webListener用于简化servlet,filter,listener的声明。使得web.xml部署符不再是必须的。使用注解配置可以更加直观的看到Servlet跳转页面或者其他的功能。
在此之前,servlet本身没有对此提供直接的支持,需要使用第三方框架。Servlet3.0中提供了该功能。
HttpServletRequest提供了两个方法用于从请求中解析出上传的文件
Part getPart(String name) 获取请求中给定name的文件
CollectiongetParts() 获取所有的文件
每一个文件用一个javax.servlet.http.Part对象来表示。可以与@MultipartConfig注解配合使用,进行一些自定义的配置。
用于在基于java类定义Bean配置中开启MVC支持。
用来代替spring-mvc.xml中的component-scan元素,开启组件扫描,自动扫描包路径下@ComponentScan注 解进行注册Bean实例。
处理器映射RequestMappingHandlerMapping和处理器适配器RequestMappingHandlerAdapter。
ExceptionHandlerExceptionResolver来代替3.0的AnnotationMethodHandlerExceptionResolver
用于支持@RequestBody和@ResponseBody.
consumes指定请求的内容是什么类型的内容,即处理方法消费什么类型的数据,
如:consumes="application/json"表示json类型的内容。spring会根据相应的HttpMessageConverter进行请求内容区数据到@RquestBody注解的命令对象的转换。
produces 指定生产什么类型的内容
如produces="application/json"表示JSON类型的内容,Spring的根据相应的HttpMessageConverter进行请求内容区数据到@RequestBody 注解的命令对象的转换,Spring 会根据相应的HttpMessageConverter进行模型数据(返回值)到JSON响应内容的转换。
Spring Web MVC提供了flashMapManager用于管理FlashMap,默认使用SessIonFlashMapManager。即数据默认存储在session中。
Spring4.X新特性参照:Spring4.x的新特性
升级到了jdk8,将java8,javaee7作为最低版本要求,增加了新的spring-webflux模块,基于reactive的spring-webmvc,完全的异步非阻塞。旨在使用enent-loop 执行模型和传统的线程池模型。web部分变成了:WebSocket,WebMVC,web,WebFlux。
首先需要引入spring-webmvc-x.y.z.jar 。里面包含了DispatcherServlet类。
springmvc依赖Apache Commons Logging组件。commons-logging-x.y.z.jar
tomcat7默认使用servlet3.0,tomcat8和8.5X默认使用Servlet3.1。tomcat9使用4.0
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.1.0version>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version> 4.1.5.RELEASEversion>
dependency>
servlet3.0之前的部署工作必须要一个部署描述符,即web.xml。
servlet3.0+的版本进行了规范,允许servlet,filter,listener不必在web.xml中声明,以硬编码的方式存在,实现容器的零配置。
<web-app id="WebApp_ID" version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>
classpath*:applicationContext*.xml
param-value>
context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
listener-class>
listener>
<servlet>
<servlet-name>dispatcherServletservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring-*.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>dispatcherServletservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
<filter>
<filter-name>encodingFilterfilter-name>
<filter-class>
org.springframework.web.filter.CharacterEncodingFilter
filter-class>
<init-param>
<param-name>forceEncodingparam-name>
<param-value>trueparam-value>
init-param>
<init-param>
<param-name>encodingparam-name>
<param-value>UTF-8param-value>
init-param>
filter>
<filter-mapping>
<filter-name>encodingFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
<session-config>
<session-timeout>60session-timeout>
session-config>
<error-page>
<exception-type>java.lang.Exceptionexception-type>
<location>/frame/errorpage/exception.jsplocation>
error-page>
<error-page>
<error-code>404error-code>
<location>error.htmllocation>
error-page>
<welcome-file-list>
<welcome-file>mainfordiscern.jspwelcome-file>
welcome-file-list>
web-app>
配置了一个名称为dispatcherServlet的Servlet,类型是DispatcherServlet,是Srping MVC的入口。自动启动,会mapping所有的请求。通过设置contextConfigLocation参数指定SpringMVC配置文件的位置,通过url-pattern元素配置成,将所有请求的url映射到该servlet。
web.xml中相关组件启动顺序为:context-name -> listener -> filter -> servlet
1.解析context-param 中的键值对
2.创建一个application内置对象,即servletContext,servlet上下文 用于全局共享
3.将context-param的键值对放入servletContext中,web应用内全局共享
4.读取listener标签创建监听器,在servlet2.3-servlet3版本中可以采取ContextLoaderListener和ContextLoaderServlet,但是在Spring3中移除了ContextLoaderServlet。这里使用ContextLoaderListener实现类,Spring会创建一个WebapplicationContext类(IOC容器)的对象,这个IOC容器是 根IOC容器(全局性),将它放入到servletContext中,作为应用中全局共享。键名为:WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE.可以通过两种方式获取。
WebApplicationContext applicationContext = (WebApplicationContext)application
.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
WebApplicationContext applicationContext1WebApplicationContextUtils
.getWebApplicationContext(application);
全局的根IOC容器只能获取到该容器中创建的Bean,不能访问到其他容器创建的Bean,即不能读取web.xml配置的contextConfigLocation参数的applicationContext*.xml文件来创建的Bean。
5.listener创建完成之后创建filter
6.初始化创建servlet,即这里的DispatcherServlet
7.DispatcherServlet的父类FrameworkServlet会重写其父类的initServletBean方法,并调用initWebApplicationContext()以及onRefresh();
8.initWebApplicationContext()方法会创建一个当前servlet的一个IOC子容器,如果存在WebApplicationContext则将它设置为父容器,不存在父容器则设置为null。
9.读取servlet标签的init-param配置的spring-xml文件并加载相关的Bean
10onRefresh()方法创建web应用相关组件。
<beans default-lazy-init="true"
xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd" >
<context:component-scan base-package="com.ppxx,com.command" use-default-filters="false">
context:component-scan>
<mvc:annotation-driven conversion-service="conversionService" />
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
bean>
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/toLogin.do"/>
<mvc:exclude-mapping path="/toRegist.do"/>
<mvc:exclude-mapping path="/checkcode.do"/>
<bean class="com.ppxx.interceptors.SessionInterceptor"/>
mvc:interceptor>
mvc:interceptors>
<bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/" />
<property name="suffix" value=".jsp" />
<property name="order" value="0" />
<property name="contentType" value="text/html;charset=UTF-8" />
bean>
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="utf-8" />
<property name="maxUploadSize" value="10485760000" />
<property name="maxInMemorySize" value="40960" />
bean>
beans>
1.首先需要在spring-mvc.xml文件中声明spring-context
2.接着应用context:component-scan 元素。
3.配置mvc:annotation-driven/
4.配置视图解析器
配置此标签来扫描通过注解配置的类。
context:component-scan中的属性:
use-default-filters:Indicates whether automatic detection of classes annotated with @Component, @Repository, @Service, or @Controller should be enabled. Default is “true”
默认true,会扫描指定包下所有类上的注解,将类注册成bean。
@Component 基本注解,标识一个受Spring管理的Bean组件
@Repository 指定此类是一个容器类,是DAO层类的实现。标识持久层Bean组件
@Service 指定为Service类,标识持久层Bean组件,默认情况会自动加载它到spring容器中。
@Controller 标识表现层Bean组件
@Autowried 自动注入
@Scope 指定此spring bean的scope是单例
context:component-scan还提供了两个子标签:
context:include-filter 指定扫描
context:exclude-filter 指定不扫描
如果认为扫描的粒度较大,只想扫描到指定包的Controller层,便可以通过
设置成只扫描controller层。同时需要将use-default-filter设置成false。
当然如果所有的Controller类都在同个子包下,可以将base-package=“com.ppxx.controller”
注意:context:component-scan只允许有两个子节点。
filter中type的表达式有多种:
Type | Examples Expression | Description |
---|---|---|
annotation | org.example.SomeAnnotation | 符合SomeAnnoation的target class |
assignable | org.example.SomeClass | 指定class或interface的全名 |
aspectj | org.example…*Service+ | AspetJ语法 |
regex | org.example.Default.* | Regelar Expression |
custom | org.example.MyTypeFilter | org.springframework.core.type.TypeFilter(Spring3新增) |
mvc:annotation-driven是SpringMVC 提供的一键式配置方法,Spring3.2+版本实现。配置此标签会自动注册DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter 两个bean,是spring MVC为@Controllers分发请求所必须的。
为了在Spring MVC应用程序中使用Formatter,需要用到ConversionService的bean对它进行注册,
1.Spring通过ConversionService进行类型转换,通过ConverterRegistry中的add方法注册一个转换器
2.ConversionService中的canConverter判断是否可以转换,convert调用相应的转换器进行类型转换操作。
interceptor拦截器作用于拦截用户的请求并作相应的处理。可以进行权限验证,或者判断用户是否登录等等。
那么如何实现呢?
实现有两种方式
1.SpringmVC中的interceptor拦截请求是通过HandlerInterceptor来实现的。所以要定义的interceptor类需要去实现这个接口,或者继承实现了HandlerInterceptor接口的类。比如说抽象类HandlerIntercreptorAdapter。
两者的区别在于,如果直接去实现HandlerInterceptor,需要显式的重写接口的三个方法,而继承不需要。
2.当然也可以通过实现Spring的WebRequestInterceptor接口或者继承实现了WebRequestInterceptor接口的类。
这里是通过实现HandlerInterceptor接口来实现Seesion验证的拦截器:
/**
* 用于session验证的拦截器
*/
public class SessionInterceptor implements HandlerInterceptor {
/**在整个请求结束之后,即DispatcherServlet渲染了对应视图之后执行。
* 主要作用于进行资源清理工作。
*/
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
}
/**在当前请求进行处理之后,即Controller方法调用之前,DispatcherServlet进行视图返回渲染之前执行
* 多个postHandler调用顺序与preHandle相反。即先声明的Interceptor中的postHandler反而会后执行
* 可以对Controller处理之后的ModelAndView对象进行操作。
*/
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
throws Exception {
}
/**在请求处理之前进行调用,SpingMVC中可以设置多个Intercreptor,
*最先执行的都是Interceptor中的preHandler方法,根据声明顺序依次执行
*在方法中进行一些前置初始化操作或者对当前请求进行预处理。或者进行一些判断决定请求是否继续
*@retrun false表示请求结束 ,后续的Intercreptor和Controller不再执行。
* true 继续调用下一个Intercreptor的preHandler方法,然后调用Controller
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
System.out.println("preHandle()");
HttpSession session = request.getSession();
Object obj = session.getAttribute("userName");
if (null == obj) {
// 没有登录,重定向到登录页面
response.sendRedirect("toLogin.do");
return false;
}
// 登录过,则继续向后调用
return true;
}
}
1.首先需要在spring-mvc.xml中添加支持mvc的schema,这样就可以使用mvc标签了。
2.使用mvc:interceptor标签声明需要加入到SpringMVC拦截器链路中的拦截器类。
在servlet3.0之前的版本中,servlet,filter,listener等都需要在web.xml文件中进行配置(servlet3.0也支持),但是servlet3.0中引入了注解,只需要在对应的Servlet类上使用@WebServlet注解进行标记。应用程序启动之后就会访问到该servlet。载配合其他注解实现无xml配置servlet。
@WebServlet(urlPatterns = "/demo", asyncSupported = true)
public class AsyncDemoServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException, ServletException {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
out.println("进入Servlet的时间:" + new Date() + ".");
out.flush();
//在子线程中执行业务调用,并由其负责输出响应,主线程退出
AsyncContext ctx = req.startAsync();
//监听器,提供了四种方法监听四种事件
ctx.addListener(new AsyncListener() {
//异步线程执行超时时
public void onTimeout(AsyncEvent arg0) throws IOException {
}
//异步线程开始时
public void onStartAsync(AsyncEvent arg0) throws IOException {
}
//异步线程出错时
public void onError(AsyncEvent arg0) throws IOException {
}
//异步线程执行完毕时
public void onComplete(AsyncEvent arg0) throws IOException {
}
});
new Thread(new Executor(ctx)).start();
out.println("结束Servlet的时间:" + new Date() + ".");
out.flush();
}
@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.getWriter().write("Hello User.");
}
}
class Executor implements Runnable {
private AsyncContext ctx = null;
public Executor(AsyncContext ctx){
this.ctx = ctx;
}
public void run(){
try {
//等待十秒钟,以模拟业务方法的执行
Thread.sleep(10000);
PrintWriter out = ctx.getResponse().getWriter();
out.println("业务处理完毕的时间:" + new Date() + ".");
out.flush();
ctx.complete();
} catch (Exception e) {
e.printStackTrace();
}
}
}
@WebServlet有两个属性用来表示Servlet的访问路径。value和urlPatterns。注意不能同时使用。
通过@WebServlet注解,便可以将该类声明为Servlet,在部署的时候就会被容器处理。但是在springmvc中,想要去声明DispatcherServlet,无法在源码上加上这个注解。所以这种方式无法在springmvc中使用。
1.Servlet3.0定义了一个接口ServletContainerInitializer,通过实现该接口,完成servlet,filter,listener的注册。
后来又进行了简化,只需要继承AbstractAnnotationConfigDispatcherServletInitializer类即可。通过该类中的getRootConfigClasses和getServletConfigClasses方法返回配置类。
2.通过@Configuration和@Component注解替代applicationContext.xml。使用@Configuration注解需要作为配置的类,替代xml中的定义bean的元数据;使用@Bean注解相应的方法,该方法名默认就是Bean的名称,该方法的返回值就是Bean的对象;使用@ComponentScan
的参数中指定要扫描的包名和要过滤的类。@ComponentScan要过滤的类通常有两种:一种是留给mvc容器的,一种是mvc容器本身的配置类。
3.通过@EnableMvc注解或者继承WebMvcConfigurationSupport或者WebMvcConfigurerAdapter来配置mvc容器。
Servlet3.0+环境中 通过java配置DispatcherServlet。Servlet容器自动检测该类。
public class MyWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletCxt) {
// Load Spring web application configuration
AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
ac.register(AppConfig.class);
ac.refresh();
// Create and register the DispatcherServlet
DispatcherServlet servlet = new DispatcherServlet(ac);
ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
registration.setLoadOnStartup(1);
registration.addMapping("/app/*");
}
}
WebApplicationInitializer是Spring MVC提供的接口,确保基于java类代码的配置被检测到并自动初始化servlet。
AbstractAnnotationConfigDispatcherServletInitializer继承了AbstractDispatcherServletInitializer抽象类,这个抽象类继承了AbstractContextLoaderInitializer抽象类,这个抽象类就实现了WebApplicationInitializer接口。通过这个类可以更加容易地注册DispatcherServlet。
基于java的spring配置,建议使用该方法:
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
//如果不需要应用程序上下文层次结构,可以返回null。
return new Class<?>[] { RootConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
//AppConfig类是自定义,用来配置mvc处理。
return new Class<?>[] { AppConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
AppConfig配置:
@Configuration
//@EnableWebMvc 使用该注解来开启mvc支持
@ComponentScan("org.keyou.web")
public class AppConfig extends WebMvcConfigurerAdapter {
@Bean
public InternalResourceViewResolver viewResolver(){
InternalResourceViewResolver viewResolver=new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
viewResolver.setOrder(1);
return viewResolver;
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/js/**").addResourceLocations("/WEB-INF/js/");
registry.addResourceHandler("/css/**").addResourceLocations("/WEB-INF/css/");
registry.addResourceHandler("/images/**").addResourceLocations("/WEB-INF/images/");
}
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
基于xml的spring配置,建议使用该方法
public class MyWebAppInitializer extends AbstractDispatcherServletInitializer {
@Override
protected WebApplicationContext createRootApplicationContext() {
return null;
}
@Override
protected WebApplicationContext createServletApplicationContext() {
XmlWebApplicationContext cxt = new XmlWebApplicationContext();
cxt.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");
return cxt;
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
dispatcher-config.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="org.keyou.web"/>
beans>
参考资料:
Spring MVC 学习指南(第2版) [美]Paul Deck 著 林仪明 译
Spring官网