概述
在上一讲中,我们分析清楚了Servlet3.0整合Spring MVC的底层原理。接下来,我们就要以注解的方式将Spring MVC整合进来。
Servlet3.0与SpringMVC的整合
首先,我们来编写一个类,例如MyWebAppInitializer,来继承AbstractAnnotationConfigDispatcherServletInitializer这个抽象类,一开始我们写成下面这样。
package com.meimeixia;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
// TODO Auto-generated method stub
return null;
}
@Override
protected Class<?>[] getServletConfigClasses() {
// TODO Auto-generated method stub
return null;
}
@Override
protected String[] getServletMappings() {
// TODO Auto-generated method stub
return null;
}
}
按照我们之前的分析,上面的类会在容器启动的时候创建对象,而且在整个创建的过程中,会来调用相应方法初始化容器以及前端控制器。
我们自己写的MyWebAppInitializer类继承了AbstractAnnotationConfigDispatcherServletInitializer抽象类之后,发现它里面需要重写三个抽象方法,下面我们就来详细说一下它们。
- getRootConfigClasses方法:它是来获取根容器的配置类的,该配置类就类似于之前写的Spring配置文件,通过监听器的方式来读取Spring的配置文件,然后创建根容器。
- getServletConfigClasses方法:它是来获取web容器的配置类的,该配置类就类似于我们以前经常写的Spring MVC的配置文件,然后创建子容器。
- getServletMappings方法:它是来获取DispatcherServlet的映射信息的。该方法需要返回一个String[]。
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
那么DispatcherServlet就会来拦截所有请求,包括静态资源,比如xxx.js文件、xxx.png等等,但是不包括*.jsp,即不会拦截所有的jsp页面。
如果我们返回的是这样一个new String[]{“/*”}东东,即:
@Override
protected String[] getServletMappings() {
return new String[]{"/*"};
}
那么DispatcherServlet就是真正来拦截所有请求了,包括*.jsp,也就是说就连jsp页面都拦截,所以,我们切忌不可写成这样(即/*)。否则的话,jsp页面一旦被Spring MVC拦截,最终极有可能我们就看不到jsp页面了,因为jsp页面是由Tomcat服务器中的jsp引擎来解析的。
也就是说,我们最好是在getServletMappings方法中返回这样一个new String[]{“/”}东东,即:
package com.meimeixia;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
/**
* 在web容器启动的时候创建对象,而且在整个创建对象的过程中,会调用相应方法来初始化容器以及前端控制器
* 编写好该类之后,就相当于是在以前我们配置好了web.xml文件
* @author liayun
*
*/
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
/*
* 获取根容器的配置类,该配置类就类似于我们以前经常写的Spring的配置文件,然后创建出一个父容器
*/
@Override
protected Class<?>[] getRootConfigClasses() {
// TODO Auto-generated method stub
return null;
}
/*
* 获取web容器的配置类,该配置类就类似于我们以前经常写的Spring MVC的配置文件,然后创建出一个子容器
*/
@Override
protected Class<?>[] getServletConfigClasses() {
// TODO Auto-generated method stub
return null;
}
// 获取DispatcherServlet的映射信息
/*
* /:拦截所有请求,包括静态资源,比如xxx.js文件、xxx.png等等等等,但是不包括*.jsp,也即不会拦截所有的jsp页面
* /*:真正来拦截所有请求了,包括*.jsp,也就是说就连jsp页面都拦截
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
由于我们还需要在getRootConfigClasses和getServletConfigClasses这俩方法中指定两个配置类的位置,所以我们来创建上两个配置类,分别如下:
- 根容器的配置类,例如RootConfig
package com.meimeixia.config;
public class RootConfig {
}
- web容器的配置类,例如AppConfig
package com.meimeixia.config;
public class AppConfig {
}
以上这两个配置类最终需要形成父子容器的效果。一点需要重点说明,即AppConfig配置类只来扫描所有的控制器(即Controller),以及和网站功能相关的哪些逻辑组件;RootConfig配置类只来扫描所有的业务逻辑核心组件,包括dao层组件、不同的数据源等等,反正他只扫描和业务逻辑相关的组件。
接下来,我们来完善以上两个配置类。首先,先来完善RootConfig配置类,我们可以使用@ComponentScan注解来指定扫描com.meimeixia包以及子包下的所有组件,而且为了形成父子容器,还必须得排除掉一些组件,那排除掉哪些组件呢?很显然,应该排除掉controller控制层组件,即Controller。所以,我们得通过@ComponentScan注解的excludeFilters()方法按照注解的方式来排除掉所有标注了@Controller注解的组件。
package com.meimeixia.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.stereotype.Controller;
// 该配置类相当于Spring的配置文件
// Spring容器不扫描Controller,它是一个父容器
@ComponentScan(value="com.meimeixia",excludeFilters={
@Filter(type=FilterType.ANNOTATION, classes={Controller.class})
})
public class RootConfig {
}
然后,再来完善AppConfig配置类,我们同样使用@ComponentScan注解来指定扫描com.meimeixia包以及子包下的所有组件,但是呢,与上面正好相反,这儿只扫描controller控制层组件,即Controller,如此一来就能与上面形成互补配置了。OK,那我们就通过@ComponentScan注解的includeFilters()方法按照注解的方式来指定只扫描controller控制层组件。
package com.meimeixia.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.stereotype.Controller;
// 该配置类相当于Spring MVC的配置文件
// Spring MVC容器只扫描Controller,它是一个子容器
// useDefaultFilters=false:禁用默认的过滤规则
@ComponentScan(value="com.meimeixia",includeFilters={
@Filter(type=FilterType.ANNOTATION, classes={Controller.class})
},useDefaultFilters=false)
public class AppConfig {
}
尤其要注意这一点,在以上配置类中通过@ComponentScan注解的includeFilters()方法来指定只扫描controller控制层组件时,需要禁用掉默认的过滤规则,即必须得加上useDefaultFilters=false这样一个配置。千万记得必须要禁用掉默认的过滤规则,否则扫描不会生效。
但是,在RootConfig配置类中通过@ComponentScan注解的excludeFilters()方法来指定排除哪些组件时,是不需要对useDefaultFilters进行设置的,因为其默认值就是true,表示默认情况下标注了@Component、@Repository、@Service以及@Controller这些注解的组件都会被扫描,即扫描所有。
接下来,我们就要分别来编写一个controller控制层组件和service业务层组件来进行测试了。首先,编写一个service业务层组件,例如HelloService,如下所示。
package com.meimeixia.service;
import org.springframework.stereotype.Service;
@Service
public class HelloService {
public String sayHello(String name) {
return "Hello, " + name;
}
}
然后,编写一个controller控制层组件,例如HelloController,并且我们还可以在该HelloController中注入HelloService组件,来调用其方法。
package com.meimeixia.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.meimeixia.service.HelloService;
@Controller
public class HelloController {
@Autowired
HelloService helloService;
@ResponseBody
@RequestMapping("/hello")
public String hello() {
String hello = helloService.sayHello("tomcat...");
return hello;
}
}
从以上HelloController的代码中,我们可以看到它里面的hello方法是来处理hello请求的,而且通过@ResponseBody注解会直接将返回的结果(即字符串)写到浏览器页面中。
MyWebAppInitializer类中指定两个配置类的位置。
package com.meimeixia;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import com.meimeixia.config.AppConfig;
import com.meimeixia.config.RootConfig;
/**
* 在web容器启动的时候创建对象,而且在整个创建对象的过程中,会调用相应方法来初始化容器以及前端控制器
* 编写好该类之后,就相当于是在以前我们配置好了web.xml文件
* @author liayun
*
*/
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
/*
* 获取根容器的配置类,该配置类就类似于我们以前经常写的Spring的配置文件,然后创建出一个父容器
*/
@Override
protected Class<?>[] getRootConfigClasses() {
// TODO Auto-generated method stub
return new Class<?>[]{RootConfig.class};
}
/*
* 获取web容器的配置类,该配置类就类似于我们以前经常写的Spring MVC的配置文件,然后创建出一个子容器
*/
@Override
protected Class<?>[] getServletConfigClasses() {
// TODO Auto-generated method stub
return new Class<?>[]{AppConfig.class};
}
// 获取DispatcherServlet的映射信息
/*
* /:拦截所有请求,包括静态资源,比如xxx.js文件、xxx.png等等等等,但是不包括*.jsp,也即不会拦截所有的jsp页面
* /*:真正来拦截所有请求了,包括*.jsp,也就是说就连jsp页面都拦截
*/
@Override
protected String[] getServletMappings() {
// TODO Auto-generated method stub
return new String[]{"/"};
}
}
这就相当于分别来指定Spring配置文件和Spring MVC配置文件的位置。
最后,我们就可以启动项目来进行测试了。项目启动成功之后,我们在浏览器地址栏中输入http://localhost:8080/springmvc-annotation-liayun/hello进行访问,发现浏览器页面上确实打印出来了一串字符串,如下图所示。
这说明我们controller控制层组件和service业务层组件都起作用了。
总结
现在对Servlet3.0整合SpringMVC做一个整体的总结。
- web容器(即Tomcat服务器)在启动应用的时候,会扫描当前应用下面每一个jar包里面的META-INF/services/javax.servlet.ServletContainerInitializer文件。
- web容器会调用META-INF/services/javax.servlet.ServletContainerInitializer文件中指定实现类onStartUp方法。
- 在spring-web-4.3.11.RELEASE.jar中的META-INF/services/目录里面有一个javax.servlet.ServletContainerInitializer文件,并且在该文件中指定的实现类就是org.springframework.web.SpringServletContainerInitializer,打开该实现类,发现它上面标注了@HandlesTypes(WebApplicationInitializer.class)这样一个注解。
- web容器在启动应用的时候,便会来扫描并加载org.springframework.web.SpringServletContainerInitializer实现类,而且会传入我们感兴趣的类型(即WebApplicationInitializer接口)的所有后代类型,最终再运行其onStartup方法。
- onStartup方法中会遍历感兴趣的类型(即WebApplicationInitializer接口)的所有后代类型,然后利用反射技术创建WebApplicationInitializer类型的对象,我们自定义的MyWebAppInitializer就是WebApplicationInitializer这种类型的。
- 创建完之后,都会存储到名为initializers的LinkedList集合中。接着,又会遍历该集合,并调用每一个WebApplicationInitializer对象的onStartup方法。
- 遍历到每一个WebApplicationInitializer对象之后,调用其onStartup方法,实际上就是先调用其(例如我们自定义的MyWebAppInitializer)最高父类的onStartup方法,创建根容器;然后再调用其次高父类的onStartup方法,创建web容器以及DispatcherServlet;接着,根据其重写的getServletMappings方法来为DispatcherServlet配置映射信息。
参考
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/99965.html