SpringMVC 学习2

1 课程计划

  1. 高级参数绑定
    1. 数组类型的参数绑定
    2. List类型的绑定
  2. @RequestMapping注解的使用
  3. Controller方法返回值
  4. Springmvc中异常处理机制
  5. 图片上传处理
  6. Josn数据交互
  7. Springmvc实现restful
  8. 拦截器

2 高级参数绑定

2.1 数组的绑定

2.1.1 需求分析

根据选中的商品ID进行删除

2.1.2 页面的修改

<input type="checkbox" name="ids" value="${user.id}" >

2.1.3 controller的编写

@RequestMapping("/batchDeleteSave")

public String batchDeleteSave(Long[] ids,UserVo vo) {

System.out.println("---");

 

return "success";

}

2.1.4 userMpper.xml的编写

  <!-- 批量删除用户 batchDeleteUser -->

  <delete id="batchDeleteUser" parameterType="Long[]" >

delete from user

<where>

<foreach collection="array" item="id" open="and id in(" close=")" separator="," >

#{id}

</foreach>

</where>

  </delete>

2.2 将表单的数据绑定到List(了解)

2.2.1 需求分析

把数据绑定到List集合

2.2.2 修改Pojo

private List<User> users;

 

public List<User> getUsers() {

return users;

}

 

public void setUsers(List<User> users) {

this.users = users;

}

2.2.3 修改jsp页面

<tr>

<td>用户名称</td>

<td><input type="text" name="users[0].username" value="${user.username }" /></td>

</tr>

<tr>

<td>生日</td>

<td><input type="text" name="users[0].birthday" value="<fmt:formatDate value="${user.birthday}" pattern="yyyy-MM-dd" />" /></td>

</tr>

<tr>

<td>性别</td>

<td>

<select name="users[0].sex" >

<option value="1"  <c:if test="${user.sex=='1'}">selected="selected"</c:if> ></option>

<option value="2" <c:if test="${user.sex=='2'}">selected="selected"</c:if> ></option>

</select>

</td>

</tr>

2.2.4 controller编写

@RequestMapping(value="/addUser",method=RequestMethod.POST)

public String addUserVo(UserVo vo) {

System.out.println("....");

return "addUser";

}

3 RequestMapping

作用:通过requestMapping注解可以定义不同的处理器映射规则。

3.1 窄化请求映射

可以在class上面加上RequestMapping(url)指定通用的请求前缀。

@Controller

@RequestMapping("/user")

public class UserController {

3.2 URL路径映射和请求方法限制

@RequestMapping(value={"/ceshi1","/ceshi2"},method=RequestMethod.POST)

public String ceshi(UserVo vo) {

System.out.println("....");

return "addUser";

}

4 controller 方法返回值

4.1 返回ModelAndView或者Model

Controller方法中定义ModelAndView对象并返回,对象中可添加model数据,指定view视图。

//model可以配合返回值进行逻辑视图的跳转。

model.addAttribute("name","张三"); //return "url"

 

 

//modelAndView

modelAndView.setViewName("url");

modelAndView.addObject("name","李四");

4.2 返回void

在controller方法形参上可以定义request和reresponse,使用requset和response指定响应结果。

//转发

request.getRequestDispatcher("/WEB-INF/adduser.jsp").forward(request, response);

 

//重定向

response.sendRedirect("/user/addUser.do");

 

//返回json格式的数据

response.setCharacterEncoding("utf-8");

response.setContentType("application/json;charset=utf-8"); //设置响应正文的编码格式

//

response.setContentType("text/json;charset=utf-8")

response.getWriter().println("/{/"aa"/:/"bb"/}/"); jackson fastJSON

 

Ajax接受服务端响应的json字符串,{“username”:”张三”,”password”:”123”}; [object][object]

var data =xmlHttpRequest.ResponseText;

alert(data);  {“username”:”张三”,”password”:”123”}

怎么把JSON字符串编程对象? Var data = eval(“(“+data+”)”)

data.username

客户端使用的是原生ajax xmlHttpRequest

var data = xmlHttpRequest.ResponseXml

客户端可以接受的响应数据格式

