SpringMVC
SSM: SpringMVC+Spring+Mybatis. MVC三层架构
1、回顾MVC
MVC:模型(Model)【dao、service】 视图(View)【jsp】 控制器(Control)【Servlet】
2、SpringMVC
2.1、概述
SpringMVC是Spring Framework的一部分,是基于Java实现MVC的轻量级Web框架
SpringMVC的特点
- 轻量级,简单易学
- 高效,基于请求响应的MVC框架
- 与Spring兼容性好,无缝结合
- 我们可以将所有SpringMVC中要用到的bean,注册到Spring中
- 约定优于配置
- 功能强大:RESTful、数据验证、格式化、本地化、主题等
- 简洁灵活
- 使用的人多
Spring的web框架围绕DispatcherServlet [ 调度Servlet ] 设计。
DispatcherServlet的作用是将请求分发到不同的处理器。
从Spring 2.5开始,使用Java 5或者以上版本的用户可以采用基于注解形式进行开发,十分简洁;
简单 , 便捷 , 易学 , 天生和Spring无缝集成(使用SpringIoC和Aop) , 使用约定优于配置 . 能够进行简单的junit测试
. 支持Restful风格 .异常处理 , 本地化 , 国际化 , 数据验证 , 类型转换 , 拦截器 等等….
2.2、中心控制器
Spring的web框架围绕DispatcherServlet设计。DispatcherServlet的作用是将请求分发到不同的处理器。从Spring 2.5开始,使用Java 5或者以上版本的用户可以采用基于注解的controller声明方式。
Spring MVC框架像许多其他MVC框架一样, 以请求为驱动 , 围绕一个中心Servlet分派请求及提供其他功能,DispatcherServlet是一个实际的Servlet (它继承自HttpServlet 基类)。
SpringMVC的原理如下图所示:
2.3、SpringMVC执行原理
图为SpringMVC的一个较完整的流程图,实线表示SpringMVC框架提供的技术,不需要开发者实现,虚线表示需要开发者实现。
简要分析执行流程
- DispatcherServlet表示前置控制器,是整个SpringMVC的控制器。用户发出请求,DispatcherServlet接收请求并拦截请求。
- 假设我们的请求url为:http://localhost:8080/02/hello
- http://localhost:8080是服务器域名
- 02是部署在服务器上的web站点(项目
- hello则表示控制器(Controller)
- HandlerMapping为处理器映射。DispatcherServlet调用HandlerMapping去解析url,在Spring容器里寻找名为hello的Handler
- HandlerExecution表示具体的Handler,该handler会执行对应的Controller,例如id为hello的Handler会执行根据其class路径找到HelloController
- HandlerExecution则将解析后的信息传递给DispatcherServlet,如解析控制器映射(HelloController)等
- HandlerAdapter表示处理器适配器(springmvc-servlet.xml中),其按照特定规则去执行Handler
- Handler让具体的Controller执行(HelloController)
- Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView
- HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet。
- DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名。
- 视图解析器将解析的逻辑视图名传给DispatcherServlet。
- DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图。
- 最终视图呈现给用户。
3、第一个mvc程序
- 新建一个Module:SpringMVC-02-hello,添加web的支持
- 确定导入了SpringMVC的依赖
- 配置web.xml注册DispatcherServlet
<!--1.注册DispatcherServlet-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--关联一个springmvc的配置文件:【servlet-name】-servlet.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!--启动级别-1-->
<load-on-startup>1</load-on-startup>
</servlet>
<!--/ 匹配所有的请求;(不包括.jsp)-->
<!--/* 匹配所有的请求;(包括.jsp)-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
- 生成SpringMVC的配置文件:springmvc-servlet.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
- 在文件中添加处理映射器、处理适配器【固定写法】
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
- 添加视图解析器
<!--视图解析器:DispatcherServlet给他的ModelAndView
1.获得了ModelAndView的数据
2.解析ModelAndView的视图名字
3.拼接视图名字,找到对应的视图 /WEB-INF/jsp/hello.jsp
4.将数据发送到页面
-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="InternalResourceViewResolver">
<!--前缀-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!--后缀-->
<property name="suffix" value=".jsp"/>
</bean>
- 编写操作业务的Controller【该类要么实现Controller接口,要么增加注解;需要返回一个ModelAndView】
//实现Controller接口
public class HelloController implements Controller {
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
//ModelAndView 模型和视图
ModelAndView mv = new ModelAndView();
//封装对象,放在ModelAndView中。Model
mv.addObject("msg", "HelloSpringMVC!");
//封装要跳转的视图,放在ModelAndView中
mv.setViewName("hello"); //: /WEB-INF/jsp/hello.jsp
return mv;
}
}
- 将该类注册到SpringIoC容器中
<!--Handler-->
<bean id="/hello" class="com.roderick.controller.HelloController"/>
- 编写jsp页面,显示ModelAndView存放的数据
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Kuangshen</title>
</head>
<body>
${msg}
</body>
</html>
可能出现的问题!
- 如果访问遇到404
- 首先查看控制台输出,是否缺少jar包
- 如果没有缺少jar包,则在IDEA中打开项目结构,在Artifacts中查看对应项目的WEB-INF目录中是否有lib文件夹,如果没有则添加一个lib文件夹,并且使用+号添加library file,添加所有jar包
- 重启tomcat即可
4、注解版本!
- 第一步,新建Module,配置web.xml信息【同样需要配置DIspatcherServlet】
<!--1.注册DispatcherServlet-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--关联一个springmvc的配置文件:【servlet-name】-servlet.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!--启动级别-1-->
<load-on-startup>1</load-on-startup>
</servlet>
<!--/ 匹配所有的请求;(不包括.jsp)-->
<!--/* 匹配所有的请求;(包括.jsp)-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
- 第二步,配置Springmvc-servlet.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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
<context:component-scan base-package="com.roderick.controller"/>
<!-- 让Spring MVC不处理静态资源 .css .js .html .png-->
<mvc:default-servlet-handler />
<!--
支持mvc注解驱动
在spring中一般采用@RequestMapping注解来完成映射关系
要想使@RequestMapping注解生效
必须向上下文中注册DefaultAnnotationHandlerMapping
和一个AnnotationMethodHandlerAdapter实例
这两个实例分别在类级别和方法级别处理。
而annotation-driven配置帮助我们自动完成上述两个实例的注入。
-->
<mvc:annotation-driven/>
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!-- 后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
- 第三步,编写Controller文件
- 通过注解实现,不需要实现Controller类了,只需要在类上加一个@Controller的注解即可
- 然后在类中写各种方法,在方法上加伤@RequestMapping(“/xxx”)的注解,这就相当于一个servlet请求了,注解中的是请求的相对路径
- 在类上添加@RequstMapping可以起到父路径的功能
- 需要在方法参数中传入一个Model
- 同样在方法里进行具体的业务操作(对Model进行数据封装)等
- 最后返回一个字符串,该字符串会被springmvc-servlet.xml的视图解析器解析,最后在项目中寻找对应的jsp文件,如下代码最后会解析为/WEB-INF/jsp/hello.jsp
@Controller
@RequestMapping("/hello") //多级路径
public class HelloController {
// localhost:8080/../hello/h1
@RequestMapping("/h1")
public String hello(Model model){
//封装数据(像模型中添加属性msg与值,可以在jsp页面中取出并渲染
model.addAttribute("msg","hello,SpringMVCAnnotation!");
return "hello"; //会被视图解析器处理(拼接前缀后缀然后寻找hello.jsp页面
}
}
- 最后编写jsp页面获取数据
5、Controller 控制器
- 控制器负责提供访问应用程序的行为、通常通过接口定义或者注解定义两种方式实现
- 控制器负责解析用户的请求并将其转换为一个模型
- 在Spring MVC中一个控制器类可以包含多种方法
5.1、方式一:实现Controller接口
//只要实现了Controller接口的类,说明这就是一个控制器了
public class ControllerTest01 implements Controller {
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
....
}
}
实现Controller接口,Controller接口中只有一个方法:handleRequest方法
通过该方法返回一个模型视图对象交给DispatcherServlet来处理
- 该方法具体操作就是对ModelAndView对象进行数据的封装以及设置跳转的页面
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView mv = new ModelAndView();
mv.addObject("msg","ControllerTest01!"); //封装数据
mv.setViewName("test"); //设置跳转的页面--->通过路径解析器来拼接前缀后缀
return mv;
- 通过实现Controller接口来定义控制器需要在Springmvc容器中注册bean
name对应请求路径,class对应Controller类
<bean name="/t1" class="com.kuang.controller.ControllerTest1"/>
注意
- 通过实现接口Controller来定义控制器是比较老的方法,不推荐使用
- 他有一个缺点,那就是一个控制器只有一个方法,如果需要多个方法,则需要定义多个Controller
5.2、方式二:@Controller注解
- @Controller注解类型用于声明Spring类的实例是一个控制器(@Conponent,@Service…) )
- Spring可以使用扫描机制来找到应用程序中所有基于注解的控制类,所以我们需要在配置文件中声明组建扫描
<!-- 自动扫描指定的包,下面所有注解类交给IOC容器管理 -->
<context:component-scan base-package="com.roderick.controller"/>
<mvc:default-servlet-handler/>
<mvc:annotation-driven/> <!--支持mvc注解驱动 @RequestMapping ...-->
- Controller类
@Controller
//代表这个类会被Spring接管, 这个类的所有方法,
// 如果返回值是String,并且有具体的页面可以跳转,就会被视图解析器解析
public class ControllerTest02 {
@RequestMapping("/t2")
public String test1(Model model){
model.addAttribute("msg","ControllerTest2");
return "test";
}
@RequestMapping("/t3")
public String test2(Model model){
model.addAttribute("msg","ControllerTest3!!");
return "test";
}
}
- 使用注解配置控制器可以在一个Controller类中定义多个方法
- 如上我定义了两个方法,他们的返回值都是test(访问页面路径),但是设置的参数不同,这样就可以实现同一jsp页面的复用
- 这样控制器与视图之间就形成弱耦合关系
- 注解方式是平时用的最多的方式!
6、RestFul风格
6.1、概念
RestFul就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
6.2、功能
资源:互联网所有的事物都可以被抽象为资源
资源操作:使用POST、DELETE、PUT、GET,使用不同方法对资源进行操作。
分别对应 添加、 删除、修改、查询。
传统方式操作资源 :
通过不同的参数来实现不同的效果!方法单一,post 和 get
http://127.0.0.1/item/queryItem.action?id=1 查询,GET
http://127.0.0.1/item/saveItem.action 新增,POST
http://127.0.0.1/item/updateItem.action 更新,POST
http://127.0.0.1/item/deleteItem.action?id=1 删除,GET或POST
使用RESTful操作资源 :
可以通过不同的请求方式来实现不同的效果!如下:请求地址一样,但是功能可以不同!
http://127.0.0.1/item/1 查询,GET
http://127.0.0.1/item 新增,POST
http://127.0.0.1/item 更新,PUT
http://127.0.0.1/item/1 删除,DELETE
测试代码
@Controller
public class RestFulController {
//原来的风格 : http://localhost:8080/04/add?a=1&b=2
//restFul风格: http://localhost:8080/04/add/a/b
@RequestMapping("/add/{a}/{b}")
public String test1(@PathVariable int a,@PathVariable int b, Model model){
int res= a+b;
model.addAttribute("msg","结果为"+res);
return "test";
}
}
- 在test1方法中定义两个参数a和b,给他们添加@PathVariable注解就可以在RequestMapping中的路径上使用这两个参数变量了
- 如图所示,我们就可以通过/add/1/2的方式来替换/add?a=1&b=2了
使用路径变量的好处
- 使路径变得更加简洁;
- 获得参数更加方便,框架会自动进行类型转换。
- 通过路径变量的类型可以约束访问参数,如果类型不一样,则访问不到对应的请求方法.如上例子我们的两个变量都是int类型,如果传入了其他类型他只会导致路径和方法不匹配,而不会是参数转换失败的错误
使用Method属性指定请求类型
上面我们举过RestFul使用的例子,比如
- http://127.0.0.1/item 新增,POST
- http://127.0.0.1/item 更新,PUT
两个一样的url路径却有不一样的功能,这是如何实现的呢?
在@RequestMapping注解中有一个属性RequestMethod[] method() default {};
该属性可以指定请求类型,例如
@RequestMapping(path="/add/{a}/{b}",method= {RequestMethod.GET,...})
{
Get...
}
@RequestMapping(path="/add/{a}/{b}",method= {RequestMethod.POST})
{
Post..
}
- 这样指定后,通过不同请求访问同一个路径将会有不同的方法来执行
- 也可以在method中指定多个请求
也可以通过组合注解的方式来替代RequestMapping
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping
@GetMapping是一个组合注解,平时使用的会比较多!
它所扮演的是 @RequestMapping(method =RequestMethod.GET) 的一个快捷方式。
7、数据处理及跳转
7.1、结果跳转方式
ModelAndView
通过实现Controller接口创建的Controller类一般需要返回一个ModelAndView结果
在过程操作中,我们会设置ModelAndView对象
通过addObject(“xx”,”xx”)绑定数据和setViewName(“x x x”)设置视图要跳转的页面来实现跳转
- 而这个setViewName的值会被Spring配置文件中的设计图解析器解析
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>
- 页面:{视图解析器前缀} + viewName + {视图解析器后缀}
ServletAPI
通过设置ServletAPI , 不需要视图解析器 .
1、通过HttpServletResponse进行输出
2、通过HttpServletResponse实现重定向
3、通过HttpServletResponse实现转发
@Controller
public class ResultGo {
@RequestMapping("/result/t1")
public void test1(HttpServletRequest req, HttpServletResponse rsp) throwsIOException {
rsp.getWriter().println("Hello,Spring BY servlet API");
}
@RequestMapping("/result/t2")
public void test2(HttpServletRequest req, HttpServletResponse rsp) throwsIOException {
//重定向
rsp.sendRedirect("/index.jsp");
}
@RequestMapping("/result/t3")
public void test3(HttpServletRequest req, HttpServletResponse rsp) throwsException {
//转发
req.setAttribute("msg","/result/t3");
req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,rsp);
}
}
SpringMVC(无视图解析器)
@Controller
public class ResultSpringMVC {
@RequestMapping("/rsm/t1")
public String test1(){
//转发
return "/index.jsp";
}
@RequestMapping("/rsm/t2")
public String test2(){
//转发二
return "forward:/index.jsp";
}
@RequestMapping("/rsm/t3")
public String test3(){
//重定向
return "redirect:/index.jsp";
}
}
SpringMVC(有视图解析器)
重定向 , 不需要视图解析器 , 本质就是重新请求一个新地方嘛 , 所以注意路径问题.
可以重定向到另外一个请求实现 .
@Controller
public class ResultSpringMVC2 {
@RequestMapping("/rsm2/t1")
public String test1(){
//转发
return "test";
}
@RequestMapping("/rsm2/t2")
public String test2(){
//重定向
return "redirect:/index.jsp";
//return "redirect/rsm2/t1"; //重定向到另一个请求/
}
}
7.2、数据处理
提交到域名称和处理方法参数名一致
请求url为://localhost:8080/04/user/t1?name=xxx
@GetMapping("/t1")
public String test1(String name, Model model){
//1-接收前端参数
System.out.println("接收到的参数为:"+name);
//2-将返回的结果传递给前端 -> Model
model.addAttribute("msg",name);
//3-跳转视图
return "test";
}
- url路径中参数名称和方法参数名称都为name,可以直接接收到
提交到域名称和处理方法参数名不一致
请求url为://localhost:8080/04/user/t1?username=xxx
@GetMapping("/t1")
public String test1(@RequetParam("username") String name, Model model){
//1-接收前端参数
System.out.println("接收到的参数为:"+name);
//2-将返回的结果传递给前端 -> Model
model.addAttribute("msg",name);
//3-跳转视图
return "test";
}
- 在请求方法参数前加上@RequestParam(“xxx”)注解,可以给参数设置别名以达到和请求参数一致到效果
- 一般情况下都推荐使用该注解
提交的参数是一个对象
请求路径://localhost:8080/04/user/t2?id=1&name=xxx&age=22
//接收一个对象 ": id name age
/*
* 1-接收前端用户传递的参数,判断参数的名字,假设名字直接在方法上,可以直接使用
* 2-假设传递的是一个对象(User),匹配User对象的属性,如果和参数一致则接收并封装
* 2-1-如果不一致则接收为null */
@GetMapping("/t2")
public String test2(User user){
System.out.println(user);
return "test";
}
输出:User(id=1, name=xxx, age=22)
- User类中包含 id ,name ,age三个属性
- 当url中参数和对象属性一致时,则正常接收到
- 如果url中参数不一致,例如name被替换为username,就会被null替代
- 得到结果:User(id=1, name=null, age=22)
7.3、数据显示到前端
第一种、ModelAndView
public class ControllerTest1 implements Controller {
public ModelAndView handleRequest(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse) throws Exception {
//返回一个模型视图对象
ModelAndView mv = new ModelAndView();
mv.addObject("msg","ControllerTest1");
mv.setViewName("test");
return mv;
}
}
第二种、ModelMap
@RequestMapping("/hello")
public String hello(@RequestParam("username") String name, ModelMap model){
//封装要显示到视图中的数据
//相当于req.setAttribute("name",name);
model.addAttribute("name",name);
System.out.println(name);
return "hello";
}
第三种、Model
@RequestMapping("/ct2/hello")
public String hello(@RequestParam("username") String name, Model model){
//封装要显示到视图中的数据
//相当于req.setAttribute("name",name);
model.addAttribute("msg",name);
System.out.println(name);
return "test";
}
这几种方法的对比
- Model:只有几个方法,只适合用于存储数据
- ModelMap:继承了LinkedHashMap,除了实现自身的一些方法,同样的继承LinkedHashMap的方法和特性
- ModelAndView :可以在存储数据的同时,进行设置返回的逻辑视图,进行控制展示层的跳转
8、乱码问题
web.xml ->配置过滤器
<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
但是我们发现 , 有些极端情况下.这个过滤器对get的支持不好 .
处理方法 :
1、修改tomcat配置文件 :设置编码!
<Connector URIEncoding="utf-8" port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
2、自定义过滤器
package com.kuang.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;
/**
* 解决get和post请求 全部乱码的过滤器
*/
public class GenericEncodingFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChainchain) throws IOException, ServletException {
//处理response的字符编码
HttpServletResponse myResponse=(HttpServletResponse) response;
myResponse.setContentType("text/html;charset=UTF-8");
// 转型为与协议相关对象
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
// 对request包装增强
HttpServletRequest myrequest = new MyRequest(httpServletRequest);
chain.doFilter(myrequest, response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
}
//自定义request对象,HttpServletRequest的包装类
class MyRequest extends HttpServletRequestWrapper {
private HttpServletRequest request;
//是否编码的标记
private boolean hasEncode;
//定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰
public MyRequest(HttpServletRequest request) {
super(request);// super必须写
this.request = request;
}
// 对需要增强方法 进行覆盖
@Override
public Map getParameterMap() {
// 先获得请求方式
String method = request.getMethod();
if (method.equalsIgnoreCase("post")) {
// post请求
try {
// 处理post乱码
request.setCharacterEncoding("utf-8");
return request.getParameterMap();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} else if (method.equalsIgnoreCase("get")) {
// get请求
Map<String, String[]> parameterMap = request.getParameterMap();
if (!hasEncode) { // 确保get手动编码逻辑只运行一次
for (String parameterName : parameterMap.keySet()) {
String[] values = parameterMap.get(parameterName);
if (values != null) {
for (int i = 0; i < values.length; i++) {
try {
// 处理get乱码
values[i] = new String(values[i]
.getBytes("ISO-8859-1"), "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
hasEncode = true;
}
return parameterMap;
}
return super.getParameterMap();
}
//取一个值
@Override
public String getParameter(String name) {
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
if (values == null) {
return null;
}
return values[0]; // 取回参数的第一个值
}
//取所有值
@Override
public String[] getParameterValues(String name) {
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
return values;
}
}
一般情况下,SpringMVC默认的乱码处理就已经能够很好的解决了!
然后在web.xml中配置这个过滤器即可!
乱码问题,需要平时多注意,在尽可能能设置编码的地方,都设置为统一编码 UTF-8!
9、JSON
前后端分离
Json是一种轻量级的数据交换格式
9.1、JackSon
JacksonUtils
public static String getJson(Object object,String dateFormat){
ObjectMapper mapper = new ObjectMapper();
//关闭自动转换为时间戳
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false);
//自定义日期格式
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
mapper.setDateFormat(sdf); //sdf传入mapper
try {
return mapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
SpringMVC的Json乱码问题解决
本来需要在RequestMapping中设置produces的值为application/json;charset=utf-8
@RequestMapping(value = "/j1",produces = "application/json;charset=utf-8")
Public String json1(){
....
}
替代方法:在spring的配置文件中添加以下代码
<!--json乱码问题-->
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
</bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="failOnEmptyBeans" value="false"/>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
9.2、FastJson
测试demo:
public static void main(String[] args) {
//创建一个对象
User user1 = new User("雨落1", 3, "男");
User user2 = new User("雨落2", 3, "男");
User user3 = new User("雨落3", 3, "男");
User user4 = new User("雨落4", 3, "男");
List<User> list = new ArrayList<User>();
list.add(user1);
list.add(user2);
list.add(user3);
list.add(user4);
System.out.println("*******Java对象 转 JSON字符串*******");
String str1 = JSON.toJSONString(list);
System.out.println("JSON.toJSONString(list)==>"+str1);
String str2 = JSON.toJSONString(user1);
System.out.println("JSON.toJSONString(user1)==>"+str2);
System.out.println("\n****** JSON字符串 转 Java对象*******");
User jp_user1=JSON.parseObject(str2,User.class);
System.out.println("JSON.parseObject(str2,User.class)==>"+jp_user1);
System.out.println("\n****** Java对象 转 JSON对象 ******");
JSONObject jsonObject1 = (JSONObject) JSON.toJSON(user2);
System.out.println("(JSONObject) JSON.toJSON(user2)==>"+jsonObject1.getString("name"));
System.out.println("\n****** JSON对象 转 Java对象 ******");
User to_java_user = JSON.toJavaObject(jsonObject1, User.class);
System.out.println("JSON.toJavaObject(jsonObject1, User.class)==>"+to_java_user);
System.out.println("\n****** 自定义Json时间格式 ******");
Date date=new Date();
String JsonDate = JSON.toJSONStringWithDateFormat(date, "HH:mm:ss");
System.out.println(JsonDate);
System.out.println("\n****** 默认Json时间格式 ******");
System.out.println(JSON.toJSONStringWithDateFormat(date,JSON.DEFFAULT_DATE_FORMAT));
}
10、整合SSM
10.1、搭建环境
- 数据库
CREATE DATABASE `ssmbuild`;
USE `ssmbuild`;
DROP TABLE IF EXISTS `books`;
CREATE TABLE `books` (
`bookID` INT(10) NOT NULL AUTO_INCREMENT COMMENT '书id',
`bookName` VARCHAR(100) NOT NULL COMMENT '书名',
`bookCounts` INT(11) NOT NULL COMMENT '数量',
`detail` VARCHAR(200) NOT NULL COMMENT '描述',
KEY `bookID` (`bookID`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO `books`(`bookID`,`bookName`,`bookCounts`,`detail`)VALUES
(1,'Java',1,'从入门到放弃'),
(2,'MySQL',10,'从删库到跑路'),
(3,'Linux',5,'从进门到进牢');
Maven
依赖
<!--依赖: junit,数据库驱动,连接池,servlet,jsp,mybatis,mybatis-spring,spring--> <dependencies> <!--Junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <!--数据库驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.20</version> </dependency> <!-- 数据库连接池 c3p0--> <dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.2</version> </dependency> <!--Servlet - JSP --> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.2</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!--Mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.3</version> </dependency> <!--Spring--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.1.9.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.1.9.RELEASE</version> </dependency> </dependencies>
静态资源导出问题
<build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> </build>
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
</configuration>
- applicationContext.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
- 建立文件结构
com.roderick—>【dao,pojo,service,controller】
10.2、Mybatis层
- 数据库配置文件database.properties
jdbc.driver=com.mysql.jdbc.Driver
#如果使用的是MySQL 8.0+,增加时区配置 &serverTimezone=Asia/Shanghai
jdbc.url=jdbc:mysql://localhost:3306/ssmbuild?useSSL=true&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=luoyu-3-233
- 开始编写mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--配置数据源,交给Spring去做-->
<!--别名-->
<typeAliases>
<package name="com.roderick.pojo"/>
</typeAliases>
<!--注册mapper-->
<mappers>
<mapper class="com.roderick.dao.BookMapper"/>
</mappers>
</configuration>
- 编写数据库对应的pojo实现类
package com.roderick.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Books {
private int bookID;
private String bookName;
private int bookCounts;
private String detail;
}
编写Dao(Mapper)层
- BookMapper.java接口
public interface BookMapper {
//增加一本书
int addBook(Books books);
//删除一本书
int deleteBookById(@Param("bookID") int id);
//更新一本书
int updateBook(Books books);
//查询一本书
Books queryBookById(@Param("bookID") int id);
//查询全部的书
List<Books> queryAllBook();
}
- BookMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.roderick.dao.BookMapper">
<insert id="addBook" parameterType="Books">
insert into books(bookName,bookCounts,detail)
values(#{bookName},#{bookCounts},#{detail});
</insert>
<delete id="deleteBookById" parameterType="int">
delete from books where bookID = #{bookID};
</delete>
<update id="updateBook" parameterType="Books">
update books
set bookName = #{bookName}, bookCounts=#{bookCounts},
detail=#{detail}
where bookID=#{bookID};
</update>
<select id="queryBookById" resultType="Books">
select * from books where bookID=#{bookID}
</select>
<select id="queryAllBook" resultType="Books">
select * from books;
</select>
</mapper>
Tips:写完mapper后记得去mybatis-config.xml中注册mapper
Service层
- BookService.java接口
public interface BookService {
//增加一本书
int addBook(Books books);
//删除一本书
int deleteBookById(int id);
//更新一本书
int updateBook(Books books);
//查询一本书
Books queryBookById(int id);
//查询全部的书
List<Books> queryAllBook();
}
- BookServiceImpl.java实现类
public class BookServiceImpl implements BookService {
//service 调用dao层 ->组合dao
private BookMapper bookMapper;
public void setBookMapper(BookMapper bookMapper) {
this.bookMapper = bookMapper;
}
public int addBook(Books books) {
return bookMapper.addBook(books);
}
public int deleteBookById(int id) {
return bookMapper.deleteBookById(id);
}
public int updateBook(Books books) {
return bookMapper.updateBook(books);
}
public Books queryBookById(int id) {
return queryBookById(id);
}
public List<Books> queryAllBook() {
return queryAllBook();
}
}
以上,底层的需求操作就编写完毕了
10.3、Spring层
Spring就是一个容器,将各种配置注入到spring的配置文件中
spring配置dao
Spring-dao.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
https://www.springframework.org/schema/context/spring-context.xsd">
<!--1.关联数据库配置文件-->
<context:property-placeholder location="classpath:database.properties"/>
<!--2.DataSource连接池
dbcp: 半自动化操作, 不能自动连接
c3p0: 自动化操作(自动化的加载配置文件,并且可以自动设置到对象中)
druid、hikari:
-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<!-- c3p0连接池的私有属性 -->
<property name="maxPoolSize" value="30"/>
<property name="minPoolSize" value="10"/>
<!-- 关闭连接后不自动commit -->
<property name="autoCommitOnClose" value="false"/>
<!-- 获取连接超时时间 -->
<property name="checkoutTimeout" value="10000"/>
<!-- 当获取连接失败重试次数 -->
<property name="acquireRetryAttempts" value="2"/>
</bean>
<!--3.SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--判断mybatis的配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!--配置dao接口扫描包,动态的实现Dao接口可以注入到Spring容器中-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 注入sqlSessionFactory -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!--要扫描的包-->
<property name="basePackage" value="com.roderick.dao"/>
</bean>
</beans>
spring配置service
Spring-service.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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--1.扫描service下的包-->
<context:component-scan base-package="com.roderick.service"/>
<!--2.将所有业务类注入到spring,可以通过配置或者注解实现-->
<bean id="BookServiceImpl" class="com.roderick.service.BookServiceImpl">
<!--注入bookMapper-->
<property name="bookMapper" ref="bookMapper"/>
</bean>
<!--替代方式@Service+@Autowired-->
<!--3.声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--4.aop事务支持!-->
</beans>
10.4、SpringMVC层
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--DispatcherServlet-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--乱码过滤-->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>uft-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--Session-->
<session-config>
<session-timeout>15</session-timeout>
</session-config>
</web-app>
配置tomcat
- 切记进入项目结构添加lib文件夹
10.5、业务功能
展示书籍
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>书籍展示页面</title>
<%--BootStrap--%>
<link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row clearfix">
<div class="col-md-12 column">
<div class="page-header">
<h1>
<small>书籍列表</small>
</h1>
</div>
</div>
</div
<div class="row clearfix">
<div class="col-md-12 column">
<table class="table table-hover table-striped">
<thead>
<tr>
<th>书籍编号</th>
<th>书籍名字</th>
<th>书籍数量</th>
<th>书籍详情</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<c:forEach var="book" items="${requestScope.get('list')}">
<tr>
<td>${book.getBookID()}</td>
<td>${book.getBookName()}</td>
<td>${book.getBookCounts()}</td>
<td>${book.getDetail()}</td>
<td></td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>
添加书籍
allBook.jsp
<%--toAddBook--%>
<a href="${pageContext.request.contextPath}/book/toAddBook">新增书籍</a>
addBook.jsp
<form action="${pageContext.request.contextPath}/book/addBook" method="post">
书籍名称:<input type="text" name="bookName" required><br><br><br>
书籍数量:<input type="text" name="bookCounts" required><br><br><br>
书籍详情:<input type="text" name="detail" required><br><br><br>
<input type="submit" value="添加">
</form>
Controller
//跳转到增加书籍页面
@RequestMapping("/toAddBook")
public String toAddPaper(){
return "addBook";
}
//添加书籍的请求
@RequestMapping("/addBook")
public String addBook(Books books){
System.out.println("addBook===>"+books);
bookService.addBook(books);
return "redirect:/book/allBook"; //添加完跳转到书籍列表页面
}
修改书籍
allBook.jsp 【这里使用?传参】
<a href="${pageContext.request.contextPath}/book/toUpdate?id=${book.bookID}">修改</a>
updateBook.jsp【复用添加书籍的页面稍作修改,注意这里需要添加隐藏域传递id,负责id默认为0则修改失败】
<form action="${pageContext.request.contextPath}/book/updateBook" method="post">
<%--隐藏域传递id--%>
<input type="hidden" name="bookID" value="${Books.bookID}">
书籍名称:<input type="text" name="bookName" required value="${Books.bookName}"><br><br><br>
书籍数量:<input type="text" name="bookCounts" required value="${Books.bookCounts}"><br><br><br>
书籍详情:<input type="text" name="detail" required value="${Books.detail}"><br><br><br>
<input type="submit" value="修改">
</form>
controller
//跳转到修改页面
@RequestMapping("/toUpdate")
public String toUpdatePaper(int id,Model model){
Books books = bookService.queryBookById(id);
model.addAttribute("Books",books);
return "updateBook";
}
//修改书籍
@RequestMapping("/updateBook")
public String updateBook(Books books){
System.out.println("updateBook===>"+books);
bookService.updateBook(books);
return "redirect:/book/allBook"; //修改完跳转到书籍列表页面
}
删除书籍
allBook.jsp 【这里使用restFul风格传参】
<a href="${pageContext.request.contextPath}/book/deleteBook/${book.bookID}">删除</a>
controller
//删除数据
@RequestMapping("/deleteBook/{bookID}")
public String deleteBook(@PathVariable("bookID") int id){
System.out.println("deleteBook===>"+id);
bookService.deleteBookById(id);
return "redirect:/book/allBook";
}
查询书籍
allBook.jsp
<div class="col-md-8 column">
<%--查询数据--%>
<form class="form-inline" action="${pageContext.request.contextPath}/book/queryBook" method="post" style="float: right;">
<input type="text" name="queryBookName" class="form-control" placeholder="请输入要查询的书籍名称">
<input type="submit" value="查询" class="btn btn-primary">
</form>
</div>
dao层
//根据名字查询书籍
List<Books> queryBookByName(@Param("bookName") String bookName);
<select id="queryBookByName" resultType="Books">
select * from books where bookName like "%"#{bookName}"%"
</select>
service层
public List<Books> queryBookByName(String bookName) {
return bookMapper.queryBookByName(bookName);
}
controller
//查询书籍
@RequestMapping("/queryBook")
public String queryBook(String queryBookName,Model model){
List<Books> list = bookService.queryBookByName(queryBookName);
model.addAttribute("list",list);
return "allBook";
}
错误总结
出现错误:点击书籍展示页面出现500错误,无法找到Bean
- 查看bean注入是否成功
- junit单元测试,检查代码是否能够查询出结果
- 如果上面两步都成功,那么问题不在底层,而是在spring配置的时候出了问题
- springMVC,整合的时候没有调用到我们的service层的bean
- applicationConterxt.xml没有导入其他三个xml
- web.xml 导入DispatcherServlet中需要导入applicationContext.xml主文件而不是spring-mvc.xml
排错工具
- aop织入事务
Spring-service.xml
<!--3.声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--4.aop事务支持!-->
<!--配置事务通知:-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--给哪些方法配置事务-->
<!--配置事务的传播特性:new-->
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--配置事务切入-->
<aop:config>
<!--切入点-->
<aop:pointcut id="txPointCut" expression="execution(* com.roderick.dao.*.*(..))"/>
<!--切入-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
- mybatis添加日志—>显示sql
Mybatis-config.xml
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
- 输出错误信息(err)
System.err.println();
11、Ajax
12、拦截器
SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。
拦截器与过滤器的区别: 拦截器是AOP思想的具体实现
- 过滤器:
- servlet规范中的一部分,任何java web工程都能使用
- 在url-pattern中配置了/*之后,可以对所有要访问的资源进行拦截
- 拦截器:
- 拦截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能使用
- 拦截器只会拦截访问的控制器方法,如果访问的是jsp/html/css/image/js等静态资源是不会拦截等
自定义拦截器:
想要自定义拦截器,必须实现 HandlerInterceptor 接口。
1、新建一个Moudule , springmvc-07-Interceptor , 添加web支持
2、配置web.xml 和 springmvc-servlet.xml 文件
<!--Spring拦截器配置-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/user/**"/> //拦截/user下的所有请求
<bean class="com.roderick.config.LoginInterceptor"/> //拦截器class
</mvc:interceptor>
</mvc:interceptors>
3、编写一个拦截器
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
//return true : 执行下一个拦截器,放行
//return false : 不执行下一个拦截器(不放行)
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("===========处理前===========");
return true;
}
//一般用于拦截日志的记录
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("===========处理后============");
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("============清理=============");
}
}
登录验证实例
- 未登录的用户无法进入首页,而是跳转到login页面进行登录
拦截器
public class LoginInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
//登录页面放行
if (request.getRequestURI().contains("login")){
return true;
}
//已登录 放行
if (session.getAttribute("userLoginInfo")!=null){
//根据session中是否存有用户登录信息来判断用户是否登录
return true;
}
//未登录-->跳到登录页面
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response);
return false;
}
}
Controller
@Controller
@RequestMapping("/user")
public class LoginController {
@RequestMapping("/main")
public String main(){
return "main";
}
@RequestMapping("/gologin")
public String login(){
return "login";
}
@RequestMapping("/login")
public String login(HttpSession session, String username, String password, Model model){
System.out.println("login===>"+username);
//将登录成功的信息存入session
session.setAttribute("userLoginInfo",username);
model.addAttribute("username",username);
return "main";
}
@RequestMapping("/logout")
public String logout(HttpSession session){
session.removeAttribute("userLoginInfo");
return "login";
}
}
13、文件上传下载
文件上传方法1 io流读取文件
@RequestMapping("/upload") //@RequestParam("file") 将name=file控件得到的文件封装成CommonsMultipartFile 对象
public String upload(@RequestParam("file")CommonsMultipartFile file, HttpServletRequest request) throws IOException {
//获取文件名: file.getOriginalFilename();
String uploadFileName = file.getOriginalFilename();
//如果文件名为空,直接回到首页
if ("".equals(uploadFileName)){
return "文件名不能为空";
}
System.out.println("上传文件名"+uploadFileName);
//上传路径保存设置
String path = request.getServletContext().getRealPath("/upload");
//如果路径不存在,创建一个
File realPath = new File(path);
if (!realPath.exists()){
realPath.mkdirs(); //创建文件夹
}
System.out.println("上传文件保存地址:"+realPath);
InputStream is = file.getInputStream();
FileOutputStream os = new FileOutputStream(new File(realPath, uploadFileName));//文件输出流
//读取写出
int len=0;
byte[] buffer = new byte[1024];
while ((len=is.read(buffer))!=-1){
os.write(buffer,0,len);
os.flush();
}
os.close();
is.close();
return "上传成功";
}
文件上传方法2 transferTo方法
@RequestMapping("/upload2")
public String fileUpload2(@RequestParam("file") CommonsMultipartFile file,HttpServletRequest request) throws IOException{
//上传路径保存设置
String path = request.getServletContext().getRealPath("/upload");
File realPath = new File(path);
if (!realPath.exists()){
realPath.mkdir();
}
//上传文件地址
System.out.println("上传文件保存地址:"+realPath);
//通过CommonsMultipartFile的方法直接写文
file.transferTo(new File(realPath +"/"+ file.getOriginalFilename()));
return "上传成功";
}
文件下载
@RequestMapping(value="/download")
public String downloads(HttpServletResponse response , HttpServletRequest request)throws Exception{
//要下载的图片地址
String path = request.getServletContext().getRealPath("/upload");
String fileName = "地铁老人.jpeg";
//1、设置response 响应头
response.reset(); //设置页面不缓存,清空buffer
response.setCharacterEncoding("UTF-8"); //字符编码
response.setContentType("multipart/form-data"); //二进制传输数据
//设置响应头
response.setHeader("Content-Disposition",
"attachment;fileName="+ URLEncoder.encode(fileName, "UTF-8"));
File file = new File(path,fileName);
//2、 读取文件--输入流
InputStream input=new FileInputStream(file);
//3、 写出文件--输出流
OutputStream out = response.getOutputStream();
byte[] buff =new byte[1024];
int index=0;
//4、执行 写出操作
while((index= input.read(buff))!= -1){
out.write(buff, 0, index);
out.flush();
}
out.close();
input.close();
return "ok";
}