常见的注解:
- @RequestMapping注解
- @RequestMapping(value="item")或者@RequestMapping("/item"),value的值可以是数组,可以将多个的url映射到同一个方法
- 在class上添加@RequestMapping(url)指定通用请求前缀,来限制该类下的所有方法请求url必须以请求前缀开头
- 还可以限定请求的方法。@RequestMapping(method=RequestMethod.GET)相当于@GetMapping,@RequestMapping(method=RequestMethod.POST)相当于@PostMapping
- @RequestMapping(produces="text/html;charset=utf-8"),前台向后台发起一个ajax请求时,可以用这个来指定编码,不会导致返回到前台的数据出现中文乱码
controller层方法返回类型:
- ModelAndView:无敌的 带数据 返回视图路径
@RequestMapping(value = "/item/itemlist.action")
public ModelAndView itemList(){
List<Product> productList = ProductService.selectProductList();
ModelAndView mav = new ModelAndView();
mav.addObject("productList",productList);
mav.setViewName("productManager");
return mav;
}
- String:返回视图路径 model带数据 官方推荐 数据视图解耦
@RequestMapping(value = "/item/itemlist.action")
public String itemList(Model model) throws MessageException{
List<Product> productList = ProductService.selectProductList();
model.addAttribute("productList",productList); //带数据
return "productManager"; //返回路径
}
- 注意:当String类型打上@ResponseBody注解时,就不是返回路径了,而是返回json字符串,@ResponseBody注解相当于Servlet时代的response.getWriter().write(json),将包装好的json字符串返回到前台
@RequestMapping(value = "/item/toEdit.action",produces="text/html;charset=utf-8",method = RequestMethod.POST)
@ResponseBody
public String toEdit(String id,HttpServletRequest request,HttpServletResponse response,HttpSession session,Model model){
//查询一个商品
Product product = ProductService.selectProductById(id);
JSONObject jsonObject = JSONObject.fromObject(product);
return jsonObject.toString();
}
- void:适合不用更换界面和返回json字符串的ajax请求
数据绑定:数据绑定的原则是前台传到后台的数据名要跟方法参数名一样才能绑定得上
- 简单数据类型绑定(以绑定String类型为例):
——————————后台代码(参数为id)——————————————
@RequestMapping(value = "/item/toEdit.action",produces="text/html;charset=utf-8",method = RequestMethod.POST)
@ResponseBody
public String toEdit(String id){
Product product = ProductService.selectProductById(id);
JSONObject jsonObject = JSONObject.fromObject(product);
//System.out.println(jsonObject.toString());
return jsonObject.toString();
}
——————————前台代码(传到后为id)——————————————
$.post(
"${pageContext.request.contextPath}/item/toEdit.action",
{"id":id},
function(data){
alert(data)
},
"json"
)
- 数组类型的绑定:
——————————后台代码——————————————
//删除多个
@RequestMapping(value="/deletes.action")
public ModelAndView deletes(String[] ids){
//删除
ModelAndView mav = new ModelAndView();
mav.setViewName("productManager");
return mav;
}
——————————前台代码——————————————
<form action="${pageContext.request.contextPath}/deletes.action" method="post">
<tr class="text-danger">
<th class="text-center"><input type="checkbox" name="ids" value=""></th>
<th class="text-center">编号</th>
<th class="text-center">商品</th>
<th class="text-center">价格</th>
<th class="text-center">产品类型</th>
<th class="text-center">操作</th>
</tr>
<c:forEach var="item" items="${productList}" varStatus="s">
<tr>
<td><input type="checkbox" name="ids" value="${item.id}"></td>
<td><input type="text" name="id" value="${item.id}"></td>
<td><input type="text" name="name" value="${item.name}"></td>
<td><input type="text" value="${item.price}"></td>
<td><input type="text" value="${item.cid}"></td>
<td class="text-center">
</c:forEach>
<input type="submit" value="删除所选" >
</form>
- pojo类型的绑定:
——————————后台代码(参数为product)——————————————
//提交修改页面入参 为Product对象
@RequestMapping(value = "/updateProduct.action")
public String updateProduct(Product product) throws IllegalStateException, IOException{
ProductService.updateProductById(product);
return "forward:/item/itemlist.action";
}
——————————前台代码(每个input的name都要和product的属性对应)——————————————
<form action="${pageContext.request.contextPath}/updateProduct.action" class="form-horizontal" method="post" >
<input type="text" class="form-control" id="pro-num" name="id" readonly>
<input type="text" class="form-control" id="pro-name" value="" name="name">
<input type="text" class="form-control" id="pro-price" value="" name="price">
<button class="btn btn-primary updatePro">修改</button>
</form>
- 绑定包装的pojo
——————————包装的pojo——————————————
public class QueryVo {
//商品
private Product product;
private Person person;
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
}
——————————后台代码——————————————
@RequestMapping(value="/updates.action")
public String updates(QueryVo vo){
return "redirect:/item/itemlist.action";
}
——————————前台代码(指定包装pojo属性product,再指定product的属性)——————————————
<form action="${pageContext.request.contextPath}/updateProduct.action" class="form-horizontal" method="post" >
<input type="text" class="form-control" id="pro-num" name="product.id" readonly>
<input type="text" class="form-control" id="pro-name" value="" name="product.name">
<input type="text" class="form-control" id="pro-price" value="" name="product.price">
<button class="btn btn-primary updatePro">修改</button>
</form>
- 绑定List类型(List类型无法作为参数直接绑定,比如将其放入包装类中):
——————————包装的pojo——————————————
public class QueryVo {
//商品
private Product product;
private List<Product> productList;
public List<Product> getProductList() {
return productList;
}
public void setProductList(List<Product> productList) {
this.productList = productList;
}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
}
——————————后台代码——————————————
//修改多个
@RequestMapping(value="/updates.action")
public String updates(QueryVo vo){
return "redirect:/item/itemlist.action";
}
——————————前台代码——————————————
<form action="${pageContext.request.contextPath}/updates.action" method="post">
<c:forEach var="item" items="${productList}" varStatus="s">
<tr>
<td><input type="checkbox" name="ids" value="${item.id}"></td>
<td><input type="text" name="productList[${s.index}].id" value="${item.id}"></td>
</tr>
</c:forEach>
<input type="submit" value="修改全部">
Restful风格开发:
//RestFul风格的开发
@RequestMapping(value = "/item/{id}.action")
public void toEdit1(@PathVariable String id) throws IOException{
Product product = ProductService.selectProductById(id);
System.out.println(product.toString());
}
通过@pathVariable将url中的参数绑定到形参,这种方式的开发在今后是比较常用的(有前后分离的雏形)
异常处理器(处理未知异常):
背景:Servlet时代,我们在Dao处理SQL语句的时候,会出现一个SQLException,这是个编译异常,我们必须手动捕获或者抛出,否则编译不通过。这个时候我们可以抛到service层,也可以在dao层进行捕获处理,service也同样可以选择处理或者抛出,但不管如何选择,最终都要在一个层进行捕获处理,否则最终抛到虚拟机便会出错。这就导致了我们程序要写一堆跟业务逻辑无关的try catch语句,代码冗余十分严重。Mybatis的出现将Dao的所有编译异常全部处理成运行异常,不在需要捕获处理和手动抛出,运行异常出现时,他会自动向上层抛出,我们只需要在SpringMVC定义一个异常处理器来集中处理即可。(AOP思想)。
过程:dao层抛异常到service层,service层抛异常到controller层,controller层抛异常到前端控制器,前端控制器调用异常处理器进行处理
代码实现:
- 自定义已知异常
//自定义异常
public class MessageException extends Exception{
private String msg;
public MessageException(String msg) {
super();
this.msg = msg;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
- 定义异常处理器
public class ExceptionResolver implements HandlerExceptionResolver{
public ModelAndView resolveException(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2,
Exception arg3) {
ModelAndView mav = new ModelAndView();
//判断异常为什么类型
if (arg3 instanceof MessageException) {
//预期异常
MessageException me = (MessageException)arg3;
mav.addObject("error",me.getMsg());
}else {
mav.addObject("error", "未知异常啊啊啊啊");
}
mav.setViewName("error");
return mav;
}
}
- 在springmvc上下文映射文件(springmvc.xml)进行相关配置
<!-- Springmvc的异常处理器 -->
<!-- class定位到自定义类的位置(包名+类名) -->
<bean class="exception.ExceptionResolver"></bean>
拦截器:
- 自定义拦截器
public class Interceptor1 implements HandlerInterceptor{
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
// TODO Auto-generated method stub
System.out.println("页面渲染后");
}
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
throws Exception {
// TODO Auto-generated method stub
System.out.println("方法后");
}
public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
// TODO Auto-generated method stub
//判断用户是否登录 如果没有登录 重定向到登录页面 不放行 如果登录了 就放行
//URL http://localhost:8080/..........
//URI /login.action
System.out.println("方法前");
String requestURI = arg0.getRequestURI();
if (!requestURI.contains("/login")) {
String username = (String) arg0.getSession().getAttribute("USER_SESSION");
if (null==username) {
arg1.sendRedirect(arg0.getContextPath()+"/login.action");
return false;
}
}
return true;
}
}
- 在springmvc上下文映射文件(springmvc.xml)进行相关配置
<!-- 拦截器 -->
<mvc:interceptors>
<!-- 多个拦截器 -->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<!-- 自定义的拦截器类 -->
<!-- 包名+类名 -->
<bean class="interceptor.Interceptor1"></bean>
</mvc:interceptor>
</mvc:interceptors>
- 定义多个拦截器时,方法的执行顺序
- preHandle:按拦截器定义的顺序调用(先定义的先执行)
- postHandler:按拦截器定义逆序调用
- afterCompletion:按拦截器定义逆序调用
- postHandler:拦截器链内所有拦截器都返回成功才调用
- afterCompletion:只有preHandle返回true才调用
转换器(交由处理适配器):处理适配器在处理数据逻辑时,运用转换器就数据进行转换处理
- 自定义转换器
/**
* 转换日期类型的数据
* S:页面传递过来的类型
* T:转换后的类型
* @author 曾沛
*
*/
public class Conveter implements Converter<String, Date>{
public Date convert(String source) {
// TODO Auto-generated method stub
try {
if (source !=null) {
DateFormat df = new SimpleDateFormat("yyyy/MM/dd HH/mm/ss");
return (Date) df.parse(source);
}
} catch (Exception e) {
// TODO: handle exception
}
return null;
}
}
- 在springmvc上下文映射文件(springmvc.xml)进行相关配置
<!-- 将转换器交给处理适配器 -->
<mvc:annotation-driven conversion-service="conversionServiceFactoryBean"></mvc:annotation-driven>
<bean id="conversionServiceFactoryBean" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<list>
<!-- 包名+类名 可以配置多个转换器 -->
<bean class="conversion.Conveter"></bean>
</list>
</property>
</bean>
文件上传:
- 先配置虚拟服务器(根据自己的编译工具配置,这里不做详细讲解)
- 代码分析:
——————————后台代码——————————————
/*
* 上传文件时,前台的表单提交必须设置为enctype="multipart/form-data",否则提交不了
* 但是一旦设置了enctype="multipart/form-data",那么参数用pojo进行绑定就会失效
* 必须用包装的pojo才能进行文件上传的同时绑定参数
*/
@RequestMapping(value = "/updateProduct.action")
public String updateProduct(QueryVo vo , MultipartFile image) throws IllegalStateException, IOException{
//保存图片到D盘
String name = UUID.randomUUID().toString().replace("-", "");
//后缀名
String extension = FilenameUtils.getExtension(image.getOriginalFilename());
image.transferTo(new File("D:\\upload\\"+name+"."+extension));
//组合到指定路径
vo.getProduct().setImage(name+"."+extension);
//修改
ProductService.updateProductById(vo.getProduct());
return "forward:/item/itemlist.action";
}
——————————前台代码——————————————
<form action="${pageContext.request.contextPath}/updateProduct.action" method="post" enctype="multipart/form-data">
<input type="text" id="pro-num" name="product.id" readonly>
<input type="text" id="pro-name" value="" name="product.name">
<input type="text" id="pro-price" value="" name="product.price">
<input type="file" id="pro-image" name="image" value="">
<button class="btn btn-primary updatePro">修改</button>
</form>
——————————springmvc上下文配置文件(springmvc.xml)——————————————
<!-- 上传图片配置实现类 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 上传图片的大小 -->
<property name="maxUploadSize" value="5000000"></property>
</bean>
文件下载:
@RequestMapping("/dl.action")
public ResponseEntity<byte[]> download(){
//获取文件对象
try {
//服务器地址
byte[] bytes = FileUtils.readFileToByteArray(new File("D:\\upload\\1.png"));
HttpHeaders headers=new HttpHeaders();
//下载后的文件名称
headers.set("Content-Disposition","attachment;filename=zp.png");
ResponseEntity<byte[]> entity=new ResponseEntity<byte[]>(bytes,headers,HttpStatus.OK);
return entity;
} catch (IOException e) {
System.out.println("下载失败");
return null;
}