首页 > 其他分享 >SpringMvc-初识

SpringMvc-初识

时间:2022-12-01 19:59:25浏览次数:67  
标签:RequestMapping return String SpringMvc 初识 ------ public 请求

----------------------------------------------------------------

1、环境搭建

1.1 jar包

<spring.version>4.3.18.RELEASE</spring.version>

<!-- spring-mvc begin -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>${spring.version}</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>${spring.version}</version>
</dependency>
<!-- spring-mvc end -->

1.2 web.xml

  • 前端核心控制器(dispatcherServlet)

    • 配置初始化参数,指定springMvc的核心配置文件,目的就是自动创建容器对象
    • 启动级别 1
  • Rest风格 请求转换过滤器

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         id="WebApp_ID" version="3.1">

    <!--前端核心控制器  springMvc框架提供的 -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 配置初始化参数,指定springMvc的核心配置文件,目的就是自动创建容器对象 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
        <!-- web容器启动,优先创建前端核心控制器实例对象 -->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <!-- 拦截所有的请求处理,放行jsp -->
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!-- Rest风格 请求转换过滤器-->
    <filter>
        <filter-name>hiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>hiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>

1.3spring-mvc.xml

  • 组件扫描
  • 视图解析器
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 组件扫描 -->
    <context:component-scan base-package="com.kgc.mvc"></context:component-scan>

    <!-- jsp 视图解析器 -->
    <!-- 作用:当请求处理方法返回一个目标视图页面的名称,视图解析器会自动将返回的视图名称解析为真实的物理视图(prefix + 目标视图页面名称 + suffix) -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 统一前缀,请求处理方法返回目标视图名称时,会被统一增加此前缀目录(目录视图目录),结尾的 / 不能少 -->
        <property name="prefix" value="/WEB-INF/views/"></property>
        <!-- 统一后缀,请求处理方法返回目标视图名称时,会被统一增加此后缀 -->
        <property name="suffix" value=".jsp"></property>
    </bean>

</beans>

2、Mvc 使用流程

需求,浏览器发送请求helloMvc,请求后端处理方法,返回目标success。jsp页面,并在页面中显示 Hello Mvc in KH96;

2.1 HelloMvcController

@Controller //不可以用其他的分层注解替换,只能用controller  (1.可以作为一个控制层组件扫描到容器  2.代表一个控制器)
public class HelloMvcController {

    //接收helloMvc,跳转到目标success.jsp
    @RequestMapping("/helloMvc") //定义当前请求控制器中,可以处理请求的映射url字符串,前面的 / 可写 可不写
    public String helloSpringMvc(){
        System.out.println("------ 调用 HelloMvcController控制器的 helloSpringMvc() 请求处理方法------");

        //目标jsp视图页面的名称(不带后缀),区分大小写
        return "success";
    }
}

2.2 success.jsp

webapp/WEB-INF/views/success.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>成功页面</title>
</head>
<body>
    <h2>KH96 Success Page</h2>
</body>
</html>

2.3 测试

2.4 总结

springMvc使用流程小结:

  • 1、Tomcat服务器启动时,自动创建前端核心控制器的实例-DispatcherServlet,同时自动读取配置的spring-mvc.xml核心配置文件,创建容器对象,并扫描组件加入到容器中;
    即:将控制器组件(加了@Controller注解)加入到容器中,并自动解析加入的请求控制器,可以得到请求处理url请求处理方法映射
  • 2、客户端浏览器,发送请求http://localhost:8080/springmvc-01/helloMvc
  • 3、请求会被 前端核心控制器 拦截(匹配到前端核心控制器url-pattern
  • 4、前端核心控制器 ,自动根据请求url和请求处理方法的映射关系,进行映射查找,如果找到了对应关系,自动将当前请求发送给目标请求处理器的请求处理方法,进行业务处理,如果没有找到,直接报错误404
  • 5、目标请求处理方法,处理完业务后,返回一个目标视图的名称-success
  • 6、返回的目标视图名称,仍然会交给 前端核心控制器 进行视图解析(自动调用容器中添加的视图解析器解析),会得到一个真实的目标物理视图页面(要跳转的真实页面),进行响应跳转目标页面给浏览器显示;

3、@RequestMapping

3.1 @RequestMapping可以写的位置

类上方法上 :

  • 类上:相对于web应用根目录的;

  • 方法上:相对于类上的映射,如果类上没有,就是相对于web应用根目录

3.1.1 只用方法上有

@RequestMapping("/testSpringMvcRequestMappingTarget")
public String testMvcRequestMappingTarget(){
    //返回成功页面
    return "hello";
}

测试

3.1.2 类上 和 方法上都有

@Controller
@RequestMapping("/kh96")
public class SpringMvcController {

    @RequestMapping("/testSpringMvcRequestMappingTarget")
    public String testMvcRequestMappingTarget(){
        //返回成功页面
        return "hello";
    }
    
}

测试

3.2 method属性

  • 作用:指定当前请求处理方法的请求方式

  • 一旦指定了请求方式,就只能使用指定的请求方式,才可以映射处理,不指定,自动适配(所有方式都可以);

  • method 属性可以指定多个请求方式

3.2.1 method = RequestMethod.GET

@RequestMapping(value = "/testSpringMvcRequestMappingMethodGet",method = RequestMethod.GET)
public String testSpringMvcRequestMappingMethodGet(){

    System.out.println("----- @RequestMapping method = RequestMethod.GET ------");

    //返回成功页面
    return "hello";
}

使用PostMan测试

Get测试

Post测试

3.2.2 method = RequestMethod.POST

@RequestMapping(value = "/testSpringMvcRequestMappingMethodPost",method = RequestMethod.POST)
public String testSpringMvcRequestMappingMethodPost(){

    System.out.println("----- @RequestMapping method = RequestMethod.Post ------");

    //返回成功页面
    return "hello";
}

Get测试

Post测试

3.2.3 method = {RequestMethod.GET,RequestMethod.POST})

@RequestMapping(value = "/testSpringMvcRequestMappingMethodGetAndPost",method = {RequestMethod.GET,RequestMethod.POST})
public String testSpringMvcRequestMappingMethodGetAndPost(){

    System.out.println("----- @RequestMapping method = RequestMethod.Post ------");

    //返回成功页面
    return "hello";
}

Get测试

Post测试

3.3 params属性

指定当前请求处理方法对于请求,必要携带的参数

  • 写法1:指定当前请求必须携带的参数名没有参数值,可以指定多个(大括号,逗号分割),如果请求没有携带params的所有参数,请求404;
  • 写法2:指定当前请求必须携带的参数名对应的参数值,可以指定多个(大括号,逗号分割),如果请求没有携带params指定的所有参数请求404,如果请求携带的参数及对应的参数值不匹配,请求404;
    • 注意:携带的参数值必须是指定的参数值
  • 总结,必须携带所有参数,如果有指定参数值必须携带参数值且携带的参数值必须一致

3.3.1 params ={"uname"}

指定一个参数,不指定参数值;

@RequestMapping(value = "/testSpringMvcRequestMappingParams",params ={"uname"} )
public String testSpringMvcRequestMappingParams(){

    System.out.println("----- @RequestMapping 属性 params------");

    //返回成功页面
    return "hello";
}

测试,不带参数

测试,带参数,不带值

测试,带参数,带值

3.3.2 params ={"uname=kh96"}

指定一个参数,指定值;

@RequestMapping(value = "/testSpringMvcRequestMappingParams",params ={"uname=kh96"} )
public String testSpringMvcRequestMappingParams(){

    System.out.println("----- @RequestMapping 属性 params------");

    //返回成功页面
    return "hello";
}

测试,带参数,带值,值不匹配

测试,带参数,带值,值匹配

3.3.3 params ={"uname=kh96","upwd=123"}

指定两个参数,并指定值;

@RequestMapping(value = "/testSpringMvcRequestMappingParams",params ={"uname=kh96","upwd=123"})
public String testSpringMvcRequestMappingParams(){

    System.out.println("----- @RequestMapping 属性 params------");

    //返回成功页面
    return "hello";
}

测试,带一个参数,并指定值

测试,带两个参数,并指定第一个值

测试,带两个参数,并指定第两个值

3.4 headers属性

指定当前请求处理方法对于请求,必要携带的请求头参数,用法和params类似,参数位置不同;

@RequestMapping(value = "/testSpringMvcRequestMappingHeaders",headers = {"token=123"})
public String testSpringMvcRequestMappingHeaders(){

    System.out.println("----- @RequestMapping 属性 headers------");

    //返回成功页面
    return "hello";
}

