栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java - SpringMVC 框架详解(三)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java - SpringMVC 框架详解(三)

目录
      • 1. 关于@RequestMapping注解
      • 2. 关于@RequestParam注解
      • 3. 关于Session
      • 4. 拦截器(Interceptor)
      • 5. 使用过滤器解决POST请求的乱码问题
      • 6. SpringMVC小结
      • 附1:关于拦截器(Interceptor)和过滤器(Filter)

1. 关于@RequestMapping注解

在处理请求的方法之前添加@RequestMapping注解,可以配置请求路径与处理请求的方法的映射关系!

在该注释中声明了:

@AliasFor("path")
String[] value() default {};

和:

@AliasFor("value")
String[] path() default {};

则可以在使用注解时,配置一个名为value的属性,该属性的值是字符串类型的数组:

@RequestMapping(value={"值1", "值2", "值3"})

该属性的作用就是配置映射的路径,是默认的属性,所以,在配置时,可以不必显式的声明属性名称,即:以上配置可以简化为:

@RequestMapping({"值1", "值2", "值3"})

在配置注解的属性时,如果属性的值是某种数组类型的,其值可以是数组格式的,也可以是它的某个元素,并不一定需要写成数组,例如:

@RequestMapping("值1")

所以,以下几种写法是等效的:

@RequestMapping(value={"reg.do"})
@RequestMapping({"reg.do"})
@RequestMapping(value="reg.do")
@RequestMapping("reg.do")

即:当值是数组格式,且当前只设置1个值时,写不写成数组格式是无所谓的,当设置的是value值,不需要写出value=,而是直接写值即可!

该注解中的path和value属性是完全等效的,path是从SpringMVC 4.2版本加入的!

而且,该属性并不仅仅只是用于配置方法之前的路径,还可以将该注解声明在控制器类之前并进行配置,例如:

@RequestMapping("user")
@Controller
public class UserController {

	@RequestMapping("login.do")
	public String showLogin() {
		return "login";
	}

}

在类之前添加注解后,当访问控制器类中配置在某个方法之前映射的路径时,在URL中需要先添加在类之前的路径,再添加方法之前的路径!以以上代码为例,访问路径应该是http://localhost:8080/项目名/user/login.do!

并且,在类之前添加注解后,在当前控制器类的每一个方法的映射的路径之前都必须添加这一层路径!

强烈建议在每一个控制器类之前都添加该注解!

在配置路径时,左右两侧多余的/可以被忽略,并且,在类和方法之前都配置时,也不需要考虑类的注解路径拼接方法的注解路径之间的/的问题!即几下在类和方法之前添加配置的效果是等效的:

/user	/login.do
/user	login.do
/user/	/login.do
/user/	login.do
user	/login.do
user	login.do
user/	/login.do
user/	login.do

以上做法都是等效的,在实际应用时,选取其中1种风格,并在项目中保持使用固定的风格即可!

另外,在注解的源代码中,还声明了:

RequestMethod[] method() default {};

以上属性是用于约束请求方式的!假设控制器中配置为:

@RequestMapping(path="handle_reg.do", method=RequestMethod.POST)

客户端却使用GET方式提交请求,就会出现405错误:

HTTP Status 405 – Method Not Allowed

Message : Request method 'GET' not supported

当注解中配置多个属性时,每个属性的配置中,都必须显式的指定属性名!仅当只配置1个属性,且属性是默认的,才可以不必显式的指定属性名!

2. 关于@RequestParam注解

可以在处理请求的方法的参数之前添加@RequestParam注解!

在该注解中声明了:

@AliasFor("name")
String value() default "";

@AliasFor("value")
String name() default "";

也就是说,在注解中可以配置value或name属性,这2个属性是等效的!其作用是配置将客户端提交的哪个请求参数,绑定到当前方法的参数中!默认情况下,假设客户端提交了名为username的参数,在处理请求的方法的参数列表中,声明String username即可获取到客户端提交的参数的值,即只要同名即可!但是,如果名称无法匹配,就可以使用该注解进行配置!

假设客户端提交了名为uname的请求参数,则在控制器的处理请求的方法中,为参数添加注解:

@RequestParam("uname") String username

也可以正常的获取到请求参数!所以,以上配置的作用就是:将客户端提交的名为uname的参数,绑定到处理请求的方法中名为username的参数中!

当添加以上注解后,如果客户端发出请求时,并没有提交匹配名称的参数,默认情况下,会出现400错误:

HTTP Status 400 – Bad Request

Message : Required String parameter 'uname' is not present

之所以会出现以上问题,是因为在@RequestParam的源代码中存在:

boolean required() default true;

即:该源代码中声明了名为required的属性,值是boolean类型的,默认值为true!

所以,如果一定要使用该注解,并且允许不提交该请求参数,可以配置为:

@RequestParam(name="uname", required=false) String username

最后,该注解的源代码中还有:

String defaultValue() default ValueConstants.DEFAULT_NONE;

该属性是用于配置默认值的,即客户端没有提交指定的请求参数时,等同于提交了某个值!例如配置为:

@RequestParam(name="uname", required=false, defaultValue="admin") String username

当然,在使用defaultValue属性时,务必将required属性设置为false!

3. 关于Session

因为HTTP协议是无状态协议,所以,同一个客户端的多次请求,服务器是无法区分这是来自同一个客户端的,如果需要保存用户的某些数据或状态,就需要使用Session。

Session是保存在服务器端的内存中的数据,每个客户端提交请求后,都会在服务器端的内存中有一份专属于这个客户端的数据!

在SpringMVC中,如果需要使用Session,只需要在处理请求的方法的参数列表中添加HttpSession类型的参数即可,然后,在处理请求的过程中,调用该参数对象的API封装或取出数据。

直接使用HttpSession作为处理请求的方法的参数,主要问题在于不便于执行单元测试!

在SpringMVC中,也提供了框架中管理Session的做法,就是将需要封装在Session中的数据放在ModelMap中,也就是说,不管是要转发的数据,还是Session的数据,都放在ModelMap中,然后,还要在控制器类之前添加@SessionAttributes注解,用于配置“ModelMap中的哪个属性的数据是Session中的数据”,并且,这种做法并不是真的将数据放到了Session中,而是由框架进行管理的,即使调用HttpSession的invalidate()方法也不能清除这些数据!

总的来说,使用SpringMVC的做法来管理Session相对比较麻烦,使用HttpSession的用法虽然不便于执行单元测试,但是,用法却非常简单,代码也很直观,所以,一般使用HttpSession的较多。

@RequestMapping(path = "handleLogin.do", method = { RequestMethod.GET, RequestMethod.POST})
public String handleLogin (
          String username,
          String password,
          @RequestParam(name = "sex", required = false, defaultValue = "未知") String sex,
          String[] skill,
          ModelMap modelMap,
          HttpSession session
  ) {
      System.out.println("username = " + username);
      System.out.println("password = " + password);
      System.out.println("sex = " + sex);
      System.out.println("skill = " + skill);
//        System.out.println("skill.length = " + skill.length);
//        if (skill.length > 0) {
//            for (int i = 0; i < skill.length; i++) {
//                System.out.println("skill[" + i + "] = " + skill[i]);
//            }
//        }

      if ("admin".equals(username)) {
          if ("123456".equals(password)) {
              System.out.println("登录成功");
              session.setAttribute("id", username + password);
              modelMap.addAttribute("username", username);
              return "redirect:../main/index.do";
          } else {
              System.out.println("密码错误");
              return "loginFailed";
          }
      } else {
          System.out.println("用户名不存在");
          return "loginFailed";
      }
  }
4. 拦截器(Interceptor)

拦截器:是一种可以使得若干种请求都会自动的执行其中的代码组件!该组件对所处理的请求可以选择放行,或选择阻止继续执行!

在SpringMVC项目中,如果需要使用拦截器,首先,需要自定义类,实现HandlerInterceptor拦截器接口:

public class LoginInterceptor implements HandlerInterceptor {
	
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		System.out.println("LoginInterceptor.preHandle()");
		return false;
	}

	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		System.out.println("LoginInterceptor.postHandle()");
	}

	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		System.out.println("LoginInterceptor.afterCompletion()");
	}

}

然后,在spring.xml中添加配置:


	
	
		
		
		
		
			
	

可以看到,当拦截器类的preHandle()方法返回false时,表示阻止继续运行,浏览器的页面中将显示一片空白,当返回true时,表示放行,会依次执行preHandle() -> 控制器类中的方法 -> postHandle() -> afterCompletion()。

所以,真正具有“拦截”功能的,其实只有preHandle()方法!

