SpringMVC笔记
实现步骤
springmvc就是一个spring,直接像spring一样用就好了
spring通过IOC管理对象,可以通过bean标签,annotation
springmvc强化了注解annotation的使用
我们要做的是使用@controller创建控制器对象,吧对象放入到springmvc容器中,把创建的对象作为控制器使用,这个控制器可以处理用户请求,就像servlet一样,但他并不是servlet, 通过@controller创建的类是个普通类
一般一个请求的流程是:前端页面发送请求— 给servlet---- 再将请求交给我们的service、dao等
而springmvc是这样子:
他有一个servlet类:DispatcherServlet
前端发送请求— 给我们的DispatcherServlet---- 再将请求交给我们的controller
- 新建web模板
- 加入依赖
- spring-webmvc依赖, 简介把spring的依赖都加入到项目
- jsp, servlet 依赖
- 在web.xml中注册springmvc的核心对象DispatcherServlet
- 创建前端页面
- 创建控制器类
- 添加@Controller注解,并放入到springmvc容器中
- 在类方法中添加@RequestMapping注解
- 创建springmvc的配置文件
- 声明组件扫描器,指定@controller所在的package
- 声明视图解析器
依赖
我们首先添加依赖:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
servlet
然后我们在web.xml中注册DispatcherServlet
:
<servlet>
<servlet-name>myconf</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:webconfig.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
这里通过
load-on-startup
标签,确定了这个servlet会被加载
DispatcherServlet
被加载的时候会执行init
方法:// 创建容器,读取配置文件 WebApplicationContext ctx = new WebApplicationContext("${contextConfigLocation}") // 把容器对象放到servletcontext中 getServletContext.setAttribute(key, ctx)
servlet mapping:
<servlet-mapping>
<servlet-name>myconf</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
表示所有的.do结尾的请求都交给
myconf
这个servlet处理
servlet-mapping
关于servlet-mapping:我们有两种方式:
一种就是如上使用*.do
这个
还有一种是使用/
关于URL,我们的URL可以由我们指定,而我们没有指定的,会交给Tomcat自带的servlet处理:
<!-- tomcat/conf/web.xml -->
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- The mappings for the JSP servlet -->
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.jspx</url-pattern>
</servlet-mapping>
默认servlet-mapping冲突
当我们使用/
来作为url-pattern
的时候,显然和默认的url-pattern
是冲突的,那么默认的就会失效,就不能访问静态资源。
那么我们这时候有两种方法解决这个问题:
第一种方法
我们在contextConfig文件中添加<mvc:default-servlet-handler/>
标签
原理呢是这个标签会自动为我们添加DefaultServletHttpRequestHandler
类,就是类会使用RequestDispatcher
这个类的forward
方法将请求转发给Tomcat
处理
单独添加这个
<mvc:default-servlet-handler/>
标签是会有问题的:他将所有的/
请求都转发给了默认handler
,我们自己写的就不会使用了
所以我们要再添加个标签<mvc:annotation-driven/>
来激活我们的注解
这个<mvc:annotation-driven/>
标签还有别的作用,就是我们使用@ResponseBody
注解,也就是想要返回json
数据的时候,要添加这个标签才能处理我们自定义的数据,否则就只有内置的类String
等可以被转换为json
第二种方法(主要)
使用<mvc:resources mapping="static/**" location="/static/"/>
标签,其中mapping
是静态资源的URI,location
是静态资源所在的位置
配置文件
我们创建好servlet后,还要在配置文件中设置:
<context:component-scan base-package="conf"/>
这就是个spring配置文件,我们在
servlet
标签中配置的contextConfigLocation
属性所指向的配置文件
component-scan标签表示从base-package
这个包里面去找组件。
我们可以用xml或annotation来创建组件
接下来我们创建controller:
//在类上方添加
@Controller
public class myController {
// 在方法上添加
@RequestMapping(value = "ww.do")
public ModelAndView doSome(){
System.out.println("it is doing!@#$!%!^!^");
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg","wbwb");
modelAndView.setViewName("sm.jsp");
return modelAndView;
}
}
表示ww.do这个路径,交给这个controller处理
返回一个ModelAndView
对象,指向了sm.jsp
这个前端页面
整理一下一个请求的过程
发送请求 some.do
Tomcat接收到请求,到web.xml文件中去找
找到了url-pattern
,根据url-pattern
找到了servlet
标签,是一个dispatcherservlet
这个servlet
对应的配置文件是webconfig.xml
这个webconfig.xml
声明了组件在conf
这个package中
在conf
这个package中找到了所有的controller
这些controller
中的resultmapping
中有找到了some.do
对应的方法
所有就调用这个方法
在根据源码看一下过程
1、Tomcat启动,创建容器的过程:
通过load-on-startup来确定顺序,创建dispatcherservlet
对象
这个dispatcherservlet
是继承自各个servlet
的,一层一层来说,启动是会调用init
方法
这个init
方法调用了initservletbean
方法
这个initservletbean
方法又调用了initWebApplicationContext
方法来创建容器
2、处理请求的过程:
要知道这个我们先在浏览器disable cache
然后再试
会先执行service
方法
这个service
方法,执行processRequest
方法
这个processRequest
方法,调用doService
方法
这个doService
方法,调用doDispatch
方法
这个doDispatch
方法,调用processDispatchResult
方法
视图view
改变视图路径
由于view文件如果直接放在外面会被直接访问到
我们把他放到WEB-INF
目录下,这个目录下的文件受到保护,不能直接通过网址输入访问到
我们可以在配置文件中设置view文件的路径:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/"/>
<property name="suffix" value=".jsp"/>
</bean>
然后在modelAndView.setViewName()
的时候只需要填写prefix
和suffix
中间的就行了
这个@ResultMapping
也可以在类上面添加,这样子就会在类里面的方法路径前面添加一个路径了:
@Controller
@RequestMapping("/test")
public class myController {
@RequestMapping(value = "/ww.do")
public ModelAndView doSome(){
System.out.println("it is doing!@#$!%!^!^");
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg","wbwb");
modelAndView.setViewName("sm.jsp");
return modelAndView;
}
}
这样子就会访问/test/ww.do
了
@ResultMapping
也可以指定请求方法
请求参数
就直接在方法名后面接收参数就行了
public ModelAndView doSome(HttpServletRequest request,
HttpServletResponse response,
HttpSession session){}
请求参数也可以按照顺序直接输入具体的参数:
public ModelAndView doSome(String name, int age){}
或者使用annotation指定参数的名称:
public ModelAndView doSome(@RequestParam("rname" required=false) String name, @RequestParam("rage") int age){}
这样子会自动执行如下操作:
//使用request对象接收参数
String strName = request.getParameter("name");
String strAge = request.getParameter("age");
//mvc框架通过DispatcherServlet调用doSome方法
doSome(strName, Integer.valueOf(strAge))
还可以使用类的方法获取参数
@RequestMapping("/qq.do")
public ModelAndView doOther(Student student){
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("name", student.getName());
modelAndView.addObject("age", student.getAge());
modelAndView.setViewName("sm");
return modelAndView;
}
原理和setter注入差不多,request参数为
name
,就会给student
执行setName()
方法
添加encoding Filter防止post请求出现乱码
<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>
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
使用json
1、利用HttpServletResponse
@RequestMapping("aa.do")
public void doVoid(Student student, HttpServletResponse response) throws IOException {
PrintWriter writer = response.getWriter();
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(student);
writer.println(json);
writer.flush();
writer.close();
}
2、利用注解
@RequestMapping(value = "/cc.do", method = RequestMethod.POST)
@ResponseBody
public Student doCC(String name, Integer age){
Student student1 = new Student();
student1.setAge(age);
student1.setName(name);
return student1;
}
使用注解的时候我们还需要添加依赖和注解驱动,
依赖 pom.xml:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.2</version>
</dependency>
驱动 context.xml
<mvc:annotation-driven >
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"></bean>
</mvc:message-converters>
</mvc:annotation-driven>
或者
<mvc:annotation-driven />
这个驱动默认添加有
HttpMessageConverter
的7个实现类
我们也可以指定添加哪些实现类
这个HttpMessageConverter
接口有四个方法:
canRead
canWrite
read
write
他是先使用canWrite
检验我们的方法返回值Object能不能转换成为json,然后使用write
将我们的Object转换为json
这个write
方法就做了类似上面void
返回值时候我们做的事情:
String json = objectMapper.writeValueAsString(student);
使用注解
@ResponseBody
的时候,方法返回的Object
,包括String
类型的,都是数据
不适用这个注解的时候,方法返回如果为String
,那么这个String
就是view
的路径
URL
指的是在静态文件里的路径,比如HTML文件里的<a>
标签
绝对路径
也不是真的绝对路径,而是以/
开头的路径
那么默认的/
的前面就是项目的主URL,也就是启动项目时,浏览器自动打开的URL
有个叫pageContext.request.contextPath
的对象,他表示的就是项目的主URL
相对路径
就是相对于当前URL的路径,比如相对路径为cc
,当前URL为http://www.baidu.com/aa
,那么就会去http://www.baidu.com/aa/cc
我们可以在静态资源页面中写上<base href="http://www.baidu.com/aa"/>
那么这个页面中所有的相对路径就都是相对于这个base
标签的了