测试,主要观察参数位置,其他参数规则跟params一致

3.5 @RequestMapping + @PathVariable

请求映射注解:参数占位符注解 @PathVariable:

  • 写法:只能携带请求处理方法的形参中,自动将RequestMapping中指定的url使用的占位符绑定参数值
    • 即:请求地址映射为: /url/{参数名},目标请求为:/url/参数值,自动将参数值绑定到指定参数名上;
  • 要求1:@RequestMapping("指定参数名")注解请求url中,指定的占位符参数名必须跟当前请求方法形参中@PathVariable注解指定的参数名一致;否者报错: Could not find @PathVariable [utel] in @RequestMapping
  • 要求2:@RequestMapping,只写注解,不指定参数名,指定的占位符参数名,必须跟当前请求方法形参名一致(也就是默认为形参名);否则,报错:Could not find @PathVariable [uemail] in @RequestMapping

3.5.1 测试

3.5.1.1 占位符与@PathVariable注解指定的参数名不一致
@RequestMapping("/testSpringMvcRequestMappingPathVariable/{utel#}/{uemail}")
public String testSpringMvcRequestMappingPathVariable(@PathVariable("utel") String utel,@PathVariable("uemail") String uemail){

    System.out.println("----- @RequestMapping + @PathVariable utel:"+utel+",uemail"+uemail+" ------");

    //返回成功页面
    return "hello";
}

测试结果

3.5.1.2 占位符与@PathVariable注解指定的参数名一致
@RequestMapping("/testSpringMvcRequestMappingPathVariable/{utel}/{uemail}")
public String testSpringMvcRequestMappingPathVariable(@PathVariable("utel") String utel,@PathVariable("uemail") String uemail){

    System.out.println("----- @RequestMapping + @PathVariable utel:"+utel+",uemail"+uemail+" ------");

    //返回成功页面
    return "hello";
}

测试结果

3.5.1.3 @PathVariable不指定参数名

测试,占位符与形参不一致

@RequestMapping("/testSpringMvcRequestMappingPathVariable/{utel}/{uemail#}")
public String testSpringMvcRequestMappingPathVariable(@PathVariable("utel") String utel,@PathVariable String uemail){

    System.out.println("----- @RequestMapping + @PathVariable utel:"+utel+",uemail"+uemail+" ------");

    //返回成功页面
    return "hello";
}

测试结果

测试,占位符与形参一致

@RequestMapping("/testSpringMvcRequestMappingPathVariable/{utel}/{uemail}")
public String testSpringMvcRequestMappingPathVariable(@PathVariable("utel") String utel,@PathVariable String uemail){

    System.out.println("----- @RequestMapping + @PathVariable utel:"+utel+",uemail"+uemail+" ------");

    //返回成功页面
    return "hello";
}

测试结果

4.Rest风格

  • get------查询select
  • post------新增insert
  • put------更新update
  • delete------删除delete

相同请求路径,通过请求方式判断请求方法

自定义请求方式,一定要带一个名字为_method的参数

4.1 Rest风格 请求转换过滤器

<!-- Rest风格 请求转换过滤器-->
<filter>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

4.1 get------查询 select

请求方法

//请求映射注解:rest风格处理-get,查询select
//@RequestMapping(value = "/testSpringMvcRequestMappingRestGet/{uid}",method = RequestMethod.GET)
@RequestMapping(value = "/testSpringMvcRequestMappingRest/{uid}",method = RequestMethod.GET)
public String testSpringMvcRequestMappingRestGet(@PathVariable("uid") String uid){

    System.out.println("----- Rest Get 根据 uid:"+uid+" 查询用户详情 ------");

    //返回成功页面
    return "hello";
}

jsp

<h3>Get 请求映射注解:rest风格处理-get,查询select</h3>
<h3><a href="${pageContext.request.contextPath}/testSpringMvcRequestMappingRest/KH96_01">Test SpringMvc Rest Get</a> </h3>

测试

4.2post------新增 insert

//请求映射注解:rest风格处理-post,新增insert
//@RequestMapping(value = "/testSpringMvcRequestMappingRestPost",method = RequestMethod.POST)
@RequestMapping(value = "/testSpringMvcRequestMappingRest",method = RequestMethod.POST)
public String testSpringMvcRequestMappingRestPost(){

    System.out.println("----- Rest Post 新增用户详情  ------");

    //返回成功页面
    return "hello";
}

jsp

<h3>Post 求映射注解:rest风格处理-post,新增insert</h3>
<form action="testSpringMvcRequestMappingRest" method="post">
    <input type="submit" value="Test SpringMvc Rest Post">
</form>

测试

4.3 put------更新 update

//请求映射注解:rest风格处理-put,更新update
//@RequestMapping(value = "/testSpringMvcRequestMappingRestPut",method = RequestMethod.PUT)
@RequestMapping(value = "/testSpringMvcRequestMappingRest",method = RequestMethod.PUT)
public String testSpringMvcRequestMappingRestPut(){

    System.out.println("----- Rest Put 更新用户详情 ------");

    //返回成功页面
    return "hello";
}

jsp

<h3>Put 请求映射注解:rest风格处理-put,更新update</h3>
<form action="testSpringMvcRequestMappingRest" method="post">
    <input type="hidden" name="_method" value="put">
    <input type="submit" value="Test SpringMvc Rest put">
</form>

测试

4.4 delete------删除 delete

//请求映射注解:rest风格处理-delete,删除delete
//@RequestMapping(value = "/testSpringMvcRequestMappingRestDelete/{uid}",method = RequestMethod.DELETE)
@RequestMapping(value = "/testSpringMvcRequestMappingRest/{uid}",method = RequestMethod.DELETE)
public String testSpringMvcRequestMappingRestDelete(@PathVariable("uid") String uid){

    System.out.println("----- Rest Delete 根据 uid:"+uid+" 删除用户  ------");

    //返回成功页面
    return "hello";
}

jsp

<h3>Delete 请求映射注解:rest风格处理-delete,删除delete</h3>
<form action="testSpringMvcRequestMappingRest/KH96_01" method="post">
    <input type="hidden" name="_method" value="delete">
    <input type="submit" value="Test SpringMvc Rest delete">
</form>

测试

 

----------------------------------------------------------------

1、请求处理参数

1.1 请求参数 @RequestParam

1.1.1 不使用 @RequestParam 注解

请求参数处理,不使用参数注解
1.如果请求参数名和请求处理的形参名一致,springMvc 框架会自动将你的请求参数名对应的参数值绑定到请求方法的形参中方法内就可以直接使用,不用手动获取;

2.如果你的请求没有携带参数,不会报错,只不过,请求处理方法的形参值都是null或其他值,如果请求处理方法形参是一个实体参数,原理和单个类型参数一致(只要请求参数名和实体名一致也可以自动绑定);

测试不使用 @RequestParam 注解

@RequestMapping("/testSpringMvcNoRequestParam")
public String testSpringMvcNoRequestParam( String userId,String userName){

    System.out.println(String.format("------ 请求参数:uid :%s ,uname:%s------",userId,userName));

    return "success";
}

测试 请求不带参数

测试 请求带参数

1.1.3 使用 @RequestParam 注解

@RequestParam 只能写在请求方法的形参上
required = false 设置参数不是必传
defaultValue 默认值

  • 1.如果请求处理方法,形参前,只增加了此注解,没有指定任何属性:
    • 作用:当前形参必传,且参数名形参名必须一致,如果不一致,参数绑定失败,请求直接报400错误,提示你的参数没有提供;
  • 2.如果请求处理方法,形参前,只增加了此注解,并添加了value属性值
    • 作用:请求的参数名必须和value属性指定的名称一致,此value属性可以省略(只写 请求参数别名时,如果有其他的属性,value属性必须指定);
  • 3.如果要取消每个参数的必传校验,可以给此注解增加属性 required=false(默认是true,必传),参数可以不传,不需要参数值的绑定;
  • 4.如果某个参数必传,但是为了防止请求没有携带此参数,可以通过指定属性defaultValue增加默认值,如果调用当前请求方法处理,没有提供参数,自动使用默认值,绑定到方法的对应形参中;

测试

