第一章:三层架构和MVC
1. 三层架构
1. 咱们开发服务器端程序,一般都基于两种形式,一种C/S架构程序,一种B/S架构程序
2. 使用Java语言基本上都是开发B/S架构的程序,B/S架构又分成了三层架构
3. 三层架构
1. 表现层:WEB层,用来和客户端进行数据交互的。表现层一般会采用MVC的设计模型
2. 业务层:处理公司具体的业务逻辑的
3. 持久层:用来操作数据库的
2. MVC模型
1. MVC全名是Model View Controller 模型视图控制器,每个部分各司其职。
2. Model:数据模型,JavaBean的类,用来进行数据封装。
3. View:指JSP、HTML用来展示数据给用户
4. Controller:用来接收用户的请求,整个流程的控制器。用来进行数据校验等。
3. SpringMVC 在三层架构的位置
第二章 SpringMVC 的入门
入门案例:
1.配置环境
1.1 IDEA的前期准备
更快完成构建:name=archetypeCatalog,value=internal
然后接着下一步直到maven构建完成!
因为我们选择的是webapp这个模块,里面还不够完整,比如说现在和不可以写java代码和外部配置。
我们在main包下创建java和resources包。
右键指定:
指定完成会变色。
1.2 在java里面创建项目结构
1.3 然后在resources下创建springmvc.xml文件
在WEBINF下创建pages包在创建Success.jsp 用于跳转成功展示:
2.接下来在pom.xml中导入maven坐标
<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>
<!-- spring版本锁定 -->
<spring.version>5.0.2.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
3. 配置核心的控制器(配置DispatcherServlet)
3.1. 在web.xml配置文件中核心控制器DispatcherServlet
<!--SpringMVC的核心控制器-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--配置servlet的初始化参数,读取springmvc的配置文件,创建spring容器-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--配置servlet启动时加载对象-->
<load-on-startup>1</load-on-startup>
</servlet>
<!--servlet被请求-->
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern><!--使用/,发送任何请求都会经过这里-->
</servlet-mapping>
3.2 编写springmvc.xml的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--配置spring创建容器要扫描的包-->
<context:component-scan base-package="com.itmei"></context:component-scan>
<!--配置视图解析器-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property> <!--配置文件所在的目录-->
<property name="suffix" value=".jsp"></property><!--配置文件的后缀-->
</bean>
<!--前端控制器,哪些静态资源不拦截-->
<!--属性:location元素表示webapp目录下的包下的所有文件
mapping元素表示以/static开头的所有请求路径,如/static/a 或者/static/a/b-->
<mvc:resources location="/css/" mapping="/css/**"/> <!-- 样式 -->
<mvc:resources location="/images/" mapping="/images/**"/> <!-- 图片 -->
<mvc:resources location="/js/" mapping="/js/**"/> <!-- javascript -->
<!--配置spring开启注解mvc的支持-->
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
这里还可以吧,静态资源不拦截给配上,以后用得到。
4编写index.jsp和HelloController控制器类
4.1 index.jsp
4.2 HelloController 编写
4.3 在WEB-INF目录下创建pages文件夹,编写success.jsp的成功页面
5 配置服务器tomca
添加项目
6. 启动Tomcat服务器,进行测试
可以看出已经成功了
原理图:
详细图:
2.3.入门案例中涉及的组件:
1.DispatcherServlet:前端控制器
用户请求到达前端控制器,它就相当于 mvc 模式中的 c,dispatcherServlet 是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet 的存在降低了组件之间的耦合性。
2.HandlerMapping:处理器映射器
HandlerMapping 负责根据用户请求找到 Handler 即处理器,SpringMVC 提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
3.Handler:处理器
它就是我们开发中要编写的具体业务控制器。由 DispatcherServlet 把用户请求转发到 Handler。由Handler 对具体的用户请求进行处理。
4.HandlAdapter:处理器适配器
通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。
5.View Resolver:视图解析器
View Resolver 负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。
6.View:视图
SpringMVC 框架提供了很多的 View 视图类型的支持,包括:jstlView、freemarkerView、pdfView等。我们最常用的视图就是 jsp。
一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开
发具体的页面。
7.配置文件的 < mvc:annotation-driven>说明
在 SpringMVC 的各个组件中,处理器映射器、处理器适配器、视图解析器称为 SpringMVC 的三大组件。
使 用 < mvc:annotation-driven> 自动加载 RequestMappingHandlerMapping (处理映射器) 和
RequestMappingHandlerAdapter ( 处 理 适 配 器 ) , 可 用 在 SpringMVC.xml 配 置 文 件 中 使 用< mvc:annotation-driven>替代注解处理器和适配器的配置。
注意:
一般开发中,我们都需要写上此标签(虽然从入门案例中看,我们不写也行,随着课程的深入,该标签还有具体的使用场景)。
明确:
我们只需要编写处理具体业务的控制器以及视图。
2.4 RequestMapping 注解
2.4.1 使用说明
访问路径:
第3章 请求参数的绑定
3.1绑定说明
我们都知道,表单中请求参数都是基于 key=value 的。
SpringMVC 绑定请求参数的过程是通过把表单提交请求参数,作为控制器中方法参数进行绑定的。
例如:
3.1.2 支持的数据类型:
基本类型参数:
包括基本类型和 String 类型
POJO 类型参数:
包括实体类,以及关联的实体类
数组和集合类型参数:
包括 List 结构和 Map 结构的集合(包括数组)
SpringMVC 绑定请求参数是自动实现的,但是要想使用,必须遵循使用要求。
3.1.3 使用要求
如果是基本类型或者 String 类型: 要求我们的参数名称必须和控制器中方法的形参名称保持一致。(严格区分大小写)
如果是 POJO 类型,或者它的关联对象:
要求表单中参数名称和 POJO 类的属性名称保持一致。并且控制器方法的参数类型是 POJO 类型。
如果是集合类型,有两种方式: 第一种:
要求集合类型的请求参数必须在 POJO 中。在表单中请求参数名称要和 POJO 中集合属性名称相同。
给 List 集合中的元素赋值,使用下标。
给 Map 集合中的元素赋值,使用键值对。
第二种:
接收的请求参数是 json 格式数据。需要借助一个注解实现。
注意: 它还可以实现一些数据类型自动转换。
如遇特殊类型转换要求,需要我们自己编写自定义类型转换器。
3.1.4 使用示例
3.1.4.1 基本类型和 String 类型作为参数
创建param.jsp 页面:
创建ParamController类,用于测试请求参数的封装
测试运行:
可以看出已经自动封装了。
3.1.4.2 POJO(实体类) 类型作为参数
POJO表达式:就是把实体类作为参数
创建实体类:Account 并且实现序列化接口,生成get,set和tostring
package com.itmei.domain;
import java.io.Serializable;
public class Account implements Serializable {
private String username;
private String password;
private Integer money;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getMoney() {
return money;
}
public void setMoney(Integer money) {
this.money = money;
}
public String toString() {
return "Account{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", money=" + money +
'}';
}
}
然后在创建一个方法用于模拟实体类方式获取请求的封装
更改param.jsp的form表单中的访问路径:param/saveParam
测试运行:这里没有给注解添加访问属性(method),通过这次可以发现,表单使用post请求,我们注解没有设置,也可以获取信息,证明不设置访问属性默认post可以被访问到, method 不写的话,默认GET、POST都支持,根据前端方式自动适应
如果我们在Account里面又引用User实体类呢:
如:
创建User实体类:
Account 用于User实体类对象:
更改parm.jsp添加两个输入框:
测试运行结果:ParamController这个里面的代码不需要修改。
注意: 这些演示中我没有输入任何中文,如果用中文不会报错,但是显示的是问号,那怎么解决呢!
3.1.4.3 请求参数乱码问题
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>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<!-- 过滤器所有请求 -->
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
测试:
这样就解决了乱码问题:
补充:
3.1.4.4 POJO 类中包含集合类型参数
常用的集合类型list,map
我们在Account实体类添加两个集合,并且集合类型是User的实体类,创建set,get和toString方法
然后改造param.jsp代码:
里面name的值:点后面的是User的属性名称。map的[]里面值可以随便因为设置的时候key设置类型为String类型,而且[]的值不会影响map下标,而list的[]的值会影响list的下标。
测试运行:ParamController这个里面的代码不需要修改。
3.2特殊情况
3.2.1 自定义类型转换器
3.2.1.1 使用场景:
页面提交的数据都是字符串格式的,springMVC会自动转换格式,但是也有特殊情况转换不了,所以我们要学会自定义类型转换器
在user中添加date日期对象和getset,toString方法;
ParamController类中添加方法saveUser方法
修改param.jsp代码:访问路径等信息
测试运行:
使用其他日期格式:发现springMVC不能把这个格式转为日期类型所以出现问题,连方法都进不去。这样我们需要定义我们的类型转换器。
发现出现错误
3.2.1.2 使用步骤
第一步:定义一个类,实现 Converter 接口,该接口有两个泛型。
StringToDateConverter类:字符串转日期类型
public class StringToDateConverter implements Converter<String, Date> {
/**
* @param source 传入进来的值
* @return
*/
public Date convert(String source) {
//判断
if (source==null){
throw new RuntimeException("请您传入数据");
}
//定义日期格式
DateFormat df=new SimpleDateFormat("yyyy-MM-dd");
//把字符串转换日期
try {
return df.parse(source);
} catch (Exception e) {
throw new RuntimeException("数据类型转换出现错误!");
}
}
}
第二步:在 spring 配置文件中配置类型转换器。
spring 配置类型转换器的机制是,将自定义的转换器注册到类型转换服务中去。
<!--配置自定义类型转换器-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.itmei.utils.StringToDateConverter"></bean>
</set>
</property>
</bean>
第三步:在 annotation-driven 标签中引用配置的类型转换服务
<!-- 引用自定义类型转换器 -->
<mvc:annotation-driven conversion-service="conversionService" />
测试运行:
可以看出我们自己定义的类型转换器已经生效了。
3.2.2 使用 ServletAPI 对象作为方法参数
SpringMVC 还支持使用原始 ServletAPI 对象作为控制器方法的参数。支持原始 ServletAPI 对象有:
HttpServletRequest
HttpServletResponse
HttpSession
java.security.Principal
Locale
InputStream
OutputStream
Reader
Writer
我们可以把上述对象,直接写在控制的方法参数中使用。
部分示例代码
控制器中的代码:
运行结果:
第4章 常用注解
4.1RequestParam
4.1.1 使用说明
作用:
把请求中指定名称的参数给控制器中的形参赋值。
属性:
value:请求参数中的名称。
required:请求参数中是否必须提供此参数。默认值:true。表示必须提供,如果不提供将报错。
4.1.2 使用示例
jsp代码:
控制器中的代码:
运行结果:要把jsp里面a标签的问号改成name
4.2RequestBody
4.2.1 使用说明
作用:
用于获取请求体内容。直接使用得到是 key=value&key=value…结构的数据。
get 请求方式不适用。 属性:
required:是否必须有请求体。默认值是:true。当取值为 true 时,get 请求方式会报错。如果取值为 false,get 请求得到是 null。
4.2.2 使用示例
post 请求 jsp 代码:
控制器代码
运行结果:post的结果
如果是get 请求会是这么样呢?
jsp代码添加:
运行get的结果:直接出错,试了挺多次还是这样
4.3PathVaribale
4.3.1 使用说明
作用:
用于绑定 url 中的占位符。例如:请求 url 中 /delete/{id},这个{id}就是 url 占位符。
url 支持占位符是 spring3.0 之后加入的。是 springmvc 支持 rest 风格 URL 的一个重要标志。
属性:
value:用于指定 url 中占位符名称。
required:是否必须提供占位符。
4.3.2 使用示例
jsp代码:
控制器代码:
运行结果:
4.3.3 REST 风格 URL
- 请求路径一样,可以根据不同的请求方式去执行后台的不同方法
- restful风格的URL优点
1. 结构清晰
2. 符合标准
3. 易于理解
4. 扩展方便
举例:
4.4RequestHeader
4.4.1 使用说明
作用:
用于获取请求消息头。
属性:
value:提供消息头名称
required:是否必须有此消息头
注:
在实际开发中一般不怎么用。
4.4.2 使用示例
jsp 代码:
控制器中代码:
运行代码:
4.5CookieValue
4.5CookieValue
作用:
用于把指定 cookie 名称的值传入控制器方法参数。
属性:
value:指定 cookie 的名称。
required:是否必须有此 cookie。
4.5.2 使用示例
jsp 中的代码:
控制器中的代码:
运行结果:
4.6ModelAttribute
4.6.1 使用说明
作用:
该注解是 SpringMVC4.3 版本以后新加入的。它可以用于修饰方法和参数。
出现在方法上,表示当前方法会在控制器的方法执行之前,先执行。它可以修饰没有返回值的方法,也可以修饰有具体返回值的方法。出现在参数上,获取指定的数据给参数赋值。
属性:
value:用于获取数据的 key。key 可以是 POJO 的属性名称,也可以是 map 结构的 key。
应用场景:
当表单提交数据不是完整的实体类数据时,保证没有提交数据的字段使用数据库对象原来的数据。
例如:
我们在编辑一个用户时,用户有一个创建信息字段,该字段的值是不允许被修改的。在提交表单数据是肯定没有此字段的内容,一旦更新会把该字段内容置为 null,此时就可以使用此注解解决问题。
举例:
我们只传递2个值,控制代码的参数我使用User实体类接收并且自动封装
运行代码:
你会发现date的数据没有被封装进去,因为前端数据里面没有传递date数据使用为null那么怎么解决呢? 可以通过ModelAttribute注解,因为只要在方法上出现,那么表示会在控制器的方法执行之前先执行。
4.6.2.2 基于 Map 的应用场景示例 1:ModelAttribute 修饰方法带返回值
jsp 代码:
控制的代码:
运行结果:
4.6.2.3 基于 Map 的应用场景示例 1:ModelAttribute 修饰方法不带返回值
jsp 代码:
控制的代码:
运行结果:也封装上了
4.7SessionAttribute
4.7.1 使用说明
作用:
用于多次执行控制器方法间的参数共享。
属性:
value:用于指定存入的属性名称
type:用于指定存入的数据类型。
4.7.2 使用示例
jsp 代码:
控制器的代码:
注意:SessionAttribute只能写类上
存入session通过:Model
获取session:通过ModelMap
清除session 通过:SessionStatus
运行结果:
点击存入,点击取出,点击清除,点击取出分别对应下面
补充:如果要在jsp中获取有2种:
运行结果:
SpringMVC 第二天
和之前的配置环境一样
第1章 响应数据和结果视图
1.1返回值分类
1.1.1 字符串的返回值跳转视图
controller 方法返回字符串可以指定逻辑视图名,通过视图解析器解析为物理视图地址。
//指定逻辑视图名,经过视图解析器解析为 jsp 物理路径:/WEB-INF/pages/success.jsp
控制器代码:
运行结果:
你可能会想为什么只有2个值呀,因为我只设置了2个值分别是姓名,年龄
1.1.2 void 的无返回值跳转视图
在昨天的学习中,我们知道 Servlet 原始 API 可以作为控制器中方法的参数:
控制器代码:
如果是重定向到 jsp 页面,则 jsp 页面不能写在 WEB-INF 目录中,否则无法找到。
1.1.3 ModelAndView
ModelAndView 是 SpringMVC 为我们提供的一个对象,该对象也可以用作控制器方法的返回值。
该对象中有两个方法:
控制器代码:
运行结果:
注意:
我们在页面上上获取使用的是user.username取的,所以返回 ModelAndView 类型时,浏览器跳转只能是请求转发。
1.2转发和重定向
1.2.1 forward 和 Redirect
控制器代码:
注意:
请求转发:需要注意的是,如果用了 formward:则路径必须写成实际视图 url,不能写逻辑视图。
它相当于“request.getRequestDispatcher(“url”).forward(request,response)”。使用请求转发,既可以转发到 jsp,也可以转发到其他的控制器方法。
重定向:它相当于“response.sendRedirect(url)”。需要注意的是,如果是重定向到 jsp 页面,则 jsp 页面不能写在 WEB-INF 目录中,否则无法找到。
1.3ResponseBody 响应 json 数据
1.3.1 使用说明
作用:
该注解用于将 Controller 的方法返回的对象,通过 HttpMessageConverter 接口转换为指定格式的数据如:json,xml 等,通过 Response 响应给客户端
1.3.2 使用示例
需求:
使用@ResponseBody 注解实现将 controller 方法返回对象转换为 json 响应给客户端。
前置知识点:
Springmvc 默认用 MappingJacksonHttpMessageConverter 对 json 数据进行转换,需要加入jackson 的坐标。注意:2.7.0以下的版本用不了
在pom.xml中添加:json字符串和JavaBean对象互相转换的过程中,需要使用jackson的jar包
<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-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>
然后在webapp包下创建一个js的包用于存放jq
在response.sjp中写一个测试的代码
单点击按钮会弹出对话框
发现点击没有反应。
原因在于 DispatcherServlet会拦截到所有的资源,导致(js,css,img)也会被拦截,从而不能使用解决方式是在我们的配置文件springmvc.xml中添加mvc:resources这个标签:
属性
- location元素表示webapp目录下的包下的所有文件
- mapping元素表示以/static开头的所有请求路径,如/static/a 或者/static/a/b
<!-- 设置静态资源不过滤 -->
<mvc:resources location="/css/" mapping="/css/**"/> <!-- 样式 -->
<mvc:resources location="/images/" mapping="/images/**"/> <!-- 图片 -->
<mvc:resources location="/js/" mapping="/js/**"/> <!-- javascript -->
在重启服务器,点击按钮可以弹出对话框了。
接下来切入正题,方式ajax的请求:编写代码
jsp 代码:
控制器代码:
通过:@RequestBody 注解把前台代码的json数据值全部封装到user对象中,其中传递的值要和user实体类的属性名称一样,否则没办法自动封装。
通过:@ResponseBody 注解把后端的数据响应给前端,并且把对象转为json字符串给客户端,这样客户端就可以解析服务器响应的数据
运行结果:
客户端响应给服务器username=mei,password=123456,age=19 ,然后我们模拟从数据库查询把用户名username改成w,age改10,而password不动还是123456,并且返回给了客户端
第2章 SpringMVC 实现文件上传
环境搭建
和之前的差不多!
2.1文件上传的回顾
2.1.1 文件上传的必要前提
2.1.2 文件上传的原理分析
2.1.3 借助第三方组件实现文件上传
使用 Commons-fileupload 组件实现文件上传,需要导入该组件相应的支撑 jar 包:Commons-fileupload 和commons-io。commons-io 不属于文件上传组件的开发 jar 文件,但Commons-fileupload 组件从 1.1 版本开始,它工作时需要 commons-io 包的支持。
2.1.4实现步骤
现在写jsp的代码:
控制器代码:
运行结果:这个是测试并没有什么意义,点击上传按钮就会运行后台代码
借助第三方组件实现文件上传,在pom.xml中添加2个坐标:
<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.5</version>
</dependency>
编写控制器代码:
/**
* 控制器代码
*/
("/user")
public class UserController {
/**
* 文件上传
* @return
*/
("/fileUpload1")
public String fileUpload1(HttpServletRequest request) throws Exception {
System.out.println("文件上传..执行了");
//使用fileupload组件完成文件上传
//上传的位置
String path= request.getSession().getServletContext().getRealPath("/uploads");
//判断,该路径是否存在
File file=new File(path);
if (!file.exists()){
//不存在,创建文件夹
file.mkdirs();
}
//解析request对象,获取上传文件项
DiskFileItemFactory factory=new DiskFileItemFactory();
ServletFileUpload upload=new ServletFileUpload(factory);
//解析request
List<FileItem> Items = upload.parseRequest(request);//有异常抛出
//遍历
for (FileItem item:Items){
//做一个判断,当前的item对象是否是上传文件项
if (item.isFormField()){
//说明是普通表单向
}else {
//说明是上传文件项
//获取上传文件的名称
String fileName = item.getName();
//完成文件上传
item.write(new File(path,fileName));
//删除临时文件
item.delete();
}
}
return "success";
}
}
运行结果:
注意:怎么看文件是否真的存在,那你要去看你的tomcat的文件夹的工作空间项目里面。
还有一个小问题,就是如果上传相同文件名会把之前的覆盖了,那么我们通过UUID这个生成唯一的值
效果:
这样存储查找文件是不是比较麻烦,那我们可以更改存放的位置,因为存放的位置是字符串path,我们找好路径把文件的路径复制到path中就可以了。
效果:
2.2springmvc 传统方式的文件上传
2.2.1 说明
传统方式的文件上传,指的是我们上传的文件和访问的应用存在于同一台服务器上。
并且上传完成之后,浏览器可能跳转
2.2.2 实现步骤
2.2.2.1 第一步:把2个需要的坐标导入pom.xml中
<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>
<spring.version>5.0.2.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.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.5</version>
</dependency>
</dependencies>
2.2.2.2 第二步:编写 jsp 页面
<h3>Springmvc文件上传</h3>
<%--Springmvc文件上传--%>
<form action="user/fileUpload2" method="post" enctype="multipart/form-data">
名称:<input type="text" name="picname"/><br/>
选择文件: <input type="file" name="upload"><br>
<input type="submit" value="上传文件">
</form>
2.2.2.3 第三步:编写控制器
/**
* Springmvc上传文件
* @param request
* @param upload MultipartFile的接口
* @param picname 用于获取客户端是否给名称
* @return
* @throws Exception
*/
("/fileUpload2")
public String fileUpload2(String picname,HttpServletRequest request, MultipartFile upload) throws Exception {
//定义文件名
String fileName="";
//1.获取原始文件名称
String uploadFileName = upload.getOriginalFilename();
//2.截取文件扩展名
String extendName=uploadFileName.substring(uploadFileName.lastIndexOf(".")+1,uploadFileName.length());
//3.把文件加上随机数,防止文件重复
String uuid=UUID.randomUUID().toString().replace("-","").toUpperCase();
//4.判断是否输入了文件名
if(!StringUtils.isEmpty(picname)){
//客户端有设置文件名称
fileName=uuid+"_"+picname+"."+extendName;
}else{
//客户端没有设置文件名称
fileName=uuid+"_"+uploadFileName;
}
System.out.println("保存成功:"+fileName);
//2.获取文件路径
String path="F:\\Download\\uploads\\mei";
//3.解决同一文件夹中文件过多问题
String datePath = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
//4.判断路径是否存在
File file=new File(path+"/"+datePath);
if (!file.exists()){
//没有这个就创建
file.mkdirs();
}
//5.使用 MulitpartFile 接口中方法,把上传的文件写到指定位置
upload.transferTo(new File(file,fileName));
return "success";
}
2.2.2.4 第四步:配置文件解析器
注意:
文件上传的解析器 id 是固定的,不能起别的名称,否则无法实现请求参数的绑定。(不光是文件,其他字段也将无法绑定)
运行结果:
不设置文件名称:
设置了文件名称:
但是还有一个小问题,如果传递的名称是中文呢,那肯定会出现乱码:解决方式在web.xml配置一个中文乱码的过滤器就可以解决:
<!--解决中文乱码的过滤器-->
<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>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
SpringMVC的框架上传原理图:
2.3springmvc 跨服务器方式的文件上传
2.3.1 分服务器的目的
在实际开发中,我们会有很多处理不同功能的服务器。例如:
应用服务器:负责部署我们的应用
数据库服务器:运行我们的数据库
缓存和消息服务器:负责处理大并发访问的缓存和消息
文件服务器:负责存储用户上传文件的服务器。
(注意:此处说的不是服务器集群)
分服务器处理的目的是让服务器各司其职,从而提高我们项目的运行效率。
2.3.2 准备两个 tomcat 服务器,并创建一个用于存放图片的 web 工程
新的模块环境
添加一个可以让工程构建更快:name=archetypeCatalog
value=internal
在index中添加几句话
添加tomcat,之前的是8080的
启动图片服务器的tomcat
2.3.3 拷贝 jar 包
我们使用maven工程导入坐标
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-core</artifactId>
<version>1.18.1</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
<version>1.18.1</version>
</dependency>
2.3.4 编写 jsp 页面
<h3>Springmvc 跨服务器文件上传</h3>
<%--Springmvc文件上传--%>
<form action="user/fileUpload3" method="post" enctype="multipart/form-data">
名称:<input type="text" name="picname"/><br/>
选择文件: <input type="file" name="upload"><br>
<input type="submit" value="上传文件">
</form>
2.3.5 编写控制器实现上传图片
/**
* Springmvc 跨服务器 文件上传
* @return
*/
("/fileUpload3")
public String fileUpload3(String picname, MultipartFile upload) throws Exception {
//定义文件名
String fileName="";
//1.获取原始文件名称
String uploadFileName = upload.getOriginalFilename();
//2.截取文件扩展名
String extendName=uploadFileName.substring(uploadFileName.lastIndexOf(".")+1,uploadFileName.length());
//3.把文件加上随机数,防止文件重复
String uuid=UUID.randomUUID().toString().replace("-","").toUpperCase();
//4.判断是否输入了文件名
if(!StringUtils.isEmpty(picname)){
//客户端有设置文件名称
fileName=uuid+"_"+picname+"."+extendName;
}else{
//客户端没有设置文件名称
fileName=uuid+"_"+uploadFileName;
}
System.out.println("保存成功:"+fileName);
//5.创建 sun 公司提供的 jersey 包中的 Client 对象
Client client = Client.create();
//6.指定上传文件的地址,该地址是 web 路径
String path="http://localhost:9090/fileuploadserver_war/uploads/";
WebResource resource = client.resource(path + fileName);
//7.实现上传
String result=resource.put(String.class,upload.getBytes());
System.out.println(result);
return "success";
}
测试:
启动2个tomcat服务器:上传图片出现问题是403的
解决:
在tomcat的web.xml中加入此行的含义是:接收文件的目标服务器可以支持写入操作。
如果还是查询问题:问题是409那这个简单,在tomcat包的工作空间添加uploads文件夹就解决了。
这个时候要去tomcat包下的webapp找到图片服务器项目添加文件夹uploads
上传图片:运行成功
第3章 SpringMVC 中的异常处理
当出现我们自己的网页或者是后台代码出现问题,我们没有去处理异常的话,异常会被一层一层的向上抛异常,直到页面显示异常信息。如图:这是模拟的异常,用户看起来非常的不友好,那么怎么解决出现异常,我们跳到我们自己设计的异常页面呢,那接接着往下面学学习吧哈哈哈…
3.1异常处理的思路
系统中异常包括两类:预期异常和运行时异常 RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。
系统的 dao、service、controller 出现都通过 throws Exception 向上抛出,最后由 springmvc 前端
控制器交由异常处理器进行异常处理,如下图:
步骤:1,2,3
3.2实现步骤
3.2.1 编写异常类和错误页面
编写异常类:
/**
* 自定义异常类
*/
public class SysException extends Exception {
//存储提示信息的
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
//构造函数存入出错信息
public SysException(String message) {
this.message = message;
}
}
编写出现错误的页面:
<% page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>出现错误信息</title>
</head>
<body>
<h3>管理员正在维护....</h3>
${message}<%--使用el表达式要配 isELIgnored="false"--%>
</body>
</html>
3.2.2 自定义异常处理器
/**
* 异常处理器
* 实现了HandlerExceptionResolver
*/
public class SysExceptionResolver implements HandlerExceptionResolver {
/**
* 处理异常业务逻辑
*/
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
//1.获取异常对象
SysException e=null;
//2.如果抛出的是系统自定义异常则直接转换
if (ex instanceof SysException){
//是的话强转成这个类型
e=(SysException)ex;
}else{
//如果抛出的不是系统自定义异常则重新构造一个系统错误异常
e=new SysException("系统正在维护...");
}
//3.创建ModelAndView对象
ModelAndView mv=new ModelAndView();
//4.拿到自定义异常的错误信息
mv.addObject("message",e.getMessage());
//5.往错误页面跳转
mv.setViewName("error");
return mv;
}
}
其中判断里面的instanceof是Java中的二元运算符,左边是对象,右边是类;当对象是右边类或子类所创建对象时,返回true;否则,返回false。
3.2.3 配置异常处理器
在springmvc.xml中配置
控制器代码:可以再可能出现问题的代码上用上我们自定义的异常类
/**
* 控制器
*/
("/user")
public class UserController {
("/testException")
public String testException() throws SysException{
System.out.println("testException执行了....");
try {
//模拟异常错误 ,模拟查询用户出现错误
int a=1/0;
} catch (Exception e) {
//打印异常信息
e.printStackTrace();
//抛出自定义异常信息
throw new SysException("查询用户出现错误....");
}
return "success";
}
}
3.2.4 运行结果:这样就不会和我们一开始把错误展示在页面那么难看了。
第4章 SpringMVC 中的拦截器
4.1拦截器的作用
4.2自定义拦截器的步骤
4.2.1 第一步:编写一个普通类实现 HandlerInterceptor 接口
/**
* 自定义拦截器
*/
public class MyInerceptor implements HandlerInterceptor {
/**
* 预处理, controller 方法执行前
* return true 放行,执行下一个拦截器,如果没有,执行controller的方法
* retrunk false 不放行
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle拦截器运行了..预处理");
//请求转发
//request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request,response);
return true;
}
/**
* 后处理方法,controller方法执行后,success.jsp执行前执行
*/
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle拦截器运行了..后处理方法");
//请求转发
//request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request,response);
}
/**
* success.jsp页面执行后,该方法会执行
*最后执行
*/
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//没有办法跳页面
System.out.println("afterCompletion拦截器运行了..最后处理方法");
}
}
4.2.2 第二步:配置拦截器
<!--配置拦截器-->
<mvc:interceptors>
<!--配置拦截器-->
<mvc:interceptor>
<!--你要拦截的具体方法-->
<mvc:mapping path="/user/*"/> <!--这个意思是拦截一级目录为user下的所有方法-->
<!--不要拦截的方法
<mvc:exclude-mapping path=""/>
-->
<!--配置拦截器对象-->
<bean class="com.itmei.interceptor.MyInerceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
4.2.3 测试运行结果:
控制器代码:
jsp代码
4.3拦截器的细节
4.3.1 拦截器的放行
放行的含义是指,如果有下一个拦截器就执行下一个,如果该拦截器处于拦截器链的最后一个,则执行控制器中的方法。
4.3.2 拦截器中方法的说明
4.3.3 拦截器的作用路径
作用路径可以通过在配置文件中配置。
4.3.4 多个拦截器的执行顺序
多个拦截器是按照配置的顺序决定的
4.4正常流程测试
4.4.1 配置文件:
<!--配置拦截器-->
<mvc:interceptors>
<!--配置拦截器-->
<mvc:interceptor>
<!--你要拦截的具体方法-->
<mvc:mapping path="/user/*"/> <!--不是拦截一级目录user下的所有方法-->
<!--配置拦截器对象-->
<bean class="com.itmei.interceptor.MyInerceptor"></bean>
</mvc:interceptor>
<!--第二个拦截器-->
<mvc:interceptor>
<!--你要拦截的具体方法-->
<mvc:mapping path="/user/*"/> <!--不是拦截一级目录user下的所有方法-->
<!--配置拦截器对象-->
<bean class="com.itmei.interceptor.MyInerceptor2"></bean>
</mvc:interceptor>
</mvc:interceptors>
4.4.2 拦截器 1 的代码:
/**
* 自定义拦截器
*/
public class MyInerceptor implements HandlerInterceptor {
/**
* 预处理, controller 方法执行前
* return true 放行,执行下一个拦截器,如果没有,执行controller的方法
* retrunk false 不放行
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("拦截器1:preHandle拦截器运行了..预处理");
//request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request,response);
return false;
}
/**
* 后处理方法,controller方法执行后,success.jsp执行前执行
*/
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("拦截器1:postHandle拦截器运行了..后处理方法");
//request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request,response);
}
/**
* success.jsp页面执行后,该方法会执行
*最后执行
*/
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("拦截器1:afterCompletion拦截器运行了..最后处理方法");
}
}
4.4.3 拦截器 2 的代码:
就是把拦截器1的代码复制改一下类名称打印改一下就好
/**
* 自定义拦截器
*/
public class MyInerceptor2 implements HandlerInterceptor {
/**
* 预处理, controller 方法执行前
* return true 放行,执行下一个拦截器,如果没有,执行controller的方法
* retrunk false 不放行
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("拦截器2:preHandle拦截器运行了..预处理");
//request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request,response);
return false;
}
/**
* 后处理方法,controller方法执行后,success.jsp执行前执行
*/
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("拦截器2:postHandle拦截器运行了..后处理方法");
//request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request,response);
}
/**
* success.jsp页面执行后,该方法会执行
*最后执行
*/
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("拦截器2:afterCompletion拦截器运行了..最后处理方法");
}
}
4.4.4 运行结果:
4.5中断流程测试
4.5.1 配置文件:和上面一样
4.5.2 拦截器 1 的代码:和上面一样
4.5.3 拦截器 2 的代码:和上面一样,但是预处理我们不给他放行:
4.5.4 运行结果:
可以看出拦截器2预处理不放行,拦截器2的其他方法都不执行,因为拦截器2没有放行导致没办法执行控制器的代码,这样也导致了拦截器1的 postHandle方法不能执行。
4.6拦截器的简单案例(验证用户是否登录)
4.6.1 实现思路
4.6.2 控制器代码
4.6.3 拦截器代码
SpringMVC 第三天
SSM 整合
第一章:搭建整合环境
- 整合说明:SSM整合可以使用多种方式,咱们会选择XML + 注解的方式
- 整合的思路
- 先搭建整合的环境
- 先把Spring的配置搭建完成
- 再使用Spring整合SpringMVC框架
- 最后使用Spring整合MyBatis框架
1.1环境准备
1.1.1 创建数据库和表结构
create database ssm;
create table account(
id int primary key auto_increment,
name varchar(100),
money double
);
1.1.2 创建 Maven 工程
这里要添加键值对,这样maven工程更快构建完成:
name=archetypeCatalog
value=internal
这样子ssm项目构建完成了
1.1.3 导入坐标并建立依赖在pom.xml中
<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>
<!--版本锁定-->
<!--spring版本号-->
<spring.version>5.0.2.RELEASE</spring.version>
<!--日志版本号-->
<slf4j.version>1.6.6</slf4j.version>
<log4j.version>1.2.12</log4j.version>
<!--数据库版本号-->
<mysql.version>5.1.6</mysql.version>
<!--mybatis版本号-->
<mybatis.version>3.4.5</mybatis.version>
</properties>
<dependencies>
<!-- spring -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId> <!--aop相关技术-->
<version>1.6.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId><!--aop相关技术-->
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId><!--spring 的容器-->
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId><!--springMVC的jar包-->
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId><!--springMVC的jar包-->
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId><!--spring的单元测试-->
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId><!--spring的事务-->
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId><!--spring的jdbc模板技术-->
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId><!--单元测试-->
<version>4.12</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId><!--mysql的驱动jar包-->
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId><!--servlet的-->
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId><!--servlet的-->
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId><!--页面使用el表达式使用的包-->
<version>1.2</version>
</dependency>
<!-- log start --><!--日志的-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- log end -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId><!--mybatis的包-->
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId><!--整合mybatis使用的包-->
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId><!--连接池-->
<version>0.9.1.2</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
把项目的包补充一下:
在main下创建java包(用于存放java代码)和resource包(用于存放配置文件)
变成这样就可以使用了
并且在java创建以下包:用于存放对应的java代码
1.1.4 编写实体类
要实现序列化接口
/**
* 账户
*/
public class Account implements Serializable {
private Integer id;
private String name;
private Double money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
1.1.5 编写持久层接口
/**
* 账户dao接口
*/
public interface AccountDao {
//查询账户所有
public List<Account> findAll();
//保存用户
public void savaAccount(Account account);
}
1.1.6 编写业务层接口
/**
* 业务层账户接口
*/
public interface AccountService {
//查询所有账户
public List<Account> findAll();
//保存账户信息
void saveAccount(Account account);
}
这个需要我们自己写业务层实现类:写在impl包中
/**
* 业务层实现类
*/
public class AccountServiceImpl implements AccountService {
public List<Account> findAll() {
System.out.println("业务层:查询所有账户...");
return null;
}
public void saveAccount(Account account) {
System.out.println("业务层:保存账户信息...");
}
}
创建账户的控制器:
/**
* 账户web或者说账户的控制器
*/
public class AccountController {
}
1.2整合步骤
保证 Spring 框架在 web 工程中独立运行
1.2.1.1 第一步:编写 spring 配置文件并导入约束
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--开启注解扫描,只希望处理service和dao,controller不需要spring框架处理,因为控制器层让SpringMVC框架 去管理-->
<context:component-scan base-package="com.itmei">
<!--配置注解哪些不扫描-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>
开启注解扫描,只希望处理service和dao,controller不需要spring框架处理,因为控制器层让SpringMVC框架 去管理,我们可以设置哪些注解不去扫描就好了,如果控制层不扫描,那么在标签体里面有一个context:exclude-filter标签忽略哪些注解不扫描@Controller这个注解不扫描,要写全限定的名称
1.2.1.2 第二步:使用注解配置业务层
1.2.1.3 第三步:测试 spring 能否独立运行
运行结果:
保证 SpringMVC 在 web 工程中独立运行
1.2.2.1 第一步:在 web.xml 中配置核心控制器(DispatcherServlet)
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!-- 配置 spring mvc 的核心控制器 -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--配置springmvc配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--启动服务器,创建该servlet-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--springMVC 解决中文乱码过滤器-->
<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>
</filter>
<!-- 过滤所有请求 -->
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
1.2.2.2 第二步:编写 SpringMVC 的配置文件
创建springmvc.xml的配置文件,编写配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=" http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--开启注解扫描-->
<context:component-scan base-package="com.itmei">
<!--只扫描这个注解-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--配置视图解析器对象-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property><!--后缀是.jsp的-->
</bean>
<!--过滤器资源-->
<mvc:resources mapping="/js/**" location="/js/"></mvc:resources>
<mvc:resources mapping="/css/**" location="/css/"></mvc:resources>
<mvc:resources mapping="/images/**" location="/images/"></mvc:resources>
<!--开启springMVC注解支持-->
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
并且在WEB-INF下创建pages和jsp页面
1.2.2.3 第三步:编写 Controller 和 jsp 页面
给控制器添加注解@Controller表示这个类是控制类
@RequestMapping用来处理请求地址映射的注解,写在方法或者类上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径(一级路径)
/**
* 账户web或者说账户的控制器
*/
("/account")//一级访问地址
public class AccountController {
("/findAllAccount")//二级访问地址
public String findAllAccount(){
System.out.println("执行了查询账户...");
return "success"; //跳转成功页面
}
}
控制器代码:
jsp代码:在index中写
success.jsp代码:
运行结果:启动tomcat把项目改为这次的ssm项目,然后运行查看效果!
整合 Spring 和 SpringMVC
1.2.3.1 第一步:配置监听器实现启动服务创建容器
在web.xml中设置:
<!-- 配置 spring 提供的监听器,用于启动服务时加载容器 。
该间监听器只能加载 WEB-INF 目录中名称为 applicationContext.xml 的配置文件 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 手动指定 spring 配置文件位置 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
1.2.3.2控制器代码:调用业务层(service)的方法
/**
* 账户web或者说账户的控制器
*/
("/account")//一级访问地址
public class AccountController {
//自动注入
private AccountService accountService;
("/findAllAccount")//二级访问地址
public String findAllAccount(){
System.out.println("表现层:执行了查询账户...");
accountService.findAll();
return "success"; //跳转成功页面
}
}
1.2.3.3 第三步:运行结果
保证 MyBatis 框架在 web 工程中独立运行
1.2.4.1 第一步:使用注解编写SQL语句:
在dao接口的方法上添加对应注解
/**
* 账户dao接口
*/
public interface AccountDao {
//查询账户所有
("select * from account")
public List<Account> findAll();
//保存用户
("insert into account(name,money) values(#{name},#{money})")
public void savaAccount(Account account);
}
补充:
我们补充一下xml的方式,xml和注解方式选择一种用于开发就好,我这里补充是为了搞清楚xml要怎么配置:
首先去除接口的注解:
我们在resources中创建包,这个包的结构要和我们AccounDao接口目录一样,并且只能一个包一个包的创建。
AccountDao.xml的代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itmei.dao.AccountDao">
<!--查询所有用户-->
<select id="findAll" resultType="com.itmei.domain.Account">
select * from account
</select>
<!--保存用户-->
<insert id="savaAccount" parameterType="com.itmei.domain.Account">
insert into account (name,money) values (#{name},#{money})
</insert>
</mapper>
并且在applicationContext.xml中配置全局配置文件
如果使用注解的话
代码中不要添加全局配置文件
也不需要SqlMapConfig.xml文件
1.2.4.2 第二步:编写 SqlMapConfig 配置文件
<?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>
<!--引入外部数据库的配置文件-->
<properties resource="jdbcConfig.properties"></properties>
<!--配置环境-->
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED"><!--这里的value值要和外部配置文件的key值一样-->
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--引入映射配置文件-->
<mappers>
<package name="com.itmei.dao"/><!--这个方式是最好的,意思吧dao下的所有包都被找到-->
</mappers>
<!--外置文件 jdbcConfig.properties:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm
jdbc.username=root
jdbc.password=123456
-->
</configuration>
外置文件 jdbcConfig.properties:数据库的必备数据
编写测试类代码:
/**
* 测试Mybatis 环境
*/
public class testMybatis {
/**
* 查询所有用户信息
* @throws IOException
*/
public void run1() throws IOException {
//1.加载配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3.创建SqlSession对象
SqlSession session = factory.openSession();
//4.获取代理对象
AccountDao accountDao = session.getMapper(AccountDao.class);
//5.查询所有数据信息
List<Account> list = accountDao.findAll();
for (Account account:list){
System.out.println(account.toString());
}
//6.释放资源
session.close();
in.close();
}
/**
* 保存用户信息
* @throws IOException
*/
public void run2() throws IOException {
Account account=new Account();
account.setName("梅");
account.setMoney(1000d);
//1.加载配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3.创建SqlSession对象
SqlSession session = factory.openSession();
//4.获取代理对象
AccountDao accountDao = session.getMapper(AccountDao.class);
//5.保存用户
accountDao.savaAccount(account);
//记得提交事务
session.commit();
//6.释放资源
session.close();
in.close();
}
}
1.2.4.3 第三步:测试运行结果
运行结果:实体类的名称要和数据库列名称一样,否则封装不上数据,就会变成null。
查询所有用户测试:
保存用户测试
整合 Spring 和 MyBatis
整合思路:
把 mybatis 配置文件(SqlMapConfig.xml)中内容配置到 spring 配置文件中
同时,把 mybatis 配置文件的内容清掉。
1.2.5.1 第一步:Spring 接管 MyBatis 的 Session 工厂
<!-- 加载配置文件 -->
<context:property-placeholder location="classpath:jdbcConfig.properties"></context:property-placeholder>
<!-- 配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!--配置mybatis的session工厂-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 数据库连接池 -->
<property name="dataSource" ref="dataSource"></property>
<!-- 加载mybatis的全局配置文件 这个是用于写xml的方式,如果是注解可以不需要这个-->
<property name="configLocation" value="classpath:SqlMapConfig.xml"></property>
</bean>
1.2.5.2 第二步:配置自动扫描所有 Mapper 接口和文件
<!--配置Mapper扫描器-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--扫描所有dao下的接口包-->
<property name="basePackage" value="com.itmei.dao"></property>
</bean>
1.2.5.3 第三步:配置 spring 的事务
配置spring的事务,这样出现问题会自动回滚
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置事务的通知 关联事务管理-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--只要是find开头的方法,都是查询的,这样读取权限为true,表示只能读-->
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
<!--*代表所有方法,读取权限为false,表示可以读和写,用在,增删改-->
<tx:method name="*" propagation="REQUIRED" read-only="false"/>
</tx:attributes>
</tx:advice>
<!--配置AOP-->
<aop:config>
<!--配置切入点表达式--> <!--表示service下impl的包下的类所有方法-->
<aop:pointcut id="pt1" expression="execution(* com.itmei.service.impl.*.*(..))"/>
<!--建立通知和切入点表达式的关系-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"/>
</aop:config>
1.2.5.4 第三步:测试整合结果:
1.把AccounDao交给IOC管理
2.在业务层实现类里面添加自动注入AccountDao对象
并且补充accountDao的方法:
编写测试类,基于spring的单元测试:
/**
* 测试spring 整合mybatis
*/
(SpringJUnit4ClassRunner.class)
(locations = "classpath:applicationContext.xml")
public class SpringMybatis {
private AccountService accountService;
//查询所有用户信息
public void testFindAll(){
List<Account> all = accountService.findAll();
//遍历
for (Account account:all){
System.out.println(account.toString());
}
}
//保存用户信息
public void testSave(){
Account account=new Account();
account.setName("测试账号");
account.setMoney(2000d);
accountService.saveAccount(account);
}
}
运行结果:
项目结构分析:
如果使用Mybatis的注解开发那么可以删除2个东西:
1.2.6 测试 SSM 整合结果
1.2.6.1 编写测试 jsp
请求发起页面代码:
<% page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>主页</title>
</head>
<body>
<h3>查询账户信息</h3>
<%--测试SpringMVC 环境是否搭建成功--%>
<a href="account/findAllAccount">访问查询用户</a>
<hr>
<h3>保存用户信息</h3>
<form action="account/saveAccount" method="post">
账户名称:<input type="text" name="name"><br>
账户金额:<input type="text" name="money"><br>
<input type="submit" name="保存">
</form>
</body>
</html>
响应结果页面:
<%--忽略el表达式--%>
<% page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%--引入jstl的标签库--%>
<% taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>账户列表页面</title>
</head>
<body>
<h3>查询所有用户信息</h3>
<hr>
<table style="text-align: center" bgcolor="#b0e0e6" width="300px">
<tr>
<th>编号</th>
<th>账户名称</th>
<th>账户金额</th>
</tr>
<%--遍历数据--%>
<c:forEach items="${list}" var="account" varStatus="vs">
<tr>
<td>${vs.count}</td>
<td>${account.name}</td>
<td>${account.mony}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
1.2.6.2 修改控制器中的方法
/**
* 账户web或者说账户的控制器
*/
("/account")//一级访问地址
public class AccountController {
//自动注入
private AccountService accountService;
/**
* 查询账号信息
* @param model
* @return
*/
("/findAllAccount")//二级访问地址
public String findAllAccount(Model model){
System.out.println("表现层:执行了查询账户...");
//调用service的方法
List<Account> list = accountService.findAll();
//然后把数据存起来在用el表达式显示页面上 使用Spring 的Model
model.addAttribute("list",list);
return "success"; //跳转成功页面
}
/**
* 保存账户信息
* @param account
* @return
*/
("/saveAccount")
public String saveAccount(Account account){
//保存用户信息
accountService.saveAccount(account);
//重定向到这个findAllAccount的路径,相当于访问了上面的查询方法
return "redirect:findAllAccount";
}
}