SpringMVC
什么是MVC?
Model View Controller
-
表现层
- mvc是表现层的设计模型,和其他层次没有关系
-
业务层
- Service(功能开发都是在这里编写)
-
持久层
- dao层(数据持久化)
SpringMVC
简而言之:SpringMVC是用来处理http请求的。
重要组件:
- DispatcherServlet:前端控制器,接受所有请求(@WebServlet配置为’/‘时不包含jsp)
- HandlerMapping:解析请求格式(判断希望执行那个具体的方法)
- HandlerAdapter:负责调用具体的方法
- ViewResovler:视图解析器,跳转到具体的视图文件
第一个SpringMVC项目
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<servlet>
<servlet-name>springmvc123</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!-- 修改配置文件路径和名称 -->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc123</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
springmvc.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="demo123" class="mvc01.DemoController"></bean>
<!-- 用来解析请求 -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<!-- 需要对urlMap设值注入 -->
<property name="urlMap">
<map>
<!-- key指的是解析出来控制器逻辑名
value:往哪个控制器走...<bean>
-->
<entry key="demo" value-ref="demo123"></entry>
</map>
</property>
</bean>
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
<!-- 配置一个视图解析器,自动补充前缀和后缀 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
编写控制器
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
public class DemoController implements Controller{
@Override
public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {
System.out.println("执行了SpringMVC的控制器");
ModelAndView modelAndView = new ModelAndView("index");
return modelAndView;
}
}
看到这一行代码:返回一个ModelAndView可以直接跳转转发到对应的界面,参数就是页面路径。
ModelAndView modelAndView = new ModelAndView("index");
return modelAndView;
由于我们做了如下配置:
<!-- 配置一个视图解析器,自动补充前缀和后缀 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
所以在 new ModelAndView 的时候填写“index”即可。
注解方式环境搭建
配置mvc
<mvc:annotation-driven></mvc:annotation-driven>
这个注解实际上就包含了HandlerMapping的实现类:DefaultAnnotationHandlerMapping,不仅如此还配置了一个AnnotationMethodHandlerAdapter,也就是说:配置这一个标签,就相当于配置了这两个类。(包:org.springframe.web.servlet.mvc.annotation)
编写Controller
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class DemoController {
@RequestMapping("demo")
public String demo(){
System.out.println("执行demo");
return "main.jsp";
}
}
取消静态资源的拦截
但是静态资源也会被拦截,我们可以告诉mvc哪些资源是静态资源,不需要拦截:
<!-- 告诉mvc哪些资源是静态的,不要拦截 -->
<mvc:resources location="" mapping="/js/**"></mvc:resources>
表示:WebContent下面js文件夹下的所有文件
mapping="/js/*"
表示:WebConten下面js文件夹下的所有子文件以及子目录
mapping="/js/**"
所以一般配置都是两个"*"号
location="/WEB-INF/"
指的是在WEB-INF目录下面。
基本类型或对象参数
还是先配置web.xml
<servlet>
<servlet-name>springmvc123</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!-- 修改配置文件路径和名称 -->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc123</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 字符编码过滤器 -->
<filter>
<filter-name>encoding</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>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
上面已经配置了字符集的过滤器。
springmvc.xml
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
">
<!-- 扫描包 -->
<context:component-scan base-package="cn.jxb"></context:component-scan>
<!-- 注解驱动 -->
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
创建一个People的类,写好getXXX()setXXX()方法。
public class People {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
编写Controller
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class DemoController {
@RequestMapping("demo")
public String demo(People p){
System.out.println("执行demo,名称:"+p.getName()+",年龄:"+p.getAge());
return "main.jsp";
}
}
我们在demo方法中的参数由Spring自动帮我们传递,除了这些我们需要的参数以外,Servlet原生的所有相关类,基本都可以拿来使用,例如:
public String demo(ServletRequest request,HttpSession session){
System.out.println("Request:"+request+",Session:"+session);
return "main.jsp";
}
假如前端页面传过来的参数名称为"name1,age1",我们如果不想使用前端的名称,此时我们可以自己配置注解:
<form action="demo" method="post">
名称:<input type="text" name="name1"/>
年龄:<input type="text" name="age1"/>
<input type="submit" value="点击提交"/>
</form>
这样编写Controller:
public String demo(@RequestParam("name1") String name,@RequestParam("age1") int age){
System.out.println("执行demo,名称:"+name+",年龄:"+age);
return "main.jsp";
}
该注解还有个功能就是增加默认值:
@RequestParam(defaultValue="name1")
比如我们在做分页的时候,就可以直接这样写入默认值,避免了大量的if判断。
@RequestParam(required=true)
假如这里设置为treu则表示必须要传入该参数!假如我们需要一个sql的条件语句,则可以使用这个强制要求必须传入参数,否则就报错(页面505)。
复杂参数
假如我们需要接收一个这样的复杂对象:
<form action="demo" method="post">
名称:<input type="text" name="name"/>
年龄:<input type="text" name="age"/>
<input type="checkbox" name="hover" value="吃饭" />
<input type="checkbox" name="hover" value="睡觉" />
<input type="checkbox" name="hover" value="打游戏" />
<input type="submit" value="点击提交"/>
</form>
那么我们必须要这样配置:
@Controller
public class DemoController {
@RequestMapping("demo")
public String demo(String name,int age,@RequestParam("hover")List<String> ho){
System.out.println("执行demo,名称:"+name+",年龄:"+age+"集合:"+ho);
return "main.jsp";
}
}
那么假如我们请求的参数是这样的:
<form action="demo" method="post">
名称:<input type="text" name="peo.name"/>
年龄:<input type="text" name="peo.age"/>
<input type="submit" value="点击提交"/>
</form>
现在我们的参数名称改变为了"peo.name"这样的参数,那么我们如何接收呢?
首先我们已经拥有了People类:
public class People {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
然后我们可以创建一个Demo类,来装载peo对象:
public class Demo {
private People peo;
public People getPeo() {
return peo;
}
public void setPeo(People peo) {
this.peo = peo;
}
}
然后我们这样配置Cotroller:
@Controller
public class DemoController {
@RequestMapping("demo")
public String demo(Demo demo){
System.out.println(demo);
return "main.jsp";
}
}
基于上面的拓展,假如我们的参数变成这样:
<form action="demo" method="post">
名称:<input type="text" name="peo[0].name"/>
年龄:<input type="text" name="peo[0].age"/>
<br>
名称:<input type="text" name="peo[1].name"/>
年龄:<input type="text" name="peo[1].age"/>
<br>
<input type="submit" value="点击提交"/>
</form>
那么我们只需要对Demo类进行修改即可:
public class Demo {
private List<People> peo;
@Override
public String toString() {
return "Demo [peo=" + peo + "]";
}
public List<People> getPeo() {
return peo;
}
public void setPeo(List<People> peo) {
this.peo = peo;
}
}
restful风格参数
<a href="demo/张三/13">点击我啊</a>
@RequestMapping("demo/{peopleName}/{peopleAge}")
public String demo(@PathVariable("peopleName")String name,@PathVariable("peopleAge")int age){
System.out.println(name+":"+age);
return "/main.jsp";
}
-
@RequestMapping("demo/{peopleName}/{peopleAge}")参数对应请求格式,并且在下面的参数中使用。
-
@PathVariable("peopleName")这个注解时,名称也需要对应。
-
return "/main.jsp"这里的路径需要改写成相对于目录的路径,前面加个"/"即可。
跳转方式
我们默认的请求方式是请求转发,如果想使用重定向可以在return的参数中添加"redirect:"例如:
return "redirect:/main.jsp";
如果需要使用转发(默认的,不用配置)添加"forward:资源路径"
自定义视图解析器
SpringMVC为我们配置了默认的视图解析器。不过我们可以自己在springmvc.xml中配置:
<!-- 自定义视图解析器 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
然后我们就可以在Controller中简单的转发到main.jsp:
@RequestMapping("demo/{peopleName}/{peopleAge}")
public String demo(@PathVariable("peopleName")String name,@PathVariable("peopleAge")int age){
System.out.println(name+":"+age);
return "main";
}
转发到第二个控制器
@RequestMapping("demo")
public String demo(){
System.out.println("转发到ok控制器");
return "forward:ok";
}
@RequestMapping("ok")
public String ok(){
System.out.println("转发到我这里啦");
return "main";
}
当我们在浏览器中输入"demo"回车后,会自动转发到"ok"控制器上,然后"ok"控制器会访问"/main.jsp"这段参数是在springmvc.xml中< bean>中配置的前后缀名称。
@ResponseBody
我们之前使用Servlet实现向页面打印数据:
@Controller
public class DemoController {
@RequestMapping("demo")
public void demo(HttpServletResponse response) throws IOException{
PrintWriter writer = response.getWriter();
writer.append("OKKKKK");
writer.flush();
writer.close();
}
}
现在我们要是想输出数据:
@Controller
public class DemoController {
@RequestMapping("demo")
@ResponseBody
public People demo(){
People people = new People();
people.setName("张三");
people.setAge(13);
return people;
}
}
注意:@ResponseBody这个注解底层使用了jackson解析对象,将对象解析成json字符串打印到网页上面,所以需要导入相关jar包。
{"name":"张三","age":13}
通过@RequestMapping参数,produces="text/html;charset=utf-8":可以配置响应头(Content-Type)信息,进而解决中文乱码的问题:
@RequestMapping(value="demo",produces="text/html;charset=utf-8")
@ResponseBody
public String demo(){
return "我是中文";
}
SpringMVC基本运行原理
如果在web.xml 中设置DispatcherServlet 的< url-pattern>为/时,当用户发起请求, 请求一个控制器, 首先会执行 DispatcherServlet. 由DispatcherServlet调用HandlerMapping的DefaultAnnotationHandlerMapping 解 析 URL, 解 析 后 调 用HandlerAdatper 组 件 的AnnotationMethodHandlerAdapter 调 用Controller 中的 HandlerMethod.当 HandlerMethod 执行完成后会返回View,会被 ViewResovler 进行视图解析,解析后调用 jsp 对应的.class 文件并运行,最终把运行.class 文件的结果响应给客户端