@RequestMapping("/testSpringMvcRequestParam")
public String testSpringMvcRequestParam(@RequestParam("uid") String userId,
                                        @RequestParam(value = "uname",required = false,defaultValue = "KH96-mvc") String userName,
                                        @RequestParam String userPwd){

    System.out.println(String.format("------ 请求参数:uid :%s ,uname:%s,uPwd:%s------",userId,userName,userPwd));

    return "success";
}

测试结果

1.2 请求头参数 @RequestHeader

请求头参数@RequestHeader:

  • 处理请求头携带参数;
  • 作用:从请求头中获取参数,根据别名或者参数形参名,将请求头对应参数值,绑定到请求处理方法的形参中,直接使用,用法跟@RequestParam类似,也可以指定是否必传,默认值;

测试

@RequestMapping("/testSpringMvcNoRequestHeader")
public String testSpringMvcNoRequestHeader(@RequestHeader(value = "token",required = false,defaultValue = "KH96_mvc") String token){

    System.out.println(String.format("------ 请求头参数:token :%s ------",token));

    return "success";
}

测试结果

@CookieValue:

cookie中获取参数,根据别名或者参数形参名,将请求头对应参数值绑定到请求处理方法的形参中,直接使用,用法跟@RequestParam,@RequestHeader类似,也可以指定是否必传默认值

测试

@RequestMapping("/testSpringMvcCookieValue")
public String testSpringMvcCookieValue(@CookieValue("JSESSIONID") String sessionId){

    System.out.println(String.format("------ 请求cookie参数:JSESSIONID :%s ------",sessionId));

    return "success";
}

测试结果:

1.4 实体参数

请求参数处理 实体参数
自动将请求参数自动将实体的属性名与形参绑定,原理和单个参数类型一致;

测试

@RequestMapping("/testSpringMvcBeanParam")
public String testSpringMvcBeanParam(UserVO userVO){

    System.out.println(String.format("------ 请求实体参数:userVO :%s ------",userVO));

    return "success";
}

测试结果

1.5 原生servlet的API接口作为请求参数

用法和javaWeb截断学习的用法完全一致;

@RequestMapping("testSpringMvcServletAPI")
public  void testSpringMvcServletAIP(HttpServletRequest request, HttpServletResponse response,HttpSession session) throws IOException, ServletException {
    //获取请求方式
    System.out.println(String.format("------ 请求方式:%s ------",request.getMethod()));

    //获取请求参数
    System.out.println(String.format("------ 请求参数:%s ------",request.getParameter("userTel")));

    //手动转发
    // request.getRequestDispatcher("/WEB-INF/views/success.jsp").forward(request,response);

    //获取session对象
    //手动添加参数
    HttpSession sessionHad = request.getSession();

    //sessionHad.setAttribute("userTel",request.getParameter("userTel"));

    //方式2;直接将HttpSession对象声明在请求处理方法的形参中,可以自动获取 session

    //重定向到
    //        response.sendRedirect("http://www.baidu.com");

}

2、响应数据处理

2.1 ModelAndView

ModelAndView 的数据模型放在request的作用域中视图模型,放在request.getRequestDispatcher(前缀+视图名字+后缀);

结论:SpringMVC框架的前端核心控制器,会自动将返回的ModelAndView对象中的数据模型,添加到request作用域使用内部转发方式,转发到目标视图;

测试

@RequestMapping("/testSpringMvcModelAndView")
public ModelAndView testSpringMvcModelAndView(String userEmail){

    //创建一个 ModelAndView 对象
    ModelAndView mav = new ModelAndView();

    //添加模型数据
    mav.addObject("uemail",userEmail);
    //可以添加多个模型数据,内部使用ModelMap集合存放的
    mav.addObject("uage",18);

    //设置目标视图
    mav.setViewName("ok");

    //返回  ModelAndView 对象
    return  mav;

}

测试结果

2.2 Map

要求:使用map返回模型数据,必须将map声明在请求处理方法的形参中,定义在方法的内部是无效的;

总结:

  • 1.springMVC底层在调用目标请求处理方法前,会自动创建一个Model接口的隐含对象作为模型数据存储对象,如果目标请求处理方法定义了一个map接口自动将隐含对象使用引用传递方式传递到目标请求处理方法中,方法体内就可以直接添加模型数据
  • 2.SpringMvc底层,在调用完你的请求处理方法后,不管返回什么类型,都会被转换为一个ModelAndView对象

测试

@RequestMapping("/testSpringMvcMap")
public  String testSpringMvcMap(String userEmail, Map<String,Object> map){

    //输出map的目标类型
    System.out.println(map.getClass().getName());
    //org.springframework.validation.support.BindingAwareModelMap

    //添加模型数据
    map.put("uemail",userEmail);

    //返回目标视图名称
    return "ok";

}

测试结果

2.3 Model

Model,可以添加模型数据;
必须将 Model 声明在请求处理方法的形参中,定义在方法的内部是无效的,用法跟Map 一致;

测试

@RequestMapping("/testSpringMvcModel")
public  String testSpringMvcModel(String userEmail, Model model){

    //添加模型数据
    model.addAttribute("uemail",userEmail);

    //返回目标视图名称
    return "ok";

}

测试结果

2.4 forward: 转发 和 redirect: 重定向

2.4.1forward 转发

只需要在返回的视图名称前,增加一个前缀:forward 即可,直接转发到目标资源;
当前请求处理方法中的模型对象可以带走;

@RequestMapping("/testSpringMvcForward")
public String testSpringMvcForward(Map<String,Object> map){

    //转发页面跳转
    //return "forward:/forward.jsp";

    //转发到其他请求中
    //return "forward:/[email protected]";

    //自定义转发,携带模型数据
    map.put("uaddr","nj_wd_kh96");

    return "forward:/[email protected]";
}

2.4.2 redirect 重定向

只需要在返回的视图名称前,增加一个前缀:redirect 即可,不做视图解析器,直接重定向

注意,重定向,当前请求处理方法中的模型对象不能带走

@RequestMapping("/testSpringMvcRedirect")
public String testSpringMvcRedirect(){

    //重定向页面跳转
    //return "redirect:/redirect.jsp";

    //重定向 可以重定向到 其他请求中
    return "redirect:/[email protected]";

}

2.5 @ResponseBody

返回JSON数据;

  • 注意:返回不需要手动转换为json字符串,直接返回对象;
  • @ResponseBody 将返回请求处理方法返回的源对象数据,直接转换为json格式字符串,返回,不走视图解析;
  • 注意:必须增加Jackson依赖的jar包,否者报错,且必须要增加注解扫描<mvc:annotation-driven></mvc:annotation-driven> 才可以;
  • 位置:可以写在请求处理方法上或者类上,写在类上,就是对当前类中所有请求处理方法生效,写在方法上,只有对当前方法生效;

2.5.1 jar包

<!-- JSON begin -->
<!--jackson-->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.8.4</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.8.4</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.8.4</version>
</dependency>
<!--jackson-->
<!-- JSON end -->

2.5.2 配置

<beans xmlns=
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
">
    <!-- 开启MVC 的注解扫描 -->
    <mvc:annotation-driven></mvc:annotation-driven>

</beans>

2.5.3 测试

@RequestMapping("/testSpringMvcJson")
@ResponseBody
public UserVO testSpringMvcJson(){

    //模拟获取去用户详情对象
    UserVO userVO = new UserVO();

    userVO.setUserId("KH96_01");
    userVO.setUserName("GKC");
    userVO.setUserPwd("123");

    return userVO;

}

测试结果

 

-----------------------------------------------------------------

1、springMvc的form表单

1.1 标签

<!-- springMvc的form表单标签 -->
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

1.2 form: 标签

使用springMvc的form表单,快速开发表单及数据自动回显;

原理:在数据模型中添加一个 参数名为 command 的参数,springMvc的form表单会自动映射

//跳转到添加页面
map.put("command",new Detail()); //参数没有内容

//跳转到修改页面
map.put("command",detailService.getDetail(id)); //有对应的参数内容

1.3 修改和添加页面复用

1.3.1 动态改变请求方式

<%-- 修改和添加是共用页面,页面要判断是添加功能还是修改,根据模型数据详情实体,有没有id值,有修改,无增加   --%>
<c:if test="${!empty command.id}" var="flag">
    <form:hidden path="id"></form:hidden>
    <!-- rest 风格修改,将post请求,改为put请求 -->
    <input type="hidden" name="_method" value="put">
</c:if>

1.3.2 动态切换修改和添加标题

<c:if test="${flag}">
    <th colspan="2">修改新闻详情</th>
</c:if>
<c:if test="${!flag}">
    <th colspan="2">添加新闻详情</th>