如果需要实现“登录拦截”的效果,即:如果已经登录,允许正常访问,如果没有登录,则重定向到登录页。可以在拦截器的preHandle()方法中,对Session中的数据进行判断,选择放行或阻止运行,并且,在阻止运行时,重定向到登录页面即可!例如:

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
	System.out.println("LoginInterceptor.preHandle()");
	HttpSession session = request.getSession();
	if (session.getAttribute("id") == null) {
		// 重定向:必须使用绝对路径!
		response.sendRedirect(request.getContextPath() + "/user/login.do");
		// 返回 false 代表被拦截,不再继续执行
		return false; // // 这里必须要return false,否则程序依然会往下执行!
	}
	return true;
}

如果还有其它路径也需要被拦截器处理,可以在配置中添加更多的节点,例如:


	
	
		
		
		
		
		
		
		
		
			
	

在配置路径时,还可以使用星号(*)通配符,例如:


则以上配置可以匹配到/blog/delete.do、/blog/edit.do等……

需要注意的是:1个星号只能表示资源,不能通配多层级路径,例如/blog/*就不可以匹配到/blog/2019/spring.do这样的路径!如果需要通配多层级路径及资源,必须使用2个星号(**),例如配置为/blog/**!

除此以外,还可以添加节点来配置例外路径/排除路径,例如已经配置了,为了保证注册、登录功能的正常使用,应该把相关路径设置为“例外”:


     
     
         
         
         
         
         
         
         
         
         
     
     
 

凡是添加在“例外”的路径,就相当于拦截器并不处理这些路径对应的请求,拦截器中的所有方法都是不执行的!

的配置中,子级的节点顺序必须是先配置,最后配置

5. 使用过滤器解决POST请求的乱码问题

在SpringMVC框架中,默认使用的编码全部是ISO-8859-1,是不支持中文的!

在SpringMVC框架中,定义了CharacterEncodingFilter过滤器类,用于处理字符编码,所以,应该在web.xml中配置这个过滤器类,并指定所使用的编码即可:


	CharacterEncodingFilter
	org.springframework.web.filter.CharacterEncodingFilter
	
		encoding
		utf-8
	



	CharacterEncodingFilter
	/*

6. SpringMVC小结
  1. 理解SpringMVC框架的作用;

  2. 认识在web.xml中关于DispatcherServlet和CharacterEncodingFilter的配置;

  3. 认识在spring.xml中关于组件扫描和Thymeleaft相关的配置,主要是使用哪种模版解析器,及前缀与后缀的配置;

  4. 掌握创建类的正确创建方式(必须在组件扫描范围内,必须添加@Controller注解);

  5. 掌握添加方法处理请求;

  6. 掌握@RequestMapping和@RequestParam注解的使用;

  7. 掌握接收请求参数的方式;

  8. 掌握转发数据的方式;

  9. 掌握重定向的做法;

  10. 掌握Session的管理;

  11. 掌握拦截器的使用;

  12. 理解SpringMVC核心执行流程图。

附1:关于拦截器(Interceptor)和过滤器(Filter)

拦截器和过滤器都是可以应用于若干种不同的请求的组件,并且,都可以实现阻止运行或放行的效果,甚至,这2种组件都可以形成“链”。

过滤器(Filter)是Java EE中的组件,而拦截器(Interceptor)是SpringMVC中的组件!即:任何一个Java Web项目都可以使用过滤器,但是,只有使用了SpringMVC框架的项目才可以使用拦截器,并且,如果SpringMVC框架中关于DispatcherServlet的路径配置为*.do的话,只有被SpringMVC框架处理的请求(以.do作为后缀的请求),才可能被拦截器处理!

过滤器(Filter)是执行在所有的Servlet之前的组件,而SpringMVC中的拦截器的第1次执行是在DispatcherServlet之后,且在Controller之前执行的!(对应SpringMVC核心执行流程图,过滤器是1号位置执行,而拦截器第1次执行是在4号位置)

过滤器(Filter)需要在web.xml中进行配置,且映射的路径配置相对比较受限(没有白名单),产生的配置代码量也非常大(每个路径对应4行配置代码),而拦截器(Interceptor)的配置简单、灵活!

所以,如果某个请求是被SpringMVC框架所处理的,并且,不在乎处理的时间,应该优先使用拦截器(Interceptor),反之,如果必须在所有Servlet之前就必须处理的,就必须使用过滤器(Filter)。

如果这篇文章有帮助到您,请简单给个赞吧,谢谢~

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/337739.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号