文章目录
Spring的基本概念和基本使用
说明:学习笔记是对B站狂神说的Spring视频的整理和总结。
Spring概述
Spring相关的网址:
Spring官网
Spring各版本官方下载地址
Spring的GitHub
优点:
- Spring是一个开源免费的框架(容器)
- Spring是一个轻量级的、非侵入式的框架(非侵入指的是不会对现有代码产生影响)
- 控制反转(IOC) , 面向切面编程(AOP)
- 支持事务的处理 , 对框架整合的支持
总结:Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器(框架)。
Spring项目的创建
创建一个Spring项目,最主要的过程是以下几个步骤。
1、添加依赖
由maven项目来创建Spring项目。首先在maven的配置文件pom.xml中添加主要的依赖:
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<!--webmvc依赖添加进来,其它很多依赖也就添加进来了-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<!--jdbc的依赖,后期整合Mybatis的时候会用到-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
2、配置文件
Spring的配置文件beans.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--使用Spring来创建对象,在Spring中这些都称为bean
java创建对象:类型 变量名 = new 类型();
Spring通过bean来创建对象:
id:变量名
class:new的对象
property:相当于给对象中的属性设置一个值
-->
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
</beans>
可以理解为一个类就是一个bean,在Spring中没有显式的new关键字,而是通过bean在Spring容器中来创建对象。
3、获取对象
Spring读取含有bean的配置文件,在容器中实例化各个类,得到ApplicationContext类型的对象,通过此对象来获得各个实例。
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService) context.getBean("userServiceImpl");
userService.getUser();
}
}
Java中DAO层、Service层和Controller层的区别:
- DAO层:数据访问层,属于底层的操作,具体对应某个表的增删改查,也就是说某个DAO一定是和数据库的某一张表一一对应的。
- Service层:服务层,是对一个或多个DAO进行的再次封装,封装成一个服务,所以这里也就不会是一个原子操作了,需要事务控制。
- Controler层:Controler层负责请求转发,接受页面过来的参数,传给Service处理,接到返回值,再传给页面。
Spring的配置
Spring的配置文件中主要有以下几种配置,最主要最重要的是bean的配置。
1、别名
<!--user是某个bean的唯一标识符,user2是这个bean的别名-->
<alias name="user" alias="user2"/>
2、Bean的配置
<!--
id:bean的唯一标识符
class:bean对象所对应的全限定名,即包名+类型
name:也是别名,而且name可以取多个别名
-->
<bean id="user" class="com.xlq.pojo.User" name="user2">
<constructor-arg name="name" value="xlq"/>
<constructor-arg name="age" value="25"/>
</bean>
3、import
- 用于团队开发使用,将多个人写的beans.xml合并为一个总的applicationContext.xml。
- 使用的时候直接加载总的配置文件即可。
IoC的基本概念
由问题引出IoC
传统的Dao层和Service层的文件构成:
- UserDao–dao层的底层接口
- UserDaoImpl–实现类
- UserService–业务接口
- UserServiceImpl–业务实现类
在业务层中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求去修改源代码,如果程序量十分庞大,修改一次的成本代价十分昂贵。
第一版代码:
public class UserServiceImpl implements UserService {
// private UserDao userDao = new UserDaoImpl();
// 如果用户想从Mysql中获取数据,有一个问题:这样的代码适应不了用户需求的变更
private UserDao userDao = new UserDaoMysqlImpl();
// 在业务层调DAO层
public void getUser() {
userDao.getUser();
}
}
如果使用set方法进入动态注入,将会发生革命性的变化。
第二版代码:
public class UserServiceImpl implements UserService {
private UserDao userDao;
// 利用set方法进入动态注入
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
// 在业务层调DAO层
public void getUser() {
userDao.getUser();
}
}
控制反转的意思:
- 第一版代码中,userDao是程序员主动创建的,控制权在程序员手上。
- 第二版代码中,通过set方法动态注入,程序不再具有主动性,而是变成了被动地接收对象。
IoC的本质
IoC(Inversion of Control,控制反转),是一种设计思想,DI(Dependency Injection,依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入。
IoC创建对象的方式
第一类:无参构造。Spring默认使用无参构造创建对象。
第二类:我们需要使用有参构造创建对象。官网提供了三种方式。
- 下标赋值
<bean id="user" class="com.xlq.pojo.User">
<constructor-arg index="0" value="xlq"/>
</bean>
- 类型
<!--如果有多个参数,不推荐使用-->
<bean id="user" class="com.xlq.pojo.User">
<constructor-arg type="java.lang.String" value="xlq"/>
</bean>
- 参数名
<bean id="user" class="com.xlq.pojo.User">
<constructor-arg name="name" value="xlq"/>
<constructor-arg name="age" value="25"/>
</bean>
总结:在配置文件加载的时候,容器中管理的对象就已经初始化了!
依赖注入(DI)
1、构造器注入
2、Set方法注入【重点】
依赖注入,即Set注入,含义是什么?
- 依赖:bean对象的创建依赖于容器。
- 注入:bean对象中的所有属性,由容器来注入!
示例:
类信息为:
public class Address {
private String addr;
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
@Override
public String toString() {
return "Address{" + "addr='" + addr + '\'' + '}';
}
}
public class Student {
private String name;//基本类型
private Address addr;//引用类型
private String[] books;
private Set<String> hobbies;
private Map<String, String> cards;
private List<String> games;
private String wife;// 测试null的注入
private Properties info;
// 这里还必须要有各个属性的Set方法,太长先省略
}
beans.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="address" class="com.xlq.pojo.Address">
<property name="addr" value="nanjing"/>
</bean>
<bean id="student" class="com.xlq.pojo.Student">
<!--基本类型的注入-->
<property name="name" value="xiaoming"/>
<!--引用类型的注入-->
<property name="addr" ref="address"/>
<!--数组类型的注入-->
<property name="books">
<array>
<value>红楼梦</value>
<value>西游记</value>
<value>水浒传</value>
<value>三国演义</value>
</array>
</property>
<!--下面三个是集合类的注入-->
<property name="hobbies">
<set>
<value>唱歌</value>
<value>跑步</value>
<value>打羽毛球</value>
</set>
</property>
<property name="cards">
<map>
<entry key="身份证" value="123456"/>
<entry key="银行卡" value="123456"/>
</map>
</property>
<property name="games">
<list>
<value>LOL</value>
<value>CS</value>
<value>GTA5</value>
</list>
</property>
<!--空值null的注入-->
<property name="wife">
<null/>
</property>
<!--Properties的注入-->
<property name="info">
<props>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
</beans>
3、扩展方法注入–p命名空间和c命名空间
实体类:
public class User {
private String name;
private int age;
public User() {
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" + "name='" + name + '\'' + ", age=" + age +'}';
}
}
配置文件beans2.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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--p命名空间注入,可以直接注入属性的值,p表示property-->
<bean id="user" class="com.xlq.pojo.User" p:age="18" p:name="xxx"/>
<!--c命名空间的注入,通过构造器注入:construct-args-->
<bean id="user2" class="com.xlq.pojo.User" c:age="18" c:name="xxx"/>
</beans>
注意点:p命名和c命名空间不能直接使用,需要导入xml约束。
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
Bean的作用域
官网中关于Bean的作用域的说明:
简要介绍:
- 单例模式【Spring默认机制】
- 原型模式:每次从容器中get的时候,都会产生一个新的对象。
- 其余的request、session、application这些只能在web开发中使用到。
Bean的自动装配
自动装配是Spring满足bean依赖的一种方式。Spring会在上下文中自动寻找,并自动给bean装配属性。
Spring中有三种装配的方式:
- 在xml中显式配置;
- 在Java中显式配置;
- 隐式地自动装配bean【重要】
1、ByName自动装配
<bean id="dog" class="com.xlq.pojo.Dog"/>
<bean id="cat" class="com.xlq.pojo.Cat"/>
<!--Persong含有两个Set方法:
public void setDog(Dog dog)
public void setCat(Cat cat)-->
<bean id="person" class="com.xlq.pojo.Person" autowire="byName">
<property name="name" value="xxx"/>
</bean>
2、ByType自动装配
<bean class="com.xlq.pojo.Dog"/>
<bean class="com.xlq.pojo.Cat"/>
<bean id="person" class="com.xlq.pojo.Person" autowire="byType">
<property name="name" value="xxx"/>
</bean>
区别:
- byname的时候,需要保证自动装配的bean的id唯一,并且id的名字需要和自动注入的set方法的形参名字一一对应;
- bytype的时候,需要保证自动装配的bean的class唯一,并且class的名字需要和自动注入的set方法的形参类型一一对应;
3、注解实现自动装配
使用注解的步骤:
- 导入约束:context约束
- 配置注解的支持:context:annotation-config
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"><!--导入约束-->
<!--配置注解的支持-->
<context:annotation-config/>
</beans>
@Autowired
基本使用:直接在属性上使用即可,也可以在set方法上使用。
使用Autowired我们可以不用编写set方法了,前提是这个自动装配的属性在IoC容器中存在,且符合名字byname。
public class Person {
private String name;
// 如果显式定义了Autowired属性为false,说明这个对象可以为null,否则不允许为空
@Autowired(required = false)
private Dog dog;
@Autowired
private Cat cat;
}
如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候,我们可以使用@Qualifier(value=“xxx”)去配置@Autowired的使用,指定一个唯一的bean对象注入。
public class Person {
private String name;
@Autowired
@Qualifier(value = "dog2")
private Dog dog;
@Autowired
private Cat cat;
}
@Resource
public class Person {
private String name;
@Resource(name = "dog2")
private Dog dog;
@Resource
private Cat cat;
@Resource和@Autowired的区别:
- 都是用来自动装配的,都可以放在属性字段上;
- @Autowired通过bytype的方式实现,而且必须要求这个对象存在。
- @Resource默认通过byname的方式实现,如果找不到名字,则通过bytype实现,如果两个都找不到则报错。
使用注解开发
前置条件
- 在Spring4之后,要使用注解开发,必须要保证aop包导入了。
- 使用注解需要导入context约束,增加注解的支持。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
</beans>
常用注解
@Autowired
:自动装配通过类型,名字;如果@Autowired
不能唯一自动装配,则需要通过@Qualifier(value="xxx")
@Nullable
:字段标记了这个注解,说明这个字段可以为null@Resource
:自动装配通过名字,类型@Component
:组件,放在类上,说明这个类被Spring管理了,就是bean。@Component
有几个衍生注解,下面几个衍生注解的含义与@Component
相同。在web开发中,会按照MVC三层架构分层:- dao层:
@Repository
- service层:
@Service
- controller层:
@Controller
- dao层:
@Scope
:作用域
@Component
@Scope("singleton")
public class User {
@Value("xlq")
public String name;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
XML和注解使用总结:
- XML更加万能,适用于任何场合,维护简单方便。
- 注解不是自己的类不能使用,也就是说,注解只是针对使用注解的这个类。维护相对复杂。
- 最佳实践:XML用来管理bean,注解只负责注入属性。
JavaConfig
注:在Springboot中较为常见。
实体类:
@Component
public class User {
@Value("xxx")
public String name;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
配置类:通过Java类来进行配置,代替了原先配置文件的作用。
// @Configuration表示这是一个配置类,作用相当于是beans.xml
// 同时也会注册到Spring容器中,本质上是一个@Component
@Configuration
@ComponentScan("com.xlq") // 扫描指定包下的注解
public class MyConfig {
// 注册一个bean,作用相当于是原先配置文件中的<bean>标签
// 这个方法的名字,相当于<bean>标签中的id属性
// 这个方法的返回值,相当于<bean>标签中的class属性
@Bean
public User getUser() {
return new User();
}
}
测试类:
public class MyTest {
@Test
public void test() {
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
User user = context.getBean("getUser", User.class);
System.out.println(user);
}
}
代理模式
代理模式:Spring AOP的底层!
代理模式的分类:
- 静态代理
- 动态代理
静态代理
代理模式示例:
角色介绍:
- 抽象角色:一般会使用接口或者抽象类来实现。
- 真实角色:被代理的角色。比如:房东。
- 代理角色:代理真实角色,代理真实角色后,还会有一些附属操作,比如:房屋中介,附属操作是看房子、签合同等。
- 客户端:访问代理角色的人。比如:租户。
代码步骤:
- 接口
- 真实角色
- 代理角色
- 客户端访问代理角色
代理模式的好处:
- 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务(比如看房、签合同)
- 公共业务都交给代理模式,实现了业务的分工。
- 公共业务发生扩展的时候,方便集中管理。
缺点:一个真实角色就会产生一个代理角色,代码量会翻倍,开发效率会降低。(从代码的角度来理解,创建中介对象的时候会传入一个房东对象,体现出了静态代理)
动态代理
动态代理中的各个角色和静态代理是一致的。但是动态代理的代理类是动态生成的,不是我们直接写好的。
动态代理分为两大类:基于接口的动态代理和基于类的动态代理。
- 基于接口:JDK动态代理
- 基于类:cglib
- Java字节码实现:JAVAsist
动态代理的好处:
- 一个动态代理类代理的是一个接口,接口对应的是某一类业务,比如:房屋租赁业务。
- 一个动态代理类可以代理多个类,这些类实现同一个接口即可。
动态代理中两个重要的类和接口:Proxy(类)和InvocationHandler(接口)
- InvocationHandler接口里面只有一个invoke()方法,该方法是proxy代理实例的调用处理程序。每一个proxy代理实例都有一个关联的调用处理程序;代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法。invoke方法如下:
/**
* proxy:代理对象
* method:代理对象调用接口方法所对应的Method实例。注:我们是通过代理对象来调用真实对象中的方法。
* args:指代理对象调用方法传递的参数
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
- Proxy类就是用来创建一个代理对象的类,它提供了很多方法,但是我们最常用的是newProxyInstance方法。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
三个参数的含义:
- loader:一个Classloader对象,定义了由哪个Classloader对象对生成的代理类进行加载;
- interfaces:一个interface对象数组,表示我们将要给我们的代理对象提供一组什么样的接口,如果我们提供了这样一个接口对象数组,那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。
- h:一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用。
参考资料:
Java动态代理InvocationHandler和Proxy学习笔记
Java中InvocationHandler接口中第一个参数proxy详解
AOP
AOP的实现机制:
AOP的实现方式
前提是先要导入AOP的依赖:
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
方式一:使用原生Spring API接口
新增日志业务:
public class Log implements MethodBeforeAdvice {
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
}
}
public class AfterLog implements AfterReturningAdvice {
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了"+method.getName()+"方法,返回结果为:"+returnValue);
}
}
配置文件中注册bean和配置AOP:
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注册bean-->
<bean id="userService" class="com.xlq.service.UserServiceImpl"/>
<bean id="log" class="com.xlq.log.Log"/>
<bean id="afterLog" class="com.xlq.log.AfterLog"/>
<!--使用AOP方式一:使用原生Spring API接口-->
<!--配置AOP:需要导入AOP的约束-->
<aop:config>
<!--切入点
id:该切入点的唯一标识符
expression:按照表达式进行匹配
第一个 * 表示任意返回值
com.xlq.service.UserServiceImpl.*表示匹配UserServiceImpl类里面的任意方法
(..)表示有无参数均可,有参数的话可以是任意参数-->
<aop:pointcut id="pointcut" expression="execution(* com.xlq.service.UserServiceImpl.*(..))"/>
<!--执行环绕增加-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
测试类:
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.delete();
}
}
方式二:自定义切面来实现AOP
要点:
- 在配置文件中自定义切面,切面实际上就是一个类,所以切面要和一个类进行关联。
- 在切面里面定义切入点
<aop:pointcut>
和相应的通知机制<aop:before>
、<aop:after>
。
自定义类DiyPoint,所在的包是com.xlq.diy
public class DiyPointCut {
public void before() {
System.out.println("在切入点之前");
}
public void after() {
System.out.println("在切入点之后");
}
}
配置文件:
<!--自定义类-->
<bean id="diy" class="com.xlq.diy.DiyPointCut"/>
<aop:config>
<!--自定义切面,ref为要引入的类-->
<aop:aspect ref="diy">
<!--切入点-->
<aop:pointcut id="point" expression="execution(* com.xlq.service.UserServiceImpl.*(..))"/>
<!--通知-->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
方式三:使用注解来实现。
自定义类AnnotationPointCut.java
@Aspect // 标注这个类是一个切面
public class AnnotationPointCut {
@Before("execution(* com.xlq.service.UserServiceImpl.*(..))")
public void before() {
System.out.println("方法执行之前");
}
@After("execution(* com.xlq.service.UserServiceImpl.*(..))")
public void after() {
System.out.println("方法执行之后");
}
// 在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点
@Around("execution(* com.xlq.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
Signature signature = jp.getSignature();
System.out.println("signature: "+signature);
Object proceed = jp.proceed();// 执行方法
System.out.println("环绕后");
}
}
XML配置:
<bean id="annotationPointCut" class="com.xlq.diy.AnnotationPointCut"/>
<!--开启注解支持-->
<aop:aspectj-autoproxy/>
整合MyBatis
参考资料:mybatis-spring官方文档
一般步骤(推荐)
1、数据源
2、sqlSessionFactory
3、sqlSessionTemplate
4、接口的实现类
<!--用Spring来管理数据源-->
<!--DataSource:使用Spring的数据源来替换Mybatis的配置 c3p0 dpcp druid
使用Spring提供的JDBC:org.springframework.jdbc.datasource-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--绑定mybatis配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/xlq/dao/*.xml"/>
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--只能使用构造器注入sqlSessionFactory,因为SqlSessionTemplate没有set方法-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
UserMapper接口的实现类:
public class UserMapperImpl implements UserMapper {
// 我们的所有操作,原来使用sqlSession来执行,现在是使用SqlSessionTemplate
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
public List<User> getUserList() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.getUserList();
}
}
还需要把实现类注入到Spring中:
<bean id="userMapper" class="com.xlq.dao.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
SqlSessionDaoSupport
获取SqlSession的另外一种方法。SqlSessionDaoSupport 是一个抽象的支持类,用来为你提供 SqlSession。调用 getSqlSession() 方法你会得到一个 SqlSessionTemplate,之后可以用于执行 SQL 方法。
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
public List<User> getUserList() {
return getSqlSession().getMapper(UserMapper.class).getUserList();
}
}
SqlSessionDaoSupport 需要通过属性设置一个 sqlSessionFactory 或 SqlSessionTemplate。如果两个属性都被设置了,那么 SqlSessionFactory 将被忽略。 配置信息如下:
<bean id="userMapper2" class="com.xlq.dao.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
事务
事务概述
- 把一组业务当成一个业务来做,要么都成功,要么都失败;
- 事务在项目开发中,十分重要,设计到数据的一致性问题,不能马虎;
- 确保完整性和一致性。
事务ACID特性:
- 原子性
- 一致性
- 隔离性:多个业务可能操作同一个资源,防止数据损坏。
- 持久性:事务一旦提交,无论系统发生什么问题,结果都不会再被影响,被持久得写到存储器中。
事务管理的引入
在之前的Spring整合Mybatis的工程中,用户Mapper实现类中加入插入用户和删除用户的功能。
public class UserMapperImpl implements UserMapper {
// 我们的所有操作,原来使用sqlSession来执行,现在是使用SqlSessionTemplate
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
public List<User> getUserList() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User(6, "lili", "123456");
mapper.insertUser(user);
mapper.deleteUser(6);
return mapper.getUserList();
}
public void insertUser(User user) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.insertUser(user);
}
public void deleteUser(int id) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.deleteUser(6);
}
}
配置文件:
<?xml version="1.0" encoding="GBK" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace就是绑定一个对应的Mapper接口-->
<mapper namespace="com.xlq.dao.UserMapper">
<!--select查询语句-->
<select id="getUserList" resultType="user">
select * from test.user
</select>
<insert id="insertUser" parameterType="user">
insert into test.user(id, name, pwd) values (#{id}, #{name}, #{pwd})
</insert>
<delete id="deleteUser" parameterType="int">
deletes from test.user where id=#{id}
</delete>
</mapper>
可以看到,delete语句含有语法错误,这是故意写错的。在获取所有用户的方法getUserList()
中有添加一个用户,和删除一个用户的方法,删除一个用户出现错误,但是添加的用户依旧被添加了,所以getUserList()
方法不是原子性的。
Spring的事务
一个使用 MyBatis-Spring 的其中一个主要原因是它允许 MyBatis 参与到 Spring 的事务管理中。而不是给 MyBatis 创建一个新的专用事务管理器,MyBatis-Spring 借助了 Spring 中的 DataSourceTransactionManager 来实现事务管理。
要开启 Spring 的事务处理功能,在 Spring 的配置文件中创建一个 DataSourceTransactionManager 对象,传入的 DataSource 可以是任何能够与 Spring 兼容的 JDBC DataSource。包括连接池和通过 JNDI 查找获得的 DataSource。
<!--配置声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="dataSource" />
</bean>
Spring支持两种事务:
- 声明式事务
- 编程式事务
声明式事务就是用AOP来进行横向切入,在原有基础上增加事务功能。编程式事务是用try-catch来捕获异常,从而进行事务的回滚。所以我们肯定都是用声明式事务,借用AOP来进行横向切入。
<!--结合AOP实现事务的织入-->
<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--给哪些方法配置事务-->
<!--配置事务的传播特性-->
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<tx:method name="query" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--配置事务切入-->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.xlq.dao.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>