SpringMVC
简介:与用户交互的组件(表现层技术),是一种基于Java实现mvc模型的轻量级web框架。
Model 数据模型 :用于封装数据
view 视图:用于展示数据
入门案例(xml+注解)
-
导入maven坐标:servlet、jsp、spring、spring-webmvc、spring-web
tomcat插件
<dependencies> <!-- servlet3.0规范的坐标 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <!--jsp坐标--> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.1</version> <scope>provided</scope> </dependency> <!--spring的坐标--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.9.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.1.9.RELEASE</version> </dependency> <!--springmvc的坐标--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.1.9.RELEASE</version> </dependency> </dependencies> <!--构建--> <build> <!--设置插件--> <plugins> <!--具体的插件配置--> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.1</version> <configuration> <port>80</port> <path>/</path> </configuration> </plugin> </plugins> </build>
-
WEB-INF下配置文件(web.xml):
- servlet配置(加载springmvc前端控制器)
- 加载配置文件
- 配置映射路径
<!--过滤器,中文乱码处理--> <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!--加载核心控制器--> <servlet> <servlet-name>DispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--加载springmvc配置文件-->d <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:spring-mvc.xml</param-value> </init-param> </servlet> <!--配置映射路径--> <servlet-mapping> <servlet-name>DispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
-
springmvc配置文件(spring-mvc.xml):初始化spring容器
- 扫描包(Bean)
- 注解驱动
- 静态资源放行
<!--扫描包--> <context:component-scan base-package="com.itheima"/> <!--注解驱动--> <mvc:annotation-driven/> <!--静态资源放行--> <mvc:default-servlet-handler/> <!--设置访问地址前缀后缀--> <!--<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/page/"/> <property name="suffix" value=".jsp"/> </bean>-->
-
创建控制器(Controller):
-
定义类:(配置注解@Controller)
-
创建方法:
指定访问路径(@RequestMapping("/请求地址"))
设置方法体返回值为string,return返回页面名称(等同请求转发)
@Controller public class AjaxController { @RequestMapping("/ajaxController") public String ajaxController(){ System.out.println("ajax request is running..."); return "page.jsp"; } }
-
技术架构图
-
服务器启动
- 加载web.xml中的DispatcherServlet
- 读取Spring-mvc.xml中的配置,加载所有com.itheima包下标记为bean的类
- 读取bean中方法上标注@RequestMapping的内容
-
处理请求
- DispatcherServlet配置拦截所有请求 /
- 使用请求路径与所有加载的@RequestMapping的内容进行比对
- 执行对应的方法
- 根据方法的返回值在webapp目录中查找对应的页面并展示
基本配置-常规配置
-
Controller加载控制(Springmvc配置:扫描包)
<context:component-scan base-package="com.itheima"/>
-
静态资源加载(核心控制器拦截所有资源:Springmvc配置)
<!--核心控制器拦截的是所有请求,需要对静态资源请求进行放行,通过配置进行资源实现--> <mvc:resources mapping="/img/**" location="/img/"/> <mvc:resources mapping="/js/**" location="/js/"/> <!--使用简化格式可以放行所有普通资源调用,无需一一枚举--> <mvc:default-servlet-handler/>
-
中文乱码处理(web.xml配置)
<filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
注解驱动
-
注解:表明配置、扫描注解、扫描过滤配置
-
继承WebMvcConfugurer,重写配置默认servlet方法加载静态资源
//Spring-mvc.xml文件对应配置 @Configuration @ComponentScan(value = "com.itheima",includeFilters = @ComponentScan.Filter(type=FilterType.ANNOTATION,classes = {Controller.class}) ) public class SpringMVCConfiguration implements WebMvcConfigurer{ //注解配置放行指定资源格式 // @Override // public void addResourceHandlers(ResourceHandlerRegistry registry) { // registry.addResourceHandler("/img/**").addResourceLocations("/img/"); // registry.addResourceHandler("/js/**").addResourceLocations("/js/"); // registry.addResourceHandler("/css/**").addResourceLocations("/css/"); // } //注解配置通用放行资源的格式 @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable();; } }
-
继承AbstractDispatcherServlet,重写加载配置文件、映射配置、
-
对onStartup方法增强,设置字符集,映射
//web.xml文件对应配置
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
//创建Servlet容器时,使用注解的方式加载SPRINGMVC配置类中的信息,并加载成WEB专用的ApplicationContext对象
//该对象放入了ServletContext范围,后期在整个WEB容器中可以随时获取调用
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringMVCConfiguration.class);
return ctx;
}
//注解配置映射地址方式,服务于SpringMVC的核心控制器DispatcherServlet
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
@Override
protected WebApplicationContext createRootApplicationContext() {
return null;
}
//乱码处理作为过滤器,在servlet容器启动时进行配置,相关内容参看Servlet零配置相关课程
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
CharacterEncodingFilter cef = new CharacterEncodingFilter();
cef.setEncoding("UTF-8");
FilterRegistration.Dynamic registration = servletContext.addFilter("characterEncodingFilter", cef);
registration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST,DispatcherType.FORWARD,DispatcherType.INCLUDE),false,"/*");
}
}
请求
请求参数
-
普通类型
@RequestParam(“参数名”,required=true)
-
pojo类型
-
数组类型
-
集合类型
@Controller public class UserController { //方法传递普通类型参数,数量任意,类型必须匹配 //http://localhost/requestParam1?name=itheima //http://localhost/requestParam1?name=itheima&age=14 @RequestMapping("/requestParam1") public String requestParam1(String name,int age){ System.out.println(name+","+age); return "page.jsp"; } //方法传递普通类型参数,使用@RequestParam参数匹配URL传参中的参数名称与方法形参名称 //http://localhost/requestParam2?userName=Jock @RequestMapping("/requestParam2") public String requestParam2(@RequestParam(value = "userName",required = true) String name){ System.out.println(name); return "page.jsp"; } //方法传递POJO类型参数,URL地址中的参数作为POJO的属性直接传入对象 //http://localhost/requestParam3?name=Jock&age=39 @RequestMapping("/requestParam3") public String requestParam3(User user){ System.out.println(user); return "page.jsp"; } //当方法参数中具有POJO类型参数与普通类型参数嘶,URL地址传入的参数不仅给POJO对象属性赋值,也给方法的普通类型参数赋值 //http://localhost/requestParam4?name=Jock&age=39 @RequestMapping("/requestParam4") public String requestParam4(User user,int age){ System.out.println("user="+user+",age="+age); return "page.jsp"; } //使用对象属性名.属性名的对象层次结构可以为POJO中的POJO类型参数属性赋值 //http://localhost/requestParam5?address.city=beijing @RequestMapping("/requestParam5") public String requestParam5(User user){ System.out.println(user.getAddress().getCity()); return "page.jsp"; } //通过URL地址中同名参数,可以为POJO中的集合属性进行赋值,集合属性要求保存简单数据 //http://localhost/requestParam6?nick=Jock1&nick=Jockme&nick=zahc @RequestMapping("/requestParam6") public String requestParam6(User user){ System.out.println(user); return "page.jsp"; } //POJO中List对象保存POJO的对象属性赋值,使用[数字]的格式指定为集合中第几个对象的属性赋值 //http://localhost/requestParam7?addresses[0].city=beijing&addresses[1].province=hebei @RequestMapping("/requestParam7") public String requestParam7(User user){ System.out.println(user.getAddresses()); return "page.jsp"; } //POJO中Map对象保存POJO的对象属性赋值,使用[key]的格式指定为Map中的对象属性赋值 //http://localhost/requestParam8?addressMap['job'].city=beijing&addressMap['home'].province=henan @RequestMapping("/requestParam8") public String requestParam8(User user){ System.out.println(user.getAddressMap()); return "page.jsp"; } //方法传递普通类型的数组参数,URL地址中使用同名变量为数组赋值 //http://localhost/requestParam9?nick=Jockme&nick=zahc @RequestMapping("/requestParam9") public String requestParam9(String[] nick){ System.out.println(nick[0]+","+nick[1]); return "page.jsp"; } //方法传递保存普通类型的List集合时,无法直接为其赋值,需要使用@RequestParam参数对参数名称进行转换 //http://localhost/requestParam10?nick=Jockme&nick=zahc @RequestMapping("/requestParam10") public String requestParam10(@RequestParam("nick") List<String> nick){ System.out.println(nick); return "page.jsp"; } //数据类型转换,使用自定义格式化器或@DateTimeFormat注解设定日期格式 //两种方式都依赖springmvc的注解启动才能运行 //http://localhost/requestParam11?date=1999-09-09 @RequestMapping("/requestParam11") public String requestParam11(@DateTimeFormat(pattern = "yyyy-MM-dd") Date date){ System.out.println(date); return "page.jsp"; } //数据类型转换,使用自定义类型转换器,需要配置后方可使用 //http://localhost/requestParam12?date=1999-09-09 @RequestMapping("/requestParam12") public String requestParam12(Date date){ System.out.println(date); return "page.jsp"; } //无类映射地址访问格式 //http://localhost/requestURL1 @RequestMapping("/requestURL1") public String requestURL1(){ return "page.jsp"; } //带有类映射地址访问格式,需要将类映射地址作为前缀添加在实际映射地址的前面 //最终返回的页面如果未设定绝对访问路径,将从类映射地址所在目录中查找 //http://localhost/user/requestURL2 (注意:要配合类上定义的路径使用) @RequestMapping("/requestURL2") public String requestURL2(){ return "/page.jsp"; } //@RequestMapping参数,一个路径参数,6个访问限定性参数(了解) @RequestMapping(value="/requestURL3",params = "name") public String requestURL3(){ return "page.jsp"; } }
类型转换
springmvc配置文件定义转换类,并通过Converter接口实现
包含:标量转换器,集合、数组相关转化器,默认转换器(ObjectToObjectConverter)
日期类型转换简化版:注解驱动支持,参数@DateTimeFormat(pattern=“日期格式”)。依赖注解驱动支持
自定义类型转换器(日期类型转换)
-
创建类实现接口Converter
-
实现抽象方法
//自定义类型转换器,实现Converter接口,接口中指定的泛型即为最终作用的条件 //本例中的泛型填写的是String,Date,最终出现字符串转日期时,该类型转换器生效 public class MyDateConverter implements Converter<String, Date> { //重写接口的抽象方法,参数由泛型决定 public Date convert(String source) { DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); Date date = null; //类型转换器无法预计使用过程中出现的异常,因此必须在类型转换器内部捕获,不允许抛出,框架无法预计此类异常如何处理 try { date = df.parse(source); } catch (ParseException e) { e.printStackTrace(); } return date; } }
-
加载到spring-mvc.xml文件
<!--启动自定义转换器--> <mvc:annotation-driven conversion-service="conversionService"/> <!--自定义类型转换器--> <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <!--覆盖类型转换器定义规则,该规则是一个set集合,对类型转换器来说是追加和替换的思想,而不是覆盖整体格式化转换器--> <property name="converters"> <set> <!--添加自定义的类型转换器,会根据定义的格式覆盖系统中默认的格式--> <!--当前案例中是将String转换成Date的类型转换器进行了自定义,所以添加后,系统中原始自带的String——>Date的类型转换器失效--> <bean class="com.itheima.converter.MyDateConverter"/> </set> </property> </bean>
请求映射
@RequestMapping可以加载到类、方法上
@RequestMapping常用属性
@RequestMapping(
value="请求路径", //设定请求路径,与path属性、value属性相同
method=RequestMethod.GET, //设定请求方式
params="name", //设定请求参数条件
headers="conten-type=text/*",//设定请求消息头条件
consumes="text/*", //用于指定可以接收的请求正文类型(mime类型)
produces="text/*", //用于指定可以生成的响应正文类型(mime类型)
)
响应
无数据跳页面
- 转发(默认):return “forward:xxx”
//forward:page.jsp转发访问,支持访问WEB-INF下的页面
@RequestMapping("/showPage1")
public String showPage1() {
System.out.println("user mvc controller is running ...");
return "forward:/WEB-INF/page/page.jsp";
}
- 跳转:return “redirect:xxx”
//redirect:page.jsp重定向访问,不支持访问WEB-INF下的页面
@RequestMapping("/showPage2")
public String showPage2() {
System.out.println("user mvc controller is running ...");
return "redirect:/WEB-INF/page/page.jsp";
}
springmvc配置文件:设置前缀后缀
<!--设定页面加载的前缀后缀,仅适用于默认形式,不适用于手工标注转发或重定向的方式-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">-->
<property name="prefix" value="/WEB-INF/page/"/>-->
<property name="suffix" value=".jsp"/>-->
</bean>
@RequestMapping("/showPage3")
public String showPage3() {
System.out.println("user mvc controller is running ...");
return "page";///(WEB-INF/page/page.jsp)
}
如果使用前后缀配置,方法返回值为void,访问路径按"前缀+虚拟路径+后缀"进行访问。
带数据跳页面
-
request
//使用原生request对象传递参数 @RequestMapping("/showPageAndData1") public String showPageAndData1(HttpServletRequest request) { request.setAttribute("name","itheima"); return "page";
-
model
//使用Model形参传递参数 @RequestMapping("/showPageAndData2") public String showPageAndData2(Model model) { //添加数据的方式,key对value model.addAttribute("name","Jock"); Book book = new Book(); book.setName("SpringMVC入门案例"); book.setPrice(66.66d); //添加数据的方式,key对value model.addAttribute("book",book); return "page";
-
ModelAndView
//使用ModelAndView形参传递参数,该对象还封装了页面信息 @RequestMapping("/showPageAndData3") public ModelAndView showPageAndData3(ModelAndView modelAndView) { //ModelAndView mav = new ModelAndView(); 替换形参中的参数 Book book = new Book(); book.setName("SpringMVC入门案例"); book.setPrice(66.66d); //添加数据的方式,key对value modelAndView.addObject("book",book); //添加数据的方式,key对value modelAndView.addObject("name","Jockme"); //设置页面的方式,该方法最后一次执行的结果生效 modelAndView.setViewName("page"); //返回值设定成ModelAndView对象 return modelAndView;
返回json数据
-
response
//使用原生response对象响应数据 @RequestMapping("/showData1") public void showData1(HttpServletResponse response) throws IOException { response.getWriter().write("message"); }
-
@ResponseBody注解
-
导入maven坐标jackson
-
spring-mvc.xml配置添加annotation-driven,添加注解@ResponseBody
-
返回对象
//使用@ResponseBody将返回的结果作为响应内容,而非响应的页面名称 @RequestMapping("/showData2") @ResponseBody public String showData2(){ return "{'name':'Jock'}"; } //使用jackson进行json数据格式转化 @RequestMapping("/showData3") @ResponseBody public String showData3() throws JsonProcessingException { Book book = new Book(); book.setName("SpringMVC入门案例"); book.setPrice(66.66d); ObjectMapper om = new ObjectMapper(); return om.writeValueAsString(book); } //spring-mvc.xml配置添加annotation-driven;MVC注解驱动,对标注@ResponseBody注解的控制器方法进行结果转换,由于返回值为引用类型,自动调用jackson提供的类型转换器进行格式转换 @RequestMapping("/showData4") @ResponseBody public Book showData4() { Book book = new Book(); book.setName("SpringMVC入门案例"); book.setPrice(66.66d); return book; } //转换集合类型数据 @RequestMapping("/showData5") @ResponseBody public List showData5() { Book book1 = new Book(); book1.setName("SpringMVC入门案例"); book1.setPrice(66.66d); Book book2 = new Book(); book2.setName("SpringMVC入门案例"); book2.setPrice(66.66d); ArrayList al = new ArrayList(); al.add(book1); al.add(book2); return al; }
-
servlet相关接口
SpringMVC提供访问原始Servlet接口API的功能,通过形参声明即可
-
@RequestHeader
-
@CookieValue
-
@SessionAttribute
-
类注解@SessionAttributes
-
model设置
-
@SessionAttribute
-
@Controller
//设定当前类中名称为age和gender的变量放入session范围,不常用,了解即可
@SessionAttributes(names = {"age","gender"})
public class UserController {
//获取request,response,session对象的原生接口
@RequestMapping("/servletApi")
public String servletApi(HttpServletRequest request, HttpServletResponse response, HttpSession session){
System.out.println(request);
System.out.println(response);
System.out.println(session);
return "page";
}
//获取head数据的快捷操作方式
@RequestMapping("/headApi")
public String headApi(@RequestHeader("Accept-Encoding") String headMsg){
System.out.println(headMsg);
return "page";
}
//获取cookie数据的快捷操作方式
@RequestMapping("/cookieApi")
public String cookieApi(@CookieValue("JSESSIONID") String jsessionid){
System.out.println(jsessionid);
return "page";
}
//测试用方法,为下面的试验服务,用于在session中放入数据
@RequestMapping("/setSessionData")
public String setSessionData(HttpSession session){
session.setAttribute("name","itheima");
return "page";
}
//获取session数据的快捷操作方式
@RequestMapping("/sessionApi")
public String sessionApi(@SessionAttribute("name") String name,
@SessionAttribute("age") int age,
@SessionAttribute("gender") String gender){
System.out.println(name);
System.out.println(age);
System.out.println(gender);
return "page";
}
//配合@SessionAttributes(names = {"age","gender"})使用
//将数据放入session存储范围,通过Model对象实现数据set,通过@SessionAttributes注解实现范围设定
@RequestMapping("/setSessionData2")
public String setSessionDate2(Model model) {
model.addAttribute("age",39);
model.addAttribute("gender","男");
return "page";
}
}
异步
发送异步请求
maven项目导入坐标jackson
<!--json相关坐标3个-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>
web.xml
springmvc.xml
实体类
前端页面jsp:JQ的ajax技术
$("#testSave").click(function () {
//发送异步调用
$.ajax({
//请求方式:POST请求
type: "POST",
//请求的地址
url: "save",
//请求参数(也就是请求内容)
data: '{"name":"Jock","age":18}',
//响应正文类型
dataType: "json",
//请求正文的MIME类型
contentType: "application/json",
success: function (data) {
//打印返回结果
alert(data.name + data.age);
}
});
});
//为id="testAjax"的组件绑定点击事件
$("#testAjax").click(function () {
//发送异步调用
$.ajax({
//请求方式:POST请求
type: "POST",
//请求的地址
url: "ajaxController",
//请求参数(也就是请求内容)
data: 'ajax message',
//响应正文类型
dataType: "text",
//请求正文的MIME类型
contentType: "application/text",
});
});
//为id="testAjaxPojo"的组件绑定点击事件
$("#testAjaxPojo").click(function () {
$.ajax({
type: "POST",
url: "ajaxPojoToController",
data: '{"name":"Jock","age":39}',
dataType: "text",
contentType: "application/json",
});
});
//为id="testAjaxList"的组件绑定点击事件
$("#testAjaxList").click(function () {
$.ajax({
type: "POST",
url: "ajaxListToController",
data: '[{"name":"Jock","age":39},{"name":"Jockme","age":40}]',
dataType: "text",
contentType: "application/json",
});
});
//为id="testAjaxReturnString"的组件绑定点击事件
$("#testAjaxReturnString").click(function () {
//发送异步调用
$.ajax({
type: "POST",
url: "ajaxReturnString",
//回调函数
success: function (data) {
//打印返回结果
alert(data);
}
});
});
//为id="testAjaxReturnJson"的组件绑定点击事件
$("#testAjaxReturnJson").click(function () {
//发送异步调用
$.ajax({
type: "POST",
url: "ajaxReturnJson",
//回调函数
success: function (data) {
alert(data);
alert(data['name'] + " , " + data['age']);
}
});
});
//为id="testAjaxReturnJsonList"的组件绑定点击事件
$("#testAjaxReturnJsonList").click(function () {
//发送异步调用
$.ajax({
type: "POST",
url: "ajaxReturnJsonList",
//回调函数
success: function (data) {
alert(data);
alert(data.length);
alert(data[0]["name"]);
alert(data[1]["age"]);
}
});
});
//为id="testCross"的组件绑定点击事件
$("#testCross").click(function () {
//发送异步调用
$.ajax({
type: "POST",
url: "http://www.jock.com/cross",
//回调函数
success: function (data) {
alert("跨域调用信息反馈:" + data['name'] + " , " + data['age']);
}
});
});
接收异步请求数据
方法形参前加注解:@RequestBody
@RequestMapping("save")
//使用@RequestBody注解,可以将请求体内容封装到指定参数中
@ResponseBody
public User Save(@RequestBody User user){
System.out.println(user.getName()+user.getAge());
return user;
}
@RequestMapping("/ajaxController")
//使用@RequestBody注解,可以将请求体内容封装到指定参数中
public String ajaxController(@RequestBody String message){
System.out.println("ajax request is running..."+message);
return "page.jsp";
}
@RequestMapping("/ajaxPojoToController")
//如果处理参数是POJO,且页面发送的请求数据格式与POJO中的属性对应,@RequestBody注解可以自动映射对应请求数据到POJO中
//注意:POJO中的属性如果请求数据中没有,属性值为null,POJO中没有的属性如果请求数据中有,不进行映射
public String ajaxPojoToController(@RequestBody User user){
System.out.println("controller pojo :"+user);
return "page.jsp";
}
@RequestMapping("/ajaxListToController")
//如果处理参数是List集合且封装了POJO,且页面发送的数据是JSON格式的对象数组,数据将自动映射到集合参数中
public String ajaxListToController(@RequestBody List<User> userList){
System.out.println("controller list :"+userList);
return "page.jsp";
}
返回异步请求数据
方法上或者返回参数类型前加注解@ResponseBody,return返回对应类型数据
//使用注解@ResponseBody可以将返回的页面不进行解析,直接返回字符串,该注解可以添加到方法上方或返回值前面
@RequestMapping("/ajaxReturnString")
// @ResponseBody
public @ResponseBody String ajaxReturnString(){
System.out.println("controller return string ...");
return "page.jsp";
}
@RequestMapping("/ajaxReturnJson")
@ResponseBody
//基于jackon技术,使用@ResponseBody注解可以将返回的POJO对象转成json格式数据
public User ajaxReturnJson(){
System.out.println("controller return json pojo...");
User user = new User();
user.setName("Jockme");
user.setAge(39);
return user;
}
@RequestMapping("/ajaxReturnJsonList")
@ResponseBody
//基于jackon技术,使用@ResponseBody注解可以将返回的保存POJO对象的集合转成json数组格式数据
public List ajaxReturnJsonList(){
System.out.println("controller return json list...");
User user1 = new User();
user1.setName("Tom");
user1.setAge(3);
User user2 = new User();
user2.setName("Jerry");
user2.setAge(5);
ArrayList al = new ArrayList();
al.add(user1);
al.add(user2);
return al;
}
跨域访问
外部访问域名,通过域名绑定ip,通过ip连接计算机。请求协议、IP地址、端口号不同就叫跨域,域名不同也叫跨域。跨域访问权限允许:方法上加注解@CrossOrigin
@RequestMapping("/cross")
@ResponseBody
//使用@CrossOrigin开启跨域访问
//标注在处理器方法上方表示该方法支持跨域访问
//标注在处理器类上方表示该处理器类中的所有处理器方法均支持跨域访问
@CrossOrigin
public User cross(HttpServletRequest request){
System.out.println("controller cross..."+request.getRequestURL());
User user = new User();
user.setName("Jockme");
user.setAge(39);
return user;
}
本机跨域环境搭建
-
windows添加备用名
修改windows安装目录中的host文件,格式:ip 域名
-
动态刷新DNS命令
- ipconfig /displaydns
- ipconfig /flushdns
拦截器
一种动态拦截方法调用的机制,核心原理为AOP思想。在指定的方法调用前后执行预先设定后的代码(增强),阻止原始方法的执行。
自定义拦截器
-
制作拦截器功能类(通知):需实现HandlerInterceptor接口
//自定义拦截器需要实现HandleInterceptor接口 public class MyInterceptor implements HandlerInterceptor { //处理器运行之前执行 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("前置运行----a1"); //返回值为false将拦截原始处理器的运行 //如果配置多拦截器,返回值为false将终止当前拦截器后面配置的拦截器的运行 return true; } //处理器运行之后执行 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("后置运行----b1"); } //所有拦截器的后置执行全部结束后,执行该操作 @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("完成运行----c1"); } //三个方法的运行顺序为 preHandle -> postHandle -> afterCompletion //如果preHandle返回值为false,三个方法仅运行preHandle }
-
配置拦截器的执行位置(切入点)
<!--开启拦截器使用--> <mvc:interceptors> <!--开启具体的拦截器的使用,可以配置多个--> <mvc:interceptor> <!--设置拦截器的拦截路径,支持*通配--> <!--/** 表示拦截所有映射--> <!--/* 表示拦截所有/开头的映射--> <!--/user/* 表示拦截所有/user/开头的映射--> <!--/user/add* 表示拦截所有/user/开头,且具体映射名称以add开头的映射--> <!--/user/*All 表示拦截所有/user/开头,且具体映射名称以All结尾的映射--> <mvc:mapping path="/*"/> <mvc:mapping path="/**"/> <mvc:mapping path="/handleRun*"/> <!--设置拦截排除的路径,配置/**或/*,达到快速配置的目的--> <mvc:exclude-mapping path="/b*"/> <!--指定具体的拦截器类--> <bean class="MyInterceptor"/> </mvc:interceptor> <!--配置多个拦截器,配置顺序即为最终运行顺序--> <mvc:interceptor> <mvc:mapping path="/*"/> <bean class="MyInterceptor2"/> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/*"/> <bean class="MyInterceptor3"/> </mvc:interceptor> </mvc:interceptors>
原始方法被拦截,则不执行后置处理方法,完成处理方法无论原始方法是否执行都会执行
多拦截器配置形成链式拦截:
运行顺序参照配置的先后顺序,当拦截器中出现对原始处理器的拦截,后面的拦截器均终止运行,当拦截器运行中断,仅运行完成处理方法(afterCompletion)。
责任链模式:
是一种行为模式。沿着一条预先设定的任务链顺序执行,每个节点具有独立的工作任务。
优势:
- 独立性:只关注当前节点的任务,对其他任务直接放行到下一节点
- 隔离性:具备链式传递特征,无需知晓整体链路结构,只需等待请求到达后进行处理即可
- 灵活性:可以任意修改链路结构动态新增或删减整体链路责任
- 解耦:将动态任务与原始任务解耦
弊端:链路过长时处理效率低下;可能存在节点上的循环引用现象,造成死循环,导致系统崩溃
拦截器VS过滤器
- 归属不同:filter属于servlet技术,interceptor属于springmvc技术
- 拦截内容不同:filter对所有访问进行增强,interceptor仅针对springmvc的访问进行增强
异常处理
HandlerExceptionResolver接口(异常处理器)
@Component
public class ExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
System.out.println("my exception is running ...."+ex);
ModelAndView modelAndView = new ModelAndView();
if( ex instanceof NullPointerException){
modelAndView.addObject("msg","空指针异常");
}else if ( ex instanceof ArithmeticException){
modelAndView.addObject("msg","算数运算异常");
}else{
modelAndView.addObject("msg","未知的异常");
}
modelAndView.setViewName("error.jsp");
return modelAndView;
}
}
使用注解实现异常分类管理:
- 类注解:@ControllerAdvice
- 方法注解:@ExceptionHandler(xxxException.class)
@Component
@ControllerAdvice
public class Myexception {
@ExceptionHandler(Exception.class)
@ResponseBody
public GeneralResult doException(Exception ex){
GeneralResult generalResult = new GeneralResult();
generalResult.setxxx();
return generalResult;
}
}
异常分类
-
业务异常
-
规范的用户行为产生
-
不规范的用户行为操作产生
-
-
系统异常
- 项目运行过程中可预计且无法避免的异常
-
其他异常
- 编程人员未预期
自定义异常:
通过自定义异常将所有的异常现象进行分类管理,以统一的格式对外呈现异常信息
-
异常定义格式
//自定义异常继承RuntimeException,覆盖父类所有的构造方法 public class BusinessException extends RuntimeException { public BusinessException() { } public BusinessException(String message) { super(message); } public BusinessException(String message, Throwable cause) { super(message, cause); } public BusinessException(Throwable cause) { super(cause); } public BusinessException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } }
-
触发方式
//对用户的非法操作进行判定,并包装成异常对象进行处理,便于统一管理 if(user.getName().trim().length() < 8){ throw new BusinessException("对不起,用户名长度不满足要求,请重新输入!"); } if(user.getAge() < 0){ throw new BusinessException("对不起,年龄必须是0到100之间的数字!"); } if(user.getAge() > 100){ throw new SystemException("服务器连接失败,请尽快检查处理!"); }
文件上传
-
导入坐标apache
<!--文件上传下载--> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency>
-
springmvc配置bean
<!--配置文件上传处理器--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="1024000000"/> </bean>
-
参数添加类型enctype
<form action="/fileupload" method="post" enctype="multipart/form-data"> <%--文件上传表单的name属性值一定要与controller处理器中方法的参数对应,否则无法实现文件上传--%> 上传LOGO:<input type="file" name="file"/><br/> 上传照片:<input type="file" name="file1"/><br/> 上传任意文件:<input type="file" name="file2"/><br/> <input type="submit" value="上传"/> </form>
-
直接使用传入MultipartFile参数,用file.transferto方法
@Controller public class FileUploadController { @RequestMapping(value = "/fileupload") //参数中定义MultipartFile参数,用于接收页面提交的type=file类型的表单,要求表单名称与参数名相同 public String fileupload(MultipartFile file,MultipartFile file1,MultipartFile file2, HttpServletRequest request) throws IOException { System.out.println("file upload is running ..."+file); //首先判断是否是空文件,也就是存储空间占用为0的文件 if(!file.isEmpty()){ //如果大小在范围要求内正常处理,否则抛出自定义异常告知用户(未实现) //获取原始上传的文件名,可以作为当前文件的真实名称保存到数据库中备用 String fileName = file.getOriginalFilename(); //设置保存的路径 String realPath = request.getServletContext().getRealPath("/images"); //保存文件的方法,指定保存的位置和文件名即可,通常文件名使用随机生成策略产生,避免文件名冲突问题 file.transferTo(new File(realPath,file.getOriginalFilename())); } //测试一次性上传多个文件 if(!file1.isEmpty()){ String fileName = file1.getOriginalFilename(); //可以根据需要,对不同种类的文件做不同的存储路径的区分,修改对应的保存位置即可 String realPath = request.getServletContext().getRealPath("/images"); file1.transferTo(new File(realPath,file1.getOriginalFilename())); } if(!file2.isEmpty()){ String fileName = file2.getOriginalFilename(); String realPath = request.getServletContext().getRealPath("/images"); file2.transferTo(new File(realPath,file2.getOriginalFilename())); } return "page.jsp"; } }
-
file属性
// MultipartFile参数中封装了上传的文件的相关信息 // System.out.println(file.getSize()); // System.out.println(file.getBytes().length); // System.out.println(file.getContentType()); // System.out.println(file.getName()); // System.out.println(file.getOriginalFilename()); // System.out.println(file.isEmpty());
注意事项
-
文件命名问题
file.getOriginalFilename();获取上传文件名,并解析文件名与扩展名
-
文件名过长
-
文件保存路径
String realPath=request.getServletContext().getRealPath("/images"); file1.transferTo(new File(realPath,file1.getOriginalFilename())); if(!file.exists()) file.mkdirs();
-
重名问题
String uuid = UUID.randomUUID().toString().replace("-", "").toUpperCase();
-
Restful
rest:一种网络资源的访问风格.优点:书写简化,隐藏资源的访问行为,通过地址无法得知做的何种操作。
-
传统风格访问路径
- http://localhost/user/get?id=1
- http://localhost/user/deleteUser?id=1
-
Rest风格访问路径
- http://localhost/user/1
行为约定方式(常用):GET(查询)、POST(保存)、PUT(更新)、DELETE(删除)
入门案例
控制器方法RequestMapping("/路径/{参数}")
通配:
类上注解@RestController、@RequestMapping("/路径/")
方法@RequestMapping("{参数}")
设置访问行为
方法@RequestMapping(value="{参数}",method=RequestMethod.GET)
//@Controller
//@ResponseBody
//设置rest风格的控制器
@RestController
//设置公共访问路径,配合下方访问路径使用
@RequestMapping("/user/")
public class UserController {
//rest风格访问路径完整书写方式
@RequestMapping("/user/{id}")
//使用@PathVariable注解获取路径上配置的具名变量,该配置可以使用多次
public String restLocation(@PathVariable Integer id){
System.out.println("restful is running ....");
return "success.jsp";
}
//rest风格访问路径简化书写方式,配合类注解@RequestMapping使用
@RequestMapping("{id}")
public String restLocation2(@PathVariable Integer id){
System.out.println("restful is running ....get:"+id);
return "success.jsp";
}
//接收GET请求配置方式
@RequestMapping(value = "{id}",method = RequestMethod.GET)
//接收GET请求简化配置方式
@GetMapping("{id}")
public String get(@PathVariable Integer id){
System.out.println("restful is running ....get:"+id);
return "success.jsp";
}
//接收POST请求配置方式
@RequestMapping(value = "{id}",method = RequestMethod.POST)
//接收POST请求简化配置方式
@PostMapping("{id}")
public String post(@PathVariable Integer id){
System.out.println("restful is running ....post:"+id);
return "success.jsp";
}
//接收PUT请求简化配置方式
@RequestMapping(value = "{id}",method = RequestMethod.PUT)
//接收PUT请求简化配置方式
@PutMapping("{id}")
public String put(@PathVariable Integer id){
System.out.println("restful is running ....put:"+id);
return "success.jsp";
}
//接收DELETE请求简化配置方式
@RequestMapping(value = "{id}",method = RequestMethod.DELETE)
//接收DELETE请求简化配置方式
@DeleteMapping("{id}")
public String delete(@PathVariable Integer id){
System.out.println("restful is running ....delete:"+id);
return "success.jsp";
}
}
jsp请求标签修改
<h1>restful风格请求表单</h1>
<%--切换请求路径为restful风格--%>
<%--GET请求通过地址栏可以发送,也可以通过设置form的请求方式提交--%>
<%--POST请求必须通过form的请求方式提交--%>
<form action="/user/1" method="post">
<%--当添加了name为_method的隐藏域时,可以通过设置该隐藏域的值,修改请求的提交方式,切换为PUT请求或DELETE请求,但是form表单的提交方式method属性必须填写post--%>
<%--该配置需要配合HiddenHttpMethodFilter过滤器使用,单独使用无效,请注意检查web.xml中是否配置了对应过滤器--%>
<input type="text" name="_method" value="PUT"/>
<input type="submit"/>
</form>
web.xml配置过滤器
<!--配置拦截器,解析请求中的参数_method,否则无法发起PUT请求与DELETE请求,配合页面表单使用-->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<servlet-name>DispatcherServlet</servlet-name>
</filter-mapping>
简化版:
原方式:@RequestMapping(value="{参数}",method=RequestMethod.GET)
简版:@GetMapping("{参数}"),路径上取变量方法形参前加注解@PathVariable 参数类型 参数值
//@RequestMapping(value = "{id}",method = RequestMethod.DELETE)
@DeleteMapping("{id}")
public String delete(@PathVariable Integer id){
......
return "success.jsp";
}
apping("{id}")
public String delete(@PathVariable Integer id){
System.out.println(“restful is running …delete:”+id);
return “success.jsp”;
}
}
##### jsp请求标签修改
```jsp
<h1>restful风格请求表单</h1>
<%--切换请求路径为restful风格--%>
<%--GET请求通过地址栏可以发送,也可以通过设置form的请求方式提交--%>
<%--POST请求必须通过form的请求方式提交--%>
<form action="/user/1" method="post">
<%--当添加了name为_method的隐藏域时,可以通过设置该隐藏域的值,修改请求的提交方式,切换为PUT请求或DELETE请求,但是form表单的提交方式method属性必须填写post--%>
<%--该配置需要配合HiddenHttpMethodFilter过滤器使用,单独使用无效,请注意检查web.xml中是否配置了对应过滤器--%>
<input type="text" name="_method" value="PUT"/>
<input type="submit"/>
</form>
web.xml配置过滤器
<!--配置拦截器,解析请求中的参数_method,否则无法发起PUT请求与DELETE请求,配合页面表单使用-->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<servlet-name>DispatcherServlet</servlet-name>
</filter-mapping>
简化版:
原方式:@RequestMapping(value="{参数}",method=RequestMethod.GET)
简版:@GetMapping("{参数}"),路径上取变量方法形参前加注解@PathVariable 参数类型 参数值
//@RequestMapping(value = "{id}",method = RequestMethod.DELETE)
@DeleteMapping("{id}")
public String delete(@PathVariable Integer id){
......
return "success.jsp";
}