SpringMVC总结
涉及jar包下载:百度网盘链接
提取码:JACK
-
什么是MVC
MVC(Model View Controller)是一种软件设计的框架模式,它采用模型(Model)-视图(View)-控制器(controller)的方法把业务逻辑、数据与界面显示分离。把众多的业务逻辑聚集到一个部件里面,当然这种比较官方的解释是不能让我们足够清晰的理解什么是MVC的。用通俗的话来讲,MVC的理念就是把数据处理、数据展示(界面)和程序/用户的交互三者分离开的一种编程模式
MVC框架模式是一种复合模式,MVC的三个核心部件分别是
1:Model(模型):所有的用户数据、状态以及程序逻辑,独立于视图和控制器
2:View(视图):呈现模型,类似于Web程序中的界面,视图会从模型中拿到需要展现的状态以及数据,对于相同的数据可以有多种不同的显示形式(视图)
3:Controller(控制器):负责获取用户的输入信息,进行解析并反馈给模型,通常情况下一个视图具有一个控制器 -
为什么要使用MVC
程序通过将M(Model)和V(View)的代码分离,实现了前后端代码的分离,会带来几个好处:1、可以使同一个程序使用不同的表现形式,如果控制器反馈给模型的数据发生了变化,那么模型将及时通知有关的视图,视图会对应的刷新自己所展现的内容
2、因为模型是独立于视图的,所以模型可复用,模型可以独立的移植到别的地方继续使用
3、前后端的代码分离,使项目开发的分工更加明确,程序的测试更加简便,提高开发效率
-
JavaWeb中MVC模式的应用
在JavaWeb程序中,MVC框架模式是经常用到的,举一个Web程序的结构可以更好的理解MVC的理念V:View视图,Web程序中指用户可以看到的并可以与之进行数据交互的界面,比如一个Html网页界面,或者某些客户端的界面,在前面讲过,MVC可以为程序处理很多不同的视图,用户在视图中进行输出数据以及一系列操作,注意:视图中不会发生数据的处理操作。
M:Model模型:进行所有数据的处理工作,模型返回的数据是中立的,和数据格式无关,一个模型可以为多个视图来提供数据,所以模型的代码重复性比较低
C:Controller控制器:负责接受用户的输入,并且调用模型和视图去完成用户的需求,控制器不会输出也不会做出任何处理,只会接受请求并调用模型构件去处理用户的请求,然后在确定用哪个视图去显示返回的数据
-
Web程序中MVC模式的优点
耦合性低:视图(页面)和业务层(数据处理)分离,一个应用的业务流程或者业务规则的改变只需要改动MVC中的模型即可,不会影响到控制器与视图部署快,成本低:MVC使开发和维护用户接口的技术含量降低。使用MVC模式使开发时间得到相当大的缩减,它使程序员(Java开发人员)集中精力于业务逻辑,界面程序员(HTML和JSP开发人员)集中精力于表现形式上
可维护性高:分离视图层和业务逻辑层也使得WEB应用更易于维护和修改
-
Web程序中MVC模式的缺点
调试困难:因为模型和视图要严格的分离,这样也给调试应用程序带来了一定的困难,每个构件在使用之前都需要经过彻底的测试不适合小型,中等规模的应用程序:在一个中小型的应用程序中,强制性的使用MVC进行开发,往往会花费大量时间,并且不能体现MVC的优势,同时会使开发变得繁琐
增加系统结构和实现的复杂性:对于简单的界面,严格遵循MVC,使模型、视图与控制器分离,会增加结构的复杂性,并可能产生过多的更新操作,降低运行效率
视图与控制器间的过于紧密的连接并且降低了视图对模型数据的访问:视图与控制器是相互分离,但却是联系紧密的部件,视图没有控制器的存在,其应用是很有限的,反之亦然,这样就妨碍了他们的独立重用。依据模型操作接口的不同,视图可能需要多次调用才能获得足够的显示数据。对未变化数据的不必要的频繁访问,也将损害操作性能
-
Spring MVC简介及特点
Spring MVC采用了松散耦合的可插拔组件结构,比其他的MVC框架更具有灵活性和扩展性,Spring MVC通过使用一套注解,使一个Java类成为前端控制器(Controller),不需要实现任何接口,同时,Spring MVC支持RES形式的URL请求,除此之外,Spring MVC在在数据绑定、视图解析、本地化处理及静态资源处理上都有许多不俗的表现。
Spring MVC围绕DispatcherServlet(前端控制器)为中心展开,DispatcherServlet(前端控制器)是Spring MVC的中枢,和MVC的思想一样,它负责从视图获取用户请求并且分派给相应的处理器处理,并决定用哪个视图去把数据呈现给给用户
Spring MVC特点
1:让我们能非常简单的设计出干净的Web层和薄薄的Web层;2:进行更简洁的Web层的开发;
3:天生与Spring框架集成(如IoC容器、AOP等);
4:提供强大的约定大于配置的契约式编程支持;
5:能简单的进行Web层的单元测试;
6:支持灵活的URL到页面控制器的映射;
7:非常容易与其它视图技术集成,如Velocity、FreeMarker等,因为模型数据不放在特定的API里,而是放在一 个Model里(Map数据结构实现,因此很容易被其他框架使用);
8:非常灵活的数据验证、格式化和数据绑定机制,能使用任何对象进行数据绑定,不必实现特定框架的API;
9:提供一套强大的JSP标签库,简化JSP开发;
10:支持灵活的本地化、主题等解析;
11:更加简单的异常处理;
12:对静态资源的支持; 支持Restful风格。
-
Spring MVC请求响应
SpringMVC把视图渲染、请求处理、模型创建分离了,遵循了MVC框架模式的思想
SpringMVC的请求相应要经过七个阶段,蓝色的方框是Spring框架已经实现好的,第二阶段到第六阶段对应着Spring MVC中的一些核心理念,分别是前端控制器、处理映射器、控制器(处理器)、视图解析器、视图。要注意的是:前端控制器和控制器不是一个东西,前端控制器负责任务分发,控制器是模型的一部分,负责业务和数据的处理
SpringMVC核心控制类的请求流程
SpringMVC的请求相应步骤如下
1、用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获2、DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回
3、DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(…)方法)
4、提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作
HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
1.数据转换:对请求消息进行数据转换。如String转换成Integer、Double等 2.数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等 3.数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
5、Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象
6、根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet
7、ViewResolver 结合Model和View,来渲染视图
8、将渲染结果返回给客户端
-
web.xml文件的配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--ContextLoaderListener的作用就是启动Web容器时,自动装配
ApplicationContext.xml的配置信息。因为它实现了ServletContextListener这个接口,
在web.xml配置这个监听器,启动容器时,就会默认执行它实现的方法。-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--1、param-name是键,param-value是值
2、当服务器启动时,服务器会读取web.xml配置,
当读到<listener></listener>和
<context-param></context-param>这两个节点的时候,
容器会将这两个节点set到ServletContext(上下文对象)中,这样我们在程序中就能通过这个上下文对象去取得我们这个配置值:
-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/Spring-context.xml</param-value>
</context-param>
<servlet>
<servlet-name>app</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--可以看到<init-param>是放在一个servlet内的,所以这个参数是只针对某一个servlet而言的,
所以他们的区别就有点像全局变量和和局部变量的<context-param>是针对整个项目,
所有的servlet都可以取得使用,<init-param>只能是在指定servlet下面配置,就在指定的servlet里面调用
-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<!--
1)load-on-startup元素标记容器是否在启动的时候就加载这个servlet(实例化并调用其init()方法)。
2)它的值必须是一个整数,表示servlet应该被载入的顺序
3)当值为0或者大于0时,表示容器在应用启动时就加载并初始化这个servlet;
4)当值小于0或者没有指定时,则表示容器在该servlet被选择时才会去加载。
5)正数的值越小,该servlet的优先级越高,应用启动时就越先加载。
6)当值相同时,容器就会自己选择顺序来加载。
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>app</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
</web-app>
- Spring-context.xml文件的配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--开启注解扫描-->
<context:component-scan base-package="cn.com.SpringMvc"></context:component-scan>
<!--开启注解配置-->
<mvc:annotation-driven></mvc:annotation-driven>
<!--视图解析器对象-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀/文件所在目录-->
<property name="prefix" value="/WEB-INF/jsp/"></property>
<!--后缀/文件的后缀名-->
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
- 测试类注解详解
/**
* @author Administrator
*
* @RequestMapping(value = "/test")//类级别映射,可以没有,一般用于减少书写量(如果添加时,则是在访问路径中多添加/test)
* 原先是:http://localhost:8080/SpringMvc_war_exploded/app/getUserOne
* 在类上添加该注解之后则是http://localhost:8080/SpringMvc_war_exploded/app/test/getUserOne
* */
@Controller
public class StudyFirstController {
/**
* @RequestMapping(value = {"/getUser","/getUser1","/getUser2"},method = RequestMethod.GET)
* 需要掌握的两个参数
* 1、String value[]:是一个String类型的数组,可以为该方法设置多个请求路径,都可以接收到请求.
* 2、method = RequestMethod.GET/RequestMethod.POST或其他请求的方式.
* */
/**
* @RequestMapping("url") 和 @ResponseBody 既可以放置于类上面,也可放置于方法上面.
* @RequestMapping(“url”), 这里的 url写的是请求路径的一部分,一般作用在 Controller的方法上,作为请求的映射地址。
* @ResponseBody 是作用在方法上的,@ResponseBody 表示该方法的返回结果直接写入 HTTP response body 中,一般在异步获取数据时使用【也就是AJAX】,
* 在使用 @RequestMapping后,返回值通常解析为跳转路径,但是加上 @ResponseBody 后返回结果不会被解析为跳转路径,而是直接写入 HTTP response body 中。
* 比如异步获取 json 数据,加上 @ResponseBody 后,会直接返回 json 数据。@RequestBody 将 HTTP 请求正文插入方法中,
* 使用适合的 HttpMessageConverter 将请求体写入某个对象。
* */
@RequestMapping(value = {"/getUser","/getUser1","/getUser2"},method = RequestMethod.GET)
@ResponseBody
public String getUser(String name){
System.out.println("请求到达了controller层,my name is" + name);
return "index";
}
@RequestMapping("/getUserOne")
public String getUserOne(){
return "index";
}
/**返回ModelAndView对象*/
@RequestMapping("/getInfo")
public ModelAndView getInfo(ModelAndView mv){
/**如果要将pojo对象返回给客户端,直接将其返回是不行的,因为解析不了需要借助于外部jar包*/
User u = new User();
u.setId(1);
u.setName("张三");
u.setAge(20);
mv.setViewName("index");
mv.addObject("u",u);
return mv;
}
/**返回pojo对象解析时需要借助于JSON包*/
@RequestMapping("/getUserAllInfo")
@ResponseBody
public List getUserAllInfo(){
/**返回list集合*/
List<User> list = new ArrayList();
User u1 = new User();
u1.setId(2);
u1.setName("李四");
u1.setAge(21);
User u2 = new User();
u2.setId(3);
u2.setName("王五");
u2.setAge(22);
User u3 = new User();
u3.setId(4);
u3.setName("赵六");
u3.setAge(23);
/**添加数据*/
list.add(u1);
list.add(u2);
list.add(u3);
return list;
}
/**书写的web页面直接放置于web于放置在web/WEB-INF/下的区别
* 1、如果直接将web页面直接放置于web目录下,在客户端是可以直接输入项目路径进而直接访问的,故此不安全
* 而将其放置于web/WEB-IF/目录下是在客户端是访问不了的,因此是相对安全的
*
* 2、当静态资源放在webapp下面的时候,可直接通过浏览器访问,不需要配置映射,安全性略低
*
* 3、WEB-INF是Java的WEB应用的安全目录。所谓安全就是客户端无法访问,只有服务端可以访问的目录。
* 当静态资源放在WEB-INF下面的时候,外部是不能直接访问的,一般是在springmvc的配置文件中配置资源映射
* */
/**对参数练习*/
//1
@RequestMapping("/getLoginInfo")
public String getLoginInfo(String userName,Integer userAge){
System.out.println(userName + "---" + userAge);
return "main";
}
/**
* @RequestMapping("/getLoginInfo/{uName}/{uAge}") 包含了两种请求
* @GetMapping("") 接受前端的get请求
* @PostMapping("") 接受前端的Post请求
*
*
* getLoginInfo/{uName}/{uAge}:相当于给路径新添加了两层但是uName和uAge代表传递的参数同时也代表访问的路径
* 该种路径客户端访问方式
* http://localhost:8080/SpringMvc_war_exploded/app/getLoginInfo/张三/21
* */
//2
@RequestMapping("/getLoginInfo/{uName}/{uAge}")
public String getLoginInfo1(@PathVariable("uName") String userName,@PathVariable("uAge") Integer userAge){
System.out.println(userName + "---" + userAge);
return "main";
}
/**@RequestParam() 注解,有三个属性
* value:代表客户端传过来的参数名称
* defaultValue:当客户端未传递值时,可以定义默认值
* required:代表该参数是否必须,如果为true,则表明该参数是必须传递的
* @RequestParam(value = "userAge",defaultValue = "100",required = true) Integer userAge
* */
//3
@RequestMapping("/getLoginInfo2")
public String getLoginInfo2(String userName,@RequestParam(value = "userAge",defaultValue = "100",required = false) Integer userAge){
System.out.println(userName + "---" + userAge);
return "main";
}
/**接受客户端发送的各种形式的值*/
@RequestMapping("/getAllInfo")
public String getAllInfo(Data data){
/**Data{
* name='李四',
* id=1,
* age=88,
* student=Student{userName='张三', userAge=21},
* map={one=键值对存在},
* list=[有序可重复],
* arrs=[足球, 篮球, 排球]
* }*/
System.out.println(data);
return "main";
}
}
- RequestMapping接口类的源码解析
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
// 命名
String name() default "";
// 映射路径,与 path 属性功能一致,用于指定请求路径,如"/getUser/getUser1"
@AliasFor("path")
String[] value() default {};
@AliasFor("value")
String[] path() default {};
// 请求方法,用于指定处理哪种请求方法:GET、POST、PUT、DELETE等等,默认情况都可以处理。
RequestMethod[] method() default {};
// 指定请求路径中必须包含某些参数值时,才让该方法处理。
String[] params() default {};
// 指定请求中必须包含某些指定的header值,才能让该方法处理请求。
String[] headers() default {};
// 指定处理请求的 提交内容类型 (Content-Type),例如 application/json, text/html
String[] consumes() default {};
// 指定返回的内容类型 ,仅当请求头中的(Accept)类型中包含该指定类型才返回;
String[] produces() default {};
}
-
【Spring框架】mvc:default-servlet-handler/的作用
优雅REST风格的资源URL不希望带 .html 或 .do 等后缀.由于早期的Spring MVC不能很好地处理静态资源,所以在web.xml中配置DispatcherServlet的请求映射,往往使用 *.do 、 *.xhtml等方式。这就决定了请求URL必须是一个带后缀的URL,而无法采用真正的REST风格的URL。如果将DispatcherServlet请求映射配置为"/",则Spring MVC将捕获Web容器所有的请求,包括静态资源的请求,Spring MVC会将它们当成一个普通请求处理,因此找不到对应处理器将导致错误。
如何让Spring框架能够捕获所有URL的请求,同时又将静态资源的请求转由Web容器处理,是可将DispatcherServlet的请求映射配置为"/"的前提。由于REST是Spring3.0最重要的功能之一,所以Spring团队很看重静态资源处理这项任务,给出了堪称经典的两种解决方案。
先调整web.xml中的DispatcherServlet的配置,使其可以捕获所有的请求:
通过以下的配置,便可以捕获所有的请求。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>app</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>app</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
然后一种是采用mvc:default-servlet-handler/
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:default-servlet-handler></mvc:default-servlet-handler>
</beans>
在springMVC-servlet.xml中配置<mvc:default-servlet-handler />后,会在Spring MVC上下文中定义一个org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler,它会像一个检查员,对进入DispatcherServlet的URL进行筛查,如果发现是静态资源的请求,就将该请求转由Web应用服务器默认的Servlet处理,如果不是静态资源的请求,才由DispatcherServlet继续处理。
一般Web应用服务器默认的Servlet名称是"default",因此DefaultServletHttpRequestHandler可以找到它。如果你所有的Web应用服务器的默认Servlet名称不是"default",则需要通过default-servlet-name属性显示指定:
<mvc:default-servlet-handler default-servlet-name=“所使用的Web服务器默认使用的Servlet名称” />
再一种是采用<mvc:resources />
<mvc:default-servlet-handler />将静态资源的处理经由Spring MVC框架交回Web应用服务器处理。而<mvc:resources />更进一步,由Spring MVC框架自己处理静态资源,并添加一些有用的附加值功能。
首先,<mvc:resources />允许静态资源放在任何地方,如WEB-INF目录下、类路径下等,你甚至可以将JavaScript等静态文件打到JAR包中。通过location属性指定静态资源的位置,由于location属性是Resources类型,因此可以使用诸如"classpath:"等的资源前缀指定资源位置。传统Web容器的静态资源只能放在Web容器的根路径下,<mvc:resources />完全打破了这个限制。
其次,<mvc:resources />依据当前著名的Page Speed、YSlow等浏览器优化原则对静态资源提供优化。你可以通过cacheSeconds属性指定静态资源在浏览器端的缓存时间,一般可将该时间设置为一年,以充分利用浏览器端的缓存。在输出静态资源时,会根据配置设置好响应报文头的Expires 和 Cache-Control值。
在接收到静态资源的获取请求时,会检查请求头的Last-Modified值,如果静态资源没有发生变化,则直接返回303相应状态码,提示客户端使用浏览器缓存的数据,而非将静态资源的内容输出到客户端,以充分节省带宽,提高程序性能。
在springMVC-servlet中添加如下配置:
<mvc:resources location="/,classpath:/META-INF/publicResources/" mapping="/resources/**"/>
以上配置将Web根路径"/"及类路径下 /META-INF/publicResources/ 的目录映射为/resources路径。假设Web根路径下拥有images、js这两个资源目录,在images下面有bg.gif图片,在js下面有test.js文件,则可以通过 /resources/images/bg.gif 和 /resources/js/test.js 访问这二个静态资源。
假设WebRoot还拥有images/bg1.gif 及 js/test1.js,则也可以在网页中通过 /resources/images/bg1.gif 及 /resources/js/test1.js 进行引用。
-
静态资源(images,js,css等)放在webapp和放在WEB-INF下的区别
如果直接将web页面直接放置于web目录下,在客户端是可以直接输入项目路径进而直接访问的,故此不安全
而将其放置于web/WEB-IF/目录下是在客户端是访问不了的,因此是相对安全的当静态资源放在webapp下面的时候,可直接通过浏览器访问,不需要配置映射,安全性略低
WEB-INF是Java的WEB应用的安全目录。所谓安全就是客户端无法访问,只有服务端可以访问的目录。
当静态资源放在WEB-INF下面的时候,外部是不能直接访问的,一般是在springmvc的配置文件中配置资源映 -
注解总结
-
@RequestMapping()
是给个方法配置一个访问地址。就比如web学习的Servlet程序,在web.xml中配置了访问地址之后,它们之间就有一个访问映射关系。带有 @RequestParam 的 @RequestMapping
@RequestParam 注解配合 @RequestMapping 一起使用,可以将请求的参数同处理方法的参数绑定在一起。
@RequestParam 注解使用的时候可以有一个值,也可以没有值。这个值指定了需要被映射到处理方法参数的请求参数
如果请求参数和处理方法参数的名称一样的话,@RequestParam 注解的 value 这个参数就可省掉了,
eg:public String getLoginInfo2(String userName,@RequestParam(value = "userAge",defaultValue = "100",required = false) Integer userAge){
System.out.println(userName + "---" + userAge);
return "main";
}
-
@RequestMapping 处理 HTTP 的各种方法
Spring MVC 的 @RequestMapping 注解能够处理 HTTP 请求的方法, 比如 GET, PUT, POST, DELETE 以及 PATCH。
所有的请求默认都会是 HTTP GET 类型的。
为了能降一个请求映射到一个特定的 HTTP 方法,你需要在 @RequestMapping 中使用 method 来声明 HTTP 请求所使用的方法类型,
使用 @RequestMapping 处理动态 URI -
@RequestMapping 注解可以同 @PathVaraible 注解一起使用,
用来处理动态的 URI,URI 的值可以作为控制器中处理方法的参数。你也可以使用正则表达式来只处理可以匹配到正则表达式的动态 URI。
@RequestMapping("/getLoginInfo/{uName}/{uAge}")
public String getLoginInfo1(@PathVariable("uName") String userName,@PathVariable("uAge") Integer userAge){
System.out.println(userName + "---" + userAge);
return "main";
}
@RequestMapping("/getLoginInfo2")
public String getLoginInfo2(String userName,@RequestParam(value = "userAge",defaultValue = "100",required = false) Integer userAge){
System.out.println(userName + "---" + userAge);
return "main";
}
@PathVariable 同 @RequestParam的运行方式不同。
你使用 @PathVariable 是为了从 URI 里取到查询参数值。换言之,
你使用 @RequestParam 是为了从 URI 模板中获取参数值。
-
@responseBody
注解的作用是将controller的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到response对象的body区,通常用来返回JSON数据或者是XML数据。
既可作用在类上面,表示该类中的方法所返回的结果都是 JSON 数据
也可作用在方法上,@ResponseBody 表示该方法的返回结果直接写入 HTTP response body 中,一般在异步获取数据时使用【也就是AJAX】。
注意:在使用 @RequestMapping后,返回值通常解析为跳转路径,但是加上 @ResponseBody 后返回结果不会被解析为跳转路径,
而是直接写入 HTTP response body 中。 比如异步获取 json 数据,加上 @ResponseBody 后,会直接返回 json 数据。
@RequestBody 将 HTTP 请求正文插入方法中,使用适合的 HttpMessageConverter 将请求体写入某个对象。 -
@Controller 表示在tomcat启动的时候,把这个类作为一个控制器加载到Spring的Bean工厂,如果不加,就是一个普通的类,和Spring没有关系。
在注解扫描时一旦发现有个类上面加了类似于@Controller的注解,在容器启动的时候系统就会把它加载到Spring的Bean工厂,并且对其实例化。 -
@RequestBody 该注解是使用在控制器方法之前,该注解可以接受前端前端传过来的所有参数,并将参数以字符串的形式传到控制器方法,使用注解时必须使用POST请求
-
ModelAttribute:该注解的作用与过滤器一致,是在请求到达控制器方法之前执行,在访问到达请求方法之前将值设置完成之后返回给请求方法
//eg:在请求到达控制器方法前为其赋值,在执行方法后将其输出
@ModelAttribute
public User before(){
User u = map.get("user");
u.setId(001);
u.setName("曹总");
u.setAge(21);
return u;
}
@RequestMapping("/testInfo")
public String test(@RequestBody String str, Model model){
System.out.println(str);
//此处得到的user是类名首字母小写所获得数据,否则获取到为 null
//eg:User类——>user
System.out.println(model.getAttribute("user"));
}
结果输出
- 使用@ModelAttribute 和前端传值时的选择 当位置放置于参数前面时,@ModelAttribute(“user”)的作用是当前端有值传过来时,则会覆盖顶有@ModelAttribute注解方法内所赋的值,若是无值传递,则会使用该注解方法中添加的数值
//设置值
@ModelAttribute("u")
public User before(){
User u = new User();
u.setId(002);
u.setName("曹总总");
u.setAge(666);
return u;
}
//取值
@RequestMapping("/testInfo")
public ModelAndView test(@ModelAttribute("u") User user,ModelAndView mv,Data data){
System.out.println(user);
/**设置返回客户端的数据*/
mv.addObject("user",data);
mv.setViewName("main");
return mv;
}
(1)、客户端传参数时:
服务端接收得到的值:
(2)、客户端未传参时:
服务端输出预先设置好的值:
- 当使用该注解的时候若是无返回值,而是将数据存于集合中如Map,则在接收返回值处是对应的键来获取相应的值
//设置值
@ModelAttribute
public void getAttribute(Map<String, Object> map){
User u = new User();
u.setId(001);
u.setName("曹总");
u.setAge(6);
map.put("user",u);
}
//取值
@RequestMapping("/testInfo")
public ModelAndView test(@ModelAttribute("user") User user,ModelAndView mv,Data data){
System.out.println(user);
/**设置返回客户端的数据*/
mv.addObject("user",data);
mv.setViewName("main");
return mv;
}
-
ModelAndView类 详细地址链接
使用ModelAndView类用来存储处理完后的结果数据,以及显示该数据的视图。从名字上看ModelAndView中的
Model代表模型,
**View代表视图,**这个名字就很好地解释了该类的作用。业务处理器调用模型层处理完用户请求后,把结果数据存储在该类的model属性中,把要返回的视图信息存储在该类的view属性中,然后让该ModelAndView返回该Spring MVC框架。框架通过调用配置文件中定义的视图解析器,对该对象进行解析,最后把结果数据显示在指定的页面上。
具体作用:
1、返回指定页面
ModelAndView构造方法可以指定返回的页面名称
也可以通过setViewName()方法跳转到指定的页面2、返回所需数值
使用addObject()设置需要返回的值,addObject()有几个不同参数的方法,可以默认和指定返回对象的名字。
@RequestMapping("/testInfo")
public ModelAndView test(@ModelAttribute("user") User user,ModelAndView mv,Data data){
System.out.println(user);
mv.addObject("user",data);
mv.setViewName("main");
return mv;
}
- 拦截器(Interceptor) 详细链接地址
Spring MVC 的拦截器(Interceptor)与 Java Servlet 的过滤器(Filter)类似,它主要用于拦截用户的请求并做相应的处理,通常应用在权限验证、记录请求信息的日志、判断用户是否登录等功能上。
自定义拦截器的使用方法
xml文件的配置
<!--拦截器配置-->
<mvc:interceptors>
<!--配置全局拦截器-->
//cn.com.SpringMvc.interceptor.MyInterceptor类路径
<bean class="cn.com.SpringMvc.interceptor.MyInterceptor"></bean>
</mvc:interceptors>
示例代码如下:
public class MyInterceptor implements HandlerInterceptor {
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("afterCompletion方法在控制器的处理请求方法执行完成后执行,即视图渲染结束之后执行");
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("postHandle方法在控制器的处理请求方法调用之后,解析视图之前执行");
}
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle方法在控制器的处理请求方法调用之后,解析视图之前执行");
return false;
}
}
在上述拦截器的定义中实现了 HandlerInterceptor 接口,并实现了接口中的 3 个方法。有关这 3 个方法的描述如下。
preHandle 方法:该方法在控制器的处理请求方法前执行,其返回值表示是否中断后续操作,返回 true 表示继续向下执行,返回 false 表示中断后续操作。
postHandle 方法:该方法在控制器的处理请求方法调用之后、解析视图之前执行,可以通过此方法对请求域中的模型和视图做进一步的修改。
afterCompletion 方法:该方法在控制器的处理请求方法执行完成后执行,即视图渲染结束后执行,可以通过此方法实现一些资源清理、记录日志信息等工作。
当项目中有多个拦截器时,在执行类中的方法时,会按照配置文件中配置的类名先后顺序来执行类,类中的方法执行先后顺序如下:
MyInterceptor 拦截器
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("我是MyInterceptor的preHandle方法,在请求方法之前执行,如果返回true,程序继续执行,如果返回false,执行中断");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("我是MyInterceptor的postHandle方法,在请求方法之后执行,视图解析器之前执行.");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("我是MyInterceptor的afterCompletion方法 在请求之后执行,通常可以记录日志等操作");
}
}
xml配置:
<!--拦截器配置-->
<mvc:interceptors>
<!--配置全局拦截器-->
<bean class="cn.com.SpringMvc.interceptor.MyInterceptorSecond"></bean>
<bean class="cn.com.SpringMvc.interceptor.MyInterceptor"></bean>
<!--配置局部拦截器-->
<mvc:interceptor>
//拦截demo下的所有请求
<mvc:mapping path="/demo/*"/>
<bean class="cn.com.SpringMvc.interceptor.MyInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
-
过滤器与拦截器
过滤器(Filter):是在java web中将你传入的request、response提前过滤掉一些信息,或者提前设置一些参数。然后再传入Servlet 或Struts2的 action进行业务逻辑处理。比如过滤掉非法url(不是login.do的地址请求,如果用户没有登陆都过滤掉),或者在传入Servlet或Struts2的action前统一设置字符集,或者去除掉一些非法字符。
拦截器(Interceptor):是面向切面编程(AOP,Aspect Oriented Program)的。就是在你的Service或者一个方法前调用一个方法,或者在方法后调用一个方法。比如动态代理就是拦截器的简单实现,在你调用方法前打印出字符串(或者做其它业务逻辑的操作),也可以在你调用方法后打印出字符串,甚至在你抛出异常的时候做业务逻辑的操作。
二者的区别:
①:拦截器是基于java的反射机制的,而过滤器是基于函数的回调。
②:拦截器不依赖于servlet容器,而过滤器依赖于servlet容器。
③:拦截器只对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
④:拦截器可以访问action上下文、值、栈里面的对象,而过滤器不可以。
⑤:在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
⑥:拦截器可以获取IOC容器中的各个bean,而过滤器不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。
图示:
使用场景:
SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。
1、日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等。
2、权限检查:如登录检测,进入处理器检测检测是否登录,如果没有直接返回到登录页面;
3、性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如apache可以自动记录);
4、通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个处理器都需要的即可使用拦截器实现。
5、OpenSessionInView:如hibernate,在进入处理器打开Session,在完成后关闭Session。 -
异常处理
.xml文件配置
<!--配置异常处理器-->
<bean class="cn.com.SpringMvc.exception.MyExceptionResolver"></bean>
测试方法:
@RequestMapping("/demo/testInfo")
public ModelAndView test(@ModelAttribute("user") User user,ModelAndView mv,Data data) throws SetException {
System.out.println(user);
/**设置返回客户端的数据*/
mv.addObject("user",data);
/**设置返回逻辑视图(逻辑视图:需要配置文件中的全路径地址组成视图)*/
mv.setViewName("main");
/**测试当发生异常时*/
try{
int x = 1/0;
}catch (Exception e){
e.printStackTrace();
SetException se = new SetException();
se.setMsg("分母不能为零.");
throw se;
}
return mv;
}
信息存储类:
public class SetException extends Exception {
private String msg;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
异常处理类:
public class MyExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
SetException ex = (SetException) e;
ModelAndView mv = new ModelAndView();
mv.addObject("msg",ex.getMsg());
mv.setViewName("error");
return mv;
}
}
运行结果输出:
- 文件上传
所需jar包
文件上传是项目开发中常见的功能,那么spring MVC是怎么实现文件上传的呢,接下来我们看看!
首先,当然是jar包的导入(我这里没有使用maven,所以需要手动导包)
接下来是.xml文件的配置
<!--文件上传配置-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="8388608"></property>
</bean>
然后是测试类了
/**
* 文件上传
*/
@RequestMapping(value = "/testUploadFile", method = RequestMethod.POST)
public ModelAndView testUploadFile(MultipartFile partFile, HttpServletRequest req) throws SetException {
System.out.println("上传文件的原名字: " + partFile.getOriginalFilename());
String realpath = req.getServletContext().getRealPath("/") + "static\\";
System.out.println("realpath:" + realpath);
ModelAndView mv = new ModelAndView();
try {
/**使用UUID给每个上传的文件重新命名*/
String newName = UUID.randomUUID().toString();
String oldName = partFile.getOriginalFilename();
String sux = oldName.substring(oldName.lastIndexOf("."));
File file = new File(realpath + newName + sux);
/**判断文件是否存在,若是不存在,则创建*/
if (!file.isDirectory()) {
file.mkdirs();
/**写入本地磁盘transferTo方法可以将上传的文件写入本地磁盘*/
partFile.transferTo(file);
mv.addObject("msgInfo", "文件上传成功!");
mv.setViewName("main");
}
} catch (IOException e) {
SetException ex = new SetException();
ex.setMsg("文件上传失败!");
e.printStackTrace();
throw ex;
}
return mv;
}
前端代码:
<!--
enctype="multipart/form-data"的作用:
表单中enctype="multipart/form-data"的意思,是设置表单的MIME编码。
默认情况,这个编码格式是application/x-www-form-urlencoded,不能用于文件上传;
只有使用了multipart/form-data,才能完整的传递文件数据,进行下面的操作.
enctype="multipart/form-data"是上传二进制数据; form里面的input的值以2进制的方式传过去。
form里面的input的值以2进制的方式传过去,所以request就得不到值了。
-->
<form action="testUploadFile" method="post" enctype="multipart/form-data">
<!--此处的partFile要与后端参数MultipartFile partFile名一致,否则需要用注解来接收.-->
<input type="file" name="partFile">
<input type="submit" value="提交">
</form>
如此文件上传即可成功…