SpringMVC
SSM:spring + springMVC + mybatis
mvc:模型(dao,service)识图(jsp) 控制器(Servlet)
1、搭建环境
-
新建普通maven项目,删除src使它变成父项目。导入依赖
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.1.9.RELEASE</version> </dependency> <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> </dependencies>
-
在父项目下新建普通maven项目,然后右键模块名,添加框架支持:javaee,webapplication。变成web项目
2、接口实现
相较于传统的 用户->web->service->dao,由web传回用户数据,决定转发和重定向。
springmvc 在用户和web层中间有一层控制器(DispatcherServlet),由控制器统一决定
-
在web.xml配置springmvc的核心 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"> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--关联一个springmvc的配置文件:【servlet名字-servlet.xml】--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc-servlet.xml</param-value> </init-param> </servlet> <!--/:匹配所有请求(不包括.jsp)--> <!--/*:匹配所有请求(包括.jsp)--> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
-
编写springmvc的配置文件
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/> <!--视图解析器:ModelAndView--> <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.ndkj.controller.HelloController"/> </beans>
-
写controller
public class HelloController implements Controller { public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { //ModelAndView ModelAndView mv =new ModelAndView(); //把模型和识图封装到mv对象中 mv.addObject("msg","HelloSpringMVC"); mv.setViewName("hello"); //视图:hello和springmvc的配置文件中的视图解析器的前缀后缀拼接成完整的路径 return mv; } }
2.1、实现原理(重要)
DispatcherServlet:这是核心,也叫控制器。
-
用户发出请求,经过servlet-mapping,被核心所拦截处理请求;
-
控制器中设置了springmvc的配置文件,控制器调用HandlerMapping,然后HandlerExecution将Handler信息传递给控制器;
- HandlerMapping根据请求的URL查找Handler
-
得到Handler信息后,控制器调用HandlerAdapter,去找和Handler匹配的类;
-
类(这个类继承了Controller接口)执行:创建ModelAndView对象,并在其内部封装对象(属性、内容)和视图(页面);
-
Controller将ModelAndView返回给HandlerAdapter,再返回给控制器;
-
控制器调用视图解析器(ViewResolver)解析ModelAndView,将视图名称传给控制器
-
ViewResolver获取了数据
-
获取了视图名称
-
拼接名称,找到对应视图并渲染
-
-
控制器根据视图解析器结果调用具体的视图显示
用户 -> 控制器DispatcherServlet -> HandlerMapping -> HandlerExecution -> 控制器收到Handler ->HandlerAdapter ->具体的Controller实现类 -> 返回对象给HandlerAdapter -> 控制器 -> 视图解析器找具体视图 ->控制器调用视图
3、注解实现
springmvc配置改成:
<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
https://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.ndkj.controller"/>
<!--让springmvc不处理静态资源:.css .js .mp3 .mp4-->
<mvc:default-servlet-handler/>
<!--相当于之前的 HandlerMapping 和 HandlerAdapter-->
<mvc:annotation-driven/>
<!--视图解析器:ModelAndView-->
<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
public class HelloController {
@RequestMapping ("/hello") //就是之前的HandlerMapping实例,根据url查找controller
public String hello(Model model){
//封装数据
model.addAttribute("msg","hello,SpringMVCAnnotation");
return "hello"; //被视图解析器处理,拼接成视图名称
}
}
注解实现相较于接口实现
- springmvc配置文件中只需要创建视图解析器实例,而HandlerMapping和Adapter ,一个annotation-driven搞定。
- 而且接口实现的话,一个控制器只能写一个方法
- @Controller注解类里面的方法,如果返回值是String(默认是转发,重定向是:“redirect:viewName”),如果有具体页面可以跳转,那么就会被视图解析器解析。
- 类上面@RestController,则一个类的方法返回值都不会结果视图解析器,直接以json返回到前端
- 或者是在方法上写@ResponseBody,返回值就不会被视图解析器解析
4、Restful风格
资源定位及资源操作的风格。使设计的软件更简洁,更有层次,更易于实现缓存等机制。
通过不同的请求方式来实现不同的效果:GET、POST、DELETE、PUT
//传统风格:http://localhost:8888/springmvc_04_controller/add?a=1&b=2
//restful风格:http://localhost:8888/springmvc_04_controller/add/1/2
@RequestMapping(value = "/add/{a}/{b}",method = RequestMethod.GET)//限定方法,等价于@GetMapping
public String test(@PathVariable int a,@PathVariable int b, Model model){
int res = a+b;
model.addAttribute("msg",res);
return "test";
}
- @PathVariable绑定参数到URL的变量上
- 通过method属性约束请求类型,如果用户请求类型不一致,就会提示405错误
- @RequestMapping( method=xx) 等价于 @XxMapping
地址栏一样,可以通过不同的请求方法,执行不同的操作;地址栏隐藏了参数名,更加安全
5、前端参数传递
public String test1(@RequestParam("name") String name, Model model){
//1.接受前端参数
System.out.println(name);
//2.返回参数到前端
model.addAttribute("msg",name);
//3.跳转视图
return "test";
}
- @RequestParam 设置参数名
- 如果参数是一个对象,会将地址栏中的参数与对象的属性挨个匹配,自动生成对象
6、过滤器解决乱码
6.1、手动自定义过滤器
- 过滤器实现类:
public class EncodingFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
servletRequest.setCharacterEncoding("UTF-8");
servletResponse.setCharacterEncoding("UTF-8");
filterChain.doFilter(servletRequest,servletResponse);
}
public void destroy() {
}
}
- web.xml中配置过滤器的接受和映射
<filter>
<filter-name>encoding</filter-name>
<filter-class>com.ndkj.filter.EncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern><!--这里 /* 是为了接收jsp页面-->
</filter-mapping>
6.2、springmvc自动配置的乱码过滤器
直接在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><!--这里 /* 是为了接收jsp页面的请求 一般只用在过滤器上-->
</filter-mapping>
7、JSON(重点)
一种数据交换的文本格式,全称:JS对象标记
-
JSON字符串转化为JS对象:
var obj = JSON.parse('{"a":"hello","b":"world"}'); //结果是{a:'hello',b:'world'}
-
JS对象转为JSON字符串
var json = JSON.stringify({a:'hello',b:'world'}); //'{"a":"hello","b":"world"}'
7.1、Jackson
Jackson是JSON解析工具(还有阿里的fastjson)
-
导入依赖
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.10.0</version> </dependency>
2.编写Controller
@RequestMapping(value = "/j1")
@ResponseBody//不走视图解析器,直接返回一个字符串写入 HTTP 响应正文
public String json1() throws JsonProcessingException {
User user = new User("jason",18,"男");
//jackson
ObjectMapper mapper = new ObjectMapper();
String value = mapper.writeValueAsString(user);
return value;
}
json乱码问题:
<!--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中设置
@RequestMapping(value = "/j1",produces = "application/json;charset=utf-8")
用jackson以JSON返回当前时间:
@RequestMapping(value = "/j3")
@ResponseBody//不走视图解析器,直接返回一个字符串
public String json3() throws JsonProcessingException {
Date date = new Date();
//jackson
ObjectMapper mapper = new ObjectMapper();
//取消时间戳写法
mapper.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS,false);
//自定义日期写法
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
String value = mapper.writeValueAsString(date);
return value;
}
7.2、fastjson
-
导入依赖
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.60</version> </dependency>
-
fastjson有三个主要的类:
- JSONObject:json对象
- JSONArray:json对象数组
- JSON:JSONObject和JSONArray之间的转化
@RequestMapping(value = "/j4")
@ResponseBody//不走视图解析器,直接返回一个字符串
public String json4() throws JsonProcessingException {
User user1 = new User("jason",18,"男");
User user2 = new User("mike",19,"男");
User user3 = new User("jordan",20,"男");
List<User> list=new ArrayList<User>();
list.add(user1);
list.add(user2);
list.add(user3);
return JSON.toJSONString(list);
}
8、Ajax
异步无刷新请求
ajax核心:XMLHttpRequest对象(XHR)。
使用jQuery Ajax,其本质就是XHR,只是对它进行了封装,方便调用
$.post({
url:"${pageContext.request.contextPath}/a1",//请求地址
data:{"name":$("#name").val()}, //键值对,传入数据
success:function (data) { //callback(success或error):function,参数data是由后端提供(json)
alert(data);
}
});
-
后端只是返回了一个data,并没有控制视图的跳转
-
后端仅返回数据,主动权交给了前端
9、拦截器
- 只会拦截访问控制器的方法,而不会拦截静态资源(比过滤器更高效一点)
- 拦截器是AOP思想的具体实现
实现步骤:
-
新建一个类,实现HandlerInterceptor接口
public class MyInterceptor implements HandlerInterceptor { //return true:放行,执行下一个拦截;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("=============清理=============="); } }
- 只有preHandler有返回值,一般就是在这个方法实现拦截;
- 后面两个方法一般就是输出日志
-
在spring配置中配置拦截器
<!--配置拦截器--> <mvc:interceptors> <mvc:interceptor> <!--/admin/**:拦截这个请求下的所有请求,/admin/a1.....--> <mvc:mapping path="/**"/> <!--指定哪个类拦截--> <bean class="com.ndkj.config.MyInterceptor"/> </mvc:interceptor> </mvc:interceptors>
通过判断有无登陆记录拦截访问页面请求
public class LoginInterceptor implements HandlerInterceptor {
//拦截在index页面未登录就访问首页的情况
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
//index -> 登陆页面 直接放行
if(request.getRequestURI().contains("toLogin")){
return true;
}
//登陆 -> 首页默认登陆成功 直接放行
if(request.getRequestURI().contains("login")){
return true;
}
//在index有登录记录,放行
if (session.getAttribute("userLoginInfo")!=null){
return true;
}
//在index没有登陆想访问首页,被拦截直接跳转登录页面
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response);
return false;
}
}
10、上传和下载文件
10.1、上传文件
-
导入依赖
<dependencies> <!--文件上传--> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.3</version> </dependency> <!--servletapi导入高版本--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> </dependency> </dependencies>
-
前端form表单,enctype为multipart/form-data,是让文件以二进制流上传
<form action="${pageContext.request.contextPath}/upload" enctype="multipart/form-data" method="post"> <input type="file" name="file"/> <input type="submit" value="upload"/> </form>
-
springmvc为文件上传提供了直接支持,MultipleResolver实现
<!--文件上传配置--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!--必须和pageEncoding一致--> <property name="defaultEncoding" value="utf-8"/> <!--上传文件大小,单位是字节--> <property name="maxUploadSize" value="10485760"/> <property name="maxInMemorySize" value="40960"/> </bean>
-
服务器端处理文件
-
判断文件名是否为空、设置保存文件的路径
//获取文件名 String filename = file.getOriginalFilename(); //如果文件名为空,返回首页 if ("".equals(filename)) { return "redirect:/index.jsp"; } System.out.println("上传文件名:" + filename); //设置文件保存路径 当前绝对路径/upload (idea的out文件夹下) String path = request.getServletContext().getRealPath("/upload"); //如果没有该路径则创建一个 File realPath = new File(path); if (!realPath.exists()) { realPath.mkdir(); }
-
保存文件
-
第一种方法
InputStream inputStream = file.getInputStream();//输入流获取文件 OutputStream outputStream = new FileOutputStream(new File(realPath, filename));//输出流保存文件 //读取写出 int len = 0; byte[] buffer = new byte[1024]; while ((len = inputStream.read(buffer)) != -1){ outputStream.write(buffer,0,len); outputStream.flush(); } outputStream.close(); inputStream.close();
-
第二种方法
/* * 采用file.transferTo 保存上传的文件 * */ //通过CommonsMultipartFile的方法直接写文件 file.transferTo(new File(realPath+"/"+file.getOriginalFilename()));
-
-
10.2、下载文件
public String download(HttpServletResponse response,HttpServletRequest request) throws IOException {
//要下载的图片地址(这里是写成固定的)
String realPath = request.getServletContext().getRealPath("/upload");
String fileName = "wallpaper.png";
//1.设置response响应头
response.reset();//设置页面不缓存,清空buffer
response.setCharacterEncoding("UTF-8");
response.setContentType("multipart/form-data");//二进制流传输数据
response.setHeader("Content-Dispostion",
"attachment;fileName="+ URLEncoder.encode(fileName,"UTF-8"));
File file = new File(realPath,fileName);
//2.读取文件--输入流
InputStream inputStream = new FileInputStream(file);
//3.写出文件--输出流
OutputStream outputStream = response.getOutputStream();
byte[] buffer = new byte[1024];
int index=0;
while((index=inputStream.read(buffer))!=-1){
outputStream.write(buffer,0,index);
outputStream.flush();
}
//4.关闭流
outputStream.close();
inputStream.close();
return null;
}