spring05
回顾
spring的事务
编程式事务:事务管理器代码与业务层代码耦合在一起,进行控制,开发中一般不用
这里面涉及到3个接口,大家需要记住
PlatformTransactionManager 事务管理器平台(导入spring-orm.jar)
JDBC、mybatis使用DataSourceTransactionManager
hibernate使用HibernateTransactionManager
JPA使用JPATransactionManager
TransactionDefinition 事务定义信息
隔离级别
传播行为
REQUIRED:必须有一个事务
SUPPORTS:支持当前事务,有就用,没有就不用
超时时间
是否只读
TransactionStatus 事务的运行状态
声明式事务:
前提:配置事务管理器
通过配置的方式让事务控制和业务代码结合在一起,底层思想就是AOP(需要导入aspectweaver)
xml
使用xml做事务管理,一劳永逸
使用tx:advice声明规则
使用aop:config配置切面
注解配置
事务注解@Transactional(规则)
注解事务开启<tx:annotation-driver/>或者纯注解使用@EnableTransactionManagement
springMVC是web框架,封装servlet共有的行为(前端控制器),使开发者关注业务本身
springMVC快速入门
编写controller和jsp
springmvc配置文件中配置:
组件扫描
mvc注解支持
视图解析器(添加了前缀和后缀)
web.xml中配置前端控制器:DispacherServlet 路径为:/ 配置springmvc的路径
面试题:执行流程
springmvc常用注解
@Controller
@RequestMapping
可以作用在类上和方法上
value 访问路径
method 声明处理哪种方式请求
springmvc接收请求
接收请求参数
简单类型:参数名和方法形参名称一样
对象类型:参数名和对象属性名一样,在方法上声明javabean对象
数组类型:页面上name值一样,在方法上声明数组对象,也可以封装成逗号隔开的字符串
对象的集合属性:使用ognl表达式
中文乱码:
配置CharacterEncodingFilter,指定编码
类型转换:对于个别类型不满足需要的时候就需要转换,例如:日期 支持"/" 不支持"-"
方式1:@DateTimeFormat(pattern = "yyyy-MM-dd")
方式2:编写自定义转换器实现Converter接口,注册给springmvc
内容介绍
- 文件上传
- 请求相关的注解
- 获取servlet原生的API(request,response,session)
- 放行静态资源
- 处理响应
- ajax请求
- restful编程风格
- 之前: http://localhost/springmvc/user/delete?id=x
- 现在: http://localhost/springmvc/user/x
- 异常统一处理
一 文件上传
1 前提
springmvc文件上传,底层使用commons-fileupload.我们使用的时候就需要把fileupload的依赖导入到工程中
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
2 文件上传前端三要素
- 提供文件选择框:
- 表单提交方式必须为:method=“post”
- 表单的enctype属性必须为:multipart/form-data
3 springMVC框架完成文件上传
- 在springmvc.xml中配置文件解析器:CommonsMultipartResolver
- 注意文件解析器的id必须为:multipartResolver
- 在Controller中,在方法参数上中提供一个 MultipartFile 类型的参数 , 参数名字为 input标签name属性
- 在方法中调用MultipartFile 的api进行获取上传文件的内容(例如:文件名字,流)
<form method="post" action="${pageContext.request.contextPath}/upload/test1" enctype="multipart/form-data">
<fieldset>
<legend>a_文件上传</legend>
用户名:<input name="username"><br>
图片:<input type="file" name="photo"><br>
<input type="submit" value="提交">
</fieldset>
</form>
<!--
文件解析器
id必须为multipartResolver
-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--设置单次文件上传的总大小 单位:byte 例如:100k-->
<property name="maxUploadSize" value="102400"/>
<!--设置单个文件的大小 例如:50k-->
<property name="maxUploadSizePerFile" value="51200"/>
</bean>
@Controller
@RequestMapping("upload")
public class A_UploadController {
@RequestMapping("test1")
public String test1(MultipartFile photo,String username) throws IOException {
//获取用户名
System.out.println(username);
if (photo!=null) {
//获取文件名称
String filename = photo.getOriginalFilename();
//设置随机文件名称
filename = UUID.randomUUID().toString()+"_"+filename;
//设置保存目录
File dirPath = new File("e:/upload");
if (!dirPath.exists()) {
dirPath.mkdirs();
}
//保存文件
photo.transferTo(new File(dirPath,filename));
}
return "success";
}
}
线下阅读:https://blog.csdn.net/whc__/article/details/106168053
多文件上传:
<form method="post" action="${pageContext.request.contextPath}/upload/test2" enctype="multipart/form-data">
<fieldset>
<legend>a_文件上传</legend>
用户名:<input name="username"><br>
图片1:<input type="file" name="photo"><br>
图片2:<input type="file" name="photo"><br>
<input type="submit" value="提交">
</fieldset>
</form>
@RequestMapping("test2")
public String test1(MultipartFile photo[],String username) throws IOException {
//获取用户名
System.out.println(username);
if (photo!=null && photo.length>0) {
for (MultipartFile ph : photo) {
//获取文件名称
String filename = ph.getOriginalFilename();
//设置随机文件名称
filename = UUID.randomUUID().toString()+"_"+filename;
//设置保存目录
File dirPath = new File("e:/upload");
if (!dirPath.exists()) {
dirPath.mkdirs();
}
//保存文件
ph.transferTo(new File(dirPath,filename));
}
}
return "success";
}
二 请求相关注解
@RequestParam
- 作用:
- 可以给参数设置默认值;
- 可以重新映射(当页面上传递过来的参数和方法中参数名字不一致时)
- 可以将同名的参数封装list
@RequstHeader(了解)
- 作用:获取指定的请求头的值,赋值给方法的参数
@CookieValue(了解)
- 作用:获取指定cookie的值,赋值给方法的参数
<a href="${pageContext.request.contextPath}/requestAnno/test1?pageNum=4">b_请求相关的注解_RequestParam</a><br/>
<a href="${pageContext.request.contextPath}/requestAnno/test1?pageNum=4&pageSize=5">b_请求相关的注解_RequestParam</a><br/>
<a href="${pageContext.request.contextPath}/requestAnno/test2">b_请求相关的注解_RequestHeader_CookieValue</a><br/>
@Controller
@RequestMapping("requestAnno")
public class B_ReqeustAnnoController {
@RequestMapping("test1")
/*
使用defaultValue属性设置默认值的时候
使用value属性将页面上传递过来的参数绑定到方法中的参数上
*/
public String test1(@RequestParam("pageNum") int pageNumber, @RequestParam(defaultValue = "10") int pageSize){
System.out.println(pageNumber);
System.out.println(pageSize);
return "success";
}
/*
获取浏览器信息
获取session的id(名字为JSESSIONID的cookie中保存)
*/
@RequestMapping("test2")
public String test2(@RequestHeader("user-agent") String agent, @CookieValue("JSESSIONID") String sessionId){
System.out.println(agent);
System.out.println(sessionId);
return "success";
}
}
三 获得原生ServletAPI(重要)
获取request、response或session
方式1:在目标方法中添加参数即可
方式2:通过注入的方式
@Controller
@RequestMapping("api")
public class C_APIController {
//方式1:在方法中声明参数
@RequestMapping("test1")
public String test1(HttpServletRequest request, HttpServletResponse response, HttpSession session){
request.setAttribute("requestMsg","通过方法参数获取request");
session.setAttribute("sessionMsg","通过方法参数获取session");
System.out.println(request);
System.out.println(response);
System.out.println(session);
return "success";
}
//方法2:通过@Autowired注入
@Autowired
private HttpServletRequest request;
@Autowired
private HttpServletResponse response;//有点小bug,我们可以使用它作为占位符,不要轻易使用这种方式获取的response的api
@Autowired
private HttpSession session;
@RequestMapping("test2")
public String test2(){
request.setAttribute("requestMsg","通过注入方式获取request");
session.setAttribute("sessionMsg","通过注入方式获取session");
System.out.println(request);
System.out.println(response);
System.out.println(session);
return "success";
}
}
四 静态资源放行(重要)
当请求来的时候,servlet容器就会去web.xml中寻找对应路径处理的servlet,若找不到,就需要交给servlet容器中提供的两个servlet(JspServlet和DefaultServlet)来处理请求
- JspServlet:用来处理jsp页面
- DefaultServlet:用来处理别的serlvet都处理不了的请求,用来兜底儿的.例如:静态资源
现在我们使用springmvc框架,将前端控制器的路径设置为了"/",这时候就覆盖了默认servlet,访问静态资源的时候就报404了.
方式1:开启静态资源servlet支持
修改springmvc.xml
<!--开启tomcat默认静态资源映射 若访问的是静态资源就交给web服务器中默认servlet处理-->
<mvc:default-servlet-handler/>
方式2:通过springMVC框架自定义映射关系
修改springmvc.xml
<!--建立静态资源映射关系
可以使用通配符
mapping:请求路径
location:项目中资源的路径
-->
<mvc:resources mapping="/img/**" location="/img/"/>
<mvc:resources mapping="/css/**" location="/css/"/>
方式3:修改dispatcherservlet的匹配规则
例如:我们将DispatcherServlet的路径设置为:*.do
,这样的话springmvc就只处理以.do结尾的请求,默认的servlet就又好使了.我们只需要在请求路径后面加".do",其他的不用动
五 SpringMVC的响应
1 响应方式介绍
无非是转发或者重定向或者直接写回数据
springmvc通过方法返回值来说明响应回去的方式
- 返回String
- 默认就是转发
- 使用关键字说明转发还是重定向
- 返回void
- 使用原生的api进行操作
- 一般做文件下载用
- 返回ModelAndView(一般springmvc的底层使用)
2 String返回
默认方式
就是转发
若要想往request域中存放数据的话可以通过一下几种方式来操作
- 方式1:使用request的api进行操作
- 方式2:在方法上声明参数Map、ModelMap或者Model,通过他们往域中存放数据
- ModelMap或者Model都是map
@Controller
@RequestMapping("string")
public class D_StringController {
//默认方式:转发
//往request域中存放数据
@RequestMapping("test1")
public String test1(HttpServletRequest request, Map map, ModelMap modelMap, Model model){
request.setAttribute("requestKey","requestValue");
map.put("mapKey","mapValue");
modelMap.put("modelMapKey","modelMapValue");
model.addAttribute("modelKey","modelValue");
return "msg";
}
}
转发和重定向
通过关键字完成转发或者重定向
- forward:/转发的内部路径
- redirect:站外路径
- redirect:/站内的内部路径
- 不需要写项目应用路径
<a href="${pageContext.request.contextPath}/string/test1">D_string返回值_默认转发</a><br/>
<a href="${pageContext.request.contextPath}/string/test2">D_string返回值_forward转发</a><br/>
<a href="${pageContext.request.contextPath}/string/test3">D_string返回值_redirect重定向外部资源</a><br/>
<a href="${pageContext.request.contextPath}/string/test4">D_string返回值_redirect重定向内部资源</a><br/>
//forward进行转发
@RequestMapping("test2")
public String test2(Map map){
map.put("mapKey","forward转发过去");
return "forward:/WEB-INF/pages/msg.jsp";
}
//redirect重定向站外资源
@RequestMapping("test3")
public String test3(){
return "redirect:https://www.baidu.com/";
}
//redirect重定向站内资源
@RequestMapping("test4")
public String test4(){
return "redirect:/html/1.html";
}
3 void返回
需要使用原生的api进行转发,重定向或者打印数据
一般文件下载的时候使用
<a href="${pageContext.request.contextPath}/void/test1">e_void返回值_转发</a><br/>
<a href="${pageContext.request.contextPath}/void/test2">e_void返回值_重定向</a><br/>
//响应的返回值为void
@Controller
@RequestMapping("void")
public class E_VoidController {
//转发
@RequestMapping("test1")
public void test1(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setAttribute("requestKey","void方式");
request.getRequestDispatcher("/WEB-INF/pages/msg.jsp").forward(request,response);
}
//重定向
@RequestMapping("test2")
public void test2(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.sendRedirect(request.getContextPath()+"/html/1.html");
}
}
4 ModelAndView返回
方式一:在方法中创建
方式二:在方法的参数上声明
//返回值为ModelAndView
@Controller
@RequestMapping("mv")
public class F_MVController {
@RequestMapping("test1")
//方法中创建modelandview对象
public ModelAndView test1(){
ModelAndView mv = new ModelAndView();
//往域中放入数据
mv.addObject("modelKey","使用modelAndView返回");
//设置跳转的路径(和返回字符串的规则一样)
mv.setViewName("msg");
return mv;
}
@RequestMapping("test2")
//方法中创建modelandview对象
public ModelAndView test2(){
ModelAndView mv = new ModelAndView();
//往域中放入数据
mv.addObject("modelKey","使用modelAndView返回");
//设置跳转的路径(转发)
mv.setViewName("forward:/WEB-INF/pages/msg.jsp");
return mv;
}
@RequestMapping("test3")
//方法参数中声明modelandview对象
public ModelAndView test3(ModelAndView mv){
//设置跳转的路径(重定向)
mv.setViewName("redirect:/html/1.html");
return mv;
}
}
5 @SessionAttributes
作用在类上,从request域中将数据拷贝一份,往session域中存放数据.
@Controller
@RequestMapping("sessionAttr")
@SessionAttributes(types = {String.class},value = {"age"})//往session中存放哪些类型的数据或者哪些名字的数据
public class G_SessionAttrController {
@RequestMapping("test1")
public String test1(Map map){
map.put("username","tom");
map.put("age",18);
map.put("sex","男");
return "sessionAttr";
}
}
要想清除session域中的数据,需要通过SessionStatus对象的方法setComplete()清除.
大家还是使用原生的session进行操作吧~~
六 ajax
- 自行导入axios.js
axios.get(“路径?参数”).then(resp=>{})
axios.get(“路径”,{
params:{
key:value,
key:value
}
}).then(resp=>{})
axios.post(“路径?参数”,{
key:value,
key:value
}).then(resp=>{});
@Controller
@RequestMapping("ajax")
public class H_AjaxController {
@RequestMapping("test1")
public void test1(User user, HttpServletResponse response) throws IOException {
System.out.println(user);
response.setContentType("application/json;charset=utf-8");
response.getWriter().print("{\"success\":true,\"msg\":\"用户名可以使用\"}");
}
}
若要使用Springmvc中两个ajax相关的注解,需要把jackson依赖导入,否则就会报415的错误
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.9</version>
</dependency>
@RequestBody
将json类型的请求参数封装成指定的对象
@ResponseBody
将方法的返回值转成json字符串,写回浏览器.注意方法若返回的字符串,会原样输出到浏览器上
<script src="js/axios-0.18.0.js"></script>
<script>
document.querySelector("#btn0").onclick=function () {
/*
axios.get("ajax/test1?username=tom&age=18").then(resp=>{
console.log(resp.data);
console.log(resp.data.msg);
});*/
axios.get("${pageContext.request.contextPath}/ajax/test1",{
params:{
"username":"tom",
"age":18
}
}).then(resp=>{
console.log(resp.data);
console.log(resp.data.msg);
});
}
document.querySelector("#btn1").onclick=function () {
axios.post("${pageContext.request.contextPath}/ajax/test2",{
"username":"tom",
"age":18
}).then(resp=>{
console.log(resp.data);
console.log(resp.data.msg);
});
}
</script>
@RequestMapping("ajax")
@Controller
public class H_AjaxController {
@RequestMapping("test1")
public void test01(User user, HttpServletResponse response) throws IOException {
System.out.println(user);
response.setContentType("text/html;charset=utf-8");
response.getWriter().print("{\"success\":true,\"msg\":\"请求成功\"}");
}
/*
@RequestBody 可以将请求中的json数据直接封装成对象
@ResponseBody 可以将返回值转成json字符串响应回去
*/
@RequestMapping("test2")
@ResponseBody
public Map test02(@RequestBody User user, HttpServletResponse response) throws IOException {
System.out.println(user);
HashMap<String, Object> map = new HashMap<>();
map.put("success",true);
map.put("msg","请求成功");
/*
//之前的方式
String jsonStr = new ObjectMapper().writeValueAsString(map);
response.setContentType("text/html;charset=utf-8");
response.getWriter().print(jsonStr);
*/
return map;
}
七 RESTful
restful是一种软件架构的设计风格。主要用于客户端和服务器交互的软件架构。如果我们软件设计使用了此风格,咱们的架构层次更加分明,简洁、易于缓存…
通过请求路径+请求方式来简化路径编写
- 查询操作 发送get请求 /user/12
- 删除操作 发送delete请求 /user/12
- 保存操作 发送post请求 /user
- 更新操作 发送put请求 /user
浏览器提交的方式只能为二种:get、post
使用ajax或者请求调试工具发送不同的请求方式即可.
spring提供的注解:
- @PathVariable(“uid”) :获取路径上的变量(/user/{uid}),赋值给方法的参数.
- @GetMapping(“路径”):处理get请求的路径
- @PostMapping(“路径”):处理post请求的路径
- @DeleteMapping(“路径”)…
- @PutMapping(“路径”)…
- @RestController:一个顶俩 @Controller+@ResponseBody
<input type="button" value="查询id=1用户" id="btn_get">
<input type="button" value="删除id=1用户" id="btn_delete">
<input type="button" value="保存用户" id="btn_post">
<input type="button" value="更新用户" id="btn_put">
<script>
document.querySelector("#btn_get").onclick=function () {
axios.get("${pageContext.request.contextPath}/user/1").then(resp=>{
console.log(resp.data);
});
}
document.querySelector("#btn_delete").onclick=function () {
axios.delete("${pageContext.request.contextPath}/user/1").then(resp=>{
console.log(resp.data);
});
}
document.querySelector("#btn_post").onclick=function () {
axios.post("${pageContext.request.contextPath}/user",{
"username":"tomcat",
"age":20
}).then(resp=>{
console.log(resp.data);
});
}
document.querySelector("#btn_put").onclick=function () {
axios.put("${pageContext.request.contextPath}/user",{
"username":"tomcat",
"age":20
}).then(resp=>{
console.log(resp.data);
});
}
</script>
//@Controller
//@ResponseBody
@RestController //一个顶俩
public class I_RestUserController {
//@RequestMapping(value = "/user/{uid}",method = RequestMethod.GET)
@GetMapping("/user/{uid}")//等价于上面
//@ResponseBody
public Map findById(@PathVariable("uid") int id){
System.out.println("调用service查询数据库,获取指定的用户:"+id);
Map<String, Object> map = new HashMap<>();
map.put("success",true);
map.put("msg","返回id为"+id+"的用户");
return map;
}
//@RequestMapping(value = "/user/{uid}",method = RequestMethod.DELETE)
@DeleteMapping("/user/{uid}")
//@ResponseBody
public Map deleteById(@PathVariable("uid") int id){
System.out.println("调用service删除指定的用户:"+id);
Map<String, Object> map = new HashMap<>();
map.put("success",true);
map.put("msg","删除id为"+id+"的用户");
return map;
}
@PostMapping("/user")
//@ResponseBody
public Map save(@RequestBody User user){
System.out.println("调用service保存用户:"+user);
Map<String, Object> map = new HashMap<>();
map.put("success",true);
map.put("msg","已经保存用户");
return map;
}
@PutMapping("/user")
//@ResponseBody
public Map update(@RequestBody User user){
System.out.println("调用service更新用户:"+user);
Map<String, Object> map = new HashMap<>();
map.put("success",true);
map.put("msg","已更新用户");
return map;
}
}
八 异常统一处理
当用户访问的资源不存在的时候,默认会返回一个404页面.当服务器后台出错的时候,会给用户返回一个500页面.
用户不知道404和500代表的时候,并且这个页面太丑了.应该给用户一个友好的页面
- 可以使用web服务器中提供的统一错误友好页面
- 也可以使用springmvc提供的异常统一处理方式
1 web中的统一错误友好页面
只需要在web.xml中通过error-page标签来配置错误友好页面即可
<error-page>
<!--当出现404状态码的时候,服务器就会将请求转发到404的页面上去-->
<error-code>404</error-code>
<location>/WEB-INF/pages/error/404.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/WEB-INF/pages/error/500.jsp</location>
</error-page>
2 springmvc中的统一处理方式
以后无论那层出现异常,都要抛出去,最后有前端控制器找到处理异常解析器去解析异常.
只能处理内部异常
xml方式
步骤分析:
- 编写一个类,实现一个接口 HandlerExceptionResolver
- 重写里面的方法
- 在springmvc.xml中进行配置
public class MyExceptionhandler implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
ModelAndView mv = new ModelAndView();
//把错误信息放入model中
mv.addObject("msg",ex.getMessage());
//转发到error/mvc_500.jsp
mv.setViewName("error/mvc_500");
return mv;
}
}
<!--配置全局错误处理解析器-->
<bean class="cn.itcast.web.handler.MyExceptionhandler"/>
注解方式
步骤分析:
- 编写一个类,放在组件扫描能扫描到的包下,添加注解@ControllerAdvice
- 编写方法,返回值为ModelAndView,
- 在方法上添加注解@ExceptionHandler,通过value属性设置只处理那些异常;
@ControllerAdvice //控制器的增强类
public class MyAnnoExceptionHandler {
@ExceptionHandler(Exception.class) //处理全部异常
public ModelAndView handlerException(){
ModelAndView mv = new ModelAndView();
//转发到error/mvc_500.jsp
mv.setViewName("error/mvc_500");
return mv;
}
}