Spring学习
什么是Spring
Spring是JavaEE编程领域的一个轻量级开源框架,是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器(框架)。
它主要由以下部分组成
Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式 .
组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:
- 核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
- Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
- Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能 , 集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理任何支持 AOP的对象。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中。
- Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
- Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
- Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
- Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。
IoC
- IoC(Inversion of Control)控制反转即使用一个中间IoC容器来管理各个对象,从而实现解耦的目的
Spring的Hello World
- 在Maven中导入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
- 首先创建实体类Hello
package com.toolate.pojo;
import lombok.Data;
@Data
public class Hello {
private String name;
public void show(){
System.out.println("Hello,"+name);
}
}
- 在resources目录下添加一个bean.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就是java对象 , 由Spring创建和管理-->
<!--
value:普通字段
ref:SpringBean
-->
<bean id="hello" class="com.toolate.pojo.Hello">
<property name="name" value="Spring"/>
</bean>
</beans>
- 创建测试类进行测试
import com.toolate.pojo.Hello;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void test01(){
//解析beans.xml文件 , 生成管理相应的Bean对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//getBean : 参数即为spring配置文件中bean的id
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello.toString());
}
}
- 输出如下
Hello(name=Spring)
所谓的IoC,一句话搞定 : 对象由Spring 来创建 , 管理 , 装配 !
IoC创建对象的方式
- 通过无参构造方法来创建
<?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="user" class="com.toolate.pojo.User">
<property name="name" value="toolate"/>
</bean>
</beans>
- 通过有参构造方法来创建
<!-- 第一种根据index参数下标设置 -->
<bean id="userT" class="com.toolate.pojo.UserT">
<!-- index指构造方法 , 下标从0开始 -->
<constructor-arg index="0" value="toolate"/>
</bean>
<!-- 第二种根据参数名字设置 -->
<bean id="userT" class="com.toolate.pojo.UserT">
<!-- name指参数名 -->
<constructor-arg name="name" value="toolate"/>
</bean>
<!-- 第三种根据参数类型设置 -->
<bean id="userT" class="com.toolate.pojo.UserT">
<constructor-arg type="java.lang.String" value="toolate"/>
</bean>
Spring配置
- 别名
<alias name="hello" alias="User"></alias>
alias :给Bean一个别名,从而两个名字都可以用
<bean id="hello" class="com.toolate.pojo.Hello" name="hello2 hello3,hello4;hello5">
<property name="name" value="Spring"/>
</bean>
id:实体类对应的对象名
class:bean的包名+类型
name:别名,且可以用逗号、空格、分号分隔取多个别名
import
在applicationContext.xml中导入其他的配置文件,从而只需在使用时导入一个配置文件而使用所有的配置文件。
<import resource="beans2.xml"/>
依赖(Dependecy Injection)
- 依赖 : 指Bean对象的创建依赖于容器 . Bean对象的依赖资源 .
- 注入 : 指Bean对象所依赖的资源 , 由容器来设置和装配 .
使用了Lombok插件的@Data注解减少了get和set等方法
实体类:
- Address.java
package com.toolate.pojo;
import lombok.Data;
@Data
public class Address {
private String address;
}
- Student.java
package com.toolate.pojo;
import lombok.Data;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
@Data
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> hobbys;
private Map<String,String> card;
private Set<String> games;
private Properties info;
private String wife;
}
- beans.xml(其中包括常量注入、Bean注入、数组注入、List注入、Map注入、set注入、Null注入、Properties注入)
<bean id="address" class="com.toolate.pojo.Address">
<property name="address" value="江西"></property>
</bean>
<bean id="student" class="com.toolate.pojo.Student">
<property name="name" value="TooLate"/>
<!--Bean注入,ref-->
<property name="address" ref="address"/>
<!--数组注入-->
<property name="books">
<array>
<value>红楼梦</value>
<value>西游记</value>
<value>水浒传</value>
<value>三国演义</value>
</array>
</property>
<!--List-->
<property name="hobbys">
<list>
<value>看电影</value>
<value>打代码</value>
<value>听歌</value>
</list>
</property>
<!--Map-->
<property name="card">
<map>
<entry key="身份证" value="111111111111111"></entry>
<entry key="银行卡" value="1111112222233333"></entry>
</map>
</property>
<!--Set-->
<property name="games">
<set>
<value>LOL</value>
<value>COC</value>
<value>BOB</value>
</set>
</property>
<!--null-->
<property name="wife">
<null/>
</property>
<!--Properties-->
<property name="info">
<props>
<prop key="学号">8002221574</prop>
<prop key="性别">男</prop>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
值得注意的是Map和Properties比较特殊,需要用到key-value键值对,和其他的注入方式不太一样。
- 测试代码
import com.toolate.pojo.Hello;
import com.toolate.pojo.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void test01(){
//解析beans.xml文件 , 生成管理相应的Bean对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//getBean : 参数即为spring配置文件中bean的id
Student student = (Student) context.getBean("student");
System.out.println(student.toString());
}
}
- 注入结果如下
Student(name=TooLate, address=Address(address=江西),
books=[红楼梦, 西游记, 水浒传, 三国演义],
hobbys=[看电影, 打代码, 听歌],
card={身份证=111111111111111, 银行卡=1111112222233333},
games=[LOL, COC, BOB],
info={学号=8002221574, 性别=男, password=123456, username=root},
wife=null)
p命名空间
即property注入
- User.java
package com.toolate.pojo;
import lombok.Data;
@Data
public class User {
private String name;
private int age;
}
- userbean.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--p命名空间,即property-->
<bean id="user" class="com.toolate.pojo.User" p:name="TooLate" p:age="18"/>
</beans>
注意需要多导入一个约束
xmlns:p="http://www.springframework.org/schema/p"
- 测试代码
@Test
public void tese02(){
ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
User user = context.getBean("user", User.class);
System.out.println(user);
}
- 结果如下
User(name=TooLate, age=18)
c命名空间
即constructor注入
- User.java
package com.toolate.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String name;
private int age;
}
- userbean.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命名空间,即property-->
<bean id="user" class="com.toolate.pojo.User" p:name="TooLate" p:age="18"/>
<!--c命名空间-->
<bean id="user2" class="com.toolate.pojo.User" c:name="TooLate" c:age="20"></bean>
</beans>
同样需要多导入一个约束
xmlns:c="http://www.springframework.org/schema/c"
- 测试代码
@Test
public void tese02(){
ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
User user = context.getBean("user2", User.class);
System.out.println(user);
}
- 结果如下
User(name=TooLate, age=20)
Bean的作用域
单例模式
默认是单例模式,也可以用scope显示定义
<bean id="user" class="com.toolate.pojo.User" p:name="TooLate" p:age="18" scope="singleton"/>
原型模式
- 使用prototype原型模式
<bean id="user2" class="com.toolate.pojo.User" c:name="TooLate" c:age="20" scope="prototype"></bean>
- 测试代码
@Test
public void tese02(){
ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
User user1 = context.getBean("user2", User.class);
User user2 = context.getBean("user2", User.class);
System.out.println(user1 == user2);//false
}
结果user1和user2不是同一个对象,即为原型模式
Bean的自动装配
自动装配
- spring会在应用上下文中为某个bean寻找其依赖的bean
- Spring中bean有三种装配机制,分别是:
- 在xml中显式配置
- 在java中显式配置
- 隐式的bean发现机制和自动装配【重要】
- Dog.java
package com.toolate.pojo;
public class Cat {
public void shout(){
System.out.println("汪~");
}
}
- Cat.java
package com.toolate.pojo;
public class Dog {
public void shout() {
System.out.println("喵~");
}
}
- People.java
package com.toolate.pojo;
import lombok.Data;
@Data
public class People {
private Cat cat;
private Dog dog;
private String name;
}
- byName方式的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="cat" class="com.toolate.pojo.Cat"/>
<bean id="dog" class="com.toolate.pojo.Dog"/>
<bean id="people" class="com.toolate.pojo.People" autowire="byName">
<property name="name" value="TooLate"/>
</bean>
</beans>
- byType方式的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="cat" class="com.toolate.pojo.Cat"/>
<bean id="dog" class="com.toolate.pojo.Dog"/>
<bean id="people" class="com.toolate.pojo.People" autowire="byType">
<property name="name" value="TooLate"/>
</bean>
</beans>
- byName:会自动在容器上下文中查找和自己对象set方法后面的值对应的bean的id
- byType:会自动在容器上下文中查找和自己对象属性类型相同的bean的id
使用byName时id必须和set方法后面的名字对应,否则找不到
使用byType时可以不用使用id,因为是通过属性去找的,但是如果有多个对象时则无法使用
二者各有优点和缺点
Spring注解
自动装配注解
- 导入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"
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/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">
</beans>
- 配置注解的支持和自动扫描
<context:annotation-config/>
<context:component-scan base-package="com.toolate"/>
- 在实体类中用注解注入
@Autowired:自动注入
@Qualifier:当有多个bean时,通过指定value来指定一个bean来注入
@Data
public class People {
@Autowired
@Qualifier(value = "cat")
private Cat cat;
@Autowired
@Qualifier(value = "dog")
private Dog dog;
private String name;
}
@Resource:和@Autowired效果相同,可以用name来指定一个bean
@Resource
private Cat cat;
@Resource
private Dog dog;
@Resource和@Autowired的区别:
- @Autowired默认通过byType方式实现 ,而且对象必须存在【常用】
- @Resource默认通过byname方式实现,如果找不到就通过byType实现【常用】
- 但执行顺序不同,@Autowired先byType,@Resource先byName。
属性注入
- @Component
- @Value
@Component
public class MyUser {
@Value("TooLate")
public String name;
}
衍生的注解
@Component三个衍生注解
为了更好的进行分层,Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。
- @Controller:【controller层】
- @Service:【service层】
- @Repository:【dao层】
写上这些注解,就相当于将这个类交给Spring管理装配了!
作用域
- @Scope
@Component
@Scope("singleton")
public class MyUser {
@Value("TooLate")
public String name;
}
可使用singleton(单例模式)和prototype(原型模式)
基于Java类进行配置
- 新建一个MyConfig.java配置类
@Configuration
@ComponentScan("com.toolate.pojo")
@Import(MyConfig2.class)
public class MyConfig {
@Bean
public User getUser(){
return new User();
}
}
@Configuration:定义为配置类
@ComponentScan:Bean扫描
@Import:导入其他的配置类
@Bean:用get方法的名字来注入Bean
- 获取容器并测试
@Test
public void test05(){
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
User user = (User) context.getBean("getUser");
System.out.println(user.getName());
}
AOP
Aspect Oriented Programming即面向切面编程
代理模式
代理模式的分类:
- 静态代理
- 动态代理
静态代理
角色分析:
- 抽象角色:一般使用接口或者抽象类来实现
- 真实角色:被代理的角色
- 代理角色:代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .
- 客户:使用代理角色来进行一些操作 .
代码实现
Rent.java
//租房
public interface Rent {
public void rent();
}
LandLord.java
//房东
public class LandLord implements Rent{
public void rent() {
System.out.println("房东要出租房子");
}
}
Proxy.java
public class Proxy implements Rent {
private LandLord landLord;
public Proxy(){
}
public Proxy(LandLord landLord){
this.landLord=landLord;
}
public void rent() {
landLord.rent();
}
public void seeHouse(){
System.out.println("房东带你看房子");
}
public void Contract(){
System.out.println("签合同");
}
}
Client.java
public class Client {
public static void main(String[] args) {
LandLord landLord = new LandLord();
//代理
Proxy proxy = new Proxy(landLord);
proxy.rent();
proxy.seeHouse();
proxy.Contract();
}
}
结果为客户通过代理(中间商)租房子
静态代理的好处:
- 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
- 公共的业务由代理来完成 . 实现了业务的分工 ,
- 公共业务发生扩展时变得更加集中和方便 .
缺点 :
- 类多了 , 多了代理类 , 工作量变大了 . 开发效率降低 .
我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理 。
动态代理
- 动态代理的角色和静态代理的一样 .
- 动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的
- 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理
UserService.java
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
UserServiceImpl.java
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("执行了add方法");
}
public void delete() {
System.out.println("执行了delete方法");
}
public void update() {
System.out.println("执行了update方法");
}
public void query() {
System.out.println("执行了query方法");
}
}
ProxyInvocationHandler.java实现InvocationHandler
//自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成并得到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
//处理代理实例,并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(target, args);
return result;
}
}
Client.java
public class Client {
public static void main(String[] args) {
//真实角色
UserServiceImpl userService = new UserServiceImpl();
//代理角色
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
//设置要代理的对象
proxyInvocationHandler.setTarget(userService);
//动态生成代理类
UserService proxy = (UserService) proxyInvocationHandler.getProxy();
proxy.add();
}
}
一个动态代理 , 一般代理某一类业务 , 一个动态代理可以代理多个类,代理的是接口。
AOP(方式一:使用Spring的API)
- UserService.java
public interface UserService {
public void add();
public void delete();
public void update();
public void select();
}
- UserServiceImpl.java
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("增加了一个用户");
}
public void delete() {
System.out.println("删除了一个用户");
}
public void update() {
System.out.println("修改了一个用户");
}
public void select() {
System.out.println("查询了一个用户");
}
}
- Log.java
public class Log implements MethodBeforeAdvice {
//method:要执行的目标对象的方法
//objects:参数
//o:目标对象
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(o.getClass().getName()+"的"+method.getName()+"被执行了");
}
}
- AfterLog.java
public class AfterLog implements AfterReturningAdvice {
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("执行了"+method.getName()+"方法,结果为"+o) ;
}
}
- applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.toolate.service.UserServiceImpl"/>
<bean id="log" class="com.toolate.log.Log"/>
<bean id="afterLog" class="com.toolate.log.AfterLog"/>
<!--方式一:使用原生Spring API接口-->
<!--配置aop:需要导入aop的约束-->
<aop:config>
<!--切入点:expression,表达式:execution(需要执行的位置)-->
<aop:pointcut id="pointcut" expression="execution(* com.toolate.service.UserServiceImpl.*(..))"/>
<!--执行环绕增加-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
- 测试类
public class MyTest {
@Test
public void Test01(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//注意:这里代理的应该是接口而不是实现类
UserService userService = context.getBean("userService", UserService.class);
userService.add();
}
}
AOP(方式二:自定义实现)
- DiyPointCut.java
public class DiyPointCut {
public void before(){
System.out.println("=======方法执行前======");
}
public void after(){
System.out.println("=======方法执行后======");
}
}
- applicationContext.xml
<aop:config>
<!--自定义切面,ref为要引用的类-->
<aop:aspect ref="diy">
<!--切入点-->
<aop:pointcut id="point" expression="execution(* com.toolate.service.UserServiceImpl.*(..))"/>
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
自定义切面和切入点来实现AOP
AOP(方式三:注解)
- AnnotationPointCut.java
@Aspect
public class AnnotationPointCut {
@Before("execution(* com.toolate.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("=======方法执行前======");
}
@After("execution(* com.toolate.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("=======方法执行后======");
}
@Around("execution(* com.toolate.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("======环绕前======");
Object proceed = joinPoint.proceed();
Signature signature = joinPoint.getSignature();
System.out.println("signature:"+signature);
System.out.println("======环绕后======");
}
}
@Aspect:表明这是一个切面
@Before:执行前
@After:执行后
@Around:环绕
- applicationContext.xml
<!--方式三:注解-->
<bean id="annotationPointCut" class="com.toolate.diy.AnnotationPointCut"/>
<!--开启注解支持-->
<aop:aspectj-autoproxy/>
整合Mybatis
MyBatis-Spring
核心思想是把Mybatis是sqlsession注入到spring的ioc容器中
- 导入Mybatis-Spring
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
- 注入数据源DataSource
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
- 注入sqlSessionFactory
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<!--绑定Mybatis配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/toolate/mapper/*.xml"/>
</bean>
- 注册sqlSession
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--没有set方法,只能用构造器注入-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
- 接口UserMapper.java
public interface UserMapper {
public List<User> selectUser();
}
- UserMapper.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.toolate.mapper.UserMapper">
<select id="selectUser" resultType="user">
select * from mybatis.user;
</select>
</mapper>
- UserMapperImpl.java
public class UserMapperImpl implements UserMapper {
private SqlSessionTemplate sqlSessionTemplate;
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSessionTemplate = sqlSessionTemplate;
}
public List<User> selectUser() {
UserMapper mapper = sqlSessionTemplate.getMapper(UserMapper.class);
return mapper.selectUser();
}
}
- applicationContext.xml
在这个配置文件里写userMapper的bean
<?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">
<import resource="spring-dao.xml"/>
<bean id="userMapper" class="com.toolate.mapper.UserMapperImpl">
<property name="sqlSessionTemplate" ref="sqlSession"/>
</bean>
</beans>
声明式事务
什么是事务
事务(Transaction):是数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作;这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行;事务是一组不可再分割的操作集合(工作逻辑单元)
一般来说,事务是必须满足4个条件(ACID):
- 原子性(atomicity):一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
- 一致性(consistency):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
- 隔离性(isolation):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
- 持久性(durability):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
Spring中的事务管理
- 首先在xml中导入如下依赖(tx即事务)
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
- 注入spring的事务管理
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="dataSource"/>
</bean>
- 利用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>
- spring事务传播特性:
事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。spring支持7种事务传播行为:
- propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
- supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
- mandatory:使用当前事务,如果没有当前事务,就抛出异常。
- required_new:新建事务,如果当前存在事务,把当前事务挂起。
- not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- never:以非事务方式执行操作,如果当前事务存在则抛出异常。
- nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作
Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。
- 配置AOP切入点
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.toolate.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
为什么需要配置事务?
- 如果不配置,就需要我们手动提交控制事务;
- 事务在项目开发过程非常重要,涉及到数据的一致性的问题,不容马虎!