  1. Json
  2. xml
  3. HTML片段

4.2.1 返回逻辑视图名

Controller 方法返回字符串可以指定逻辑视图名,通过视图解析器解析为物理视图地址。

return "url";

4.2.2 重定向

Contrller方法返回结果重定向到另外一个url地址。

return "redirect:/user/getUserList.do";

4.2.3 转发(用得比较少)

return "forward:/user/getUserList.do";

注意:逻辑试图返回的字符串默认就是转发

5 异常处理器

Springmvc在处理请求过程中出现异常信息通常交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑。

5.1 异常处理思路

系统中异常包括两类。预期异常和运行时异常RuntimeException,前面的通过捕获异常从而获取异常信息,后面主要通过代码规范开发,测试减少运行时异常的发生。

异常处理思路图:

5.2 自定义异常类

为了区别不同的异常通常会根据异常类型自定义异常类,这里我们创建一个自定义系统异常,如果controller service dao抛出异常说明是系统预期处理的异常信息。

public CustomException() {

}

 

public CustomException(String message) {

this.message = message;

}

 

//异常信息

private String message;

 

public String getMessage() {

return message;

}

 

public void setMessage(String message) {

this.message = message;

}

5.3 自定义异常处理器

要实现全局异常处理,就要实现HandlerExceptionResolver接口。

String message = null;

 

//1.判断抛出的异常是否是系统自定义异常

if(ex instanceof CustomException) {

CustomException c = (CustomException)ex;

message = c.getMessage();

}else {

//2.如果不是系统自定义的异常

//3.打印异常信息

ex.printStackTrace();

 

//把异常信息输出

StringWriter sw = new StringWriter();

PrintWriter pw = new PrintWriter(sw);

ex.printStackTrace(pw);

//写日志 log4j logback

message = sw.toString();

}

 

ModelAndView modelAndView = new ModelAndView();

modelAndView.addObject("message", message);

modelAndView.setViewName("error");

return modelAndView;

5.4 错误页面

<body>

<!-- 错误页面 -->

<h6>错误页面:</h6>

<h1>${message}</h1>

</body>

5.5 异常处理器配置

<!-- 配置全局异常处理器 -->

<bean class="com.whhp.springmvc.exception.CloabException" />

5.6 异常测试

public String ceshi5() throws CustomException {

//1. 测试自定义异常

// if(true) {

// throw new CustomException("系统错误,请等待!");

// }

//2. 非自定义异常

int i = 1/0;

return "success";

}

6 图片上传

图片上传的三要素:

  1. 必须要有文件上传项
  2. 表单的请求方式必须是POST
  3. Form表单必须有一个属性 enctype=”multipart/form-data”

6.1 编写jsp页面

<form id="itemForm" action="/springmvc-02/addUser.do" method="post" enctype="multipart/form-data" >

<input type="hidden" name="id" value="${user.id }" /> 添加用户信息:

<table width="100%" border=1>

<tr>

<td>用户名称</td>

<td><input type="text" name="users[0].username" value="${user.username }" /></td>

</tr>

<tr>

<td>生日</td>

<td><input type="text" name="users[0].birthday" value="<fmt:formatDate value="${user.birthday}" pattern="yyyy-MM-dd" />" /></td>

</tr>

<tr>

<td>性别</td>

<td>

<select name="users[0].sex" >

<option value="1"  <c:if test="${user.sex=='1'}">selected="selected"</c:if> ></option>

<option value="2" <c:if test="${user.sex=='2'}">selected="selected"</c:if> ></option>

</select>

</td>

</tr>

<tr>

<td>上传图像</td>

<td><input type="file" name="picfile"/></td>

</tr>

<tr>

<td colspan="2" align="center"><input type="submit" value="提交" />

</td>

</tr>

</table>

</form>

6.2 导入jar包

 

6.3 配置解析器

因为springmvc默认不支持图片上传,所以需要配置解析器。

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">

      <!—文件最大不能超过5兆 -->

