SpringMVC
MVC概述
MVC: 模型(DAO,service) 视图(jsp) 控制层(Servlet) 是一种软件设计规范
大致流程:
dao层连接数据库
service层调用dao层执行具体的业务,但是仅仅只负责业务
servlet层用来接收前端的数据,然后将数据交给service层进行处理,然后根据service层返回的结果控制界面的跳转运作
在一般情况下,封装为实体类的对象在前端调用的时候会出现数据冗余,比如前端登录页面只需要一个用户名和密码,而实体类的其他属性并不重要,所以需要把实体类进行进一步的细化划分
pojo(总的实体类封装对象): User
vo(细化之后的实体类封装对象) Uservo
dto(传输时候的实体类封装对象)
最开始的model1时期为双层架构,现在慢慢演变为了三层架构
业务流程大致为:
用户从客户端发送请求到servlet,
然后servlet接收请求数据,并调用对应的业务逻辑方法
业务处理完毕之后返回更新的数据给servlet
servlet转向到jsp,并由jsp来进行渲染页面
最后jsp再响应给客户端更新之后的页面
功能划分:
Controller(控制层)
取得表单数据
调用业务逻辑
转向制定的页面
Model(模型)
业务逻辑
保留数据的状态
View(视图层)
显示页面
回顾Servlet
创建web子项目时,创建一个干净额项目,不适用webapp框架,直接创建
然后右击子项目,选中第二个Add Franework Support(添加框架支持),然后选中第一个web Application(web支持)最后点击ok就可以有web包了
注意,此时module会有蓝色的点,打开之后会有web-inf包和index.jsp
如果没有蓝色的点说明未识别为web项目,配置tomcat的时候.Fix也修复不了东西
使用servlet有两个注意点,一个是new一个class继承servlet,二是在web.xml中配置servlet,
一个类只要实现了servlet接口,那就是servlet
一个普通的类想要变成servlet只需要做一件事情,继承HttpServlet(因为HttpServlet的父类最终实现了servlet接口)
创建页面的时候如果微课安全,可以将页面放在web-inf下面,外部无法直接进行访问,一般公共的页面可以直接放在web下面就可以了
MVC框架需要做哪些事情?
- 将url映射到java类或者java类的方法
- 封装用户提交的数据
- 处理请求–调用相关的业务处理–封装相关的数据
- 将相应的数据进行渲染,jsp/html等表示层的数据
PS:
常见的服务端MVC框架有:Struts,SpringMVC,ASP.NET MVC,Zend Framework, JSF
常见前端框架有: vue,react,angularjs,backbone
一般一个简单的响应页面都有如下流程:
首先创建一个moduleweb子项目并且注入依赖:此处是府项目已经导入依赖了,子项目为了防止出错有重新导入了一遍
<dependencies>
<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>
</dependencies>
然后就是创建一个servlet:
//一个普通的类要想变成servlet只需要做一件事情,继承HttpServlet(HttpServlet的父类最终实现了servlet接口)
public class HelloServlet extends HttpServlet {
//全栈: 后台+前端+数据库+运维
//前端:后台Nodejs+前端
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1,获取前端参数(此处参数为方法名)
String method=req.getParameter("method");
//对比方法名
if (method.equals("add")){
req.getSession().setAttribute("msg","执行了add方法");
}
if (method.equals("delete")){
req.getSession().setAttribute("msg","执行了delete方法");
}
//2.调用业务层
//3,视图转发或者重定向
//forward(req,resp)使转发的时候可以将参数继续往下携带
req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,resp);
//重定向为resp.sendRedirect("test.jsp");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
}
然后再web.xml中注册servlet:
<?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">
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.xg.servlet.HelloServlet</servlet-class>
</servlet>
<!--设置映射地址,此处的url-pattern就是在初始页面地址栏添加的映射字段
servlet-name就是映射地址的对应映射名
-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<!-- 设置session过期时间-->
<session-config>
<session-timeout>15</session-timeout>
</session-config>
<!-- 设置欢迎页面,默认就是index.jsp-->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
注意,此处的test.jsp为自定义的跳转页面,开始tomcat时还需要在地址栏输入/hello?method=add然后才能正常跳转
Spring就是一个大杂烩,我们可以将SpringMVC中所有需要用到的bean都注册到Spring中
SpringMVC的特点:
- 轻量级,简单易学
- 高效,基于请求响应的MVC框架
- 与Spring兼容性好,无缝结合
- 约定大于配置
- 功能强大,支持restful风格(一种请求书写规范),数据验证,格式化,本地化,主题等服务均可提供
Spring的web框架围绕DispatcherServlet[调度Servlet]设计
Spring的所有请求都会经过doServlet,以后如果debug可以用到
SpringMVC流程测试
一开始先要了解DispatcherServlet的定位及作用:
用户如果一个一个调用不同的Servlet会很影响效率,所以再添加一层,让DispatcherServlet进行专门的调度作用
根据不同的请求调用不同的视图模型
动手:
首先创建一个空的子项目并添加web依赖
然后配置web.xml,注册DispatcherServlet(Spring自带)
<?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">
<!-- 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>
</web-app>
接着编写配置文件
<?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">
<!-- 添加处理映射器-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!-- 添加处理器适配器-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!-- 添加视图解析器:DispatcherServlet给他的ModelAndView
1,获取了ModelAndView中封装的数据
2,解析ModelAndView的视图名字
3,拼接视图名字,找到对应的视图
4,将数据渲染到这个视图上
-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<!-- 前提-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!-- 后缀-->
<property name="suffix" value=".jsp"/>
</bean>
<!-- Handler-->
<bean id="/hello" class="com.xg.HelloController"/>
</beans>
再然后编写Controller
ps:真实开发并不会这么去写:
这个类实现了Controller就可以去处理请求与相应
public class HelloController implements Controller {
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
//ModelAndView 模型和视图
ModelAndView mv=new ModelAndView();
//封装对象,现在直接放在ModelAndView中,不像之前还要放在session或者request中
mv.addObject("msg","HelloSpringMVC!");
//封装要跳转的视图,放在ModelAndView中,不像之前还要放在转发或者重定向中
mv.setViewName("hello");
//注意,想要跳转的视图的前缀和后缀都已经在xml中(springmvc-servlet.xml)配置好了,此处只需要将中间的视图名称传入进去就可以了
return mv;
}
}
最后就可以编写测试页面进行测试
思路整理:
请求的/hello并没有在web.xml中进行配置
配置文件中的url处理器和url适配器的作用就是把url的handler(/hello)往Spring中进行匹配,寻找可以进行处理的Controller
而Controller新建立了一个模型和视图处理方法ModelAndView
并且在这个方法中添加了一个参数:
mv.addObject(“msg”,“HelloSpringMVC!”);
最后设置需要跳转的视图地址:
mv.setViewName("/hello");
前后缀都已经在配置文件中的视图解析器中写好了,拼接钎焊后缀之后就可以进行查找并跳转了
大坑注意:
如果jar包存在,但是显示404无法进行输出,那么就需要在idea的项目结构中添加lib依赖
然后重启tomcat即可
springMVC执行原理
流程图如下:
此处的前端控制器就可以看做是DispatcherServlet
页面控制器/处理器就可以看做是Controller
用户发送请求的一瞬间就到了web.xml中的servlet-mapping中进行匹配
然后委托请求给处理器(controller层),此处没有调用业务对象返回模型数据的步骤
controller直接封装返回一个模型视图ModelAndView给前端控制器,并最后呈现给用户
执行流程分析:
- Dispatcherervlet表示前端控制器,是整个SpringMVC的控制中心,用户发送请求,DispatcherServlet进行接收请求并拦截请求
- HandlerMapping为处理器映射,DispatcherServlet调用HandlerMapping,HandlerMapping根据请求url调用Handler
- HandlerExecution表示具体的Handler,其主要作用就是根据url查找控制器,如上url被查找控制器为:hello
- HandlerExecution将解析后的信息传递给DispatcherServlet,如解析控制器映射等
- HandlerAdapter表示处理器适配器,按照特定的规则去执行Handler
- Handler让具体的Controller执行
- Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView
- HandlerAdapter将实体逻辑名或模型传递给DispatcherServlet
- DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名
- 视图解析器将解析的逻辑视图名传递给DispatcherServlet
- DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图
- 最终视图呈现给用户
其中第九步再仔细换分则为:
获取ModelAndView中封装的数据
解析ModelAndView中的视图名称
拼接视图名称,找到对应的视图
最后将数据渲染到这个视图上面
整体用人话总结就是:
用户发送请求之后第一步就是去找到web.xml中的DispatcherServlet,
然后找到绑定的配置文件springmvc.xml
并找到处理器映射器,映射器映射到/hello,
然后执行到处理器适配器通过/hello找到指定的Controller类来处理或执行指定的业务代码
处理完之后就交给了视图解析器去操作,视图解析器再拼接后缀并展示给用户
使用注解开发springMVC
首先新建web项目并导入相关jar包
编写web.xml,注册DispatcherServlet
<?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">
<!-- 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>
</web-app>
编写springMVC配置文件:
注意:如果beans的头部说明没有写全,就会报500错误
<?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
http://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.xg"/>
<!-- 让springmvc不处理静态资源-->
<mvc:default-servlet-handler/>
<!-- 现在处理器映射器和处理器适配器都不再需要配置了,只需要如下一句代码就可以自动注入-->
<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 //写完注释就不用再实现Cotroller接口了
@RequestMapping("/xg")
public class controller {
// 注解可以在Controller类上面声明,也可以直接在方法上面声明,按照这样声明的话请求的url就是localhost:8080/xg/hello
@RequestMapping("/hello")
public String hello(Model model){
//封装数据
model.addAttribute("msg","hello,SpringmvcAnnoaction!");
return "hello"; //此处return返回的结果会被视图解析器处理
}
//以后多个请求就不用再写Servlet了,只需要在Controller多写一个方法就可以了
@RequestMapping("/hello2")
public String hello2(Model model){
//封装数据
model.addAttribute("msg","hello,第二个SpringmvcAnnoaction!");
return "hello"; //此处return返回的结果会被视图解析器处理
}
}
最后将已经写好的前端视图(hello.jsp)与controller(具体返回到视图)对应好就可以测试运行了
注意:使用注解时一定要添加如下代码:
<!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一进行管理-->
<context:component-scan base-package="com.xg"/>
<!-- 让springmvc不处理静态资源-->
<mvc:default-servlet-handler/>
<!-- 现在处理器映射器和处理器适配器都不再需要配置了,只需要如下一句代码就可以自动注入-->
<mvc:annotation-driven/>
Controller配置总结
使用实现接口方法实现controller配置文件时,需要对每一个controller都配置一个bean:
<?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
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<!-- 前提-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!-- 后缀-->
<property name="suffix" value=".jsp"/>
</bean>
<!--这样一来url地址直接变为localhost:8080/t1-->
<bean name="/t1" class="com.xg.TestController"/>
</beans>
但是使用接口实现controller时,每一个controller都只能实现一个url跳转的方法
拓展:
除了controller之外,还有几个预期效果类似的注解
@Component 组件
@Service service
@Controller controller
@Repository dao/mapper
如果是使用的注解:
可以再一个controller中配置多个视图跳转方法
但是需要在配置文件中加入以下说明:
<!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一进行管理-->
<context:component-scan base-package="com.xg"/>
<!-- 让springmvc不处理静态资源-->
<mvc:default-servlet-handler/>
<!-- 现在处理器映射器和处理器适配器都不再需要配置了,只需要如下一句代码就可以自动注入-->
<mvc:annotation-driven/>
@Controller
//代表这个类会被spring接管,被这个注解的类中的所有方法,
//如果返回值是String,并且有具体页面可以跳转,那么就会被视图解析器解析
@RequestMapping("/xg")
public class controller {
// 注解可以在Controller类上面声明,也可以直接在方法上面声明,按照这样声明的话请求的url就是localhost:8080/xg/hello
@RequestMapping("/hello")
public String hello(Model model){
//封装数据
model.addAttribute("msg","hello,SpringmvcAnnoaction!");
return "hello"; //此处return返回的结果会被视图解析器处理
}
//以后多个请求就不用再写Servlet了,只需要在Controller多写一个方法就可以了
@RequestMapping("/hello2")
public String hello2(Model model){
//封装数据
model.addAttribute("msg","hello,第二个SpringmvcAnnoaction!");
return "hello"; //此处return返回的结果会被视图解析器处理
}
}
注意:使用注解时,返回的字符串只用来拼接并匹配jsp视图页面,真正的请求url地址是在@RequestMapping("/hello")中进行的
Restful风格
restful风格并不是知识点,也不是特定名词,而是一种编码习惯,就是将请求的url的分隔符都用"/"来表示,并指明请求方式
使用restful风格的有点:
- 使路径变得更加简洁
- 获得参数更加方便,框架会自动进行类型转换
- 通过路径变量的类型可以约束访问参数,如果类型不一样,则访问不到对应的请求方法
注意,对于参数和方法对的匹配优先于请求方式对于方法的匹配
例如使用post方法的三个参数去get请求两个参数的get方法,则会爆方法不匹配,而不是参数不匹配 - 方便前端进行参数接收和处理
- 安全,高效,统一
如下的controller代码:
@Controller
public class TestController {
//按照此方法则原来的url为:https://Localhost:8080/add?a=1&b=2
//此方法的Restful风格则为:https://Localhost:8080/add/a=1/b=2
//@RequestMapping(value = "/add/{a}/{b}",method= RequestMethod.GET) 可以直接简化为@GetMapping("/add/{a}/{b}")
//@RequestMapping(value = "/add/{a}/{b}",method= RequestMethod.DELETE) 可以直接简化为@DeleteMapping("/add/{a}/{b}")
@GetMapping("/add/{a}/{b}")
public String handleRequest(@PathVariable int a, int b, Model model){
int c=a+b;
model.addAttribute("msg","a+b="+c);
return "hello";
}
}
在spring中可以使用@PathVariable注解,让方法参数的值对应绑定到一个url模板变量上并规定请求方式
如果请求方式不匹配,就会报错405:方法不允许:
我们所有的变量都可以用Method来进行约束请求的类型
如:@RequestMapping(value="/add/{a}/{b}",method=RequestMethod.GET)
或者可以直接使用变相的注解请求:@GetMapping(value = “/add/{a}/{b}”)
也许这样还不能突出作用,但是重点来了:
如下为两个方法,他们的路径映射完全一样,但是只是请求方法不一样
@GetMapping(value = "/add/{a}/{b}")
public String handleRequest(@PathVariable int a, @PathVariable int b, Model model){
int c=a+b;
model.addAttribute("msg","方法1:a+b="+c);
return "hello";
}
@PostMapping(value = "/add/{a}/{b}")
public String handleRequest1(@PathVariable int a, @PathVariable int b, Model model){
int c=a+b+b;
model.addAttribute("msg","方法2:a+b+b="+c);
return "hello";
}
再如下建立一个简单的form提交表单,并声明提交方式:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/add/2/3" method="post">
<input type="text" name="a"/>
<input type="text" name="b"/>
<input type="submit">
</form>
</body>
</html>
可以发现:
使用restful风格的时候,参数和请求的url都相同,但是请求方式不同,结果就会请求不同的方法进行处理
这就是restful风格的特点
拓展:
注意如下代码:Controller中有两个方法:get提交方法需要连个参数,post提交需要是哪个参数
@GetMapping(value = "/add/{a}/{b}")
public String handleRequest(@PathVariable int a, @PathVariable int b, Model model){
int c=a+b;
model.addAttribute("msg","方法1:a+b="+c);
return "hello";
}
@PostMapping(value = "/add/{a}/{b}/{d}")
public String handleRequest1(@PathVariable int a, @PathVariable int b, @PathVariable int d, Model model){
int c=a+b+d;
model.addAttribute("msg","方法2:a+b+b="+c);
return "hello";
}
接下来直接使用前端固定代码提交,但是使用get方法提交三个参数
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/add/2/3/4" method="get">
<input type="text" name="a"/>
<input type="text" name="b"/>
<input type="submit">
</form>
</body>
</html>
重点来了:
错误信息为方法不允许,而不是参数错误
说明restful风格下程序的参数匹配判定优先于提交方式判定
重定向和转发
现在转发和重定向只需要使用注解就可以了
但是使用注解的时候要注意在resource资源文件中添加使指定包生效的代码:
<!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一进行管理-->
<context:component-scan base-package="com.xg"/>
<!-- 让springmvc不处理静态资源-->
<mvc:default-servlet-handler/>
<!-- 现在处理器映射器和处理器适配器都不再需要配置了,只需要如下一句代码就可以自动注入-->
<mvc:annotation-driven/>
@Controller
public class ModelTest {
@RequestMapping("/m1/t1")
public String Test2(HttpServletRequest req, HttpServletResponse resp){
HttpSession session = req.getSession();
System.out.println(session.getId());//还可以获取session
//此方法必须要使用视图解析器来进行代码跳转所需要拼接的字符
return "hello";
}
@RequestMapping("/m1/t2")
public String Test1(Model model){
model.addAttribute("msg","RequestMapping");
//以下代码效果相当于转发(url地址栏不变),此处的forward可以去掉
return "forward:/WEB-INF/jsp/hello.jsp";
//以下代码效果相当于重定向(url地址栏改变了)
//return "redirect:/index.jsp";
//注意重定向之后就不用再加视图解析器来进行视图跳转的字符拼接了,不然就会强行拼接导致异常
}
}
接收请求参数
提交的域名称和处理方法的参数名一致时:
提交数据: http://localhost:8080/hello?name=xg
@RequestMapping("/hello")
public String hello(String name){
System.out.println(name);
return "hello";
}
后台输出也就是xg
提交的域名称和处理方法的参数名不一致时:
前端传过来的参数名为username,但是方法接收的参数为name
思路:使用@RequestParam接收前段传过来的username,并优化转化为name
提交数据链接为:http://localhost:8080/hello?username=xg
处理方法为:
//此处使用@RequestParam("username"):username提交域的名称
@RequestMapping("/hello")
public String hello(@RequestParam("username") String name){
System.out.println(name);
return "hello";
}
后台输出为:xg
如果提交的是一个对象
则匹配user对象的字段名,如果名字一致则ok.不一致就匹配不到
同时注意,在url地址栏中直接进行拼接的时候,只需要名字一致,不需要顺序也可以进行接收
//前段接收的是一个对象: id name age
//http://localhost:8080/user/t2?id=1&name=xg&age=13
@RequestMapping("/t2")
public String test2(User user,Model model){
System.out.println(user);
model.addAttribute("msg",user);
return "hello";
}
参数回显
通过ModelAndView
之前用的一直都是这个方法,大致就是new一个ModelAndView
然后往里面addObject,传递前端指定的参数然后setViewNname
设置跳转前端页面
@RequestMapping("/a")
public ModelAndView test(HttpServletResponse httpServletResponse,HttpServletRequest httpServletRequest){
ModelAndView mv = new ModelAndView();
mv.addObject("msg","hello,SpringMVC");
mv.setViewName("/hello");
return mv;
}
通过Model
老办法,但是注意通过@RequestParam的使用,此处使用时竟然被判断为畸形语法
@RequestMapping("/b")
public String hello(String name,Model model){
//封装要显示到视图中的数据
//相当于req.setAttribute("name",name)
model.addAttribute("msg","hello,SpringMVC");
System.out.println(name+"执行了Model方法");
return "hello";
}
通过ModelMap
首先说明关系
LinkedHashMap 老大
ModelMap 继承了LinkedHashMap,所以拥有LinkedHashMap的所有功能
Model 精简版,大部分情况我们都会去使用Model
三者区别:
Model只有寥寥几个方法适合用于储存数据,简化新手对于Model对象的操作和理解
ModelMap继承了LinkedMap,除了实现自身的一些方法,同样继承了LinkedMap的方法和特性
ModelAndView可以在储存数据的同时,进行设置返回的逻辑视图,进行控制展示层的跳转
当然,更多的以后开发开发考虑的是性能和优化,不能单单仅限于此的了解
乱码问题解决:
一般来说接收前端from表单传递过来的参数时,有时候会产生一些乱码的现象
@Controller
public class EncodingTest {
@PostMapping("/t1/t3")
public String test1(String name, Model model){
System.out.println(name);
model.addAttribute("msg",name);
System.out.println(name);
return "hello";
}
}
输出时会发现两次输出的都是乱码,这说明从前端传递过来的时候就已经是乱码了,已经不能通过方法中加参数HttpServletRequest,然后setCharacterEncoding(“xxx”)来设置特定的参数来进行编码格式约束了
这个时候就要使用web中通过过滤器来进行解决乱码问题了
首先创建一个专门的包来进行储存代码:
import javax.servlet.*;
import java.io.IOException;
public class Encoding implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
response.setCharacterEncoding("utf-8");
request.setCharacterEncoding("utf-8");
//注意chain.doFilter(request,response);必须要写,不然进入过滤器的请求和相应都无法出去
chain.doFilter(request,response);
}
public void destroy() {
}
}
第二步,web.xml配置过滤器适用范围
<filter>
<!-- 给不同的过滤器起名字来进行过滤器的细化分层-->
<filter-name>Encoding</filter-name>
<filter-class>com.xg.Fliter.Encoding</filter-class>
</filter>
<filter-mapping>
<!-- 可以通过设置filter-name来设置进入不同的过滤器-->
<filter-name>Encoding</filter-name>
<!-- 下面这行代码用于限定过滤范围-->
<url-pattern>/*</url-pattern>
</filter-mapping>
注意,要想使web-inf下面的网页也经过过滤器,就不能只是用"/",而是像上图一样适用"/*",这样就完成了
同时需要注意的是,乱码只是针对post提交,适用get提交就不会出现乱码了
或者直接使用SpringMVC的乱码解决办法:
<!-- 配置SpringMVC的乱码过滤-->
<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>
JSON
按照现在前后端分离情况下,一般都是后端部署后端,并给前端提供接口,提供数据,
注意,此处说的并不是字面意义上的方法接口,而是说Controller接口和提供的@RequestMapping("/x/xx")地址栏,凡是具有抽象和封装含义或者性质的东西都可以成为接口
前端独立部署,负责渲染后端的数据
这样的话前端与后端直接的交互就需要一个格式约定,这个格式用的最多的就是JSON
json是一个格式,而不是一个新的语言,是一种轻量级的数据交换格式
采用完全独立于编程语言的文本格式(字符串)来进行储存和表示数据
JSON字符串和JavaScript对象的相互转化
要实现从JSON字符串转化为JavaScript对象,需要使用**JSON.parse()**方法
var obj =JSON.parse('{"a" : "hello", "b" : "world"}');
//结果为{a:'hello' , b: "world"}
要实现从JavaScript对象转化为json字符串,需要使用**JSON.stringify()**方法
var json=JSON.stringify({a:'hello', b: 'world'})
//结果是'{"a" : "hello", "b" : "world"}'
如下
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JSON测试</title>
<script type="text/javascript">
//编写一个JavaScript对象 ES6
var user={
name :"希哥",
age:3,
sex:"男"
}
//将js对象转换为json对象
var json = JSON.stringify(user);
//将JSON对象转换为JavaScript对象
var parse = JSON.parse(json);
console.log("初始设置对象如下");
console.log(user);
console.log("下面是JSON对象转换为JavaScript对象");
console.log(parse);
console.log("下面是将js对象转换为json对象");
console.log(json);
</script>
</head>
<body>
</body>
</html>
Controller使用jackson返回JSON对象
jackson是目前比较好的json解析工具
jackson依赖如下:
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.0</version>
</dependency>
测试如下:
//@RestController 包括使用@RestController注解时,此类只能返回字符串
@Controller
public class UserController {
@RequestMapping(value = "/j1",produces = "application/json;charset=utf-8")
@ResponseBody //使用此注解之后就不会再走视图解析器了,会直接返回一个字符串到页面上
public String json1() throws JsonProcessingException {
//jackson,ObjectMapper
ObjectMapper mapper =new ObjectMapper();
//新建一个实体类对象
User user =new User(3,"XI哥",3);
String str = mapper.writeValueAsString(user);
//接下来直接将对象toString后的字符串返回给了前端,但是此方法太low了
//return user.toString();
return str;
}
}
注意:
@ResponseBody 使用此注解之后就不会再走视图解析器了,会直接返回一个字符串到页面上
此时是不会经过过滤器进行过滤处理乱码格式的,
可以在@RequestMapping(value = “/j1”,produces = “application/json;charset=utf-8”)中进行设定编码格式
但是上面的办法太麻烦了,项目中的每一个请求都需要添加,所以
统一解决乱码问题
没什么好说的,死代码,无脑CV就行了
<!-- 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>
接下来是拓展测试:
//返回集合json测试
@RequestMapping("/j2")
@ResponseBody //使用此注解之后就不会再走视图解析器了,会直接返回一个字符串到页面上
//同时,也不会再走过滤器进行乱码过滤了
public String json2() throws JsonProcessingException {
//jackson,ObjectMapper
ObjectMapper mapper =new ObjectMapper();
List<User> user =new ArrayList<User>();
User user1 =new User(1,"XI哥",3);
User user2 =new User(2,"XI哥",3);
User user3 =new User(3,"XI哥",3);
User user4 =new User(4,"XI哥",3);
user.add(user1);
user.add(user2);
user.add(user3);
user.add(user4);
String str = mapper.writeValueAsString(user);
return str;
}
//返回时间json测试
@RequestMapping("/j3")
@ResponseBody //使用此注解之后就不会再走视图解析器了,会直接返回一个字符串到页面上
public String json3() throws JsonProcessingException {
ObjectMapper mapper =new ObjectMapper();
//创建一个对象
Date date = new Date();
//时间解析后的默认格式为Timestamp,时间戳,其实是1970年1月1号到现在的毫秒数
//String str = mapper.writeValueAsString(date);
//或者可以自定义日期的格式,如下输出结果就是"2020-08-10 23:17:05"
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return mapper.writeValueAsString(sdf.format(date));
}
再次拓展:
public class JsonUtils {
//此工具类重点在于如果上面的方法重载了下面的方法,如果只有一个参数传递进来
//那么依然调用第二个方法,只不过多传递一个默认的时间格式
public static String getJson(Object object) throws JsonProcessingException {
return getJson(object,"yyyy-MM-dd HH:mm:ss");
}
public static String getJson(Object Object, String DateFormat) throws JsonProcessingException {
ObjectMapper mapper=new ObjectMapper();
//不使用时间戳的方式
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false);
//或者可以自定义日期的格式,此处靠接收传递的参数来进行格式变化
SimpleDateFormat sdf = new SimpleDateFormat(DateFormat);
mapper.setDateFormat(sdf);
//此处在于接收格式后的后续操作不在需要传递过来的字符串格式了
//真正进行转换json格式的只有这一句代码
return mapper.writeValueAsString(Object);
}
}
重点注意
如果想要使用JSON字符串将数据从后端转入到前端,就必须要导入jackson的pom依赖,或者自己toString(只要是字符串就行)
配置乱码问题
使用工具类或者直接在本类进行配置json
new ObjectMapper().writeValueAsString(Object);
另外Controller也有注意点:
如果想要返回纯JSON字符串给前端页面
要么使用 @RestController 使用@RestController注解时,此类只能返回字符串
要么就使用@Controller再加上@ResponseBody //使用此注解之后就不会再走视图解析器了,会直接返回一个字符串到页面上
Fastjson
此款为阿里巴巴开发的一款专门用于java开发的包,依赖如下:
<!-- fastjson依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.68</version>
</dependency>
其他的jackson区别不大,使用示例如下:
//返回集合json测试
@RequestMapping("/j4")
@ResponseBody //使用此注解之后就不会再走视图解析器了,会直接返回一个字符串到页面上
//同时,也不会再走过滤器进行乱码过滤了
public String json4() throws JsonProcessingException {
//jackson,ObjectMapper
ObjectMapper mapper =new ObjectMapper();
List<User> user =new ArrayList<User>();
User user1 =new User(1,"XI哥",3);
User user2 =new User(2,"XI哥",3);
User user3 =new User(3,"XI哥",3);
User user4 =new User(4,"XI哥",3);
user.add(user1);
user.add(user2);
user.add(user3);
user.add(user4);
return JSON.toJSONString(user);
}
完结~~