Spring注解驱动之Servlet3.0整合SpringMVC

导读:本篇文章讲解 Spring注解驱动之Servlet3.0整合SpringMVC,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

概述

在上一讲中,我们分析清楚了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做一个整体的总结。

  1. web容器(即Tomcat服务器)在启动应用的时候,会扫描当前应用下面每一个jar包里面的META-INF/services/javax.servlet.ServletContainerInitializer文件。
  2. web容器会调用META-INF/services/javax.servlet.ServletContainerInitializer文件中指定实现类onStartUp方法。
  3. 在spring-web-4.3.11.RELEASE.jar中的META-INF/services/目录里面有一个javax.servlet.ServletContainerInitializer文件,并且在该文件中指定的实现类就是org.springframework.web.SpringServletContainerInitializer,打开该实现类,发现它上面标注了@HandlesTypes(WebApplicationInitializer.class)这样一个注解。
  4. web容器在启动应用的时候,便会来扫描并加载org.springframework.web.SpringServletContainerInitializer实现类,而且会传入我们感兴趣的类型(即WebApplicationInitializer接口)的所有后代类型,最终再运行其onStartup方法。
  5. onStartup方法中会遍历感兴趣的类型(即WebApplicationInitializer接口)的所有后代类型,然后利用反射技术创建WebApplicationInitializer类型的对象,我们自定义的MyWebAppInitializer就是WebApplicationInitializer这种类型的。
  6. 创建完之后,都会存储到名为initializers的LinkedList集合中。接着,又会遍历该集合,并调用每一个WebApplicationInitializer对象的onStartup方法。
  7. 遍历到每一个WebApplicationInitializer对象之后,调用其onStartup方法,实际上就是先调用其(例如我们自定义的MyWebAppInitializer)最高父类的onStartup方法,创建根容器;然后再调用其次高父类的onStartup方法,创建web容器以及DispatcherServlet;接着,根据其重写的getServletMappings方法来为DispatcherServlet配置映射信息。

参考

Spring注解驱动开发第54讲——Servlet 3.0整合Spring MVC

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/99965.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!