      <property name="maxUploadSize" value="5242880" />

</bean>

6.4 编写后台业务逻辑代码

1. 在class上面需要配置@MultiparConfig

2. MultipartFile的名称必须和表单的name保持一致

3. 创建图片保存的位置

代码的编写:

@RequestMapping(value="/addUser",method=RequestMethod.POST)

public String addUserVo(MultipartFile picfile,HttpServletRequest request) throws IllegalStateException, IOException {

//1. 获得图片的名称

String filename = picfile.getOriginalFilename();

 

//2. 获得图片保存在服务器的路径

String realPath = request.getServletContext().getRealPath("/imgs");

 

//3. 把图片上传到服务器

picfile.transferTo(new File(realPath+"/"+filename));

 

return "addUser";

}

7 json数据交互

response.setContentTyep(‘’text/xml;charset=utf-8);

7.1 RequestBody

作用:

requestBody注解用于将json格式的请求参数绑定controller的方法的参数上面。

请求参数,{“username”:”张三”,”address”:”武汉宏鹏”} 可以通过requestBody标签使用对象来接收数据。底层是通过springmvc的HttpMessageConverter接口将读取到的内容转换成json,xml绑定到方法的参数上。

7.2 ResponseBody

作用:

可以将响应的数据转换成为json响应给客户端。

7.3 请求和响应Json的案例

7.3.1 页面的编写

<button onclick="sendJsonTest();" >提交Json数据</button>

<script type="text/javascript">

function sendJsonTest() {

$.ajax({

type:"post",

url:"${pageContext.request.contextPath}/user/ceshi7.do",

contentType:"application/json;charset=utf-8",

success:function(data) {

alert(data);

}

});

}

 

</script>

评价:这里应该是掉了一个data

7.3.2 controller的编写

@RequestMapping("/ceshi7.do")

@ResponseBody

public User ceshi7(@RequestBody User user) {

System.out.println(user.toString());

return user;

}

8 RESTful支持

8.1 什么是restful

一种软件架构风格,设计风格而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制

资源定位:互联网所有的事物都是资源,要求URL中没有动词,只有名次,没有参数。

访问查询用户 http://localost/springmvc02/user/selectUserById.do?id=1

Restful软件架构风格:http://localhost/springmvc02/user/selectUserById/1

资源操作:使用put delete post get使用不同方法对资源进行操作。分别对应添加,删除,修改,查询。

8.2 使用restful风格实现通过ID查询

8.2.1 修改servlet-mapping

<servlet-mapping>

<servlet-name>springmvc</servlet-name>

<!-- 拦截任何资源,但是不包扣jsp -->

<url-pattern>/</url-pattern>

</servlet-mapping>

8.2.2 controller的编写

/**

 * 使用restful风格实现通过ID查询用户

 */

@RequestMapping("/selectUserById/{id}")

public void selectUserById(@PathVariable("id") String id) {

//去数据库查询就可以了

System.out.println(id);

}

评价:这个@PathVariable 主要就是做一个参数的映射

8.2.3 静态资源访问

<!-- 静态资源访问 -->

<mvc:resources location="/js/" mapping="/js/**"/>

评价:由于上面拦截了所有的资源,所有这里报出了未定义$的错误,这里需要再加载静态资源

9 拦截器

springWebMvc的处理器拦截器类似于过滤器Filter,用于对处理器进行预处理和后处理。

9.1 自定义拦截器

需要实现HanderInterceptor接口

/**

 * handler执行前调用此方法

 * 如果返回true表示继续执行,返回false停止执行

 * 实际开发中可以加入登录校验,权限拦截等。。。

 */

@Override

public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {

System.out.println("CustomInterceptor1 preHandler");

return false;

}

 

/**

 * handler执行后但未返回视图前调用此方法

 * 这里可以在返回视图前对模型数据进行处理,比如可以加入通用的信息进行页面的显示

 */

@Override

public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView modelAndView)

throws Exception {

System.out.println("CustomInterceptor1 postHandle");

}

 

/**

 * hadnler执行后且视图返回后调用次方法

 * 这里记录记录操作日志,资源清理,还可以得到异常信息。

 */

@Override

public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)

throws Exception {

System.out.println("CustomInterceptor1 afterCompletion");

}

