Java框架学习:SpringMVC之动态资源和静态资源访问冲突问题、常用注解

文章目录

      • 动态资源和静态资源访问冲突问题
        • 出现的问题
        • 解决方案
      • 常用注解
        • @RequestMapping
        • @PathVariable
        • @SessionAttribute和@SessionAttributes

动态资源和静态资源访问冲突问题

出现的问题

web.xml配置文件

<servlet>
  <servlet-name>DispatcherServletservlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
  <init-param>
    <param-name>contextConfigLocationparam-name>
    <param-value>classpath:springMVC.xmlparam-value>
  init-param>
  <load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
  <servlet-name>DispatcherServletservlet-name>
  <url-pattern>/url-pattern>
servlet-mapping>

springMVC.xml配置文件

<context:component-scan base-package="com.young"/>

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/"/>
    <property name="suffix" value=".jsp"/>
bean>

HelloController处理器

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class HelloController {
    @RequestMapping("hello")
    public String hello(){
        System.out.println("Hello SpringMVC");
        return "index";
    }
}

index.jsp代码,在与index.jsp同目录下存在文件"style.css"和图片"123.png"

<html>
<head>
    <link rel="stylesheet" href="style.css" type="text/css"/>
head>
<body>
    <h2>Hello World!h2>
    <img src="123.png">
body>
html>

style.css代码

h2{
  color: aqua;
}

问题:运行程序后图片无法显示,css文件没有生效,检查网络出现如下情况:style.css和123.png状态码为404
Java框架学习:SpringMVC之动态资源和静态资源访问冲突问题、常用注解_第1张图片查看后台控制台日志,存在如下记录

13-May-2020 09:49:06.534 警告 [http-nio-8080-exec-4] org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping for GET /style.css
13-May-2020 09:49:06.536 警告 [http-nio-8080-exec-5] org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping for GET /123.png

原因:DispatcherServlet的映射路径为"/",Tomcat服务器中默认的Servlet(servlet-name为default)的映射路径也为"/",所以DispatcherServlet会覆盖Tomcat中默认的Servlet,处理除jsp之外的所有资源,导致静态资源无法被访问

Tomcat服务器中默认的Servlet

<servlet-mapping>
	<servlet-name>defaultservlet-name>
	<url-pattern>/url-pattern>
servlet-mapping>

解决方案

1.可以通过配置来访问静态资源,之前讲过,这不是最佳解决方案


<servlet-mapping>
  <servlet-name>defaultservlet-name>
  <url-pattern>*.htmlurl-pattern>
servlet-mapping>

2.在springMVC.xml配置文件中配置

<mvc:default-servlet-handler/>
<mvc:resources mapping="/" location="/"/>
<mvc:annotation-driven/>

分析这三个标签

标签会加载服务器默认的Servlet映射
配置该标签后会在SpringMVC上下文中定义一个org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler,它会像
一个检查员,对进入DispatcherServlet的URL进行筛查,如果发现是静态资源的请求,就将该请求转由Web应用服务器默认的Servlet处理,如果
不是静态资源的请求,才由DispatcherServlet继续处理。
如果Web应用服务器默认Servlet名称不是"default",则需要通过default-servlet-name属性指定
但是只配置该标签和RequestMapping注解会产生冲突,导致静态资源可以被访问,但是动态资源无法被访问
此时需要配置来开启注解驱动,该标签功能强大,但在此处的作用只是使注解生效

对于标签可以使任何地方的静态资源都被访问
在之前客户端只能访问webapp目录下的静态资源,而无法访问WEB-INF目录下的静态资源
通过mapping属性配置映射路径,"/"代表webapp目录,如果想要访问该目录下的所有子目录中的静态资源,可配置"/**",如果只想访问webapp目
录下的WEB-INF目录,可配置"/WEB-INF/";location代表访问路径,"/"为最大范围。

通常情况下只需要配置标签和标签即可

<mvc:default-servlet-handler/>
<mvc:annotation-driven/>

常用注解

@RequestMapping

@RequestMapping注解可以将Java方法配置为可以供客户端访问的资源

@RequestMapping("/hello")
public String hello(){
    System.out.println("Hello SpringMVC");
    return "index";
}

value属性