</c:if>

1.3.3 下拉表单自动映射

<!-- 下拉列表,path属性指定的是select标签的id和name属性值(还可以根据此值从实体中获取参数,回显数据),items属性指定的集合数据,自动遍历,并添加option选项,itemLabel对应option选项的显示内容, itemValue对应option选项的value属性值 -->
<td><form:select path="cid" items="${categories}" itemLabel="name" itemValue="id"/></td>

1.3.4 普通参数自动映射

 <!-- 单行文本,path属性指定的是input标签的id和name属性值,还必须和返回的实体属性中的属性名一致,才可以回显数据 -->
<td><form:input path="title"/></td>

1.3.5 全部jsp

修改页面 和 增加页面 复用;

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!-- 核心标签库 -->
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<html>
<head>
    <title>添加新闻</title>
</head>
<body>
<%-- 使用springMvc的form表单,快速开发表单及数据自动回显 --%>
<form:form action="${pageContext.request.contextPath}/detail" method="post">
    <%-- 修改和添加是共用页面,页面要判断是添加功能还是修改,根据模型数据详情实体,有没有id值,有修改,无增加   --%>
    <c:if test="${!empty command.id}" var="flag">
        <form:hidden path="id"></form:hidden>
        <!-- rest 风格修改,将post请求,改为put请求 -->
        <input type="hidden" name="_method" value="put">
    </c:if>
    <table border="1px" width="40%" cellspacing="0" align="center" style="margin-top: 96px;">
        <tr style="height: 80px; font-size: 26px; ">
            <c:if test="${flag}">
                <th colspan="2">修改新闻详情</th>
            </c:if>
            <c:if test="${!flag}">
                <th colspan="2">添加新闻详情</th>
            </c:if>
        </tr>
        <tr style="height: 50px; font-size: 18px;">
            <td style="">新闻分类:</td>
            <!-- 下拉列表,path属性指定的是select标签的id和name属性值(还可以根据此值从实体中获取参数,回显数据),items属性指定的集合数据,自动遍历,并添加option选项,itemLabel对应option选项的显示内容, itemValue对应option选项的value属性值 -->
            <td><form:select path="cid" items="${categories}" itemLabel="name" itemValue="id"/></td>
        </tr>
        <tr style="height: 50px; font-size: 18px;">
            <td style="">新闻标题:</td>
            <!-- 单行文本,path属性指定的是input标签的id和name属性值,还必须和返回的实体属性中的属性名一致,才可以回显数据 -->
            <td><form:input path="title"/></td>
        </tr>
        <tr style="height: 50px; font-size: 18px;">
            <td style="">新闻摘要:</td>
            <td><form:textarea path="summary"/></td>
        </tr>
        <tr style="height: 50px; font-size: 18px;">
            <td style="">新闻作者:</td>
            <td><form:input path="author"/></td>
        </tr>
        <tr style="height: 50px; font-size: 18px;">
            <td style="">创建时间:</td>
            <td><form:input path="createDate"/></td>
        </tr>

        <tr style="height: 50px; font-size: 18px; ">
            <td colspan="2" style="text-align: center">
                <input type="submit" value="提&nbsp;&nbsp;交"/>
                <input type="button" value="返&nbsp;&nbsp;回"/>
            </td>
        </tr>
    </table>
</form:form>

</body>
</html>

1.3.6 测试

1.3.6.1 增加

1.3.6.2 修改

1.4 静态资源无法加载问题

1.4.1 静态资源无法加载

1.4.2 配置静态资源

必须同时配置 MVC 的注解扫描 和 静态资源映射

原理:
<mvc:default-servlet-handler>这个配置,可以将在 SpringMVC 上下文中定义一个 DefaultServletHttpRequestHandler,它会对进入 DispatcherServlet 的请求进行筛查
如果发现是没有经过配置映射的请求,就将该请求交由 WEB 应用服务器默认的 Servlet 处理,在tomcat的web.xml中,配置一个叫default的servlet,对应的url-patten也是配置的 /;
记住:Springmvc的DispatcherServlet的优先级高于tomcat默认的default,所以配置映射的会访问,但是没有映射的交由tomcat处理,就可以进行访问到静态资源了,如果是有配置映射的请求,才由 DispatcherServlet 继续处理;

<!-- 开启MVC 的注解扫描 -->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 静态资源过滤 -->
<mvc:default-servlet-handler></mvc:default-servlet-handler>

------------------------------------------------------------------

1、图片下载

图片下载:将服务器端的文件以流的形式写到客户端,通过浏览器保存到本地,实现下载;

1.1 图片下载步骤

1.通过session获取上下文对象(session.getServletContext())

​ session.getServletContext()

2.获取 服务器上的图片文件(输入流)

​ InputStream inputStream = servlet.getResourceAsStream("pic/dsgl.jpg");

3.定义缓存数组

​ byte[] bytes = new byte[inputStream.available()];

4.将目标文件读取到缓存数组

​ inputStream.read(bytes);

5.将目标图片文件封装到响应对象 ResponseEntity 中,响应给浏览器

​ 响应头数据:HttpHeaders httpHeaders = new HttpHeaders();

​ 响应码:HttpStatus httpStatus = HttpStatus.OK;

​ 响应的图片数据:return new ResponseEntity<byte[]>(bytes,httpHeaders,httpStatus);

1.2 请求方法

// 图片下载:将服务器端的文件以流的形式写到客户端,通过浏览器保存到本地,实现下载
@RequestMapping("/downloadPic")
public ResponseEntity<byte[]> testDownloadPic(HttpSession session) throws IOException {
    //读取服务器器上的图片文件,读取流对象,必须要借助session获取上下文对象
    InputStream inputStream = session.getServletContext().getResourceAsStream("pic/dsgl.jpg");

    //定义缓存数组,数组大小根据文件大小来定义
    byte[] bytes = new byte[inputStream.available()];

    //将目标文件读取到缓存数组
    inputStream.read(bytes);

    //将目标图片文件封装到响应对象 ResponseEntity 中,响应给浏览器
    //ResponseEntity 响应对象,必须包含三个内容:响应头数据, 响应码(响应状态), 响应的图片数据

    HttpHeaders httpHeaders = new HttpHeaders();

    //响应头数据 attachment 代表附件(才可以下载)
    httpHeaders.add("Content-Disposition","attachment;filename=" + new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()) +
                    ".jpg"); //UUID.randomUUID().toString().substring(0,8)+

    //响应码
    HttpStatus httpStatus = HttpStatus.OK;

    //响应的图片数据
    return new ResponseEntity<byte[]>(bytes,httpHeaders,httpStatus);

}

1.3 jsp

<h2>从服务器下载图片到本地</h2>
<hr/>
<h3>
    <a href="${pageContext.request.contextPath}/downloadPic" >点击下载图片</a>
</h3>

2、图片上传

2.1 图片上传步骤

1.获取上传的头像名称

​ String targetFilename = multipartFile.getOriginalFilename();

2.重新定义新的文件名

​ targetFilename = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()) + targetFilename.substring(targetFilename.indexOf("."));

3.获取真实上传路径

​ String realFilePath = session.getServletContext().getRealPath("upload");

4.创建文件目录(如果不存在)

​ targetFilePath.mkdirs()

5.创建目标文件对象(用于存放要上传的图片)

​ File targetFile = new File(targetFilePath+"/"+targetFilename);

6.文件上传到服务器,组件自动支持功能

​ multipartFile.transferTo(targetFile);

7.设置回显头像

​ map.put("targetHeadImage",targetFilename);

2.2 jsp

<h2>本地图片上传到服务器</h2>
<hr/>
<!-- 图片上传,必须使用form表单,而且石post方式 -->
<form action="${pageContext.request.contextPath}/uploadPic" method="post" enctype="multipart/form-data">
	<h3>
		选择个人头像:<br/>
		<input type="file" name="uploadHeadImage">
		<input type="submit" value="上传">
	</h3>
</form>
<p>
	上传个人头像:
	<img src="${targetHeadImage}" width="100px">
</p>

2.3 请求方法

