本文是Spring第三讲:SpringMVC 从入门到精通
1、springMvc是什么?(市场占有率40%,web开发当之无愧的霸主)
2、springMvc执行流程(已经滚瓜乱熟了)
SpringMVC启动流程?
待补充
启动流程和运行流程有何区别?20181222
待补充
1、 @RequestMapping 用来处理请求地址映射的注解,可用于类或方法上
注解在方法上的@RequestMapping路径会继承注解在类上的路径
2、@RequestBody 注解实现接收 http 请求的 json 数据,将 json 数据转换为 java 对象
3、@Controller 它标记的类就是一个控制层对象
4、@Resource 和@Autowired 都是做 bean 的注入时使用
5、@PathVariable 用于将请求 URL 中的模板变量映射到功能处理方法的参数上,即取出uri模板中的变量作为参数。
6、@requestParam
@requestParam 主要用于在 SpringMVC 后台控制层获取参数,类似一种是 request.getParameter(“name”),他有三个常用参数 defaultValue,required,value
7、@Component 相当于通用的注解,当不知道一些类归到哪个层时使用,但是不建议
8、@RestController (组合注解@Controller和@ResponseBody)
如何开启注解处理器和适配器?
1、解决post请求乱码:
<filter>
<filter-name>encodingFilterfilter-name>
<filter-class>
org.springframework.web.filter.CharacterEncodingFilter
filter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>UTF-8param-value>
init-param>
<init-param>
<param-name>forceEncodingparam-name>
<param-value>trueparam-value>
init-param>
filter>
<filter-mapping>
<filter-name>encodingFilterfilter-name>
<url-pattern>*url-pattern>
filter-mapping>
1、默认类型:
2、基本类型:string, double, float, integer, long. boolean
3、pojo类型:页面上input框的name属性值必须要等于pojo的属性名称
4、vo类型(view object 用于表现层):页面上input框的name属性值必须要等于vo中的属性.属性.属性…
5、自定义转换器converter:
1、Rest是一种web服务实现方式,Http接口按照Rest风格设计就是 restful http
要求url中没有动词,只有名词,没有参数
例如:@RequestMapping(value="/viewItems/{id}")
2、REST架构的主要原则:
3、资源操作
http://example.com/users/
GET: 获取一个资源
POST:创建一个新的资源
PUT:修改一个资源的状态
DELETE:删除一个资源
之前的操作 | RESTful的用法 | 幂等 | 安全 |
---|---|---|---|
http://127.0.0.1/user/query/1 GET查询 | http://127.0.0.1/user/1 GET | 是 | 是 |
http://127.0.0.1/user/save POST增加 | http://127.0.0.1/user POST | 否 | 否 |
http://127.0.0.1/user/update POST修改 | http://127.0.0.1/user PUT | 是 | 否 |
http://127.0.0.1/user/delete GET/POST删 | http://127.0.0.1/user DELETE | 是 | 否 |
1、如何理解 RESTful API 的幂等性?
2、如何保证接口的幂等性?****
有些接口可天然实现幂等性,比如查询接口:增加、更新、删除都要保证幂等性。那么如何来保证幂等性呢?
4、最佳实践
5、springMVC实现RESTful服务
6、发送请求工具
1、advanced REST client
是chrome浏览器下的一个插件,通过它能够发送http,https,websocket请求,这样就不必通过写一个jsp页面来实现请求,节省时间
2、HttpClient工具类
模拟了浏览器的行为,如果我们需要在后端向某一地址提交数据获取结果,就可以使用HttpClient
3、我们公司常使用的工具
restlet插件 postman插件 用于模拟浏览器的请求
7、更新资源时,需要加过滤器,解决无法提交表单的问题
@responsebody(method=requestMethod.put)
public responseBodyEntity<void> updateUser(User user){
try{
integer count = this.userservice.updateUser(user);
if(count.intValue() == 1){
//响应 204
return responseEntity.status(HttpStatus.NO_CONTENT).build();
}catch(exception e){e.printStackTrace}
//新增失败 500
return responseEntity.status(HttpStatus.INTERNAL_SERVERR_ERROR).build();
}
}
默认情况下,put请求时无法提交表单数据的,需要在web.xml中添加过滤器解决
<filter>
<filter-name>httpmethodfilter>
<filter-class>org.springframework.web.filter.httpputformContentFilter>
filter>
删除资源时,将post请求转化为DELETE或者是put要用-method指定真正的请求方法
SpringMVC通过ModelAndView,把结果集拿到以后,从配置文件里获取对应的bean,类反射
区别:
备注:如今的开发基本不使用struts2了,基本都是sprigboot这一条脚手架,里面集成了springMvc。 2021010
使用场景:
数据格式
解决了日常开发中的痛点
使用方法:
@RestControllerAdvice都是对Controller进行增强的,可以全局捕获spring mvc抛的异常。
ExceptionHandler的作用是用来捕获指定的异常。
使用 RestControllerAdvice 来捕获全局异常Demo:
public class CallResultMsg<T> {
private boolean result;
private int code;
private String message;
private T data;
}
public enum CodeAndMsg {
SUCCESS(1000, "SUCCESS"),
METHODFAIL(2000, "ENCOUNTER AN ERROR WHEN EXECUTE METHOD"),
UNKNOWEXCEPTION(3000, "THIS IS AN UNKNOW EXCEPTION");
private int code;
private String msg;
CodeAndMsg(int code, String msg){
this.code = code;
this.msg = msg;
}
}
@RestControllerAdvice(basePackages = {
"cn.gov.zcy.service.item.web.controller",
"cn.gov.zcy.service.warehouse.web.controller"})
public class ControllerErrorHandler<T> {
@ResponseStatus(HttpStatus.OK)
public CallResultMsg sendSuccessResponse(){
return new CallResultMsg(true, CodeAndMsg.SUCCESS, null);
}
@ResponseStatus(HttpStatus.OK)
public CallResultMsg sendSuccessResponse(T data) {
return new CallResultMsg(true, CodeAndMsg.SUCCESS, data);
}
@ExceptionHandler(UserDefinedException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public CallResultMsg sendErrorResponse_UserDefined(Exception exception){
return new CallResultMsg(false, ((UserDefinedException)exception).getException(), null);
}
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public CallResultMsg sendErrorResponse_System(Exception exception){
if (exception instanceof UserDefinedException) {
return this.sendErrorResponse_UserDefined(exception);
}
return new CallResultMsg(false, CodeAndMsg.UNKNOWEXCEPTION, null);
}
}
通过上面的一波操作,我们的controller中就不需要再去写大量的try-catch了,RestControllerAdvice会自动帮助catch,并匹配相应的ExceptionHandler,然后重新封装异常信息,返回值,统一格式返回给前端。
1、拦截器interceptor:
2、过滤器:filter
3、监视器:listener
4、区别:
我们定义一个配置类MyMvcConfig,继承WebMVCConfigurerAdapter,并在此类中使用@EnableWebMvc注解,开启对springMVC的配置支持
1、静态资源映射 在配置里重写addResourceHandlers方法来实现
@override
public void addResourceHandlers(ResourceHandlerRegistry registry){
registry.addResourceHandler("/asserts/**").addResourceLocation("classpath:/asserts/");//前者是对外暴露的访问路径 后者是文件放置的目录
}
2、拦截器设置Interceptor
3、@ControllerAdvice 将对控制器的全局配置放置在同一个位置
(1)定制ControllerAdvice
@ExceptionHandler(value=xx.class) 用于全局处理控制器里的异常,更人性化的将异常输出给用户
例如:
@ExceptionHandler(value=Exception.class) //value为拦截所有的异常
public ModelAndView exception(Exception exception, webRequest request){
ModelAndView modelAndView =new ModelAndView("error");
modelAndView.addObject("errorMessage",exception.getMessage());
return modelAndView;
}
@InitBinder
public void initBinder(WebDataBinder webDataBinder){
webDataBinder.setDisallowedFields("id");
}
@ModelAttribute
public void addAttributes(Model model){
model.addAttributes("msg","额外信息");
}
(2)那么,在使用控制器时:
@Controller
public class AdviceController{
@RequestMapping("/advice")
public String getSomething(@ModelAttribute("msg") String msg,DemoObj obj){
throw new IllegalArgumentException("参数有误/"+"来自@ModelAttribute:"+msg);
}
}
(3)异常展示页面 在src/main/resources/views下,新建error.jsp
${errorMessage} //运行时发现 id被过滤掉了,且获得了@ModelAttribute的msg信息
4、其他配置
5、文件上传配置(必备)springMvc通过配置MultipartResolver来上传文件,在spring的控制器中,通过MultipartFile file来接受文件
<div class="upload">
<form action="upload" enctype="multipart/form-data" method="post">
<input type="file" name="file"/>br>
<input type="submit" value="上传"/>
form>
div>
public void addViewController(ViewControllerRegistry registry){
registry.addViewController("/index").setViewName("/index");
registry.addViewController("/toUpload").setViewName("/upload");
}
@Bean
public MultipartResolver multipartResolver(){
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(1000000);
return multipartResolver;
}
@Controller
public class UploadController{
@RequestMapping(value="/upload",method="RequestMethod.POST")
public @ResponseBody String upload(MultipartFile file){
try{
FileUtils.writeByteArrayToFile(new File("e:/upload/"+file.getOriginalFileName()),file.getBytes());//快速写文件到磁盘
return "ok";
}catch(IOException e){
e.prrintStackTrace();
return "wrong";
}
}
}
6、自定义HttpMessageConvertor 用于处理request和response里的数据
定义一个pojo,它有一个 java.util.Date 类型的属性 date。
import java.util.Date;
public class DateVo {
private Date date;
public void setDate(Date date){
this.date = date;
}
public Date getDate(){
return date;
}
}
定义一个Controller
@RestController
@RequestMapping("/date/")
public class DateController {
@RequestMapping("test")
public DateVo getDate(DateVo vo){
System.out.println("date1:"+vo.getDate());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date = sdf.format(vo.getDate());
System.out.println("date2:"+date);
DateVo vo2 = new DateVo();
vo2.setDate(new Date());
return vo2;
}
}
访问 /date/test ,并传入参数:2018-08-02 22:05:55
发现并不能访问成功,会抛出异常
因为传入的参数是 String 类型的,而用来接收参数的 DateVo 的 date 属性是 java.util.Date 类型的,类型无法转换。
@DateTimeFormat
注解格式化参数,来解决上述问题public class DateVo {
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
private Date date;
public void setDate(Date date){
this.date = date;
}
public Date getDate(){
return date;
}
}
再像上面一样访问 /date/test ,并传入参数:2018-08-02 22:05:55,将在控制台上打印:
可以看到,加入 @DateTimeFormat 注解后参数可以被接收到了,但日期时间的格式还是需要自己再手动转换一下。
因为 @DateTimeFormat 注解的 pattern 属性值指定的日期时间格式并不是将要转换成的日期格式,这个指定的格式是和传入的参数对应的,假如注解为:
@DateTimeFormat(pattern=“yyyy/MM/dd HH:mm:ss”)
则传入的参数应该是这样的:2018/08/02 22:05:55,否则会抛出异常。
改造 DateVo:
public class DateVo {
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@JsonFormat(
pattern = "yyyy-MM-dd HH:mm:ss",
timezone = "GMT+8"
)
private Date date;
public void setDate(Date date){
this.date = date;
}
public Date getDate(){
return date;
}
}
这样,就能返回正确的结果了。