第一章 SpringMVC概述
- SpringMVC简述
SpringMVC是Spring为表现层提供的基于MVC设计理念的优秀的Web框架
SpringMVC通过一套MVC注解,让POJO成为处理请求的控制器,而无需实现任何接口
SpringMVC就是一个Spring。 Spring是容器,ioc能够管理对象,使用
SpringMVC能够创建对象, 放入到容器中(SpringMVC容器), springmvc容器中放的是控制器对象,我们要做的是 使用@Contorller创建控制器对象, 把对象放入到springmvc容器中, 把创建的对象作为控制器使用
这个控制器对象能接收用户的请求, 显示处理结果,就当做是一个servlet使用。
使用@Controller注解创建的是一个普通类的对象, 不是Servlet。 springmvc赋予了控制器对象一些额外的功能。
web开发底层是servlet, springmvc中有一个对象是Servlet : DispatherServlet(中央调度器)
DispatherServlet: 负责接收用户的所有请求, 用户把请求给了DispatherServlet, 之后DispatherServlet把请求转发给Controller对象, 最后是Controller对象处理请求。
-
SpringMVC优点
1)基于MVC架构,分工明确,解耦
2)上手快,使用简单
3)作为Spring框架一部分,方便整合其他部分
4)强化注解的使用 -
SpringMVC第一个程序
新建一个Web动态工程
配置
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">
<!-- 配置DispatcherServlet-->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置DispatcherServlet的一个初始化参数,配置SpringMVC配置文件的位置和名称
实际上可以不通过contextConfigLocation来配置SpringMVC的配置文件,而使用默认的
默认的配置文件为: /WEB-INF/<servlet-name>-servlet.xml
-->
<!-- <init-param>-->
<!-- <param-name>contextConfigLocation</param-name>-->
<!-- <param-value>classpath:springmvc.xml</param-value>-->
<!-- </init-param>-->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Map all requests to the DispatcherServlet for handing -->
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<!-- url可以配置为 *.do *.action 或者斜杠/-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
springDispatcherServlet-servlet.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">
<!-- 配置自动扫描的包 -->
<context:component-scan base-package="com.learn.springmvc.handlers"></context:component-scan>
<!-- 配置视图解析器:如何把handler方法返回值解析为实际的物理视图-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
接收请求的类:
@Controller
public class MyController {
/**
*
* @return 表示本次请求的处理结果 Model:数据,要显示的数据 View视图 比如jsp等
*/
@RequestMapping(value = "/some.do")
public ModelAndView doSome(){
ModelAndView mv = new ModelAndView();
//添加数据,在请求的最后把数据放入到request作用域
mv.addObject("msg", "Hello SpringMVC");
mv.addObject("function", "执行的是doSome方法");
//指定视图
mv.setViewName("/show.jsp");
return mv;
}
}
在WEB-INF下新建views/success.jsp,随便写一点东西在页面上作为显示
在首页添加超链接
<a href="/01_demo1/helloWorld">Hello World</a>
发起请求
然后部署到tomcat即可在首页跳转到success.jsp页面
- SpringMVC组件
- SpringMVC执行流程
1)用户向Tomcat发起一个请求
2)tomcat(web.xml--url-pattern知道来自用户的请求给DispatcherServlet)
3)DispatcherServlet(根据springmvc.xml配置知道 请求转发给控制器---控制器执行方法)
4)DispatcherServlet把请求转发个MyController.doSome()方法
5)框架执行doSome()把得到ModelAndView进行处理, 转发到show.jsp
执行流程补充:
-
用户发起请求
some.do
-
DispatcherServlet接收请求
some.do
,把请求转交给处理器映射器。
处理器映射器:SpringMVC框架中的一种对象,框架把实现了HandlerMapping接口的类都叫做映射器(可能存在多个)。
处理器映射器的作用:根据请求,从SpringMVC容器中获取处理器对象(也即创建控制器Controller的bean对象),然后框架把找到的处理器对象放到一个叫做处理器执行链(HandlerExecutionChain)的类中保存
HandlerExecutionChain:类中保存着 1)处理器对象(Controller);2)项目中所有的拦截器对象保存到List集合(ListinterceptorList)
中央调度器获取处理器执行链HandlerExecutionChain的方法: mappedHandler = getHandler(processedRequest);
- DispatcherServlet把2中的HandlerExecutionChain中的处理器对象交给了处理器适配器对象(可存在多个)
处理器适配器:SpringMVC框架中的对象,需要实现HandlerAdapter接口。
处理器适配器作用:执行处理器方法(调用MyController的doSome()方法),得到返回值ModelAndView
中央调度器获取适配器的方法: HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
通过适配器执行处理器方法:mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
-
DispatcherServlet把3中获取到的ModelAndView交给视图解析器对象
视图解析器:SpringMVC中的对象,需要实现ViewResoler接口(可以有多个)
视图解析器作用:组成视图View完整路径,使用前缀,后缀。
View是一个接口,用于表示视图,在框架中jsp,html不是String表示,而是使用View和它的实现类表示。eg:RedirectView,InternalResourceView
InternalResourceView:视图类,表示jsp文件,视图解析器会创建InternalResourceView类的对象,在这个对象的里面,有一个属性url=prefix+逻辑视图名称+suffix eg:url=/WEB-INF/view/show.jsp -
DispatcherSerlvet把4步骤中创建的View对象获取到,调用View类自己的方法,把Model数据放入到request作用域,执行对象视图的forward,请求结束
第二章 SpringMVC注解式开发
2.1 @RequestMapping定义请求规则
1)当@RequestMapping放在类上时,value值表示这个类的地址的公共前缀,斜杠映射到Web应用的根目录
当@RequestMapping放在方法上时,value值表示这个方法的地址,如果没有类上的@RequestMapping则映射到根目录,否则映射到类标记的目录后面
@Controller
@RequestMapping("/test/")
public class MyController {
/**
*
* @return 表示本次请求的处理结果 Model:数据,要显示的数据 View视图 比如jsp等
*/
@RequestMapping(value = "/some.do")
public ModelAndView doSome(){
ModelAndView mv = new ModelAndView();
//添加数据,在请求的最后把数据放入到request作用域
mv.addObject("msg", "Hello SpringMVC");
mv.addObject("function", "执行的是doSome方法");
//指定视图
mv.setViewName("show");
return mv;
}
@RequestMapping(value = "/other.do")
public ModelAndView doOther(){
ModelAndView mv = new ModelAndView();
//添加数据,在请求的最后把数据放入到request作用域
mv.addObject("msg", "Hello SpringMVC");
mv.addObject("function", "执行的是doOther方法");
//指定视图
mv.setViewName("show");
return mv;
}
}
2)method参数可以指定请求方式,它的值是RequestMethod的枚举值
eg:
@RequestMapping(value = "/some.do", method = RequestMethod.GET)
请求方式不合法错误码405
3)可以使用params和headers支持简单的表达式
/**
*
* @return
*/
@RequestMapping(value = "/testParamsAndHeaders", params = {"username=root"}, headers = {"Connection!=keep-alive"})
public String testParamsAndHeaders(){
System.out.println("testParamsAndHeaders");
return SUCCESS;
}
4)使用通配符
使用通配符
@RequestMapping("testAntPath/*/abc")
public String testAntPath(){
return SUCCESS;
}
2.2 处理器方法的参数
1)处理器方法可以有四个参数
HttpServletRequest
HttpServletResponse
HttpSession
请求中携带的参数
这些参数由系统自动赋值
2)接收用户提交的参数
逐个接收示例:
发起请求的页面表单
<form action="test/receiveProperty.do" method="post">
姓名:<input type="text" name="name" value="user">
年龄:<input type="text" name="age" value="18">
<input type="submit" value="提交"/>
</form>
接收方法
@RequestMapping(value = "/receiveProperty.do")
public ModelAndView doFirst(String name, int age){
ModelAndView mv = new ModelAndView();
mv.addObject("myname", name);
mv.addObject("myage", age);
//指定视图
mv.setViewName("show");
return mv;
}
要求处理器方法的形参名和请求中参数名的name必须一致,框架接收方法时:
1.使用request对象接收请求参数
String strName = request.getParameter("name")
String strAge = request.getParameter("age")
DispatcherServlet调用控制器方法时,按名称对应,把接受的参数赋值给形参,会进行类型转换,可以把String转换成int,long,float,double等类型,所以形参可以直接写基本类型
当参数转换错误时,页面出现400错误码
形参使用包装类时,传递的参数可以为空,包装类的实例为空值null
2.当用post请求提交参数时,中文有乱码,需要使用过滤器处理乱码问题
过滤器可以自定义,也可以使用框架中提供的过滤器
在web.xml中添加过滤器
CharacterEncodingFilter类中doFilterInternal方法对请求和响应的编码进行设置
<!-- 注册声明过滤器,解决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>
<!-- 强制请求对象和响应对象使用Encoding编码的值-->
<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>
使用@RequestParam解决请求中参数名和形参名不一样的问题
注解的属性value写请求的参数名name,位置在处理器方法的形参前
@RequestMapping(value = "/receiveProperty.do")
public ModelAndView doFirst(@RequestParam(value = "rname", required = false) String name, @RequestParam("rage") Integer age){
ModelAndView mv = new ModelAndView();
//添加数据,在请求的最后把数据放入到request作用域
mv.addObject("myname", name);
mv.addObject("myage", age);
System.out.println("name:" + name);
System.out.println("age:" + age);
//指定视图
mv.setViewName("show");
return mv;
}
此外@RequestParam还有一个参数required,默认为true,表示请求必须携带这个参数
对象接收:
使用一个对象去接收请求传递过来的参数,框架自动创建实例,并通过set方法注入属性值。要求属性值的名字和请求传递的参数名相同
/**
* 处理器方法形参是java对象,这个对象的属性名和请求中参数名是一样的
* 框架会创建形参的实例,通过setXxx()方法注入属性
* @return
*/
@RequestMapping(value = "/receiveObject.do")
public ModelAndView doReceiveObject(Student student){
ModelAndView mv = new ModelAndView();
//添加数据,在请求的最后把数据放入到request作用域
mv.addObject("myname", student.getName());
mv.addObject("myage", student.getAge());
mv.addObject("student", student);
System.out.println("name:" + student.getName());
System.out.println("age:" + student.getAge());
//指定视图
mv.setViewName("show");
return mv;
}
2.3 处理器方法的返回值
1)返回ModelAndView
包括数据部分和视图部分,如果处理器方法处理完后,需要跳转到其他资源,且需要在跳转的资源间传递数据,使用ModelAndView返回值。如果只需要视图或者数据,不建议使用。
addObject相当于在request域中添加一个键值对,setViewName()请求转发到指定页面
2)返回String
处理器方法返回的字符串可以指定逻辑视图名,通过视图解析器可以将其转换为物理视图地址
3)返回void(较少使用)
无法表示数据和视图,一般在处理Ajax请求,可以使用void返回值
在方法中使用HttpServletResponse输出数据,响应ajax请求
使用一个按钮发起ajax请求
<button id="btn01" >发起ajax请求</button>
给按钮绑定单击事件
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script type="text/javascript">
$(function () {
// alert("22");
$("#btn01").click(function () {
// alert("单击btn01");
$.ajax({
url:"returnVoid-ajax.do",
data: {
name: "zhangsan",
age: 20
},
type:"post",
dataType:"json",
success:function (resp) {
//返回的resp原本是一个字符串,JQuery会自动将其转换为json对象
alert(resp.name + " ," + resp.age);
}
});
});
});
</script>
接收请求的方法,获取参数,生成一个对象,返回json字符串
@RequestMapping(value = "/returnVoid-ajax.do")
public void doReturnVoidAjax(HttpServletResponse response, String name, Integer age) throws IOException {
System.out.println("name:" + name + ",age:" + age);
//处理ajax
Student student = new Student();
student.setName(name);
student.setAge(age);
ObjectMapper objectMapper = new ObjectMapper();
String s = "";
if(student != null){
s = objectMapper.writeValueAsString(student);
System.out.println("student转换的json" + s);
}
response.setContentType("application/json;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.write(s);
writer.flush();
writer.close();
}
手工实现ajax,json数据,代码存在重复部分:生成json字符串,设置响应返回值,于是引入对象返回值Object
4)返回对象Object
处理器方法返回Object对象,这个对象可以使Integer,String,自定义对象,Map,List等,但返回的对象不是作为逻辑视图出现的,而是作为直接在页面显示的数据出现的
如果需要返回对象,需要加上@ResponseBody注解,将转换后的JSON数据放入到响应体中。(相当于设置 response.setContentType("application/json;charset=utf-8")
)
SringMVC使用jackson工具处理json,需要引入依赖
需要在SpringMVC配置文件加入<mvc:annotation-driven>
注解驱动(相当于写 json = objectMapper.writeValueAsString(obj))
SpringMVC处理器方法返回Object,可以转换为json输出到浏览器,响应ajax的内部原理
<mvc:annotation-driven>
注解驱动
注册注解驱动实现的功能是完成java对象到json,xml,text,二进制等数据格式的转换,涉及到一个接口HttpMessageConvetor接口(消息转换器),接口定义了java对象到json,xml等数据格式的方法,这个接口有很多的实现类,分别完成到json、xml等数据格式的转换
HttpMessageConvetor接口内,用canWrite()方法检查数据能否转换为某个格式,如果能则转换,返回true
write()方法,调用jackson把对象转换json为字符串
注解驱动会给HttpMessageConvetor接口创建多个不同实现类,包括常用的StringHttpMessageConvetor和MappingJackson2HttpMessageConvetor实现类- @ResponseBody注解
加上注解后,自动把生成的json字符串以json格式,编码集采用utf-8格式发送给浏览器
5)返回List
//处理器返回List 实际上返回了json数组[{json1}, {json2}]
@RequestMapping(value = "/returnList-ajax.do")
@ResponseBody
public List<Student> doReturnList() throws IOException {
//处理ajax
Student student2 = new Student("张三", 22);
Student student3 = new Student("啊宝", 13);
return new ArrayList<Student>(Arrays.asList(student2, student3));
}
绑定单击事件,循环遍历获取得到的json数组
<script type="text/javascript">
$(function () {
// alert("22");
$("#btn01").click(function () {
// alert("单击btn01");
$.ajax({
url:"returnList-ajax.do",
data: {
name: "zhangsan",
age: 20
},
type:"post",
// dataType:"json",
success:function (resp) {
// alert(resp.name + " ," + resp.age);
$.each(resp, function (i, n) {
alert(n.name + "," + n.age);
})
}
});
});
});
</script>
6)返回String,但是表示数据,不表示视图
与返回视图的处理器方法的区分方式是是否有@ResponseBody注解,有这个注解表示返回数据
返回字符串中有中文乱码时,使用produces属性指定编码集(如下面的代码所示)
中文乱码是因为StringHttpMessageConvertor接口处理数据时按照其默认的字符集ISO-8859-1进行处理
//处理器返回String 且加了@esponseBody注解,
@RequestMapping(value = "/returnValueString-ajax.do", produces = "text/plain;charset=utf-8")
@ResponseBody
public String doReturnValueString(String name, Integer age) throws IOException {
//处理ajax
return "this is a return string method from 张三";
}
2.4 解读中央调度器的url-pattern
<url-pattern/>
1)当你启动服务器,访问首页index.jsp(http://localhost:8080/05demo/)由tomcat服务器接收请求并进行处理
首页展示的图片,引入的JQuery文件,都由tomcat处理
2)访问http://localhost:8080/05demo/some.do,交给了我此处定义的中央调度器<url-pattern>*.do</url-pattern>
进行处理的
3)访问http://localhost:8080/05demo/html/test.html,由tomcat处理
tomcat的conf目录下有一个web.xml文件,定义了一个DefaultServlet用来处理所有没有映射到其他Servlet的请求,可以用来处理静态资源
<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>
<url-pattern>/</url-pattern>
斜杠/代表所有的请求都可以通过这个servlet,如果自己定义的servlet也设置url-pattern为/,则无法访问首页的图片和静态资源test.html
出现静态资源(html js css image )404错误码
但是我们定义的动态资源some.do可以访问,因为程序中存在控制器方法可以处理这个请求
解决静态资源访问的方式:
方式一:在springmvc配置文件中加入<mvc:default-servlet-handler>
标签
原理是加入这个标签后,框架会创建控制器对象DefaultServletHttpRequestHandler(和我们定义的Controller类似),将请求转发给tomcat的DefaultServlet
加入标签后,我们发现静态资源可以访问了,然后惊奇的发现some.do不可以访问了!
因为DefaultServletHttpRequestHandler将所有请求都转发给了tomcat!
然后我们加入<mvc:annotation-driven></mvc:annotation-driven>
注解驱动,来解决动态资源访问的冲突问题
方式二(常用):在springmvc.xml中添加标签<mvc:resources mapping="" location=""/>
原理是框架会创建ResourceHttpRequestHandler这个处理器对象,通过这个对象处理对静态资源的访问,不依赖tomcat服务器
存在两个属性mapping和location,mapping定义访问静态资源的url地址,使用通配符**,location定义资源在项目中的目录位置
例如<mvc:resources mapping="/image/**" location="/image/"/>
表示能访问url为http:ip:port/项目路径/image/+任意请求地址 后的所有静态资源,资源存储位置在/webapp/image/路径下
<mvc:resources mapping="/html/**" location="/html/"/>
表示能访问url为http:ip:port/项目路径/html/+任意请求地址 后的所有静态资源,资源存储位置在/webapp/html/路径下
同样该方式需要加入<mvc:annotation-driven></mvc:annotation-driven>
注解驱动,来解决动态资源访问的冲突问题
在实际项目中,在web-app下建立static文件夹,把所有的静态资源文件和文件夹都放在static下,就可以配置<mvc:resources mapping="/static/**" location="/static/"/>
来进行访问了
2.5 相对路径和绝对路径问题
在jsp,html中使用的地址,都是在前端页面中的地址,都是相对地址
地址分类:
1.绝对地址:带有协议名称的地址
eg: http://www.xxxx.com,ftp://202.111.222.33
2.相对地址:不带协议开头的
eg: user/some.do,/user/som.do
相对地址不能独立使用,必须有一个参考地址
案例分析:
1)
在http://localhost:8080/06demo/index.jsp上发起请求,请求超链接地址不加斜杠
<a href="test/some.do">发起Some.do结尾的</a>
处理器方法的注解为:@RequestMapping(value = "/test/some.do")
单击超链接后访问到http://localhost:8080/06demo/test/some.do
结论:当页面的超链接没有斜杠打头时,访问地址是当前页面所在路径,加上超链接的href写的地址
即http://localhost:8080/06demo/ + test/some.do
2)超链接改为<a href="/test/some.do">发起Some.do结尾的</a>
单击后访问http://localhost:8080/test/some.do
结论:页面地址以斜杠打头后映射到http://localhost:8080/,即http://ip:port/
解决方案:
加入EL表达式:<a href="${pageContext.request.contextPage}/test/some.do">发起Some.do结尾的</a>
加入base标签:加入后,页面当前的地址如果不以斜杠打头,都以此为参考地址
在xml文件开头加上:
<%
String basePath = request.getScheme() + "://" + request.getServerName()
+ ":" + request.getServerPort() + request.getContextPath() + "/";
%>
在head标签里加上:
<base href="<%=basePath%>">
第三章 SSM整合
3.1搭建SSM开发环境
ssm整合开发
SSM: SpringMVC + Spring + MyBatis
SpringMVC: 视图层 负责接收请求,显示处理结果
Spring,业务层,管理service,dao,工具类对象
MyBatis:持久层,访问数据库
用户发起请求->SpringMVC接收->Spring中的Service对象->使用MyBatis处理数据->Service->SpringMVC->返回给前端
SSM整合也叫做SSI,整合中有容器
1.SpringMVC容器,管理Controller控制器对象
2.第二个容器Spring容器,管理Service,DAO,工具类对象
把使用的对象交给合适的容器创建,管理。把Controller还有web开发的相关对象交给SpringMVC容器,
写在SpringMVC的配置文件中
把service dao对象定义在spring的配置文件,让spring管理这些对象
SpringMVC容器和spring容器是有关系的,关系已经确定好了
SpringMVC容器是Spring的子容器
实现步骤
使用ssm数据库的student表
1. 新建maven web项目
2. 加入依赖
三个框架 jackson mysql druid jsp servlet
3. 写web.xml
1)注册DispatcherServlet,目的:创建SpringMVC容器对象,才能创建Controller对象;创建Servlet,才能接受请求
2)注册Spring的监听器,ContextLoaderListener,目的:创建Spring的容器对象,才能创建service,dao对象
3)注册字符集过滤器,解决post请求乱码的问题
4. 创建包,Controller包,service包,dao包,实体类包名创建好
5. 写3个框架配置文件
1)SpringMVC配置
2)Spring配置文件
3)MyBatis配置文件
4)数据库的属性配置文件
6.编写代码,dao接口和mapper文件,service和实现类,controller,实体类
7.写jsp页面
3.1.1 maven pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.learn</groupId>
<artifactId>ssmproject</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- servlet-->
<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.2.1-b03</version>
<scope>provided</scope>
</dependency>
<dependency> <groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<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>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.9</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
3.1.2 dispatcherServlet.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://mybatis.org/schema/mybatis-spring"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- springmvc的配置文件,声明controller 和其他相关对象-->
<!-- 开启组件扫描-->
<context:component-scan base-package="com.learn.controller"></context:component-scan>
<!-- 视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 注解驱动-->
<mvc:annotation-driven ></mvc:annotation-driven>
<!-- 注解驱动的功能:响应ajax请求,返回json/ 解决静态资源访问问题-->
</beans>
3.1.3 applicationContext.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://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- spring配置文件:声明service,dao,工具类对象-->
<!-- 声明数据源,连接数据库-->
<context:property-placeholder location="classpath:conf/jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 声明SqlSessionFactoryBean创建SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:conf/mybatis.xml"></property>
</bean>
<!-- 声明mybatis的扫描器,创建dao对象-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
<property name="basePackage" value="com.learn.dao"></property>
</bean>
<!-- 声明service的注解@Service所在的包名位置-->
<context:component-scan base-package="com.learn.service"></context:component-scan>
<!-- 事务配置-->
</beans>
3.1.4 jdbc.properties
jdbc.url=jdbc:mysql://localhost:3306/ssm?useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=123456
3.1.5 mybatis.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- <settings>-->
<!-- <setting name="logImpl" value="STDOUT_LOGGING"/>-->
<!-- </settings>-->
<typeAliases>
<!-- name一般是实体类所在的包名-->
<package name="com.learn.domain"/>
</typeAliases>
<mappers>
<!-- <mapper resource="com/learn/dao/StudentDao.xml"></mapper>-->
<package name="com.learn.dao"/>
</mappers>
</configuration>
第四章 SpringMVC核心技术
4.1 请求重定向和转发
1)请求转发,使用forward: + 资源全路径
特点:不经过视图解析器
@RequestMapping(value = "/doForward.do")
public ModelAndView doForWard(HttpServletRequest request, HttpServletResponse response,
HttpSession session){
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "你好,我是/WEB-INF/views/show.jsp页面");
mv.addObject("function", "doForward.do");
//指定视图
//显式转发
mv.setViewName("forward:/WEB-INF/views/show.jsp");
return mv;
}
2)重定向,使用redirect: + 资源全路径
同样不经过视图解析器
框架对重定向时,Model中的简单类型的数据,转为字符串string使用,作为hello.jsp的get请求的参数使用
目的是使doRedirect.do和hello.jsp两次请求之间传递数据
@RequestMapping(value = "/doRedirect.do")
public ModelAndView doRedirect(HttpServletRequest request, HttpServletResponse response,
HttpSession session){
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "你好,我是/hello.jsp页面");
mv.addObject("function", "doRedirect.do");
//重定向
System.out.println("***********doRedirect方法*********8");
mv.setViewName("redirect:/hello.jsp");
return mv;
}
但是在hello.jsp中我们使用EL表达式无法得到数据
<h3>msg数据:${msg}</h3>
<h3>function数据:${function}</h3>
需要更改写法,从请求参数中获取parameter的值
<h2>hello.jsp页面</h2><br/>
<h3>msg数据:${param.msg}</h3>
<h3>function数据:${param.function}</h3>
注意重定向不能访问WEB-INF文件夹下的资源
4.2 异常处理
原始的异常处理中try,catch处理过于繁琐
在SpringMVC框架采用的是统一,全局的异常处理,SpringMVC把所有的异常处理都集中到一个地方,采用的是aop的思想。把业务逻辑和异常处理代码分开,解耦合。
使用到两个注解:1)@ExceptionHandler 2)@ControllerAdvice
异常处理步骤:
1.新建 maven web项目
2.加入依赖
3.新建一个自定义异常类 MyException,在定义它的子类NameException,AgeException
4.在Controller抛出NameException,AgeException
5.创建一个普通类,作用全局异常处理类
1)在类的上面加上@ControllerAdvice注解:给控制器增加异常处理功能
2)在类中定义方法,方法的上面加入@ExceptionHandler注解
6.创建处理异常的视图页面
7.创建SpringMVC的配置文件
1)组件扫描器,扫描@Controller注解
2)组件扫描器,扫描@ControllAdvice所在的包名
3)声明注解驱动
异常处理类:
package com.learn.handler;
import com.learn.exception.AgeException;
import com.learn.exception.NameException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
@ControllerAdvice
public class GlobalExceptionHand {
/**
* 处理异常的方法和控制器方法的定义一样,可以有ModelAndView,可以有多个参数,可以返回String void
*/
/**
*
* @param e 表示Controller抛出的异常对象
* @return
*/
@ExceptionHandler(value = NameException.class)
public ModelAndView doNameException(Exception e){
ModelAndView modelAndView = new ModelAndView();
System.out.println("name exception");
/**
* 异常发生处理逻辑:
* 1. 需要把异常记录下来,记录到数据库,日志文件,记录异常发生的时间,哪个方法发生的,异常错误内容
* 2. 发送通知,把异常的信息通过邮件,短信,微信发送给相关的人员
* 3. 给用户有好的提示
*/
modelAndView.addObject("msg", "姓名必须是zs");
modelAndView.addObject("exception", e);
modelAndView.setViewName("nameError");
return modelAndView;
}
@ExceptionHandler(value = AgeException.class)
public ModelAndView doAgeException(Exception e){
ModelAndView modelAndView = new ModelAndView();
/**
* 异常发生处理逻辑:
* 1. 需要把异常记录下来,记录到数据库,日志文件,记录异常发生的时间,哪个方法发生的,异常错误内容
* 2. 发送通知,把异常的信息通过邮件,短信,微信发送给相关的人员
* 3. 给用户有好的提示
*/
modelAndView.addObject("msg", "age不能为空,不能大于80岁");
modelAndView.addObject("exception", e);
modelAndView.setViewName("ageError");
return modelAndView;
}
/**
* 处理其他异常
* @param e
* @return
*/
@ExceptionHandler
public ModelAndView doOtherException(Exception e){
ModelAndView modelAndView = new ModelAndView();
/**
* 异常发生处理逻辑:
* 1. 需要把异常记录下来,记录到数据库,日志文件,记录异常发生的时间,哪个方法发生的,异常错误内容
* 2. 发送通知,把异常的信息通过邮件,短信,微信发送给相关的人员
* 3. 给用户有好的提示
*/
modelAndView.addObject("msg", "出现了未知错误!");
modelAndView.addObject("exception", e);
modelAndView.setViewName("defaultError");
return modelAndView;
}
}
在jsp页面使用el表达式获取异常处理方法传递的数据
提示信息:${msg}<br/>
异常信息:${exception.message}
4.3 拦截器
1)拦截器是SpringMVC中的一种,需要实现HandlerInterceptor接口
2)拦截器和过滤器类似,功能方向侧重点不同,过滤器是用来过滤请求参数,设置编码字符集等;拦截器是拦截用户的请求,对请求做判断处理的
3)拦截器是全局的,可以对多个Controller做拦截。一个项目中可以有0个或多个拦截器,他们在一起拦截用户的请求。拦截器常用在:用户登录处理,权限检查,记录日志。
拦截器的使用步骤:
1)在自定义类实现HandlerInterceptor接口
2)在SpringMVC配置文件中,声明拦截器,让框架知道拦截器的存在
拦截器的执行时间:
1)在请求处理之前,也就是Controller类中的方法执行之前先被拦截
2)在控制器方法执行之后也会执行拦截器
3)在请求处理完成之后也会执行拦截器
拦截器步骤:
1.新建web项目
2.加入依赖
3.创建Controller类
4.创建一个普通类,作为拦截器使用
1)实现HandlerInterceptor接口
2)实现接口中的三个方法
5.创建show.jsp
6.创建SpringMVC的配置文件
1)组件扫描器,扫描@Controller注解
2)声明拦截器,并指定拦截的请求url
拦截器:
public class MyInterceptor implements HandlerInterceptor {
/**
* preHandler叫做预处理方法
* 特点:>在控制器方法之前先执行的,用户的请求首先到达此方法
* >在这个方法中,可以获取请求的信息,验证请求是否符合要求,可以验证用户是否登录,验证用户是否有权限访问某个链接url
* >如果验证失败,可以截断请求,请求不能不诶处理
* >如果验证成功,可以放行请求,此时控制器方法才能执行
* @param request
* @param response
* @param handler 被拦截的控制器对象的引用
* @return 一个布尔值,true/false true表示放行,false表示拦截
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("拦截器的MyInterceptor的preHandle()");
return false;
}
/**
* postHandle 后处理方法
* 特点:
* >在处理器方法之后执行
* >能够获取到处理器方法的返回值ModelAndView,可以修改ModelAndView的数据和视图,可以影响到最后的执行结果
* >主要是对数据的修正和处理
* @param request
* @param response
* @param handler 被拦截的处理器对象Controller对象
* @param modelAndView 处理器方法的返回值
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("拦截器的MyInterceptor的postHandle");
}
/**
* afterCompletion:最后执行的方法,在请求完成之后执行
* 特点:
* >在请求处理完成后执行的,框架中规定是当你的视图处理完成后,对视图执行了forward,就认为请求处理完成
* >一般做资源回收工作的,程序请求过程中创建了一些对象,在这里可以删除,把占用内存回收。
* @param request
* @param response
* @param handler 被拦截的处理器对象
* @param ex 程序中发生的异常
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
System.out.println("拦截器的MyInterceptor的afterCompletion()");
}
}
拦截器的配置(在springmvc.xml中)
<!-- 声明拦截器 可以有0个或多个-->
<mvc:interceptors>
<mvc:interceptor>
<!-- 指定拦截的请求uri地址
path:就是uri地址,可以使用通配符**
**: 表示任意的字符,文件或者多级目录和目录中的文件
在这里以/user/xxx的uri都会被MyInterceptor拦截器拦截
-->
<mvc:mapping path="/user/**"/>
<!-- 声明拦截器对象-->
<bean class="com.learn.handler.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
preHandle是整个项目的入口,门户,当preHandle返回true,请求可以被处理,返回false,则请求到此截止
拦截器:看作是多个Controller公用的功能,集中到拦截器统一处理,使用的aop的思想
多个拦截器声明:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/user/**"/>
<bean class="com.learn.handler.MyInterceptor"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/user/**"/>
<bean class="com.learn.handler.MyInterceptor2"/>
</mvc:interceptor>
</mvc:interceptors>
执行顺序由声明的顺序决定:
当两个拦截器的preHandel都返回true时
执行顺序是:拦截器1的preHandle->拦截器2的preHandle->控制器方法
->拦截器2的postHandle->拦截器1的postHandle->中央调度器(视图解析器)->拦截器2的afterCompletion->拦截器1的afterCompletion
当拦截器1的prHandle返回true,拦截器2的preHandle返回false
执行顺序:拦截器1的preHandle->拦截器2的preHandle->拦截器1的afterCompletion
当拦截器1的preHandle返回false,与拦截器2无关
执行顺序:拦截器1的preHandle
拦截器和过滤器的区别:
- 过滤器是servlet中的对象;拦截器是框架中的对象
- 过滤器实现Filter接口的对象;拦截器实现HandlerInterceptor
- 过滤器是用来设置request,response的参数,属性的,侧重对数据过滤的;拦截器是用来验证请求的,能截断请求
- 过滤器是在中央调度器之前执行;拦截器在中央调度器之后执行
- 过滤器是tomcat服务器创建的对象;拦截器是SpringMVC容器中创建的对象
- 过滤器只有一个执行时间;拦截器有3个执行时间点
- 过滤器可以处理jsp,js,html等等;拦截器侧重拦截对Controller的对象,如果你的请求不能被DispatcherServlet接收,这个请求不会执行拦截器的内容
实现一个简单登录流程,用拦截器验证用户是否登录
步骤:
其他基本步骤不再列出
1)创建一个login.jsp,模拟登录(把用户的信息放入到session)
2)创建一个logout.jsp,模拟退出系统(把用户信息从session中删除)
3)创建拦截器,从session中获取用户的登录数据,验证是否能访问系统
4)创建一个验证的jsp,如果验证失败,给出提示
login.jsp
通过设置session域模拟登录
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录</title>
</head>
<body>
zs登录系统
<%
session.setAttribute("name", "zs");
%>
</body>
</html>
logout.jsp
删除session域
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登出</title>
</head>
<body>
登出系统
<%
session.removeAttribute("name");
%>
</body>
</html>
拦截器方法:
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("1111111拦截器的MyInterceptor的preHandle()");
//验证用户登录信息
HttpSession session = request.getSession();
Object name = session.getAttribute("name");
String loginName = null;
if (name != null){
loginName = (String)name;
}
if (!"zs".equals(loginName)){
//无法登录,给出提出
request.getRequestDispatcher("/tips.jsp").forward(request, response);
return false;
}
return true;
}
}
index.jsp
<%
String basePath = request.getScheme() + "://" + request.getServerName()
+ ":" + request.getServerPort() + request.getContextPath() + "/";
%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<base href="<%=basePath%>">
</head>
<body>
<p>拦截器</p>
<form method="post" action="user/some.do">
姓名:<input type="text" name="name"><br/>
年龄:<input type="text" name="age"><br/>
<input type="submit" value="提交">
</form>
</body>
</html>
这样不进入login.jsp时,在首页登录会被拦截器拦截。进入logn.jsp后,在首页登录不会被拦截,进入logout.jsp,清除session域的属性,在首页登录就会被拦截器拦截
Comments | 0 条评论