MVC概念
Controller:控制器 servlet层
- 区的表单数据
- 调用业务逻辑
- 转向指定页面
Model:模型 dao+service
- 业务逻辑
- 保存数据的状态
View:视图 jsp
- 显示界面
MVC框架要做哪些事情
- 将url映射到java类或java类的方法。
- 封装用户提交的数据
- 处理请求–调用相关的业务处理–封装响应数据。
- 将相应的数据进行渲染。jsp/html等表示层数据。
官方文档
https://docs.spring.io/spring-framework/docs/4.3.24.RELEASE/spring-framework-reference/
SpringMVC执行原理图 虚线为自己要实现的步骤
简要分析执行流程
-
DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。用户发出的请求,DispatcherServlet接收请求并拦截请求。
-
假设请求url为:http://localhost:8080/SpringMVC/hello
将上url拆成三部分:
-
http://localhost:8080服务器域名
-
SpringMVC部署在服务器上的web站点
-
hello表示控制器
-
通过分析,如上url表示为请求位于服务器localhost:8080上的SpringMVC站点的hello控制器
-
-
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给他的ModelAndView 1. 获取了ModelAndView的数据 2. 解析ModelAndView的视图名字 3. 拼接视图名字,找到对应的视图/WEB-INF/jsp/hello.jsp 4. 将数据渲染到这个视图上 -->
-
视图解析器将解析的逻辑视图名传给DispatcherServlet。
-
DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图。
-
最终视图呈现给用户。
SpringMVC实现步骤
- 新建web项目
- 导入相关jar包
- 编写web.xml
- 编写springmvc配置文件
- 创建对应的控制类,controller
- 最后完善前段视图和controller之间的对应
- 测试运行调试
使用springMVC必须配置三大件:
处理器映射器、处理器适配器、视图解析器
通常,我们只需要手动配置视图解析器,而处理器映射器和处理器适配器只需要开启注解驱动即可,而省去了大段的xml配置
RestFul 风格
简洁、高效、安全。
//原来的: http://localhost:8080/add?a=1&b=2
@RequestMapping("/add")
public String test1(int a, int b, Model model){
int res = a+b;
model.addAttribute("msg","结果为"+res);
return "test";
}
// RestFul:http://localhost:8080/add/a/b
// value="/add/{a}/{b}" == path="/add/{a}/{b}"
// 每个提交方式可以写成注解
//@RequestMapping(value="/add/{a}/{b}",method = RequestMethod.GET) 等价于
@GetMapping("/add/{a}/{b}")
@RequestMapping(value="/add/{a}/{b}",method = RequestMethod.DELETE)
public String test2(@PathVariable int a, @PathVariable int b, Model model){
int res = a+b;
model.addAttribute("msg","结果为"+res);
return "test";
}
重定向和转发
不用视图解析器
转发 默认也是转发 可以不加forward:
@Controller
public class ControllerTest2 {
@RequestMapping("/m1/t2")
public String test1(Model model){
model.addAttribute("msg","ModelTest1");
return "forward:/WEB-INF/jsp/test.jsp";
}
}
重定向
@Controller
public class ControllerTest2 {
@RequestMapping("/m1/t2")
public String test1(Model model){
model.addAttribute("msg","ModelTest1");
return "redirect:/WEB-INF/jsp/test.jsp";
}
}
接收对象
//localhost:8080/user/t1?name=xxx;
// 加了@RequestParam之后 localhost:8080/user/t1?username=xxx;
@GetMapping("/t1")
public String test(@RequestParam("username") String name, Model model){
//1. 接收前段参数
//2.将返回的结果传递给前段,model
model.addAttribute("msg",name);
return "test";
}
/*
接收前端用户传递的参数,判断参数的名字,假设名字直接在方法上,可以直接使用
假设传递的是一个对象User,匹配User对象中的字段名:如果名字一致则OK,否则报错,为空
*/
Model ModelMap ModelAndview
Model 只有寥寥几个方法只适合用于储存数据,简化了新手对于Model对象的操作和理解;
ModelMap继承了Linkedrap ,除了实现了自身的一些方法,同样的继承 LinkedMap的方法和特性;
ModelAndview可以在储存数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转。
json
单个json解决乱码
@Controller
public class UserController {
@RequestMapping(value = "/j1", produces = "application/json;charset=utf-8")
@ResponseBody //它就不会走视图解析器,会直接返回一个字符串
public String json1() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
User user = new User("秦疆1号",3,"男");
String str = mapper.writeValueAsString(user);
return str;
}
}
乱码统一解决 放得位置有问题也不能解决问题
<!--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>
//@Controller
@RestController //直接返回字符串,不走视图解析
@ResponseBody //它就不会走视图解析器,会直接返回一个字符串 用在方法上
需要阿里工具包
时间输出两种
@RequestMapping("/j2")
@ResponseBody //它就不会走视图解析器,会直接返回一个字符串
public String json2() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
Date date = new Date();
//自定义日期格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年-MM月-dd日 HH:mm:ss");
String s = mapper.writeValueAsString(sdf.format(date));
return s;
}
@RequestMapping("/j3")
public String json3() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
//不适用时间戳的方式
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
//自定义时间格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年-MM月-dd日 HH:mm:ss");
mapper.setDateFormat(sdf);
Date date = new Date();
return mapper.writeValueAsString(date);
}
拦截器
-
拦截器是AOP思想的应用
-
拦截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能使用
-
拦截器只会拦截访问的控制器方法,如果访问的是jsp/html/css/image/js是不会进行拦截的
实现拦截器
想要自定义拦截器,必须实现HandlerInterceptor
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("===========清理============");
}
}
在springmvc.xml文件里配置拦截器
<mvc:interceptors>
<mvc:interceptor>
<!--拦截这个请求下面的所有请求-->
<mvc:mapping path="/**"/>
<!--拦截器位置-->
<bean class="com.can.config.MyInterceptor"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/user/**"/>
<bean class="com.can.config.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
文件上传
导包
<dependencies>
<!--文件上传-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
<!--servlet-api导入高版本的-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
</dependencies>
表单要支持文件上传 要加上enctype=“multipart/form-data” ,要用post方式提交 get有大小限制
<form action="/upload" enctype="multipart/form-data" method="post">
springmvc-servlet.xml 添加配置
<!--文件上传配置-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--请求的编码格式,必须和jsp的pageEncoding属性一致,以便于正确的
读取表单的内容,默认ISO-8859-1
-->
<property name="defaultEncoding" value="utf-8"/>
<!--
下面两个可以不配置 有默认值
上传文件大小上线,单位为字节10485760=10m-->
<property name="maxUploadSize" value="10485760"/>
<!--低于此值,只保留在内存里,超过此阈值,生成硬盘上的临时文件。-->
<property name="maxInMemorySize" value="40960"/>
</bean>
文件上传方法
方式一
//@RequestParam("file")将name=file控件得到的文件封装成CommonsMultipartFiLe对象
//批量上传CommonsMultipartFile则为数组即可
@RequestMapping("/upload")
public String upload(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {
//获取文件名:file.getOriginalFilename()
String uploadFileName = file.getOriginalFilename();
//如果文件名字为空,直接返回到首页
if("".equals(uploadFileName)){
return "rediret:/index.jsp";
}
System.out.println("上传文件名"+uploadFileName);
//上传路径保存位置
String path = request.getServletContext().getRealPath("/upload");
//如果路径不存在,创建一个
File realPath = new File(path);
if(!realPath.exists()){
realPath.mkdir();
}
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 "redirect:index.jsp";
}
方式二
/*
* 采用file.Transto来保存上传文件
*/
@RequestMapping("/upload")
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 "redirect:index.jsp";
}
文件下载
方式一
方式二
@RequestMapping("/download")
public String downloads(HttpServletResponse response , HttpServletRequest request) throws Exception{
//要下载的图片地址
String path = request.getServletContext().getRealPath("/upload");
String fileName ="基础语法.jpg";
// 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 null;
}