springMVC环境:基于spring环境;spring-web,spring-webMVC,commons-BeanUtils,commons-Collections
jackson组件3个jar.
简单了解spring与springMVC父子容器的概念:
initServletBean()方法中:
this.webApplicationContext = this.initWebApplicationContext();
//spring父容器对象
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
//去全局的ServletContext对象中获取对象
Object attr = ServletContext.getAttribute("WebApplication.ROOT");
//springmvc子容器对象:XmlWebApplicationContext
WebApplicationContext wac = this.createWebApplicationContext(rootContext);
//子容器持有了父容器
wac.setParent(rootContext);
//子容器持有了全局的ServletContext对象
wac.setServletContext(this.getServletContext());
//加载springmvc.xml,进行包扫描,创建容器对象。
wac.refresh();
前端控制器:在tomcat启东时,初始化springmvc子容器(N个bean对象),并持有了spring父容器。
写业务处理器(控制层):对于一个实体类的所有操作,都放在一个处理器类中。
处理器映射器:RequestMappingHandlerMapping
处理器适配器:RequestMappingHandlerAdapter
视图解析器:IntelResourceViewResolver
jackson消息转换器:MappingJackson2HttpMessageConverter
#springMVC
1.转发与重定向:
同步开发中,处理器方法之间的转发与重定向。
在返回的String以forward:或redirect:开头.
2.resturl:
同步开发中不能使用,前后端分离的项目中使用最多
rest是对url的写法,描述性状态转移,url中不含动词
url | RequestMethod | data | resturl | RequestMethod |
---|---|---|---|---|
/user/add | post | 请求体:{k:v,k,v} | /user | POST添加 |
/user/selectById?uid=1 | GET | /user/1 | GET查询 | |
/user/updateById | POST | 请求体:{k:v,k:v} | /user | PUT修改 |
/user/deleteById?uid=1 | GET | /user/1 | DELETE删除 |
注意点:
put和delete请求:如果请求体中的数据是key=value&key=value格式的话,收不到数据。可以通过配置FormContentFilter过滤器来接收数据。
3.异常处理:
把服务器端处理器方法内部产生的各种信息进行处理。
- 在web.xml中对不同的错误码进行错误页面配置。
<error-page>
<error-code>404</error-code>
<location>/404.html</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/500.html</location>
</error-page>
缺点:提示信息无法动态定义。只能够返回页面,在前端分离的情况下,不能使用。
- 局部异常处理:只对一个处理器类中的异常生效。
@ExceptionHandler(Exception.class)
public ResponseEntity doException(Exception e){
String message = e.getMessage();
Map<String,String> datas = new HashMap<>();
datas.put("msg",message);
datas.put("date",System.currentTimeMillis()+"");
return ResponseEntity.ok(datas);
}
缺点:范围太小,只对一个类有效
- 全局异常处理:对所有处理器类中的异常生效。
public class MyExceptionHandler implements HandlerExceptionResolver {
//适合于同步开发,当出现异常,转到一个自定义的异常页。
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
HandlerMethod hm = (HandlerMethod)o;
Method method = hm.getMethod();
String clzName = method.getDeclaringClass().getName();
String methodName = method.getName();
ModelAndView mv = new ModelAndView();
mv.setViewName("error");
mv.addObject("msg",e.getMessage());
mv.addObject("date",System.currentTimeMillis()+"");
return mv;
}
缺点:只能够同步开发中使用。异步开发中不支持。
- 全局统一异常处理:项目中使用,在局部异常处理的基础上结合着@ControllerAdvice注解一起使用
前端程序员调用后端接口,后端需要把执行结果返回前端(数字状态码,中文文字信息,数据)。
状态码枚举类;StatusBean;ResponseBean;自定义异常;定义全局的异常处理类;
4.静态资源处理:重要
同步开发,半分离异步开发。(前端资源和后端代码在一个项目中)
在web目录下的前端资源无法直接访问。
为什么jsp能访问,也是因为tomcat,tomcat中有默认的serlvet:default,jsp,当访问XXX.jsp进入JspServlet中处理。当我们访问XXX.html,XXX.css等非jsp后缀的时候,进入DispatcherServlet,前端控制器解析url,找处理器对象,找不到,则404.
- 解决方法1:在web.xml中配置defaultServlet
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.html</url-pattern>
<url-pattern>*.css</url-pattern>
<url-pattern>*.jpg</url-pattern>
<url-pattern>*.png</url-pattern>
<url-pattern>*.js</url-pattern>
</servlet-mapping>
- 解决方式2:springMVC中有标签自动配置defaultServlet,建议使用这种。
<!--所有的静态资源都走DefaultServlet, default-servlet-name="default"-->
<mvc:default-servlet-handler></mvc:default-servlet-handler>
- 解决方式3:springMVC中通过静态资源映射器,对静态资源进行处理。
<mvc:resources mapping="/static/**" location="/static/"></mvc:resources>
<mvc:resources mapping="/page/**" location="/page/"></mvc:resources>
5.文件上传:
springMVC的文件上传仍然基于apache common-fileupload。
- 添加fileupload;io两个文件上传的jar包;
- 在springmvc.xml中配置文件上传解析对象。
<!--文件上传解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"></property><!--上传中文文件名-->
<property name="maxUploadSize" value="50000000"></property><!--上传文件最大大小-->
<property name="maxInMemorySize" value="10000000"></property><!--临时文件的存储域-->
<property name="uploadTempDir" value="/upload/tmp"></property><!--临时文件存储目录-->
</bean>
- 写文件上传处理器
前端提交url:
@RequestParam只能用在key=value数据格式,只能用在简单类型上。
@RequestBody只能 用在{k:v}数据格式,只能用在自定义对象类型上,map上。
@PathValiable只能用来获取url内的变量。
前端提交的url | 传输数据 | 提交方式 | 后端获取数据 |
---|---|---|---|
/url?k=v&k1=v1 | 数据在url上?后 | get | 可以直接加形参,@RequetParam |
/url/v | 数据在url内 | get|delete | 后端使用@PathValiable来获取uri中的变量 |
/url | 数据在请求体,而且是json格式,无乱码 | post|put|delete | 后端使用@RequestBody来获取数据 |
/url | 数据在请求体,而且是k=v&k=v | post|put|delete | 可以直接加形参,@RequetParam |
项目中使用全局统一异常处理:
自定义状态码枚举,自定义异常(持有了一个枚举对象)
定义一个全局统一的异常处理类,使用@ControllerAdvice注解。对所有RequestMapping做增强处理。
定义响应体。
静态资源处理:前端未分离
mvc:default-servlet-handler静态资源由DefaultServlet处理。不在走DispathcerServlet
文件上传下载:
添加两个jar包:commons-fileupload,commons-io
配置文件解析器:CommonsMultipartResolver,该bean对象的id必须是multipartResolver
在文件上传处理器中,表单参数类型写成MultipartFile对象即可。
6.拦截器:interceptor
类似于过滤器filter,拦截器是框架中的概念。
登陆拦截器是个项目中都会使用。权限拦截器
- 编写拦截器类
public class LoginInterceptor implements HandlerInterceptor {
//前拦截
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
Object login_user = session.getAttribute("LOGIN_USER");
if (login_user != null) {
return true;
}
throw new MvcException(StatusEnum.NO_LOGIN);
}
}
- 配置拦截器
<mvc:interceptors>
<mvc:interceptor>
<!--拦截路径-->
<mvc:mapping path="/**"/>
<!--忽略路径-->
<mvc:exclude-mapping path="/u/login"></mvc:exclude-mapping>
<!--拦截器类-->
<bean class="com.javasm.common.interceptor.LoginInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
7.跨域处理:
发起的请求与当前页面所在服务, 协议,ip,端口三者只要有一个不同,违反了浏览器的同源策略,造成跨域问题。
需要在服务器端配置响应头,告诉浏览器服务端允许这个客户端的请求:
响应头需要配置如下信息:
允许的域:http://127.0.0.1:8848
允许的请求头:
允许的请求方法:GET/POST/PUT/DELETE/OPTIONS预检.
是否允许请求携带cookie信息:
可以暴露给客户端的响应头:
跨域产生的问题:
问题1:请求不能正常响应。因为浏览器的同源保护(springmvc.xml中配置全局跨域)
解决方法:配置springMVC全局跨域设置mvc:cors
问题2:httpSession保存用户会话信息:每次请求服务器都会创建新的HttpSession对象,无法做到多次请求之间共享session中数据。
解决方法:前端axios请求配置withCredentials=true,表示请求时携带cookie
服务端跨越配置中允许接收客户端cookie,allow-credentials=“true”
如果使用chrome发现配置后仍然不行,开发中暂时先chrome://flags/,把samesite禁用了。
仍然存在的问题:
tomcat分布式部署的时候,多服务器之间的session共享问题(通过spring-session,结合redis使用)。
chrome浏览器的samesite同站点默认配置lax,不保存第三方的cookie问题。
解决方法2:JWT组件,token工具包
注意点:
在跨域的情况下,浏览器对POST,PUT,DELETE三种请求,会先向服务器发送预检请求,(检查服务端是否支持客户端的本次请求).
8.aop事务切面
定义出事务管理器对象。
定义事务切面,切入点表达式使用注解方式。
总结:
拦截器在项目中登录拦截,权限拦截。
从HandlerInterceptor接口派生,重写preHandle前拦截。(在拦截方法中一定把OPTIONS请求放行,避免预检请求被拦着,造成跨域设置失败)
配置拦截器
跨域:
服务端:springMVC全局跨域配置或者配置跨域过滤器:
<mvc:mapping path="/**" allowed-origins=“http://127.0.0.1:8848” allowed-methods="" allowed-headers="" allow-credentials=“true”/>
登录用户的状态信息维护:
服务器端HttpSession保存用户登录信息:
客户端:axios配置携带cookie:axios.defaults.withCredentials = true;
服务端:allow-credentials=“true”
如果chrome浏览器不保存第三方的cookie,禁止chrome的samesite=lax。