常见开发架构:C/S客户端服务器;B/S浏览器服务器;在B/S架构中,通常使用三层架构:表现层、业务层、持久层。
- 表现层:即web层,分为展示层和控制层,表现层依赖业务层。
- 业务层:即service层,负责业务逻辑处理
- 持久层:即dao层,负责数据持久化,作用也就是和数据库进行交互
==MVC模型==:M指Model,通常指数据模型,用于封装数据(java bean);V指View,通常指JSP或HTML,用来展示数据;C值Controller,处理程序逻辑(servlet)
SpringMVC简介
- SpringMVC是一种基于Java的实现MVC设计模型的请求驱动类型的轻量级Web框架,属于 Spring FrameWork 的后续产品。
SpringMVC入门案例
需求分析
graph LR A(inidex.jsp 超链接标签)--发送请求-->B(编写类和方法,并转到成功页面)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
* 思路:1、搭建开发环境;2、编写入门程序;
### 开发环境搭建
* 创建Maven工程,添加文件夹并设置文件夹属性
* 在web.xml中添加Servlet基本配置
其中url为/代表拦截所有请求
```xml
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>创建SpringMVC配置文件springmvc.xml
配置Tomcat服务器,选择浏览器,Deployment中添加项目,设置路径名
入门初始代码编写
在html中创建一个超链接,在java文件下创建控制器,并编写方法
1
2
3
4
5<body>
<h3>入门程序</h3>
<--这里的href为对应控制器方法上的path--/>
<a href="/hello">点击跳转</a>
</body>1
2
3
4
5
6
7
8
9
10
public class HelloController {
//代表请求映射,这里/hello代表映射这个方法,/不要忘记
"/hello") (path =
public String helloController(){
System.out.println("hello controller!");
//这里返回值是视图解析器要访问的网页名,具体实现SpringMvc框架默认规则
return "success";
}
}在springmvc.xml中添加组件扫描,将控制器类交给Spring管理
1
<context:component-scan base-package="com.zjut.controller"/>
在控制器的类和方法上添加注解,并将方法上的path添加到html中的href
将配置文件的加载交给web.xml中的servlet加载并使其生效,在servlet-class标签下添加
1
2
3
4
5
6
7<!--将springmvc配置文件交给servlet加载并生效-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--代表启动时便创建servlet,并加载springmvc.xml配置文件-->
<load-on-startup>1</load-on-startup>创建将要跳转的页面,在控制器中的方法执行后要打开这个页面
在spring.mvc配置文件中配置视图解析器对象,类名固定,在内部添加参数,配置完之后才能解析控制器中方法返回的页面
1
2
3
4
5
6
7<!--配置视图解析对象,这里由这个视图解析器负责解析返回值并跳转页面-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--配置文件路径,到该路径下去搜索-->
<property name="prefix" value="/WEB-INF/pages/"/>
<!--目标文件的后缀名为.html-->
<property name="suffix" value=".html"/>
</bean>开启SpringMVC框架注解的支持
1
2<!--开启SpringMVC框架注解的支持-->
<mvc:annotation-driven/>入门案例的执行流程
- 当启动Tomcat服务器的时候,因为配置了load-on-startup标签,所以会创建DispatcherServlet对象,就会加载springmvc.xml配置文件
- 开启了注解扫描,那么HelloController对象就会被创建
- 从index.html点击超链接发送请求,请求会先到达DispatcherServlet核心控制器,根据配置@RequestMapping注解找到执行的具体方法
- 根据执行方法的返回值,再根据配置的视图解析器,去指定的目录下查找指定名称的html文件
- Tomcat服务器渲染页面,做出响应
- 流程图
SpringMVC基于组件的运行流程
处理器映射器:开发中要编写的具体业务控制器,由DispatcherServlet把用户请求转发到Handler,由Handler对具体的用户请求进行处理
处理器适配器:通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行
视图解析器:负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。
<mvc:annotation-driven>
说明:配置文件中声明该语句,能够默认进行处理器映射器和处理器适配器的配置。RequesMapping
作用:用于建立请求URL和处理请求方法之间的对应关系。
使用:可添加在类上和方法上,类上的话在访问方法要多级路径,目的是模块化管理
属性:
value/path:用于指定请求的URL
method:用于指定请求的方式,如图当值设为post时,get请求无法访问1
"/saveAccount",method={RequestMethod.POST}) (value=
params:用于指定限制请求参数的条件
问题
==tomcat无法访问html后缀的文件==
原因是默认的配置DispatcherServlet屏蔽了html页面的访问,需要加上如下web.xml中后面多加这样一个mapping
1
2
3
4<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
请求参数绑定
请求参数绑定入门操作
mvc框架会自动将传过来的参数绑定入控制器的方法中
1
<a href="/param/paramTest?user=wdc&password=12345" >请求参数绑定入门</a>
1
2
3
4
5
6
7
8
9
"/param") (path =
public class ParamController {
"/paramTest") (path =
public String paramTest(String user, String password){
System.out.println("user:" + user + " password:" + password);
return "success";
}
}
请求参数为bean类型
此时html中提交一个表单,这里提交的是一个对象,有
user,money,account
三个属性,其中user
为自定义类1
2
3
4
5
6
7
8
9<!--这里action为对应RequestMapper的url,metho为post类型-->
<form action="/param/paramBean" method="post">
<!--这里的name与后面的实体类对象的属性要相同-->
姓名:<input type="text" name="user.name"/><br/> <!--因此用这种形式来绑定引用类型user内部的属性,属性名也要求相同-->
年龄:<input type="text" name="user.age"/><br/>
金额:<input type="text" name="money"/><br>
账户:<input type="text" name="account"/><br>
<input type="submit" name=""/><br>
</form>创建Java实体对象UserAccount与User,并编写表单提交所对应的方法
1
2
3
4
5"paramBean") (path =
public String paramBean(UserAccount userAccount){
System.out.println(userAccount);
return "success";
}在web.xml中配置过滤器解决post请求中文乱码问题
1
2
3
4
5
6
7
8
9
10
11
12<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>
请求类型为List或Map
在实体类中存在
List<User> userList, Map<String, User> userMap
时,表单中如下填写1
2
3
4<input type="text" name="userList[0].name" ><br/>
<input type="text" name="userList[0].age" ><br/>
<input type="text" name="userMap['用户1'].name" ><br/> <!--这里前者填写key,后者填写value-->
<input type="text" name="userMap['用户1'].money" ><br/>SpringMVC会自动帮你转换数据类型,如前端界面是String,后端实体类为Integer,会帮你自动完成转换,当无法自动转换时,可以使用自定义类型转换器来解决
问题
页面跳转再返回,Chrome会从缓存中读取页面,而不访问服务器,导致无法查看参数信息
- 解决办法,在Chrome开发者工具中
Prefrence-Network-disable chache
- 解决办法,在Chrome开发者工具中
在请求参数为Bean类型时,跳转时会出现500异常,并提示
org.springframework.beans.InvalidPropertyException:Invalid property 'users'
- 解决办法,在创建实体对象时,==必须要有无参构造方法==
在web.xml中添加了filter之后,webapp标签标红
- 解决办法:web.xml中有标签顺序的格式要求,错误就会标红,将filter写在servlet前面即可
常用注解
RequestParam注解
作用:把请求中的指定名称的参数传递给控制器中的形参赋值(当请求的变量与控制器的变量名不同时使用)
属性:value:请求参数中的名称;required:请求参数中是否必须提供此参数,默认值是true,必须提供
1
2
3
4
5"/hello") (path=
public String sayHello(@RequestParam(value="username",required=false)String name) {
System.out.println(name);
return "success";
}
ResponseBody
- 用来响应json数据,详见下一节
RequestBody注解
作用:用于获取请求体的内容(注意:get方法不行,get方法不带请求体,而是将内容放在地址栏后面),打印的是请求体的内容
1
2
3
4
5"/hello") (path=
public String sayHello(@RequestBody String body) {
System.out.println(body);
return "success";
}
PathVariable注解
作用:拥有绑定url中的占位符的。例如:url中有/delete/{id},{id}就是占位符
属性:value:指定url中的占位符名称
Restful风格(REST,Representational State Transfer):特点:1. 结构清晰,2.符合标准,3.易于理解,4.扩展方便
使用:
- html中的url直接“/”后跟内容,==url中占位符来替代,注解能帮助解析该内容==
1
<a href="user/hello/1">入门案例</a>
1
2
3
4
5
6"/hello/{uid}") (path=
//注解解析{id}里的内容,并将uid赋值给方法入参的id
public String sayHello(@PathVariable(value="uid") String id) {
System.out.println(id);
return "success";
}
ModelAttribute注解
- 出现在方法上,表示当前方法会在控制器的方法执行之前,先执行(可修饰没有返回值与有具体返回值的方法);出现在参数上,获取指定的数据给参数赋值。
- 属性:value:用于获取数据的key,key可以是POJO的属性名称,也可以是map结构的key
SessionAttribute
作用:用于多次执行控制器方法间的参数共享
属性:value:用于指定存入的属性名称;type:用于指定存入的数据类型。
使用:1、在控制器的方法中用Model接口设置request域,5-10行;2、接下来在jsp文件中打印request域的信息,能够发现已经写入;3、在类上添加
@SessionAttributes
注解,value为设置的name
,在jsp中打印session域的信息;4、添加两个方法,验证参数共享的实现,第二个方法从中取name并打印在控制台,第三个方法删除session域。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
"/annotation") (path =
"name") //添加注解后会将name小高放入Session域中,以实现控制器之间的参数共享 (value =
public class AnnotationController {
"/save_SessionAttributes") (path =
public String save_SessionAttribute(Model model){
//底层会存储到request域对象中
model.addAttribute("name", "小高");
return "success";
}
"/get_SessionAttributes") (path =
public String get_SessionAttribute(ModelMap modelmap){
//从Session域中取值
System.out.println(modelmap.get("name"));
return "success";
}
"/delete_SessionAttributes") (path =
public String delete_SessionAttribute(SessionStatus status){
System.out.println("清除Session域...");
status.setComplete();//调用方法删除Session域,结束参数共享
return "success";
}
}1
2
3
4
5
6
7
8
9
10
11
12
13<!DOCTYPE html>
<%@ page contentType="text/html; charset=UTF-8" language="java" isELIgnored="false" %>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>已跳转</title>
</head>
<body>
<h3>成功访问控制器!</h3>
${requestScope.name}<br>
${sessionScope}
</body>
</html>
响应数据和返回视图
返回值是字符串
返回普通的视图,上面讲的跳转的都是普通的视图
从数据库中调用一个对象,并写入responseScope,以供前端界面读取
1
2
3
4
5
6
7
8"/testString") (path =
public String testString(ModelMap modelMap){
System.out.println("成功执行testString...");
//模拟从数据库中读取一个User,并添加到responseScope
User user = new User("小高", "25");
modelMap.addAttribute("user", user);
return "success";
}
返回值是void
返回值是void类型,视图解析器首先有一个默认的url,会报错找不到
/WEB-INF/pages/response/testVoid.jsp
,有三种方式来处理void响应。==使用转发或重定向时,不使用视图解析器,而要保证路径名不错==1
2
3
4
5
6
7
8
9
10
11
12
13
14"/testVoid") (path =
public void testVoid(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1、请求转发,发送一个请求
request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request, response);
//2、重定向,发送两个请求,无法访问/WEB-INF/pages/内的文件
response.sendRedirect(request.getContextPath() + "/index.jsp");
//3、直接响应数据
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
response.getWriter().println("你好");
return;
}
返回值是ModelAndView
- 和String没有区别,事实上返回值是String时,底层就是调用ModelAndView对象来实现
用ResponseBody响应json数据
事实上,web.xml中配置的
DispatcherServlet
会将所用静态资源也全部拦截,因此界面上的js、css、images都会被拦截而无法响应,因此先在springmvc.xml中配置1
2<!--配置前端控制器不拦截静态资源-->
<mvc:resources mapping="/js/**" location="/js/"/>ajax请求的发送
1
2
3
4
5
6
7
8
9
10
11
12$('#button1').click(function () {
$.ajax({
url: "/response/testJson",
contentType: "application/json; charset=utf-8;",
data_type: "json",
data: JSON.stringify({"name": "xiaogao", "age" : "24"}),
type: 'post',
success:function (data) {
console.log(data);
}
});
});控制器接收post请求并作出响应
- 这里需要在maven中添加
com.fasterxml.jackson
的三个相关依赖,这样以后,@RequestBody User user
就能直接将传过来的数据解析成User对象(==要求穿过来的json中的参数名和User中的属性相同==);在接收完成后,在方法上加@ResponseBody
就能直接将数据以JSON格式返回给ajax。
1
2
3
4
5
6
7"/testJson") (path =
public User testJson(@RequestBody User user){
System.out.println(user);
user.setName("阿高");
user.setAge("1000");
return user;
}- 这里需要在maven中添加
问题
ajax请求post时出现415错误
- 问题原因,在ajax中的
contentType: "application/x-www-form-urlencoded; charset=utf-8;"
不行,应改成application/json; charset=utf-8;
- 问题原因,在ajax中的
在ajax的data的json中存在中文时,若控制器中传入String,则会出现中文乱码
- 问题原因,直接传json会解析出错,因为
@ResponseBody
注解接收的是字符串而不是JSON对象,解决办法是ajax发送时用data:JSON.stringify(data)
将数据转换成字符串
- 问题原因,直接传json会解析出错,因为
Tomcat在使用时,若是web目录下的内容发生变动,只需更新resource和class,若java目录下的内容发生变动,则需要重新部署才会生效
文件上传
- 分服务器目的:在实际开发中,我们会有很多处理不同功能的服务器。例如:
- 应用服务器:负责部署我们的应用
- 数据库服务器:运行我们的数据库 (mysql oracle)
- 缓存和消息服务器:负责处理大并发访问的缓存和消息(redis)
- 文件服务器:负责存储用户上传文件的服务器
异常处理
SpringMVC异常处理流程
解决思路
编写一个自定义异常类
1
2
3
4
5
6
7
8
9
10
11
12public class MyException {
private String message;
public MyException(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}模拟Comtroller中出现了异常
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
"/exceptionTest") (path =
public class exceptionController {
"myExceptionTest") (path =
public String myExceptionTest() throws Exception{
try {
int num = 10 / 0; //模拟异常
} catch (Exception e) {
e.printStackTrace(); //打印异常信息
//抛出自定义异常信息
throw new MyException("页面发生了错误...");
}
return "error";
}
}编写异常处理器(需要继承
HandlerExceptionResolver
接口),编写完后将其交给Spring处理1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MyExceptionHandler implements HandlerExceptionResolver {
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
//获取异常对象
MyException myException;
if(e instanceof MyException){
myException = (MyException) e;
}else {
e = new MyException("页面出错了...");
}
//床架ModelAndView对象并从中添加内容
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("errorMessage", e.getMessage());
modelAndView.setViewName("error");
return modelAndView;
}
}编写前端友好提示的页面,打印
ModelAndView
中添加的内容${errorMessage}
拦截器
概述
- SpringMVC框架中的拦截器用于对处理器进行预处理和后处理的技术
- 可以定义拦截器链,在访问被拦截的方法时,拦截器链中的拦截器会按着定义的顺序执行
- 拦截器和过滤器的功能比较类似,有区别
- 过滤器是Servlet规范的一部分,任何框架都可以使用过滤器技术
- 拦截器是SpringMVC框架独有的
- 过滤器配置了/*,可以拦截任何资源
- 拦截器只会对控制器中的方法进行拦截
- 拦截器也是AOP思想的一种实现方式
- 想要自定义拦截器,需要实现HandlerInterceptor接口
自定义拦截器编写
编写拦截器类,实现HandlerIntercepter接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public class MyInterceptor implements HandlerInterceptor {
//预处理,在controller方法执行前执行
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor的预处理方法执行了...pre");
return true;
}
//后处理,在controller方法执行后,在success.jsp执行前
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor的后处理方法执行了...post");
}
//完成后处理,在success.jsp执行后
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptor的完成处理方法执行了...after");
}
}springmvc.xml中配置拦截器,==若有多个拦截器,需要配置多个==
<mvc:interceptor>
标签1
2
3
4
5
6
7
8<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--配置要拦截的方法,也可用mvc:exclude-mapping配置不用拦截的方法-->
<mvc:mapping path="/interceptor/*"/>
<bean class="com.zjut.domain.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>效果
1
2
3
4
5MyInterceptor的预处理方法执行了...pre
拦截器控制器执行了...
MyInterceptor的后处理方法执行了...post
success.jsp执行了...
MyInterceptor的完成处理方法执行了...after
SSM整合
流程
- 由Spring去整合另外两个框架
1 | graph LR |
环境搭建
Navicat中创建数据库与表
1
2
3
4
5
6
7create database ssm;
use ssm;
create table account(
id int primary key auto_increment,
name varchar(20),
money double
);创建Maven工程,并添加所需依赖
创建domain文件夹存放javabean,controller文件夹存放控制器,dao文件夹存放dao接口,service文件夹存放业务实现类,目录结构如下
编写Spring框架
创建配置文件,开启注解扫描
1
2
3
4
5
6
7
8
9
10
11
<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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--开启组件扫描-->
<context:component-scan base-package="com.zjut.service"/>
<context:component-scan base-package="com.zjut.dao"/>
<context:component-scan base-package="com.zjut.domain"/>
</beans>在业务层实现类上添加注解
1
2"accountService") (
public class AccountServiceImpl implements AccountService {创建测试类,测试Spring框架运行情况
1
2
3
4
5
6
7
8
9
10
11
12.class) (value = SpringJUnit4ClassRunner
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringTest {
"accountService") (value =
private AccountServiceImpl accountService;
public void springTest(){
accountService.findAll();
}
}
编写SpringMVC框架
- web.xml中添加前端控制器,配置读取springmvc.xml配置文件,并添加解决中文乱码的过滤器
- 创建springmvc.xmlpei配置文件,并开启controller文件夹的组件扫描,配置视图解析器、静态资源扫描、开启springmvc注解支持
- 在index.jsp中创建超链接,并创建点击后将要跳转的页面
- 在controller文件夹中的类添加控制器方法
- 配置Tomcat服务器,并运行
Spring整合SpringMVC
目的:控制器中能共访问业务层的对象,并调用业务层的方法
思路
这个监听器spring框架提供了,可以直接在web.xml中配置监听器即可使用
该间监听器只能加载WEB-INF目录中名称为applicationContext.xml的配置文件,因此要另外指定spring配置文件的位置
1
2
3
4
5
6
7
8
9<!--配置监听器,配置spring提供的监听器,用于启动服务时加载容器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--手动指定配置文件位置-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
编写Mybatis框架
创建查询映射文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<mapper namespace="com.zjut.dao.AccountDao">
<!--查找所有账户-->
<select id="findAll" resultType="com.zjut.domain.Account">
SELECT
*
FROM `account`
</select>
<!--插入用户-->
<insert id="save" parameterType="com.zjut.domain.Account">
INSERT INTO `account` ( `name`, `money` )
VALUES
( #{name},
#{money});
</insert>
</mapper>创建Mybatis配置文件SQLMapConfig.xml,配置数据库内容,并添加映射
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23<configuration>
<!-- 和spring整合后 environments配置将废除 -->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事务管理 -->
<transactionManager type="JDBC" />
<!-- 数据库连接池 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver" />
<property name="url"
value="jdbc:mysql://localhost:3306/ssm?characterEncoding=utf-8&serverTimezone=UTC" />
<property name="username" value="root" />
<property name="password" value="Wdc82563815" />
</dataSource>
</environment>
</environments>
<!--加载映射文件-->
<mappers>
<!--<package name="com.zjut.dao.mapper"/>-->
<mapper resource="\mapper\accountMapper.xml"></mapper>
</mappers>
</configuration>测试运行结果
Spring整合Mybatis
目的:service能成功调用dao对象并完成查询等dao对象中的操作
思想:
- 1、SqlSessionFactory对象应该放到spring容器中作为单例存在。
- 2、传统dao的开发方式中,应该从spring容器中获得sqlsession对象。
- 3、Mapper代理形式中,应该从spring容器中直接获得mapper的代理对象。
- 4、数据库的连接以及数据库连接池事务管理都交给spring容器来完成。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26<!--加载JDBC配置文件-->
<context:property-placeholder location="jdbc.properties"/>
<!--配置数据库连接池-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="url" value="${jdbc.url}"/>
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<!--连接池最大连接数与最大空闲数-->
<property name="maxActive" value="10"/>
<property name="maxIdle" value="5"/>
</bean>
<!--配置SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--数据源,ref即数据库连接池的id-->
<property name="dataSource" ref="dataSource"/>
<!-- mybatis核心配置文件 -->
<!--<property name="configLocation" value="classpath:com/zjut/controller/SQLMapConfig.xml"/>-->
</bean>
<!--动态dao开发方式-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.zjut.dao"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
声明事务管理
以上已经基本完成了SSM整合,但是保存还没有实现,因此要配置声明式事务管理
配置事务管理器
配置事务通知
配置AOP增强
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<!--配置事务管理器-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager">
<tx:attributes>
<!--表示find开头的方法都是只读的-->
<tx:method name="find*" read-only="true"/>
<tx:method name="*" isolation="DEFAULT"/>
</tx:attributes>
</tx:advice>
<aop:config>
<!--对service层接口中的所有方法都增强-->
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.zjut.service.impl.*AccountServiceImpl.*(..))"/>
</aop:config>
问题
在springMVC和spring整合时,在控制器中注入业务层类时,使用@Resource注解会报错
另外若在controller中直接使用@AutoWired注入service对象会警告
原因:通常依赖注入方式有三种
- constructor
为了强制依赖,或者为了易变性,使用构造方法注入 - getter & setter
为了可选的或者可变的依赖,使用setter注入 - 通过反射直接注入到fields
@Autowired就是通过这种方式
尽量避免使用直接在属性上注入
- constructor
属性注入的坏处
1、你不能使用属性注入的方式构建不可变对象。
2、你的类和依赖容器强耦合,不能再容器外使用。
3、你的类不能绕过反射(例如单元测试的时候)进行实例化,必须通过依赖容器才能实例化。
4、实际的依赖被隐藏在外面,不是在构造方法或者其它方法里面反射的。
5、一个类经常会有超过10个的依赖。如果使用构造方法的方式注入的话,构造方法会有10个参数,明显有点蠢。但是如果使用属性注入的话就没有这样的限制。但是一个类有很多的依赖,是一个危险的标志,因为很有可能这个类完成了超过一件事,违背了单一职责原则。解决:使用构造器注入该属性,IDEA里直接alt+enter即可
在最终测试能否跑通时,发现在controller中注入service接口属性时报错
org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'accountService' is expected to be of type 'com.zjut.service.impl.AccountServiceImpl'
- 解决办法:在注入serivce时,类型应写成接口类型而不是实现类的类型,否则就会报错