//图片上传
//将本地图片上传到服务器
@RequestMapping(value = "/uploadPic",method = RequestMethod.POST)
public String testUploadPic(HttpSession session, @RequestParam("uploadHeadImage") MultipartFile multipartFile, Map<String, String> map) throws IOException {

    //获取上传的头像名称
    String targetFilename = multipartFile.getOriginalFilename();
    System.out.println("------ 上传文件名"+targetFilename+" ------");

    //重新定义新的文件名,要保留上传文件的类型
    targetFilename = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()) + targetFilename.substring(targetFilename.indexOf("."));

    System.out.println("------ 新的文件名"+targetFilename+" ------");

    //上传文件,要保存服务器上的真实路径中,idea项目发布,默认不会放到目标tomcat中,
    String realFilePath = session.getServletContext().getRealPath("upload");

    System.out.println("------ 服务器真实路径"+realFilePath+" ------");

    //文件目录可能不存在,也不能人为干预,必须程序主动处理
    File targetFilePath = new File(realFilePath);

    if(!targetFilePath.exists()){
        //目标不存在,主动创建
        if(targetFilePath.mkdirs()){
            System.out.println("上传目录创建成功");
        }
    }

    //**创建目标文件对象
    File targetFile = new File(targetFilePath+"/"+targetFilename);

    //文件上传到服务器,只需要一步,组件自动支持功能
    multipartFile.transferTo(targetFile);

    //设置回显头像
    map.put("targetHeadImage",targetFilename);

    return "forward:hellopic.jsp";
}

2.4 配置文件 和 jars

2.4.1 配置文件

<!-- 配置支持文件上传的组件,可插拔,配置才可使用 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- 指定文件上传的字符集,要跟jsp页面字符集一致 -->
    <property name="defaultEncoding" value="utf-8"></property>
    <property name="maxInMemorySize" value="10485678"></property>
</bean>

<!-- 配置服务器的静态资源的映射目录,自动根据指定的目录(真实文件路径),按照文件名查找文件 -->
<mvc:resources mapping="/*.jpg" location="file:///D:/KEGONGCHANG/DaiMa/IDEA/KH96/SpringMVC/SpringMVC/springmvc-04/target/springmvc-04/upload/"></mvc:resources>

2.4.2 jars

 <!-- 文件上传 -->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.1</version>
</dependency>
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.4</version>
</dependency>

3、拦截器

拦截器,必须实现 HandlerInterceptor 接口

执行时机:
过滤器是在DispatcherServlet处理之前拦截,拦截器是在DispatcherServlet处理请求然后调用控制器方法(即我们自己写的处理请求的方法,用@RequestMapping标注)之前进行拦截。

3.1 实现三个方法

3.1.1 preHandle

  • 执行时机:调用目标请求处理器中的目标请求处理方法前,执行此方法;

  • 调用处:在前端核心控制器的962行,不同spring版本不一样;

  • 执行次序:从第一个拦截器,依次往后执行所有拦截器的此方法,只要有一个拦截器返回false,一组请求就都过不去;

  • 返回值:

    • false:代表不可以调用请求目标处理方法,即需要拦截请求;
    • true:代表可以调用请求目标处理方法,即不需要拦截请求;

3.1.2 postHandle

  • 执行时机:在调用目标请求处理器中的目标请求处理方法后,在模型数据渲染之前,执行此方法;
  • 调用处:在前端核心控制器的974行,不同spring版本不一样;
  • 执行次序:从最后一个拦截器,依次往前执行所有拦截器的此方法;

3.1.3 afterCompletion

  • 执行时机:
    • 正常情况:
      • 没有拦截请求,也没有发生异常,在调用目标请求处理器中的目标请求处理方法后,在模型数据渲染之后(获取,渲染,转发或者重定向等),执行此方法;
      • 调用处:在前端核心控制器的1059行,不同spring版本不一样,
      • 执行次序:从最后一个拦截器,依次往前执行所有拦截器的此方法
    • 异常情况:
      • 多个拦截器中,其中一个拦截了请求(preHandle方法返回了false),此方法被调用,且是从当前拦截器前一个拦截器开始倒序执行(第一个拦截器拦截除外);
      • 在前端核心处理器,处理请求过程中发生了异常,此方法也会被调用,原理和上面类似;
  • 小结:所有通过的拦截器,都会执行此方法,释放资源,拦截请求的当前拦截器是不会执行此方法(包括其后的所有拦截器);

3.2 测试 (一个拦截器)

@Controller //将拦截器作为普通组件,不可以直接生效,必须将容器,加入到拦截器组中
public class FirstInterceptor implements HandlerInterceptor  {

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        
        System.out.println(" ------ FirstInterceptor  preHandle ------");
        
        return false; //返回false
		//return true; //返回true
        
    }

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

    }

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

    }
}

配置文件

<!-- 添加自定义拦截器 到 拦截器组 -->
<mvc:interceptors>
    <ref bean="firstInterceptor"></ref>
</mvc:interceptors>

请求方法

@RequestMapping("/testFirstInterceptor")
public String testSpringMvcInterceptor(){
    System.out.println(" ------ 测试SprigMvc的拦截器的目标处理方法1------ ");
    return "forward:index.jsp";
}

3.2.1 返回 false

只执行 perHandle方法,不执行 目标请求处理器中的目标请求处理方法

3.2.2 返回 true

执行 preHandle -> 执行目标请求处理器中的目标请求处理方法 -> postHandle -> afterCompletion

3.3 测试(两个拦截器)

3.3.1 都返回true

第二个拦截器

@Controller 
public class SecondInterceptor implements HandlerInterceptor
{

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println(" ------ SecondInterceptor  preHandle ------");

       // return false;
        return true;
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        //执行顺序:一组执行器,从后往前执行
        System.out.println(" ------ SecondInterceptor  postHandle ------");

    }

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

    }
}

配置文件

<!-- 添加自定义拦截器 到 拦截器组 -->
<mvc:interceptors>
    <ref bean="firstInterceptor"></ref>
    <ref bean="secondInterceptor"></ref>
</mvc:interceptors>

请求方法

@RequestMapping("/testSecondInterceptor")
public String testSecondInterceptor(){
    System.out.println(" ------ 测试SprigMvc的拦截器的目标处理方法2------ ");
    return "forward:index.jsp";
}

3.3.1 两个都返回true

3.3.2 第一个 返回true,第二个返回false

 

---------------------------------------------------------------------

支付宝沙箱和关键字过滤,md5加密,SSM项目重要知识点

1、支付宝沙箱

1.1 jar包 alipay-sdk

<!-- alipay-sdk -->
<dependency>
    <groupId>com.alipay.sdk</groupId>
    <artifactId>alipay-sdk-java</artifactId>
    <version>4.10.90.ALL</version>
</dependency>

1.2 信息配置类

1.2.1 配置信息

public class AlipayConfig {
	// 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
	public static String app_id = "";

	// 商户应用私钥,您的PKCS8格式RSA2私钥
	public static String merchant_private_key = "";
	// 对应APPID下的支付宝公钥。
	public static String alipay_public_key = "";
    
	// 服务器异步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
	public static String notify_url = "http://localhost:8080(/项目名,没有的不需要)/Alipay/notify_url.do";

	// 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
	public static String return_url = "http://localhost:8080(/项目名,没有的不需要)/Alipay/alipay_return.do";

	// 签名方式
	public static String sign_type = "RSA2";

	// 字符编码格式
	public static String charset = "UTF-8";

	// 支付宝网关
	public static String gatewayUrl = "https://openapi.alipaydev.com/gateway.do";
}

1.2.2配置信息来源

登录自己已经入驻的支付宝账号:https://open.alipay.com/develop/sandbox/app

1.2.2.1 APPID

1.2.2.2 公钥,私钥

1.3 支付控制层

/**
 * 支付宝支付
 */
@Controller
@RequestMapping("/Alipay")
public class AlipayController {

    /**
     * 生成订单直接跳转支付宝付款
     */
    @RequestMapping("/to_alipay.do")
    public void toAlipay(HttpServletResponse response, HttpServletRequest request) throws Exception{

        AlipayClient alipayClient = new DefaultAlipayClient(
                AlipayConfig.gatewayUrl, AlipayConfig.app_id,
                AlipayConfig.merchant_private_key, "json", AlipayConfig.charset,
                AlipayConfig.alipay_public_key, AlipayConfig.sign_type);

        // 取购买人名称
        String in_name = request.getParameter("in_name");
        // 取手机号
        String in_phone = request.getParameter("in_phone");
        // 创建唯一订单号
        int random = (int) (Math.random() * 10000);
        String dateStr = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());

        // 订单号拼接规则:手机号后四位+当前时间后四位+随机数四位数
        String out_trade_no = in_phone.substring(7) + dateStr.substring(10)
                + random;
        // 拼接订单名称
        String subject = in_name + "的订单";

