MVC:模型视图控制器
最典型的mvc架构就是:jsp + service + servlet .
Model2时代演变成mvc架构,
以前是jsp(jsp本身就是servlet)+dao = 视图层 + 模型层
架构一定是演进过来的(ALL in One)
<<淘宝的十年架构>> 淘宝技术这十年架构发展_Apple_Web的博客-CSDN博客
王坚:去IOE化
方便团队开发,java是项目越大,越好开发
特点
约定优于配置
中心控制器DispatchServlet
回顾Servlet
尽量用**add framework support(添加框架支持)**来添加web目录,保证web.xml是最新的
M V VM:ViewModel(双向绑定)
HelloSpringMVC 运行原理
-
客户端发送请求
-
DispatcherServlet(核心控制分发器)根据请求,找到处理器映射器和处理器适配器,让他们去执行一个servlet
-
SpringMVC会调用DispatchServlet的doService(),debug可以看到
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
-
这个servlet实现了Controller,就是一个Controller(控制器)了.
-
实现其中的方法,必须返回一个ModelAndView,相当于告诉DispatcherServlet,去哪个视图,和怎么处理数据.
-
DispatcherServlet找到对应的视图,渲染数据后响应回客户端
spring-webmvc包含了所有spring和springmvc会用到的jar包
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.3.14version>
dependency>
web.xml配置DispatcherServlet及映射
java项目中的classpath到底是什么
<load-on-startup>1</load-on-startup>的作用
<servlet>
<servlet-name>springmvcservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:springmvc-servlet.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>springmvcservlet-name>
<url-pattern>/*url-pattern>
servlet-mapping>
在spring-servlet.xml中,配置视图适配器和视图解析器,设置前后缀,方便找到页面
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
bean>
<bean id="/hello" class="com.changGe.li.servlets.HelloSpringMVC" />
servlet类实现Controller的方法,返回一个ModelAndView
package com.changGe.li.servlets;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//只要实现了Controller的类,说明这就是一个控制器了
public class HelloSpringMVC implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
//模型和视图
ModelAndView modelAndView = new ModelAndView();
//封装一个对象
modelAndView.addObject("msg","HelloSpringMVC");
//跳转页面
modelAndView.setViewName("hello");
//返回这个对象才会执行
return modelAndView;
}
}
记住一定要把依赖导入进来,不然tomcat报错ClassNotFount
IDEA maven项目部署到tomcat的jar包找不到问题
注意配置maven构建查找resource目录下的资源文件
<build>
<resources>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.xmlinclude>
<include>**/*.propertiesinclude>
includes>
<excludes>
<exclude>**/*.yamlexclude>
excludes>
<filtering>truefiltering>
resource>
resources>
build>
ModelMap家族
注解开发
web.xml不变,spring-servlet.xml配置注解相关
default和annotation是不处理静态资源,如css文件,和自动注入(省略注解适配器和解析器)
<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 http://www.springframework.org/schema/mvc/spring-mvc.xsd
">
<context:component-scan base-package="com.changGe.li.servlets"/>
<mvc:default-servlet-handler/>
<mvc:annotation-driven/>
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
bean>
beans>
真实的controller类
使用注解Controller后,就必须配置注解解析器
package com.changGe.li.servlets;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
//被Spring接管,
//其下所有方法只要返回值是String,就会被视图解析器解析
@org.springframework.stereotype.Controller
//http://localhost:8080/SpringMVC/hello/SpringMVC
@RequestMapping("/hello")
public class HelloSpringMVC {
@RequestMapping("/SpringMVC")
public String helloSpringMVC(Model model){
model.addAttribute("msg","helloSpringMVC");
return "hello";
}
}
两个不同的路径,最后都跳转到同一个页面,这个是博客网站的思想,页面复用
@org.springframework.stereotype.Controller
@RequestMapping("/hello")
public class HelloSpringMVC {
@RequestMapping("/SpringMVC")
public String helloSpringMVC(Model model){
model.addAttribute("msg","helloSpringMVC");
return "hello";
}
@RequestMapping("/Spring")
public String helloSpring(Model model){
model.addAttribute("msg","helloSpringMVC");
//文件名中尽量不要用数字,就用英文就好
return "test";
}
}
RestFul风格:通过相同的请求方式实现不同的效果
RequestMapping源码中的value别名是path
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.web.bind.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
String name() default "";
@AliasFor("path")//定义别名
String[] value() default {};
@AliasFor("value")
String[] path() default {};
RequestMethod[] method() default {};
String[] params() default {};
String[] headers() default {};
String[] consumes() default {};
String[] produces() default {};
}
@org.springframework.stereotype.Controller
public class HelloSpringMVC {
@RequestMapping(path ="/mvc/{a}/{b}",method = RequestMethod.GET)
public String helloSpringMVC(@PathVariable("a") int a,@PathVariable("b") int b, Model model){
model.addAttribute("msg",a+b);
return "hello";
}
//这里有bug,报404,暂时没有找到原因
@PostMapping("/post/{a}/{b}")
public String helloSpring(@RequestParam("a") int a, @RequestParam("b") int b, Model model){
model.addAttribute("msg",a+b);
return "hello";
}
}
转发和重定向
有视图解析器的情况 下,默认是转发
把springmvc-servlet.xml中的视图解析器注掉
@RequestMapping(path ="/mvc/{a}/{b}")
public String helloSpringMVC(@PathVariable("a") int a,@PathVariable("b") int b, Model model){
model.addAttribute("msg",a+b);
//没有视图解析器:转发
return "forward:/WEB-INF/jsp/hello.jsp";
}
@RequestMapping("/get")
public String helloSpring(){
//重定向
return "redirect:/index.jsp";
}
过滤器
springMVC的乱码过滤
web.xml
<filter>
<filter-name>encodingFilterfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>utf-8param-value>
init-param>
filter>
<filter-mapping>
<filter-name>encodingFilterfilter-name>
<url-pattern>/url-pattern>
<url-pattern>/*url-pattern>
filter-mapping>
CharacterEncodingFilter中有一个encoding属性,被@Nullale注解
public class CharacterEncodingFilter extends OncePerRequestFilter {
@Nullable
private String encoding;
测试
@RequestMapping("/mvc")
public String helloSpringMVC(Model model){
model.addAttribute("msg","李长歌");
return "hello";
}
大神自定义乱码过滤器
/**
* 解决get和post请求 全部乱码的过滤器
*/
public class GenericEncodingFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 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;
}
}
JSON
切记,更新一次maven,就去配置一次项目设置中的工件
SpringMVC默认就是jackson
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
<version>2.13.1version>
dependency>
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String name;
private int age;
private char sex;
}
@RestController = @Controller + @ResponseBody组成
ResponseBody可以规定不走视图解析器,返回值是字符串
@RestController
public class HelloSpringMVC {
//解决中文乱码
@RequestMapping(value = "/mvc",produces ="application/json;charset=utf-8")
@ResponseBody
public String helloSpringMVC() throws Exception{
ObjectMapper mapper = new ObjectMapper();
User user = new User("李长歌", 18, '女');
return mapper.writeValueAsString(user);
}
}
Json返回时间戳的工具类
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import java.text.SimpleDateFormat;
public class JsonUtils {
//传入一个时间,将其转换格式
public static String getJson(Object object){
return getJson(object,"yyyy-MM-dd HH:mm:ss");
}
public static String getJson(Object object,String dateFormat){
ObjectMapper mapper = new ObjectMapper();
//不使用时间戳
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat);
mapper.setDateFormat(simpleDateFormat);
String s = null;
try {
s = mapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return s;
}
}
@RequestMapping(value = "/mvc",produces ="application/json;charset=utf-8")
public String helloSpringMVC(){
Date date = new Date();
return JsonUtils.getJson(date);
}
FastJson
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.73version>
dependency>
@org.junit.Test
public void test(){
User user = new User();
User user1 = new User();
ArrayList<Object> arrayList = new ArrayList<>();
arrayList.add(user);
arrayList.add(user1);
//转成json
JSONObject json1 = (JSONObject)JSON.toJSON(user);//{"sex":"\u0000","age":0}
String json = JSON.toJSONString(arrayList);
json = JSON.toJSONString(user);//[{"age":0,"sex":"\u0000"},{"age":0,"sex":"\u0000"}]
//转回java
User java = JSON.parseObject(json,User.class);//User(name=null, age=0, sex= )
java = (User)JSON.toJavaObject(json1, User.class);//User(name=null, age=0, sex= )
System.out.println(java);
}
8.0版本的数据库有时需要在properties配置文件中里加一个时区
三大框架整合
从底层往上写
【后端】SSM(Spring + SpringMVC + Mybatis)框架整合(一) - 双份浓缩馥芮白 - 博客园 (cnblogs.com)
(7条消息) 关于maven打包时, 资源文件没有被打包进来的问题_抠脚的大灰狼的博客-CSDN博客_maven打包resource文件没打进去
web.xml
<servlet>
<servlet-name>springmvcservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:applicationContext.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>springmvcservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
spring-mvc.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/jsp/*"/>
<bean class="com.changGe.li.filters.ForcoLoginFilter"/>
mvc:interceptor>
mvc:interceptors>
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="prefix" value="/jsp/"/>
<property name="suffix" value=".jsp"/>
bean>
<bean class="com.changGe.li.servlets.Online" id="/jsp/online.do"/>
<bean class="com.changGe.li.servlets.Register" id="/jsp/register.do"/>
<bean class="com.changGe.li.servlets.ModifyPasswordServlet" id="/jsp/user.do"/>
<bean class="com.changGe.li.servlets.LoginServlet" id="/login"/>
<bean class="com.changGe.li.servlets.LogoutServlet" id="/jsp/logout.do"/>
beans>
spring-mvc-annotation.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.changGe.li.servlets"/>
<mvc:default-servlet-handler/>
<mvc:annotation-driven/>
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"/>
<property name="maxUploadSize" value="81920000"/>
<property name="maxInMemorySize" value="81920000"/>
bean>
beans>
applicationContext.xml
<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">
<import resource="classpath*:beans.xml"/>
<import resource="classpath*:spring-mvc.xml"/>
<import resource="classpath*:spring-mvc-annotation.xml"/>
beans>
Ajax:异步的JavaScript和XML
不需要刷新网页,刷新部分网页
$.ajax({
type:"GET",
url:path+"/jsp/user.do",
data:{method:"deluser",uid:obj.attr("userid")},
dataType:"json",
success:function(data){
if(data.delResult == "true"){//删除成功:移除删除行
cancleBtn();
obj.parents("tr").remove();
window.location.href = path+"/jsp/user.do?method=query";
return;
}else if(data.delResult == "false"){
changeDLGContent("对不起,删除用户【"+obj.attr("username")+"】失败");
}else if(data.delResult == "notexist"){
changeDLGContent("对不起,用户【"+obj.attr("username")+"】不存在");
}
},
error:function(data){
changeDLGContent("对不起,删除失败");
}
});
js中所有的值或变量,最好提前获取
简化ajax:url,data,seccues,error
$.get(path+"/jsp/user.do",{method:"deluser",uid:obj.attr("userid")},function(data){
if(data.delResult == "true"){//删除成功:移除删除行
cancleBtn();
obj.parents("tr").remove();
window.location.href = path+"/jsp/user.do?method=query";
return;
}else if(data.delResult == "false"){
changeDLGContent("对不起,删除用户【"+obj.attr("username")+"】失败");
}else if(data.delResult == "notexist"){
changeDLGContent("对不起,用户【"+obj.attr("username")+"】不存在");
}
},function(data){
changeDLGContent("对不起,删除失败");
});
看一下ajax跨域的东西
ajax跨域,这应该是最全的解决方案了 - 简书 (jianshu.com)
拦截器:AOP思想的具体应用
暂时理解成servlet中的过滤器
原先实现Filter的用户登录过滤器
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resq = (HttpServletResponse) response;
User user = (User)req.getSession().getAttribute(USER_SESSION);
if(user == null){
request.setAttribute("error","请先登录,谢谢");
resq.sendRedirect(req.getContextPath()+"/login.jsp?error="+ URLEncoder.encode("请先登录,谢谢","utf-8"));
}else{
chain.doFilter(req, resq);
}
}
实现handlerIntercetor就是一个拦截器
//处理器执行前
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resq = (HttpServletResponse) response;
User user = (User)req.getSession().getAttribute(USER_SESSION);
if(user == null){
request.setAttribute("error","请先登录,谢谢");
resq.sendRedirect(req.getContextPath()+"/login.jsp?error="+ URLEncoder.encode("请先登录,谢谢","utf-8"));
}
return true;//放行到下一个拦截器
}
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/jsp/*"/>
<bean class="com.changGe.li.filters.ForcoLoginFilter"/>
mvc:interceptor>
mvc:interceptors>
文件上传下载
文件 *** 作以out目录为基准
Spring MVC文件上传 (biancheng.net)
文件上传的配置
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"/>
<property name="maxUploadSize" value="81920000"/>
<property name="maxInMemorySize" value="81920000"/>
bean>
@Controller
@RequestMapping("/jsp")
public class FileUpLoad{
@RequestMapping(value="/upload.do",method = RequestMethod.POST,produces ="application/json;charset=utf-8")
//将name=filed的控件,得到的文件封装成MultipartFile对象
public String fileUpload(@RequestParam("filed") MultipartFile multipartFile,HttpServletRequest request) throws Exception{
// 使用fileupload组件完成文件上传
// 上传的位置
String path = request.getSession().getServletContext().getRealPath("/WEB-INF/upload/");
// 判断,该路径是否存在
File file = new File(path);
if(!file.exists()){
// 创建该文件夹
file.mkdirs();
}
// 获取上传文件的名称
String filename = multipartFile.getOriginalFilename();
// 把文件的名称设置唯一值,uuid
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid+"_"+filename;
// 完成文件上传
multipartFile.transferTo(new File(path,filename));
request.setAttribute("msg","文件上传成功");
return "fileUpload";
}
}
文件上传下载-KuangStudy-文章
@RequestMapping(value="/download")
public String downloads(HttpServletResponse response , HttpServletRequest request) throws Exception{
//要下载的图片地址
String path = request.getServletContext().getRealPath("/WEB-INF/upload/");
String fileName = "springMVC执行流程.png";
//设置页面不缓存,清空
response.reset();
//字符编码
response.setCharacterEncoding("UTF-8");
//设置响应头
response.setHeader("Content-Disposition", "attachment;fileName="+ URLEncoder.encode(fileName, "UTF-8")); File file = new File(path,fileName);
//设置响应头:以二进制传输数据
response.setContentType("multipart/form-data");
//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 null;
}
.transferTo(new File(path,filename));
request.setAttribute("msg","文件上传成功");
return "fileUpload";
}
}
[文件上传下载-KuangStudy-文章](https://www.kuangstudy.com/bbs/1456814880131661826)
```java
@RequestMapping(value="/download")
public String downloads(HttpServletResponse response , HttpServletRequest request) throws Exception{
//要下载的图片地址
String path = request.getServletContext().getRealPath("/WEB-INF/upload/");
String fileName = "springMVC执行流程.png";
//设置页面不缓存,清空
response.reset();
//字符编码
response.setCharacterEncoding("UTF-8");
//设置响应头
response.setHeader("Content-Disposition", "attachment;fileName="+ URLEncoder.encode(fileName, "UTF-8")); File file = new File(path,fileName);
//设置响应头:以二进制传输数据
response.setContentType("multipart/form-data");
//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 null;
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)