import com.github.pagehelper.StringUtil;
import com.urovo.config.Constants;
import com.urovo.exception.TokenException;
import com.urovo.model.TokenModel;
import com.urovo.util.i18n.I18nMessage;
import com.urovo.util.token.TokenUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@Slf4j
public class AccessTokenInterceptor extends HandlerInterceptorAdapter {
	
    @Autowired
    private I18nMessage i18nMessage;
	
    @Autowired
    private TokenUtils tokenUtils;
	
    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //token校验
        if ((((HandlerMethod) handler).getBeanType().getName().equals("com.urovo.controller.LoginController"))) {
            String ac_token = request.getHeader(Constants.AUTH_CODE_token);
            Object o = redisTemplate.boundValueOps(Constants.AUTH_CODE + ac_token).get();
            if (o != null) {
                String code = (String) o;
                request.setAttribute(Constants.CURRENT_REQUEST_KAPTCHA, code);
            }
            return true;
        }
        //从请求参数和请求头中取出uid,token
        String uid = request.getHeader(Constants.UID);
        String token = request.getHeader(Constants.AUTHORIZATION);
        if (StringUtil.isEmpty(token) || StringUtil.isEmpty(uid)) {
            throw new TokenException(i18nMessage.getMessage("token.uid.none"));
        }
        //redis token 校验
        TokenModel tm = new TokenModel(uid, token);
        if (tokenUtils.checkToken(tm)) {
            request.setAttribute(Constants.CURRENT_USER_ID, tm.getUserId());
            request.setAttribute(Constants.CURRENT_USER_TOKEN, tm.getToken());
            //从缓存获取公司和公司子节点编号数据
            String company_node = (String)redisTemplate.boundValueOps(Constants.USER_COMPANY_NODE+tm.getUserId()).get();
            String[] split = company_node.split(":");
            request.setAttribute(Constants.CURRENT_USER_COMPANY,split[0]);
            if ( split.length > 1 ) {
                if ( "null".equalsIgnoreCase(split[1])) {
                    split[1] = null;
                }
                request.setAttribute(Constants.CURRENT_USER_COMPANY_NODE,split[1]);
            }
            return true;
        } else {
            throw new TokenException(i18nMessage.getMessage("token.invalid"));
        }
    }
}

9.2 多重拦截器

多重拦截器的执行顺序:

CustomInterceptor1 preHandler

CustomInterceptor2 preHandler

123

CustomInterceptor2 postHandle

CustomInterceptor1 postHandle

CustomInterceptor2 afterCompletion

CustomInterceptor1 afterCompletion

9.3 拦截器的实际应用

需求分析:用户登录的拦截

  1. 有一个登录页面,需要写一个controller访问页面
  2. 登录页面有一个提交的表单,需要在controller中处理。
    1. 判断用户名和密码是否正确
    2. 如果正确在session中写入用户信息
    3. 返回登录成功

3 拦截器

    1. 拦截用户请求,判断用户是否登录
    2. 如果用户已经登录,放行。
    3. 如果用户未登录,那么需要跳转到登录页面。

9.3.1 登录页面的编写

<h3>用户登录:</h3>

<form action="/springmvc-02/user/login.do" method="post" >

用户名:<input type="text" name="username" ></br>

密码:<input type="password" name="password" />

<input type="submit" value="提交" >

</form>

9.3.2 后台代码的编写

Controller代码的编写:

@RequestMapping(value="/login",method=RequestMethod.POST)

public String userLogin(String username,String password,HttpSession session) {

//用户登录成功,把用户的信息放入session域

session.setAttribute("username",username);

return "success";

}

9.3.3 拦截器的编写

@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

 

//如果是访问的登录页面,那么就放行

String url = request.getRequestURI();

if(url.contains("login")) {

return true;

}

//如果用户已经登录那么也放行

Object usernmae = request.getSession().getAttribute("username");

if(usernmae != null) {

return true;

}

 

//用户没有登录跳转到登录页面

request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);

return false;

}

 

猜你喜欢

转载自blog.csdn.net/xiaozhengN/article/details/82948767