        // 取付款金额
        String total_amount = request.getParameter("in_money");

        // 设置请求参数
        AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
        alipayRequest.setReturnUrl(AlipayConfig.return_url);//支付成功响应后跳转地址
        alipayRequest.setNotifyUrl(AlipayConfig.notify_url);//异步请求地址

        /*FAST_INSTANT_TRADE_PAY 二维码瞬时支付
         * out_trade_no 订单号 total_amount 订单金额  subject 订单名称
         */
        alipayRequest.setBizContent("{\"out_trade_no\":\"" + out_trade_no
                + "\"," + "\"total_amount\":\"" + total_amount + "\","
                + "\"subject\":\"" + subject + "\"," + "\"body\":\""
                + ""+ "\"," + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
        String result = "请求无响应";
        // 请求
        try {
            //通过阿里客户端,发送支付页面请求
            result = alipayClient.pageExecute(alipayRequest).getBody();
            response.setContentType("text/html;charset=UTF-8");
            response.setCharacterEncoding("UTF-8");
            response.getWriter().println(result);
            response.getWriter().flush();
        } catch (AlipayApiException e) {
            e.printStackTrace();
        } finally {
            response.getWriter().close();
        }
    }


    /**
     * 支付成功后处理业务
     */
    @RequestMapping("/alipay_return.do")
    public String alipayReturn(HttpServletRequest request, Map<String, Object> map) throws Exception{

        // 响应信息
        String msg = "";

        // 请在这里编写您的程序(以下代码仅作参考)
        if (verifyAlipayReturn(request)) {//验签成功后执行的自定义业务代码
            // 商户订单号
            String out_trade_no = new String(request.getParameter(
                    "out_trade_no").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);

            // 支付宝交易号
            String trade_no = new String(request.getParameter("trade_no")
                    .getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);

            // 付款金额
            String total_amount = new String(request.getParameter(
                    "total_amount").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
            msg = "支付宝交易号:" + trade_no + "<br/>商户订单号"
                    + out_trade_no + "<br/>付款金额:" + total_amount;

        } else {
            msg = "验签/支付失败";
        }

        map.put("msg", msg);

        return "forward:/success.jsp"; //支付完成后,跳转的页面
    }

    /**
     * 支付宝异步通知
     * @param request
     * @param response
     * @throws Exception
     */
    @RequestMapping("/notify_url.do")
    public void alipayNotify(HttpServletRequest request,HttpServletResponse response)
                                                throws Exception {
        // ——请在这里编写您的程序(以下代码仅作参考)——

        /*
         * 实际验证过程建议商户务必添加以下校验: 1、需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
         * 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
         * 3、校验通知中的seller_id(或者seller_email)
         * 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email)
         * 4、验证app_id是否为该商户本身。
         */
        if (verifyAlipayReturn(request)) {// 验证成功
            // 商户订单号
            String out_trade_no = new String(request.getParameter(
                    "out_trade_no").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
            System.out.println(out_trade_no);
            // 支付宝交易号
            String trade_no = new String(request.getParameter("trade_no")
                    .getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
            System.out.println(trade_no);

            // 交易状态
            String trade_status = new String(request.getParameter(
                    "trade_status").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);

            if (trade_status.equals("TRADE_FINISHED")) {
                // 判断该笔订单是否在商户网站中已经做过处理
                // 如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
                // 如果有做过处理,不执行商户的业务程序
                // 注意:
                // 退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知
            } else if (trade_status.equals("TRADE_SUCCESS")) {
                // 判断该笔订单是否在商户网站中已经做过处理
                // 如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
                // 如果有做过处理,不执行商户的业务程序
                // 注意:
                // 付款完成后,支付宝系统发送该交易状态通知
            }

        } else {// 验证失败
            response.setContentType("text/html;charset=UTF-8");
            response.setCharacterEncoding("UTF-8");
            response.getWriter().println("验签/支付失败!");
            response.getWriter().flush();
            response.getWriter().close();

            // 调试用,写文本函数记录程序运行情况是否正常
            // String sWord = AlipaySignature.getSignCheckContentV1(params);
            // AlipayConfig.logResult(sWord);
        }
    }

    /**
     * @author zhukang
     * @date 2021-04-23
     * @return
     * @description 验证支付宝的反馈信息
     */
    private boolean verifyAlipayReturn(HttpServletRequest request) throws UnsupportedEncodingException {
        // 获取支付宝回调反馈的信息
        Map<String, String> params = new HashMap<>();
        Map<String, String[]> requestParams = request.getParameterMap();
        for (Iterator<String> iter = requestParams.keySet().iterator(); iter
                .hasNext();) {
            String name = iter.next();
            String[] values = requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i]
                        : valueStr + values[i] + ",";
            }
            // 乱码解决,这段代码在出现乱码时使用
            valueStr = new String(valueStr.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
            params.put(name, valueStr);
        }

        boolean signVerified = false;
        try {// 调用SDK验证签名
            signVerified = AlipaySignature.rsaCheckV1(params,
                    AlipayConfig.alipay_public_key, AlipayConfig.charset,
                    AlipayConfig.sign_type);
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }

        return signVerified;
    }

}

1.4 测试访问

直接访问 Alipay/to_alipay.do 这个请求即可(参数根据实际操作进行添加);

2、关键字过滤

2.1 关键字文件 sensitivewords.txt

小笨蛋
...(根据实际需求添加关键字)

2.2 关键字工具类

2.2.1 关键字初始化


/**
 * Created On : 2022/7/26.
 * <p>
 * Author : zhukang
 * <p>
 * Description: 敏感词初始化类
 */
public class SensitiveWordInit {
    // 敏感词集合
    public static Map sensitiveWordMap;

    // 初始化敏感词
    public Map initSensitiveWord(){
        System.out.println("------- 系统启动,从文件中读取敏感字,存入sensitiveWordMap -------");

        try {
            // 读取敏感词文件,将敏感词加入HashMap
            addSensitiveWordToHashMap(readSensitiveWordFile());

        } catch (Exception e){
            e.printStackTrace();
        }
        return sensitiveWordMap;
    }

    /**
     * @author : zhukang
     * @date   : 2022/7/26
     * @param  : [java.util.Set<java.lang.String>]
     * @return : java.util.Map
     * @description : 将HashSet中的敏感词,存入HashMap中
     */
    private void addSensitiveWordToHashMap(Set<String> wordSet) {
        // 初始化敏感词容器,减少扩容操作
        sensitiveWordMap = new HashMap(wordSet.size());
        for (String word : wordSet) {
            Map nowMap = sensitiveWordMap;
            for (int i = 0; i < word.length(); i++) {
                // 转换成char型
                char keyChar = word.charAt(i);
                // 获取
                Object tempMap = nowMap.get(keyChar);
                // 如果存在该key,直接赋值
                if (tempMap != null) {
                    // 一个一个放进Map中
                    nowMap = (Map) tempMap;
                }
                // 不存在则,则构建一个map,同时将isEnd设置为0,因为他不是最后一个
                else {
                    // 设置标志位,不是最后一个
                    Map<String, String> newMap = new HashMap<String, String>();
                    // 没有这个key,就把(isEnd,0) 放在Map中
                    newMap.put("isEnd", "0");
                    // 添加到集合
                    nowMap.put(keyChar, newMap);
                    //指向当前map,继续遍历
                    nowMap = newMap;
                }
                // 最后一个
                if (i == word.length() - 1) {
                    nowMap.put("isEnd", "1");
                }
            }
        }
    }

    /**
     * @author : zhukang
     * @date   : 2022/7/26
     * @param  : []
     * @return : java.util.Set<java.lang.String>
     * @description : 读取敏感词库文件,存入HashMap中
     */
    private Set<String> readSensitiveWordFile() {
        // 敏感词集合
        Set<String> wordSet = null;
        //敏感词库
        try
                (
                // 获取输入流,读取resources目录下的static目录中的敏感词文件(一个敏感词一行)
                InputStream inputStream = new ClassPathResource("sensitivewords.txt").getInputStream();

                // 读取文件输入流
                InputStreamReader read = new InputStreamReader(inputStream, "UTF-8");

                // 高效读取
                BufferedReader br = new BufferedReader(read);
        )
        {
            // 创建set集合,存储读取的敏感字
            wordSet = new HashSet<>();


            //手动 添加词语
//            wordSet.add("笨蛋");
//            wordSet.add("傻瓜");

            // 读取文件,将文件内容放入到set中
            String txt = null;
            while ((txt = br.readLine()) != null) {
                wordSet.add(txt);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 返回敏感字集合
        return wordSet;
    }
}

2.2.2 关键字过滤类

/**
 * Created On : 2022/7/26.
 * <p>
 * Author : zhukang
 * <p>
 * Description: 敏感字过滤工具类
 */
@Component
public class SensitiveWordFilterUtil {
    // 最小匹配规则
    public static final int MIN_MATCH_TYPE = 1;

    // 最大匹配规则
    public static final int MAX_MATCH_TYPE = 2;

    // 敏感词集合
    public static Map sensitiveWordMap;

    // 应用启动后,创建实例对象,自动执行此方法
    @PostConstruct
    public void init() {
        // 从数据库查询敏感词,转换为set集合 将敏感词库加入到HashMap中,确定有穷自动机DFA
        sensitiveWordMap = new com.kgc.weddingshop.utils.SensitiveWordInit().initSensitiveWord();

        System.out.println("------ " + sensitiveWordMap + "------");
    }

    /**
     * @param : [java.lang.String]
     * @return : boolean true 包含;false 不包含
     * @author : zhukang
     * @date : 2022/7/26
     * @description : 是否包含敏感词(默认按最小匹配规则来,只要有敏感词就ok),最小匹配规则
     */
    public boolean isContainSensitiveWordMin(String txt) {
        return isContainSensitiveWord(txt, MIN_MATCH_TYPE);
    }

    /**
     * @param : [java.lang.String, int]
     * @return : boolean
     * @author : zhukang
     * @date : 2022/7/26
     * @description : 是否包含敏感词(默认按指定匹配规则来,只要有敏感词就ok)
     * 如果敏感词库为:
     * * 傻叉
     * * 傻叉人
     * * 大傻叉
     * * 初始化之后为:{傻={叉={人={isEnd=1}, isEnd=1}, isEnd=0}, 大={傻={叉={isEnd=1}, isEnd=0}, isEnd=0}}
     * * 1、按最小规则匹配,  匹配 傻叉 的时候,匹配到叉,就为最后一个了 直接break。如果输入的敏感词是傻叉人,命中的只是傻叉,而不是傻叉人
     * * 2、按最大规则匹配,  匹配 傻叉 的时候,匹配到叉,已经为最后一个,但是按照最大规则,会继续匹配人,命中的是傻叉人
     * * 3、如果关键词是傻叉猫,两种匹配规则都会匹配到傻叉,会命中,如果输入的是傻叉人猫,按最小规则匹配是傻叉,按最大规则匹配傻叉人,只是匹配敏感词不同
     */
    public boolean isContainSensitiveWord(String txt, int matchType) {
        if (txt == null || "".equals(txt)) {
            return false;
        }
        for (int i = 0; i < txt.length(); i++) {
            int matchFlag = this.checkSensitiveWords(txt, i, matchType);
            if (matchFlag > 0) {
                return true;
            }
        }
        return false;
    }

    /**
     * @param : 待判断文本 起始位置 匹配类型: 1 最小匹配原则;2 最大匹配原则
     * @return : int 大于0表示包含敏感词且表示敏感词匹配长度,否则不包含
     * @author : zhukang
     * @date : 2022/7/26
     * @description : 校验是否包含敏感词
     */
    private static int checkSensitiveWords(String txt, int beginIndex, int matchType) {
        // 敏感词结束标识位:用于敏感词只有1位的情况
        boolean flag = false;

        // 匹配标识数默认为0
        int matchFlag = 0;
        // 从内存中,获取敏感词库
        Map nowMap = sensitiveWordMap;
        for (int i = beginIndex; i < txt.length(); i++) {
            // 获取第一个字符
            char word = txt.charAt(i);

            // 获取指定key,判断当前字符是不是一个敏感词的开头
            nowMap = (Map) nowMap.get(word);

            // 不存在,直接返回
            if (nowMap == null) {
                break;
            }

            // 根据排列组合的匹配,如果出现在敏感词库中,即找到相应key,匹配标识+1
            matchFlag++;

            // 如果已经匹配到词库中完整的敏感词, 改匹配结束标识,并根据匹配规则判断,是否继续
            if ("1".equals(nowMap.get("isEnd"))) {
                // 结束标志位为true,已经命中到了一个完整敏感词
                flag = true;

                // 最小规则,直接返回, 最大规则还需继续查找
                if (matchType == MIN_MATCH_TYPE) {
                    break;
                }
            }
        }

        // 长度必须大于等于1,为词,敏感词只有1个字的情况
        if (matchFlag < 2 || !flag) {
            matchFlag = 0;
        }

        return matchFlag;
    }


    /**
     * @param : txt 待判断文本
     * @return : 匹配类型: 1 最小匹配原则;2 最大匹配原则
     * @author : zhukang
     * @date : 2022/7/26
     * @description :  获取匹配的敏感词
     */
    public Set<String> getSensitiveWords(String txt, Integer matchType) {
        Set<String> sensitiveWords = new HashSet<>();

        for (int i = 0; i < txt.length(); i++) {
            Integer length = checkSensitiveWords(txt, i, matchType);
            if (length > 0) {
                sensitiveWords.add(txt.substring(i, i + length));
                // 循环i会+1,所以需-1
                i = i + length - 1;
            }
        }
        return sensitiveWords;
    }

    /**
     * @param : txt,文本 matchType 匹配类型: 1 最小匹配原则;2 最大匹配原则
     * @return : 替换字符
     * @author : zhukang
     * @date : 2022/7/26
     * @description :  替换敏感词
     */
    public String replaceSensitiveWords(String txt, Integer matchType, String replaceStr) {
        if (txt == null || "".equals(txt)) {
            return txt;
        }
        // 获取所有敏感词
        Set<String> sensitiveWords = getSensitiveWords(txt, matchType);
        Iterator<String> iterator = sensitiveWords.iterator();
        String replaceString = "";
        while (iterator.hasNext()) {
            String sWord = iterator.next();
            replaceString = getReplaceString(replaceStr, sWord.length());
            txt = txt.replaceAll(sWord, replaceString);
        }
        return txt;
    }

    /**
     * @param : replaceStr 替换字符
     * @return : 敏感字长度
     * @author : zhukang
     * @date : 2022/7/26
     * @description :  替换为指定字符,没有指定替换字符,默认*
     */
    private static String getReplaceString(String replaceStr, Integer length) {
        // 指定替换字符为*
        if (replaceStr == null) {
            replaceStr = "*";
        }
        // 可变字符串对象
        StringBuffer replaceString = new StringBuffer();
        // 循环遍历,替换内容
        for (int i = 0; i < length; i++) {
            replaceString.append(replaceStr);
        }
        return replaceString.toString();
    }

}

2.3 关键字过滤 控制层

/**
 * Created On : 2022/7/26.
 * <p>
 * Author : zhukang
 * <p>
 * Description: 敏感词测试入口
 */
@Controller
public class SensitiveWordController {

    @Autowired
    private SensitiveWordFilterUtil sensitiveWordFilterUtil;

    /**
     * @author : zhukang
     * @date   : 2022/5/17
     * @param  : [java.lang.String]
     * @return : com.kgc.sbt.util.RequestResult<java.lang.String>
     * @description : 测试搜索中的敏感词,并指定规则
     */
    @RequestMapping(value = "/testSensitiveWord", produces = {"application/json;charset=utf-8"})
    @ResponseBody
    public String testSensitiveWord(@RequestParam String searchKey, @RequestParam int matchType){

        // 校验搜索关键字中,是否包含敏感词,如果包含,提示错误
        if(sensitiveWordFilterUtil.isContainSensitiveWord(searchKey, matchType)){
            System.out.println(String.format("------ 命中敏感词,搜索关键字:%s ------", searchKey));
            System.out.println(String.format("------ 命中敏感字为:%s ------", sensitiveWordFilterUtil.getSensitiveWords(searchKey, matchType)));
            return "搜索失败,命中敏感词!";
        }
        return "搜索成功!";
    }

}

3、SSM项目 知识点

3.1 SpringMvc 重定向后,中文乱码

3.1.1 RedirectAttributes attributes

将参数,放入RedirectAttributes 中,在重定向的时候,会自动拼接参数,并且不会乱码;

@RequestMapping("/test")
public String delAllViewHistory(//attributes 请求,自动拼接参数
    RedirectAttributes attributes){
    
    attributes.addAttribute("test","测试");
    return "redirect:/viewHistory/viewViewHistoryList";

}

3.2 location.href 会暴露参数问题

解决方法:创建一个form表单,进行post方法提交;

//创建一个 form 表单,并提交
var form = $("<form>");
    
form.attr("style","display:none");
form.attr("target","");
form.attr("method","post");
    
//请求地址
form.attr("action","${pageContext.request.contextPath}/user/modUserInfo");

//请求参数
var input1 = $("<input>");
var input2 = $("<input>");
input1.attr("type","hidden");
input1.attr("name","uid");
input1.attr("value","${sessionScope.loginUser.uid}");
input2.attr("type","hidden");
input2.attr("name","password");
input2.attr("value",$password.val());

//在body标签中追加form 表单
$("body").append(form);
form.append(input1);
form.append(input2);

//表单体提交
form.submit();

//移除表达
form.remove();

3.3 mysql 查询日期操作 ,今天,本周,本月,本季度,今年

-- 今天
select to_days(now()) -- 738788 天

-- 本周
select yearweek(now(),1) -- 202239 周, 第二个参数,是因为,中国人喜欢将周一当作一周的第一天

-- 本月
select date_format(now(),'%Y%m') --  202209 月 

-- 本季度
select quarter(now()) -- 3 季度 quarter

-- 今年
select year(now()) -- 2022 年 year

-- 日期格式化
select DATE_FORMAT(now(),'%Y-%m-%d')

3.4 头像点击上传

3.4.1 jsp

3.4.1.1 头像修改表单
<%--     头像修改 form    start   --%>
<form action="${pageContext.request.contextPath}/user/headImg" method="post" enctype="multipart/form-data"  id="userHeadImgForm">
    <input type="file" name="userHeaderImg" id="userHeaderPic" style="display: none"/>
</form>
<%--     头像修改 form    end   --%>
3.4.1.2 头像展示
<%--     头像 展示  start   --%>
<h3>
    <a style="margin-left: 130px">
        <img style=" width: 50px;height: 50px"
             id="userHeaderImg"
             src="${pageContext.request.contextPath}/${sessionScope.loginUser.uhead}"/>
    </a>
</h3>
<%--     头像 展示  end   --%>
3.4.1.3 点击头像触发 图片选择input 头像选择
//============  头像更换 start ================
$(function (){

    // 点击头像图片,触发文件预点击
    $("#userHeaderImg").click(function (){
        $("#userHeaderPic").click();
    });

    // 当文件域内容改变,实现头像预览
    $("#userHeaderPic").change(function () {

        // 获取上传文件对象
        var file = $(this)[0].files[0];

        // 判断类型
        var imageType = /^image\//;
        if (file === undefined || !imageType.test(file.type)) {
            alert("请选择图片!");
            return;
        }
        // 判断大小
        if (file.size > 512000) {
            alert("图片大小不能超过500K!");
            return;
        }

        // 读取文件URL,预览文件
        var reader = new FileReader();
        // 读取文件
        reader.readAsDataURL(file);
        // 阅读文件完成后触发的事件
        reader.onload = function () {
            // 读取的URL结果:this.result
            $("#userHeaderImg").attr("src", this.result);
            // alert("图片上传");
            $("#userHeadImgForm").submit();
        };

        // TODO 还可以不预览,直接异步ajax发送请求到后台,上传文件,然后返回页面显示头像,并将图片的路径防止页面隐藏域,提交表单记录头像的地址
    });
//============  头像更换 end ================

3.4.2 控制层

1.获取头像文件流,并保存图片;

2.将图片地址保存到用户的头像中;

3.刷新session中的用户信息;

@RequestMapping("/headImg")
public String headImg(HttpSession session,
                      @RequestParam("userHeaderImg") MultipartFile multipartFile,
                      Map<String, String> map) throws IOException {
    // 获取上传的头像文件名称
    String targetFileName = multipartFile.getOriginalFilename();
    System.out.println("------ 上传文件名:" + targetFileName + " ------");

    // 重新定义新的文件名,要保留上传文件的类型
    targetFileName = UUID.randomUUID().toString().substring(0, 8) + targetFileName.substring(targetFileName.indexOf("."));

    System.out.println("------ 新的文件名:" + targetFileName + " ------");

    // 上传文件,要保存服务器上的真实路径中,idea项目发布,默认不会放到目标tomcat中,放在本地项目的target目录中
    String realFilePath = session.getServletContext().getRealPath("img/about");

    System.out.println("------ 服务器真实路径:" + realFilePath + " ------");

    // 目标文件目录可能不存在,不能人为干预,必须程序主动处理
    File targetFilePath = new File(realFilePath);
    if(!targetFilePath.exists()){
        // 目标不存在,主动创建
        if(targetFilePath.mkdirs()){
            System.out.println("------ 上传目录创建成功 ------");
        }
    }

    // 创建目标文件对象
    File targetFile = new File(targetFilePath + "/" + targetFileName);

    // 文件上传到服务器,只需要一步,组件自动支持功能
    multipartFile.transferTo(targetFile);

    // 数据库存入头像信息
    targetFileName = "img/about/"+targetFileName;

    //获取当前登录用户对象
    User loginUser =  (User)session.getAttribute(CommConstant.SYS_SESSION_LOGINUSER);

    //换头像
    loginUser.setUhead(targetFileName);

    //重置 session 中的 用户对象
    session.setAttribute(CommConstant.SYS_SESSION_LOGINUSER,loginUser);

    // 调用 修改用户信息 方法,修改用户信息
    User userForm = new User();
    userForm.setUid(loginUser.getUid());
    userForm.setUhead(loginUser.getUhead());
    userService.modUserInfo(userForm);

    //放入修改成功提示信息,可以不提示(操作过后感觉,不返回效果好些,看时机需求)
    map.put("modUserInfoMsg","用户头像修改成功!");

    return "/personCenter";
}

----------------------------------------------------------------------

标签:RequestMapping,return,String,SpringMvc,初识,------,public,请求
From: https://www.cnblogs.com/hanease/p/16942487.html

相关文章

  • 初识JAVA
    初识JavaJAVA的特性与优势简单性面向对象可移植性高性能分布式多线程安全性健壮性动态性JAVA的三大版本JAVASE全称为JAVAStandardEdit......
  • springmvc工作流程
    springmvc工作原理图精简版:springmvc工作流程1、用户向服务端发送一次请求,这个请求会先到前端控制器DispatcherServlet(也叫中央控制器)。2、DispatcherServlet接收......
  • 初识网络(3)
    一.三次握手 1.pc1会发送syn报文请求连接seq=x(数据段的序号)SYN=1(同步位为1代表请求建立连接)2.pc2收到请求包会回复SYN+ACK的报文seq=y(pc2会发送的数据段序号)ack=......
  • 初识网络(2)
    一.二类线序T568A白绿绿白橙蓝白蓝橙白棕棕T568B白橙橙白绿蓝白蓝绿白棕棕二.三类地址A:0.0.0.0——127.255.255.255B:128.0.0.0——191.255.255......
  • 20-初识内部类
    类的五大成员什么是内部类内部类的应用场景小结......
  • spring4+springmvc+hibernate5的maven整合
    首先,依赖包的下载,看以下的pom.xml [html] ​​viewplain​​ ​​copy​​<dependencies>      <dependency>        <groupId>junit</groupId>    ......
  • day42 6-5 springMVC调度器、ModelAndView、配置thymeleaf模板引擎 & 6-6 thymeleaf语
    springMVC调度器-DispatcherServlet-SpringMVC框架的入口定义DispatcherServlet成为调度器,配置在web.xml文件中,用于拦截匹配的请求。并解析请求url,将请求分发给对应......
  • 初识前端
    目录前端前端与后端的概念前端后端前端三剑客一.前端前戏二.HTTP协议1.四大特性2.数据格式1).请求格式2).响应格式3).响应状态码三.HTML1.HTML简介2.HTML概览3.预备知识4.h......
  • 大数据学习3之初识实时流处理
    初识实时流处理一、业务现状分析二、实时流处理产生背景三、实时流处理概述四、离线计算与实时计算对比数据来源处理过程处理速度进程五、实时流处理框架对比......
  • SpringMVC 项目中 创建SpringBoot,使用Hibernate和JPA
    起因:老项目是SpringMVC,为了之后能使用更方便的SpringBoot。所以在其中添加了SpringBoot项目,但是老项目SpringMVC使用的Hibernate,SpringBoot希望使用JPA 解决方案:......