value属性和path属性是相同的,值为数组类型,制定请求的映射路径
关于映射路径是否加上"/"
有无"/"均可,建议加上
加"/"和不加"/"都表示从应用程序根目录( //http://localhost:8080/项目名称/ )下寻找   

method属性

RequestMethod[] method() default {};
method属性可以指定客户端提交的方式,值是RequestMethod类型的数组,默认get和post两种请求都可以访问
RequestMethod是一个枚举类型,值有GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS,TRACE

如果指定了method值为"{RequestMethod.POST}",如下所示
@RequestMapping(value = "/hello",method = {RequestMethod.POST})
如果客户端发送请求的请求方式为GET,那么网页页面会显示如下信息:
	HTTP Status 405 – 方法不允许
	Type Status Report
	消息 Request method 'GET' not supported
	描述 请求行中接收的方法由源服务器知道,但目标资源不支持
通常用于文件上传等需要指定请求方式的操作中

浏览器表单提交默认只支持get和post提交方式,如果想要使用其他6种提交方式,首先需要配置过滤器,使SpringMVC会对请求资源判断
web.xml文件中配置过滤器

<filter>
  
  <filter-name>HiddenHttpMethodFilterfilter-name>
  <filter-class>org.springframework.web.filter.HiddenHttpMethodFilterfilter-class>
filter>
<filter-mapping>
  <filter-name>HiddenHttpMethodFilterfilter-name>
  
  <url-pattern>/*url-pattern>
filter-mapping>

在处理器中增加处理请求方式的方法

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Controller
public class HelloController {
    @RequestMapping(value = "/hello",method = {RequestMethod.POST,RequestMethod.GET})
    public String hello(){
        System.out.println("Hello SpringMVC");
        return "index";
    }

    @RequestMapping(value = "/hello",method = {RequestMethod.DELETE})
    public void delete(HttpServletResponse response) throws IOException {
        System.out.println("Hello SpringMVC delete");
        response.getWriter().print("{success:true}");
    }
}

在表单中增加一个隐藏表单域

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
	<head>
	head>
	<body>
    	<form action="/hello" method="post">
    		
       		<input type="hidden" name="_method" value="Delete"/>
        	<input type="submit" value="测试delete提交方式">
    	form>
	body>
html>

运行程序发送请求,就会执行delete方法中的逻辑。
设置不同的method属性的值,对于同一个请求资源,但是不同的提交方式时,就会对应不同的处理逻辑

params属性

String[] params() default {};
params属性的值为一个字符串数组,用于限制客户端提交数据的请求参数
如果在hello方法中配置params属性,如下所示
@RequestMapping(value = "/hello",params = {"id"},method = {RequestMethod.POST,RequestMethod.GET})
如果客户端在请求"/hello"路径时没有携带请求参数"id",网页页面会出现如下异常:
	HTTP Status 400 – 错误的请求
	Type Status Report
	消息 Parameter conditions "id" not met for actual request parameters:
	描述 由于被认为是客户端对错误(例如:畸形的请求语法、无效的请求信息帧或者虚拟的请求路由),服务器无法或不会处理当前请求。
如果params = {"id","name"},那么请求需要两个请求参数id和name
如果params = {"id=1","name"},那么请求需要两个请求参数id和name,且参数id的值为1
如果params = {"id!=1","name"},那么请求需要两个请求参数id和name,且参数id的值不为1

@RequestMapping注解还可以标注在类上

以上"/hello"这个映射路径可能存在于多个Controller中,为了区分各个Controller中的相同的映射路径,可以在类上用@RequestMapping标注,如下:
	@RequestMapping("/Hello")
此时在客户端中访问该资源的映射路径为"/Hello/hello"。

@RequestMapping还支持ant配置,用处不多,了解即可

? - 匹配任意单个字符
@RequestMapping("/user/?")		如:/user/a
* - 匹配任意数量字符(含0个)	
@RequestMapping("/user/*")    	如:/user/abc
** - 匹配任意数量目录(含0个)	
@RequestMapping("/user/**/save")    	如:/user/abc/ab/ef/save

@PathVariable

@PathVariable可以在方法的参数上获取映射路径中占位符中的参数的值,从而在方法中可以访问这个值

@RequestMapping(value = "/hello/{id}",method = {RequestMethod.POST,RequestMethod.GET})
//将映射路径中的占位符"{id}"中id的值传给hello方法的参数id
public String hello(@PathVariable("id") Integer id){
    System.out.println("Hello SpringMVC "+id);
    return "index";
}

index.jsp代码

<html>
<head>
    <link rel="stylesheet" href="style.css" type="text/css"/>
head>
<body>
    <h2>Hello World!h2>
    <img src="123.png">
body>
html>

出现问题:使用@PathVariable注解时,index.jsp界面中引用的静态资源无法被访问
检查浏览器网络得:目录结构发生了改变,为程序中引用静态资源使用的是相对路径,在当前项目的根路径下的hello/路径下是找不到index.jsp页面中引用的静态资源的
Java框架学习:SpringMVC之动态资源和静态资源访问冲突问题、常用注解_第2张图片解决方法:我们可以在引用资源时使用绝对路径来解决这个问题
index.jsp代码

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<html>
<head>
    <link rel="stylesheet" href="<%=basePath%>style.css" type="text/css"/>
head>
<body>
    <h2>Hello World!h2>
    <img src="<%=basePath%>123.png">
body>
html>

@SessionAttribute和@SessionAttributes

@SessionAttribute注解应用在方法的参数上
@SessionAttributes建议应用在类上

HelloController处理器

import com.alibaba.fastjson.JSON;
import com.young.entity.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Controller
@SessionAttributes("user")
@RequestMapping("/Hello")
public class HelloController {
    //模拟登陆
    @RequestMapping("/login")
    //如果方法没有返回字符串,那么客户端发送请求后,会自动跳转至以方法名为名称的.jsp页面,即"login.jsp"
    public void login(User user){
        System.out.println("登陆成功");
    }

    //模拟修改数据
    @RequestMapping("/edit")
    public void edit(@SessionAttribute("user") User user){
        System.out.println("session中的用户:"+ JSON.toJSONString(user));

    }
}

User实体类

public class User {
    private String username;
    private String password;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}
在请求/login时增加参数username=aaa&password=aaa,会将这些数据封装为实体类User的对象user中,根据@SessionAttributes注解中的value
值将该对象会被存储进Session域中。
再请求/edit时可以使用@SessionAttribute注解将Session域中的值赋给方法中的参数。

注意:
如果有多个方法中都有user参数名,以最后一次存储进Session域中的对象为准。如果只想让login方法参数中的对象放在session中,那么需要让
这个登陆方法的参数与众不同,比如:loginUser_session。

你可能感兴趣的:(Java框架学习:SpringMVC之动态资源和静态资源访问冲突问题、常用注解)