Day—one
Spring集成web开发环境
使用Maven的webapp模板
①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.itheima</groupId>
<artifactId>itheima_springMVC_01</artifactId>
<version>1.0-SNAPSHOT</version>
<!--解决java版本错误-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<java.version>11</java.version>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<!--spring基本包-->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!--servlet基本包-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!--jsp基本包-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.2.1</version>
<scope>provided</scope>
</dependency>
<!--MySQL驱动包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
<!--c3p0数据库驱动包-->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<!--druid数据库驱动包-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!--Junit基本包-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!--Spring集成Junit-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!--webmvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.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>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.3</version>
</dependency>
<!--jdbc模板-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
<!--事务管理-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
<!--普通单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
②applicationContext.xml
<!--配置userDao-->
<bean id="userDao" class="com.itheima.dao.Impl.UserDaoIpml"></bean>
<!--配置UserService-->
<bean id="userService" class="com.itheima.service.Impl.UserServiceIpml">
<property name="dao" ref="userDao"/>
</bean>
③Dao
public class UserDaoIpml implements UserDao {
public void save() {
System.out.println("save running.....");
}
}
④Service
public class UserServiceIpml implements UserService {
private UserDao dao; //将依赖作为成员变量
public void setDao(UserDao dao) { //定义set方法
this.dao = dao;
}
//调用dao
public void save() {
dao.save();
}
}
⑤Servlet
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1、加载配置文件,获取容器对象
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
//2、从容器中获取对象
UserService service = (UserService) app.getBean("userService");
//3、执行service
service.save();
}
}
⑥web.xml
在SpringMVC中Controller替代了原始的Servlet
<!--配置servlet名字和绑定类-->
<servlet>
<servlet-name>UserServlet</servlet-name>
<servlet-class>com.itheima.web.UserServlet</servlet-class>
</servlet>
<!--配置servlet访问路径-->
<servlet-mapping>
<servlet-name>UserServlet</servlet-name>
<url-pattern>/userServlet</url-pattern>
</servlet-mapping>
Spring集成web开发环境:弊端【了解】
//1、加载配置文件,获取容器对象
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
//2、从容器中获取对象
UserService service = (UserService) app.getBean("userService");
//3、执行service
service.save();
从servlet代码中,我们发现我们每次要执行1个doGet/doPost方法都要加载Spring配置文件,创建Spring容器才能使用应用上下文获取对象。但是我们一般web项目开发中,是有很多servlet的,这就意味着我们要多次要加载文件和多次创建Spring容器,这样是不是很多此一举又影响性能?
那么应该怎么解决呢?
首先,实现ServletContextListener接口成为监听器,并且监听事件的创建(web应用启动),当web应用一启动,监听器就会加载Spring配置文件,创建ApplicationContext对象并将其存储到ServletContext容器中供给所有服务器资源共享。(ServletContext是唯一容器(域对象),我要用我就拿就行了,不用自己加载文件再创建容器获取Bean对象)
什么是ServletContext?
Spring集成web开发环境:实现监听器【了解】
在上班原有的基础上做出改变
1、ContextLoadListener类
contextInitialized方法是在web应用一启动就执行
public class ContextLoadListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
//1、加载Spring核心加载文件,创建容器对象(applicationContext)
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
//2、获取ServletContext 对象域
ServletContext servletContext = sce.getServletContext();
//3、将Spirng容器对象存储 到 ServletContext容器中(对象域)
servletContext.setAttribute("app",app);
System.out.println("Spring容器创建成功。。。。");
}
}
2、web.xml配置监听器
【web应用一启动就将spring容器存储到ServletContext】
<!-- 配置监听器 -->
<listener>
<listener-class>com.itheima.listener.ContextLoadListener</listener-class>
</listener>
3、UserServlet
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1、从Servlet对象域中获取容器对象
ServletContext servletContext = request.getServletContext();//获取对象域
//ApplicationContext app = (ApplicationContext) servletContext.getAttribute("app"); 【方法1】
ApplicationContext app = (ApplicationContext) this.getServletContext().getAttribute("app"); //【方法2】
//2、从容器中获取对象
// UserService service = (UserService) app.getBean("userDao"); 【方法1】
UserService service = app.getBean(UserService.class); //【方法2】
//3、执行service
service.save();
}
}
输出:
Spring容器创建成功。。。。
save running.....
Spring集成web开发环境:优化监听器【了解】
(在上边代码基础上进行修改)
在以上代码中,我们有2个地方需要优化:
1、在ContextLoadListener类中加载配置文件,配置文件是通过写死的字符串进行获取。
假设如果我们不使用applicationContext.xml了,我们还得修改源代码,这样就耦合了。
//1、加载Spring核心加载文件,创建容器对象(applicationContext)
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
2、UserServlet中通过开发者指定的app名称获取ServletContext对象域中的Spring容器
app这个名字,是你自己命名并想通过它来获取Spring容器,但是当别人来使用你的代码的时候,别人可能就不知道了。因为别人可能习惯不是取这个名字。
ApplicationContext app = (ApplicationContext) this.getServletContext().getAttribute("app");
解决:
问题①
我要在web.xml中配置全局参数,指定1个名字跟需要加载的配置文件进行绑定;
在加载配置文件的时候,我们获取ServletContext对象域,通过此并获取全局参数
,返回的字符串就是名称,映射的就是配置文件。
web.xml
【自定义名称与配置文件绑定,使用自定义名称就等于使用了该配置文件-】
<!--全局初始化参数-->
<context-param>
<param-name>contextConfigLocation</param-name> 【自定义名称】
<param-value>applicationContext.xml</param-value> 【绑定配置文件】
</context-param>
,tomcat在启动项目的时候,通过ContextLoaderListener监听器和DispatcherServlet分别初始化了service层的bean和controller层的bean。
ContextLoadListener
public class ContextLoadListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
//1、获取ServletContext对象域
ServletContext servletContext = sce.getServletContext();
//2、读取web.xml中的全局参数
String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation");
//3、加载Spring核心加载文件,创建容器对象(applicationContext)
ApplicationContext app = new ClassPathXmlApplicationContext(contextConfigLocation);
//4、将Spirng容器对象存储 到 ServletContext容器中(对象域)
servletContext.setAttribute("app",app);
System.out.println("Spring容器创建成功。。。。");
}
}
问题②
首先,创建1个工具类。
在使用ServletContext对象域获取ApplicationContext对象时,我们只需要传入ServletContext对象域,调用这个工具类的方法即可返回容器对象。使用者,无需知道存储在ServletContext中ApplicationContext的Key,也可以获取。
WebApplicationContextUtils
public class WebApplicationContextUtils {
//定义通过ServletContext对象域 获取 ApplicationContext对象
public static ApplicationContext getWebApplicationContext(ServletContext sc){
return (ApplicationContext) sc.getAttribute("app");
}
}
UserServlet
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1、获取ServletContext对象域
ServletContext servletContext = request.getServletContext();//获取对象域
// ApplicationContext app = (ApplicationContext) this.getServletContext().getAttribute("app");
//2、通过ServletContext对象域 获取 ApplicationContext对象
ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext);
//3、从容器中获取对象
UserService service = app.getBean(UserService.class);
//4、执行service
service.save();
}
}
Spring工具:集成web开发环境
意思是说,上面的优化,Spring已经给我们封装好了,我们配置直接用就行了。
①pom.xml如何配置
<!--web-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
④如何配置web.xml
<!--全局初始化参数-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 配置监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
③如何Servlet获取Spring容器里的对象
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1、获取ServletContext对象域
ServletContext servletContext = request.getServletContext();//获取对象域
//2、获取ApplicationContext容器
//WebApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext); 【WebApplicationContext】是ApplicationContext子类
ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext);
//3、从容器中获取对象
UserService service = app.getBean(UserService.class);
//4、执行service
service.save();
}
SpringMVC:代码演示
SpringMVC框架将Servlet相同而重复的代码进行了封装。这就意味着,我们不需要在web.xml中配置servlet了,我们配置前端控制器,使用访问前端控制器,其他业务逻辑的代码交由给自定义的controller,这个controller的数据是先被前端控制拦截处理过的。【前端控制器调用controller,也就是说访问资源时,访问的是前端控制器,前端控制再调用指定的POJO】
(POJO指普通的java类(controller))
①pom.xml导入SpringMVC包
<!--Spring-mvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
②创建spring-mvc.xml:配置controller组件扫描
- 记得配context命名空间
- 为什么要提取spring-mvc.xml另外扫描Controller?
spring-mvc扫web层的,spring扫其他的。各扫各的,这是习惯,
<!--Controller组件扫描:让Spring去指定类扫描注解-->
<context:component-scan base-package="com.itheima.controller"/>
③web.xml
1、配置Servlet
2、配置Cotroller
3、加载spring-mvc配置文件
<!--配置SpringMVC前端控制器:共有Servlet-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--当Servlet初始化时,就加载spring-mvc.xml文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup><!--服务器一启动就创建serclet-->
</servlet>
<!--配置前端控制器和url的映射关系-->
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern> <!--包括静态资源,所有的请求都找前端控制器(记得在springmvc开放资源)-->
</servlet-mapping>
注意:::
如果在web.xml中servlet-mapping的url-pattern设置的是/。
表示将所有的文件,包含静态资源文件都交给spring mvc处理。就需要用到<mvc:annotation-driven />了(springmvc.xml配置)。如果不加,DispatcherServlet则无法区分请求是资源文件还是mvc的注解,而导致controller的请求报404错误。
④Controller
@Controller //告诉Spring帮我实例化Bean
public class UserController {
@RequestMapping("/quick") //请求映射:请求xxx/quick时,访问此方法
public String save(){
System.out.println("Controller save....");
return "sucess.jsp"; //返回页面
}
}
SpringMVC:流程介绍
controller通过return响应页面。
绿色区域是共有的控制器(SpringMVC前端控制器)。
SpringMVC:组件解析之执行流程
SpringMVC:组件解析之注解解析
如果在类上添加("/xxx")必须在return的页面前添加“/”.
不然,当你访问这个方法并返回页面时,会404提示—提示/xxx/succes.jsp不存在。因为return succes.jsp的访问路径是相对于这个方法的访问路径的,你加了"/"它才会去wepapp目录下去寻找资源。
SpringMVC:组件解析之注解解析2
我们一般是这样写
<!--Controller组件扫描:让Spring去指定类扫描注解-->
<context:component-scan base-package="com.itheima.controller"/>
也有这样写
<context:component-scan base-package="com.itheima">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="aspectj" expression=""/>-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
解释一下:
让Spring实例化Bean的注解(这里是Controller):org.springframework.stereotype.Controller
include-filter type=“annotation” : 表示只扫描com.itheima"包下的annotation注解。exclude-filter相反
SpringMVC:组件解析3
SpringMVC除了扫描组件,其实还有很多组件。
这里只介绍1个类的部分。
代码演示
重定向与转发
视图名称前缀和后缀
假设场景
如果是上述这样的话,那么我们每次写方法返回都要写这么长,太烦了。
怎么解决?
- Spring-mvc.xml:
- UserController
总结
Day_two
一、SpringMVC数据响应:响应
01-SpringMVC数据响应方式(理解)
1) 页面跳转
直接返回字符串
通过ModelAndView对象返回
2) 回写数据
直接返回字符串
返回对象或集合
02-返回字符串形式(应用)
为什么转发要加前缀呢?
因为转发是服务器资源内部之间的请求访问,而重定向是外部的请求访问访问。
03、返回ModelAndView形式1(应用)
在Controller中方法返回ModelAndView对象,并且设置视图名称
public class UserController {
/**
* Model:模型(封装数据)
* View:视图(展示数据)
*/
@RequestMapping("/quick2")
public ModelAndView save2() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("username","LiZhiXi"); //相当于request.setAttribute(键值对)
modelAndView.setViewName("success");//指定显示的页面(前后缀被配置了)
return modelAndView; //返回页面(View)
}
04、返回ModelAndView形式2(应用)
@RequestMapping("/quick3")
public ModelAndView save3(ModelAndView modelAndView ) {//springMVC帮你注入该对象引用
modelAndView.addObject("username","LiZhiXi");
modelAndView.setViewName("success");
return modelAndView; //返回页面(View)
}
/**
*将Model和View分开展示
*/
@RequestMapping("/quick4")
public String save4(Model model) {//Model
model.addAttribute("username","LiZhiXi"); //相当于request.setAttribute(键值对)
return "success"; //返回页面(View)
}
=========================不常用↓=================================
@RequestMapping("/quick5")
public String save5(HttpServletRequest request) {//springMVC帮你封装
request.setAttribute("username","LiZhiXi");
return "success"; //返回页面
}
二、SpringMVC数据响应:回写数据
1、直接返回字符串
①普通字符串
@RequestMapping("/quick6")
@ResponseBody //告诉SpringMVC不要进行视图跳转,直接回写响应体(响应体就是html正文)
public String save6(HttpServletRequest request) {//springMVC帮你封装
return "hello world"; //返回页面
}
=========================不常用↓=================================
@RequestMapping("/quick7")
public void save7(HttpServletResponse response) throws IOException {//springMVC帮你封装
response.getWriter().print("hello world");
}
②SpringMVC返回原始json字符串
在①中,返回的是简单的字符串,但是在实际开发当中,我们一般是将对象or集合 转换成 json格式的字符串 再回写给Ajax异步。
1、pom.xml导包
字符串转json格式的工具
<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>
2、Controller
注意提示:
这个被转换的对象(User)必须提供get方法,因为ObjectMapper翻译中文是“对象映射”,映射什么?将User对象的键值对属性映射到json格式字符串。
@RequestMapping("/quick8")
@ResponseBody
public String save8() throws JsonProcessingException {
User user = new User();
user.setName("刘德华");
user.setAge(30);
//创建工具类
ObjectMapper mapper = new ObjectMapper();
//将对象转换成json
String json = mapper.writeValueAsString(user);
return json;
}
输出:
"{\"name\":\"刘德华\",\"age\":30}"
2、SpringMVC返回对象or集合:1
SpringMVC中的适配器已经有提供给我们把对象or集合转json字符串的方法。我们只要在SpringMVC.xml配置一下适配器的转换参数,并使用@ResponseBody(告诉SpringMVC我是回写字符串,不是跳转视图)
适配器的方法:
输出:
{"name":"张学友","age":34}
2、SpringMVC返回对象or集合:2
(springmvc.xml)
记得写mvc命名空间
所以,以后配置springmvc.xml记得第一时间去配置这个:
<!--mvc的注解驱动-->
<mvc:annotation-driven/>
注意:::
如果在web.xml中servlet-mapping的url-pattern设置的是/。
表示将所有的文件,包含静态资源文件都交给spring mvc处理。就需要用到<mvc:annotation-driven />了。如果不加,DispatcherServlet则无法区分请求是资源文件还是mvc的注解,而导致controller的请求报404错误。
知识总结
三、SpringMVC获得请求数据
0、简介
1、获得基本类型参数
代码演示
浏览器:
http://localhost/user/quick10?username=刘德华&age=11
controller:
@RequestMapping("/quick10")
@ResponseBody
public void save10(String username,int age) throws Exception {//请求参数值实际上是string类型的,但是SpringMVC可以帮我们转
System.out.println("username:"+username);
System.out.println("age:"+age);
}
输出:
username:刘德华
age:11
2、获得POJO类型参数
以前通过getPrimaterMap和空的User传进BeanUntils进行封装Bean,但是这些都被SpringMVC底层封装了。
浏览器:
http://localhost/user/quick10?username=%E5%88%98%E5%BE%B7%E5%8D%8E&age=11
controller:
@RequestMapping("/quick11")
@ResponseBody
public void save11(User user) throws Exception {//获取请求参数,并将参数值的Key与Bean的属性进行映射封装(setxxx)
System.out.println(user);
}
输出:
User{username='刘德华', age=11}
3、获得数组类型参数
为什么要使用Arrays.asList?
因为数组直接打印是地址,集合是值。
首先,该方法是将数组转化为list。有以下几点需要注意:
(1)该方法不适用于基本数据类型(byte,short,int,long,float,double,boolean)
(2)该方法将数组与列表链接起来,当更新其中之一时,另一个自动更新
(3)不支持add和remove方法
4、获得集合类型参数
1、自定义POJO:用来封装User的集合
public class VO {
private List<User> userList;
//get、setxxx
//toString
2、浏览器请求
<body>
<form action="${pageContext.request.contextPath}/user/quick12" method="post">
<input type="text" name="userList[0].username"> <!--userList通过VO的setxxx找到List集合,list[0]代表第一个User对象,User.username = 值-->
<input type="text" name="userList[0].age">
<input type="text" name="userList[1].username">
<input type="text" name="userList[1].age">
<input type="submit" value="提交">
</form>
</body>
3、controller代码
@RequestMapping("/quick12")
@ResponseBody
public void save12(VO vo) throws Exception {//获取请求参数,并将参数值的Key与Bean的属性进行映射封装(setxxx)
System.out.println(vo);
}
4、输出
VO{userList=[User{username='wewqe', age=234324}, User{username='erfdxs', age=343}]}
5、获得集合类型参数:@RequestBody
①Springmvc.xml开放资源访问权限
<!--放行访问web应用资源限制-->
<mvc:resources mapping="/js/**" location="/js/"/>
②浏览器ajax请求
<script src="${pageContext.request.contextPath}/js/jquery-3.3.1.js"></script>
<script>
var userList = new Array();//创建数组
userList.push({username:"刘德华",age:11});//存储对象
userList.push({username:"张学友",age:12});
$.ajax({
type:"POST", //请求方式
url:"${pageContext.request.contextPath}/user/quick13", //请求路径
data:JSON.stringify(userList), //请求参数(将数组格式设置为json字符串格式)
contentType:"application/json;charset:utf-8" //设置请求数据类型
})
</script>
JSON.stringify()的作用是将 JavaScript 对象转换为 JSON 字符串,而JSON.parse()可以将JSON字符串转为一个对象
③controller
@RequestMapping("/quick13")
@ResponseBody
public void save13(@RequestBody List<User> userList) throws Exception {//使用@RequestBody不需要自定义POJO,可以直接封装User进集合
System.out.println(userList);
}
6、开放资源访问
springmvc.xml
<!--放行访问web应用资源限制-->
<!--①-->
<mvc:resources mapping="/js/**" location="/js/"/><!--mapping:表示浏览器访问的映射地址;location:表示存放映射资源的实际位置-->
<mvc:resources mapping="/img/**" location="/img/"/>
<!--②-->
<mvc:default-servlet-handler/>
<!--表示SpringMVC帮你找资源,找不到就交给Tomcat服务器(Tomcat容器内找)-->
为什么如果我们不开放资源,资源就访问不到呢?
springMVC的整个架构控制器就是前段控制器。
而,问题就出在前端控制器的配置上:
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>【缺省】
</servlet-mapping>
当我们访问服务器的时候,它会先默认去寻找内部的servlet,但是我们使用SpringMVC不用servlet,而是使用前端控制器(也是servlet)。既然servlet没有,那就找前端控制器吧,而且你前端控制器配置访问资源路径是【缺省:意味着所有资源都可以访问到】,然后,前端控制器以为
<script src="${pageContext.request.contextPath}/js/jquery-3.3.1.js">
${pageContext.request.contextPath}/js/jquery-3.3.1.js中pageContext.request.contextPath}/js/jquery-3.3.1.js是【虚拟路径(服务器)/虚拟路径(类上注解)/jquery-3.3.1.js的资源】即,controller中的方法名。发现没有这个方法就显示404.
解决:
<mvc:resources mapping="/js/**" location="/js/"/>
表示:默认寻找servlet发现没有,再去这个映射地址去寻找,没有去前端控制器寻找<mvc:default-servlet-handler/>
表示:SpringMVC帮你找资源,找不到就交给Tomcat服务器(Tomcat容器内找)
7、解决Post获取请求参数中文乱码
Web.xml配置解决乱码的拦截器
<!--配置拦截器-->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param><!--服务器初始化创建该对象-->
<param-name>encoding</param-name><!--CharacterEncodingFilter的属性名-->
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern> <!--表示拦截所有资源-->
</filter-mapping>
8、参数绑定注解@RequestParam
还有什么功能?
9、获得Restful风格的参数
10、自定义类型转换器
代码演示:
controller:
@RequestMapping("/quick15")
@ResponseBody
public void save15(Data data) {
System.out.println(data);
}
自定义转换器:
public class DateConverter implements Converter<String, Date> {//泛型:<被转换的类型,需要转换的类型>
@Override
public Date convert(String dateStr) {
//将日期字符串 转换成 日期对象
SimpleDateFormat format = new SimpleDateFormat(dateStr);
Date date = null;
try {
date = format.parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
}
浏览器访问:【报错:2018-01-11格式错误】
http://localhost/user/quick15?date=2018-01-11
Springmvc.xml
<!--mvc的注解驱动-->
<mvc:annotation-driven conversion-service="conversionService"/>
<!--声明类型转换器-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters"><!--设置属性-->
<list>
<bean class="com.itheima.converter.DateConverter"/> <!--叫工厂Bean帮我造个自定义转换器-->
</list>
</property>
</bean>
浏览器访问:【成功获取:2018-01-11】
http://localhost/user/quick15?date=2018-01-11
11、获取Servlet相关的API
12、获取请求头
User-Agent:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36
Cookie:
Idea-f60c4641=0293363a-894e-42ad-aa82-7c5d82a075c1; Idea-661643a5=84c5cb46-98ab-4a2e-86d7-57484c264c08; JSESSIONID=726F5BD7C7DA3F6B2675CEFB9188F66C
Day_three
一、上传文件
⊙客户端
1-客户端表单3要素
2、文件上传原理
⊙服务器端
1、单文件上传步骤
①pom.xml导包
导入fileupload和io坐标
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.3</version>
</dependency>
②springmvc.xml配置文件上传解析器
测试: ③controller编写代码
@RequestMapping("/quick16")
@ResponseBody
public void save16(String username, MultipartFile uploadFile) {//uploadFile要跟表单的name一致;MultipartFile是用来上传的文件的
System.out.println(username);
System.out.println(uploadFile);
}
输出:
lizhxi
org.springframework.web.multipart.commons.CommonsMultipartFile@49afbd7c
③保存文件:③controller编写代码
@RequestMapping("/quick16")
@ResponseBody
public void save16(String username, MultipartFile uploadFile) throws IOException {//uploadFile要跟表单的name一致;MultipartFile是用来上传的文件的
//获取上传文件名称
String originalFilename = uploadFile.getOriginalFilename();
//存储文件
uploadFile.transferTo(new File("C:\\aaa\\"+originalFilename));//transfer To:转移 到
}
2、多文件上传
①多参式
客户端:
<form action="${pageContext.request.contextPath}/user/quick16" method="post" enctype="multipart/form-data">
名称<input type="text" name="username"><br>
文件1<input type="file" name="uploadFile"><br> <!--uploadFile-->
文件2<input type="file" name="uploadFile2"><br> <!--uploadFile2-->
<input type="submit" value="上传">
</form>
controller:
@RequestMapping("/quick16")
@ResponseBody //MultipartFile的引用变量一定要跟表单项一致,才能被注入
public void save16(String username, MultipartFile uploadFile, MultipartFile uploadFile2) throws IOException {//uploadFile要跟表单的name一致;MultipartFile是用来上传的文件的
//获取上传文件名称
String originalFilename = uploadFile.getOriginalFilename();
String originalFilename2 = uploadFile.getOriginalFilename();
//存储文件
uploadFile.transferTo(new File("C:\\aaa\\"+originalFilename));
uploadFile.transferTo(new File("C:\\aaa\\"+originalFilename2));
}
②数组式
客户端:
<form action="${pageContext.request.contextPath}/user/quick16" method="post" enctype="multipart/form-data">
名称<input type="text" name="username"><br>
文件1<input type="file" name="uploadFile"><br><!--uploadFile要一致-->
文件2<input type="file" name="uploadFile"><br>
文件2<input type="file" name="uploadFile"><br>
文件2<input type="file" name="uploadFile"><br>
文件2<input type="file" name="uploadFile"><br>
<input type="submit" value="上传">
</form>
controller:
@RequestMapping("/quick16")
@ResponseBody //MultipartFile的引用变量一定要跟表单项一致,才能被注入
public void save16(String username, MultipartFile[] uploadFile) throws IOException {//uploadFile要跟表单的name一致;MultipartFile是用来上传的文件的
//遍历数组
for (MultipartFile multipartFile : uploadFile) {
//获取上传文件名称
String originalFilename = multipartFile.getOriginalFilename();
//存储文件
multipartFile.transferTo(new File("C:\\aaa\\"+originalFilename));
}
}
二、SpringMVC拦截器
1、快速入门
①编写controller目标方法
@RequestMapping("/quick2")
public ModelAndView save2() {
System.out.println("我是目标方法。。");
//创建视图对象
ModelAndView modelAndView = new ModelAndView();
//存储数据到request
modelAndView.addObject("username", "LiZhiXi");
//绑定视图资源
modelAndView.setViewName("success"); //指定显示的页面(前后缀被配置了)
return modelAndView; //返回视图对象(转发页面)
}
②编写MyInterceptor1实现HandlerInterceptor接口
public class MyInterceptor1 implements HandlerInterceptor {
//执行:目标方法执行之前
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("我是preHandle。。。。");
return true;//false表示拦截访问资源
}
//执行:目标方法执行之后,视图对象返回之前
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("我是postHandle。。。。");
}
//执行:目标方法结束后
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("我是afterCompletion。。。。");
}
}
③springmvc.xml配置拦截器
<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--设置对哪些资源进行拦截操作:所有-->
<mvc:mapping path="/**"/>
<!--告诉spring我这个是拦截器-->
<bean class="com.itheima.controller.MyInterceptor1"/>
</mvc:interceptor>
</mvc:interceptors>
④index.jsp
<h2>Hello World!${name}</h2>
输出:
index.jsp
Hello World!LiZhiXi
控制台:
我是preHandle。。。。
我是目标方法。。
我是postHandle。。。。
我是afterCompletion。。。。
执行流程顺序:
2、拦截器方法演示
①preHandle
作用:【在执行目标方法之前执行】
一般是使用request获取参数来进行拦截操作
//执行:目标方法执行之前
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//拦截访问者获取param请求参数
String param = request.getParameter("param");
//判断请求参数是否存在or不一致
if ("yes".equals(param)){
//一致
return true; //放行访问
}else {
//没有or不一致,转发error页面
request.getRequestDispatcher("/jsp/error.jsp").forward(request,response);
return false;
}
}
②postHandle
特点:【目标方法返回视图对象之前(目标方法已经对视图对象进行了操作,只是还差返回这一步)】
一般用来操作视图对象
//执行:目标方法执行之后,视图对象返回之前
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
//重新设置视图对象中的键值对
modelAndView.addObject("name","刘德华"); //将"lizhixi"改为“刘德华”
}
③拦截器链
使用多个拦截器,这里只使用2个来意思意思。
1、配置springmvc.xml
<!--配置拦截器1-->
<mvc:interceptors>
<mvc:interceptor>
<!--设置对哪些资源进行拦截操作:所有-->
<mvc:mapping path="/**"/>
<!--告诉spring我这个是拦截器-->
<bean class="com.itheima.controller.MyInterceptor1"/>
</mvc:interceptor>
</mvc:interceptors>
<!--配置拦截器2-->
<mvc:interceptors>
<mvc:interceptor>
<!--设置对哪些资源进行拦截操作:所有-->
<mvc:mapping path="/**"/>
<!--告诉spring我这个是拦截器-->
<bean class="com.itheima.controller.MyInterceptor2"/>
</mvc:interceptor>
</mvc:interceptors>
2、2个拦截器(省略)
这里要说的是,拦截器的优先执行顺序跟springmvc.xml配置的Bean的顺序有关。
- 如果是拦截器1Bean在前,拦截器2在后
- 如果是拦截器2Bean在前,拦截器1在后
发现,哪个最优先的拦截器先出发,回来的时候就迟。(filter一样)
三、拦截器案例:用户登录
1、分析
需求:用户必须登录才能访问资源
判断是否登录的依据:根据session是否有存储指定User对象来判断是否登录(服务器共享session对象域数据)
实现:自定义拦截器,并在preHandle进行判断。(有,放行;没有,跳转登录页面)
2、代码实现
1、springmvc.xml配置拦截器
<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--设置对哪些资源进行拦截操作:所有-->
<mvc:mapping path="/**"/>
<!--告诉spring我这个是拦截器-->
<bean class="com.itheima.controller.PrivilegeInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
2、自定义拦截器:
public class PrivilegeInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//判断用户是否登录,本质:判断session中是否存在user
User user = (User) request.getSession().getAttribute("user");
if (user==null){
//没有登录
response.sendRedirect(request.getContextPath()+"/login.jsp");
return false;
}
//放行,访问目标资源
return true;
}
}
3、客户端
表单提交的访问UserController中login方法。
<form action="${pageContext.request.contextPath}/user/login" method="post">
4、修改【springmvc.xml配置拦截器】
<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--设置对哪些资源进行拦截操作:所有-->
<mvc:mapping path="/**"/>
<!--配置哪些资源被排除拦截-->
<mvc:exclude-mapping path="/user/login"/>
<!--告诉spring我这个是拦截器-->
<bean class="com.itheima.controller.PrivilegeInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
</beans>
5、UserController
@RequestMapping("/login")
public String login(String username, String password, HttpSession session) {//springMVC帮你注入Servlet原始API对象引用
//根据账户密码查询User
User user = userService.login(username, password);
//判断User是否存在
if (user != null) {
//User存在,登陆成功
//将User存储到session中,不然会被拦截器拦截
session.setAttribute("user",user);
return "redirect:/index.jsp";
}
//User不存在
return "redirect:/login.jsp"; //返回页面(View)
四、Spring异常处理机制
①异常处理思路
异常从Dao–>Service–>Controller,最后抛给前端控制器解决,因为前端控制器是最终调用者,而前端控制器去找SpringMVC内置的异常处理器。
②SpringMVC异常处理方式
1、简单异常处理器SimpleMappingExceptionResolver
解析:
“SimpleMappingExceptionResolver”接受到抛出的异常会先去
----------------------------------------↓下面这里寻找↓---------------------------------------------
<map> 异常类型 错误视图
<entry key="com.itheima.exception.MyException" value="error"/>
<entry key="java.lang.ClassCastException" value="error"/>
</map>
------------------------------------如果找不到,才去执行这里-------------------------------------
<property name=“defaultErrorView” value=“error”/> 默认错误视图
-----------------------------------下面是具体的介绍------------------------------
<property name=“defaultErrorView” value=“error”/>中name=“defaultErrorView”的defaultErrorView可能是简单异常处理器的属性吧,
“error”表示【视图(页面)】,即,当抛出了异常,而且是没有配置的异常,就去跳转到指定页面(index.jsp、login.jsp、、、)。
【问:为什么可以直接写页面的名称呢?】
答:原本不是的,只是我们在spring-mvc中配置了视图解析器,将网页的前缀后缀都配置了。
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
<map> 异常类型 错误视图
<entry key="com.itheima.exception.MyException" value="error"/>
<entry key="java.lang.ClassCastException" value="error"/>
</map>
key:表示异常的类的全限定名,当然这个“com.itheima.exception.MyException”是我自定义的异常(MyExpetion空的自定义类,继承了Exception)
value:同上
【问:跳转页面有什么用?】
当用户访问页面时出现异常,那么,如果在网页上直接报错显示500等等,用户体验不好。
我们可以使用遇到什么错,跳转什么页面的方式去“忽悠”用户。比如:访问人数较多,请稍后再来!~~
2、自定义异常处理
1、创建自定义异常处理器,实现HandlerExceptionResolver接口
instanceof :用来判断一个对象是否为一个类的实例
public class ExceptionDemo01 implements HandlerExceptionResolver {
/**
* 参数:Exception:将抛出的异常封装成异常对象
* 返回值:ModelAndView(视图):跳转到指定的错误页面
*/
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
//创建视图对象
ModelAndView modelAndView = new ModelAndView();
//判断异常类型
if (ex instanceof MyException){
//存储数据
modelAndView.addObject("error","自定义异常");
modelAndView.setViewName("error1");//跳转视图
}else if (ex instanceof ClassCastException){
modelAndView.addObject("error","格式转换异常");
modelAndView.setViewName("error2");//跳转视图
}
return modelAndView;
}
}
2、springmvc.xml配置异常处理器
<!--配置自定义异常处理器-->
<bean class="com.itheima.myException.ExceptionDemo01"/>
3、测试异常(省略)
总结
spring-mvc.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 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">
<!--Controller组件扫描:让Spring去指定类扫描注解-->
<context:component-scan base-package="com.itheima.controller"/>
<!--配置内部资源视图解析器-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前后缀-->
<property name="prefix" value="/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--配置处理映射器器
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"></bean>
</list>
</property>
</bean>-->
<!--放行访问web应用资源限制-->
<!--①-->
<!-- <mvc:resources mapping="/js/**" location="/js/"/><!–mapping:表示浏览器访问的映射地址;location:表示映射资源的实际地址–>-->
<!-- <mvc:resources mapping="/img/**" location="/img/"/>-->
<!--②-->
<mvc:default-servlet-handler/>
<!-- 表示SpringMVC帮你找资源,找不到就交给Tomcat服务器(Tomcat容器内找)-->
<!--mvc的注解驱动-->
<mvc:annotation-driven conversion-service="conversionService"/>
<!--声明类型转换器-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters"><!--设置属性-->
<list>
<bean class="com.itheima.converter.DateConverter"/> <!--叫工厂Bean帮我造个自定义转换器-->
</list>
</property>
</bean>
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--上传文件的总大小-->
<property name="maxInMemorySize" value="1024000"/>
<!--单个文件上传的大小-->
<property name="maxUploadSizePerFile" value="102400"/>
<!--上传文件的编码类型-->
<property name="defaultEncoding" value="UTF-8"/>
</bean>
<!-- <!–配置拦截器1–>
<mvc:interceptors>
<mvc:interceptor>
<!–设置对哪些资源进行拦截操作:所有–>
<mvc:mapping path="/**"/>
<!–告诉spring我这个是拦截器–>
<bean class="com.itheima.controller.MyInterceptor1"/>
</mvc:interceptor>
</mvc:interceptors>
<!–配置拦截器2–>
<mvc:interceptors>
<mvc:interceptor>
<!–设置对哪些资源进行拦截操作:所有–>
<mvc:mapping path="/**"/>
<!–告诉spring我这个是拦截器–>
<bean class="com.itheima.controller.MyInterceptor2"/>
</mvc:interceptor>
</mvc:interceptors>-->
<!-- <!–配置拦截器–>
<mvc:interceptors>
<mvc:interceptor>
<!–设置对哪些资源进行拦截操作:所有–>
<mvc:mapping path="/**"/>
<!–配置哪些资源被排除拦截–>
<mvc:exclude-mapping path="/user/login"/>
<mvc:exclude-mapping path="/user/quick"/>
<mvc:exclude-mapping path="/js/**"/>
<mvc:exclude-mapping path="/css/**"/>
<mvc:exclude-mapping path="/img/**"/>
<mvc:exclude-mapping path="/pages/**"/>
<mvc:exclude-mapping path="/plugins/**"/>
<!–告诉spring我这个是拦截器–>
<bean class="com.itheima.controller.PrivilegeInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
-->
<!--配置自定义异常处理器-->
<bean class="com.itheima.myException.ExceptionDemo01"/>
</beans>
applicationComtext.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">
<!-- 导入jdbc配置文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置数据库连接池对象-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.url}"/>
<property name="jdbcUrl" value="${jdbc.driver}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--配置jdbc模板对象-->
<bean id="template" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="vo" class="com.itheima.domain.VO"/>
<!--配置userDao-->
<bean id="userDao" class="com.itheima.dao.Impl.UserDaoIpml">
</bean>
<!--配置UserService-->
<bean id="userService" class="com.itheima.service.Impl.UserServiceIpml">
<property name="dao" ref="userDao"/>
</bean>
<context:component-scan base-package="com.itheima"/>
</beans>