Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架
官网:https://spring.io/projects/spring-framework#overview
官方下载地址:https://repo.spring.io/release/org/springframework/spring/
GitHub:https://github.com/spring-projects/spring-framework
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.6</version>
</dependency>
Core封装包是框架的最基础部分,提供IOC和依赖注入特性。这里的基础概念是BeanFactory,它提供对Factory模式的经典实现来消除对程序性单例模式的需要,并真正地允许你从程序逻辑中分离出依赖关系和配置。
构建于Core封装包基础上的Context封装包,提供了一种框架式的对象访问方法,有些象JNDI注册器。Context封装包的特性得自于Beans封装包,并添加了对国际化(I18N)的支持(例如资源绑定),事件传播,资源装载的方式和Context的透明创建,比如说通过Servlet 容器。
DAO (Data Access Object)提供了JDBC的抽象层,它可消除冗长的JDBC编码和解析数据库厂商特有的错误代码。并且,JDBC封装包还提供了一种比编程性更好的声明性事务管理方法,不仅仅是实现了特定接口,而且对所有的POJOs(plain old Java objects)都适用。
ORM 封装包提供了常用的“对象/关系”映射APIs的集成层。其中包括JPA、JDO、hibernate和iBatis 。利用ORM封装包,可以混合使用所有spring提供的特性进行“对象/关系”映射,如前边提到的简单声明性事务管理。
Spring的AOP 封装包提供了符合AOP Alliance规范的面向方面的编程实现,让你可以定义,例如方法拦截器(method-interceptors)和切点(pointcuts),从逻辑上讲,从而减弱代码的功能耦合,清晰的被分离开。而且,利用source-level的元数据功能,还可以将各种行为信息合并到你的代码中。
Spring中的Web 包提供了基础的针对Web开发的集成特性,例如多方文件上传,利用Servlet listeners进行IOC容器初始化和针对Web的ApplicationContext。当与WebWork或Struts一起使用Spring时,这个包使Spring可与其他框架结合。
Spring中的MVC封装包提供了Web应用的Model-View-Controller(MVC)实现。Spring 的MVC框架并不是仅仅提供一种传统的实现,它提供了一种清晰的分离模型,在领域模型代码和Web Form之间。并且,还可以借助Spring框架的其他特性。
Spring Boot
SpringCloud
IOC其实是一种设计思想;而实现这种思想的方式有很多
package org.dam.dao;
/**
* @Author dam
* @create 2024/1/5 19:30
*/
public interface UserDao {
public void getUser();
}
【UserDaoImpl】
package org.dam.dao.impl;
import org.dam.dao.UserDao;
/**
* @Author dam
* @create 2024/1/5 19:30
*/
public class UserDaoImpl implements UserDao {
@Override
public void getUser() {
System.out.println("获取用户数据");
}
}
【UserDaoMySqlImpl】
package org.dam.dao.impl;
import org.dam.dao.UserDao;
/**
* @Author dam
* @create 2024/1/5 19:39
*/
public class UserDaoMySqlImpl implements UserDao {
@Override
public void getUser() {
System.out.println("MySql获取用户数据");
}
}
【UserDaoOracleImpl】
package org.dam.dao.impl;
import org.dam.dao.UserDao;
/**
* @Author dam
* @create 2024/1/5 19:39
*/
public class UserDaoOracleImpl implements UserDao {
@Override
public void getUser() {
System.out.println("Oracle获取用户数据");
}
}
package org.dam.service;
/**
* @Author dam
* @create 2024/1/5 19:32
*/
public interface UserService {
public void getUser();
}
import org.dam.dao.UserDao;
import org.dam.dao.impl.UserDaoImpl;
import org.dam.service.UserService;
public class UserServiceImpl implements UserService{
// 直接new,后续如果想更换实现的话,非常麻烦
private UserDao userDao = new UserDaoImpl();
@Override
public void getUser() {
userDao.getUser();
}
}
【缺点】
// 直接new,后续如果想更换实现的话,非常麻烦
private UserDao userDao = new UserDaoImpl();
可以赋值UserDao,这样方便更改实现
public class UserServiceImpl implements UserService{
private UserDao userDao;
//利用set方法进行动态的实现值的注入!
public void setUserDao(UserDao userDao){
this.userDao = userDao;
}
@Override
public void getUser() {
userDao.getUser();
}
}
这样就可以更换实现方式
package org.dam;
import org.dam.dao.impl.UserDaoMySqlImpl;
import org.dam.dao.impl.UserDaoOracleImpl;
import org.dam.service.UserService;
import org.dam.service.impl.UserServiceImpl;
import org.junit.Test;
/**
* @Author dam
* @create 2024/1/5 19:38
*/
public class TestMain {
@Test
public void test() {
UserServiceImpl service = new UserServiceImpl();
// 一开始用mysql来实现
service.setUserDao(new UserDaoMySqlImpl());
service.getUser();
// 后面又想用Oracle去实现呢
service.setUserDao(new UserDaoOracleImpl());
service.getUser();
}
}
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从loc容器中取出需要的对象。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。
首先创建一个maven项目
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.6</version>
</dependency>
</dependencies>
public class Hello {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
@Override
public String toString() {
return "Hello{" +
"str='" + str + '\'' +
'}';
}
}
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--使用Spring来创建对象,在Spring这些都称为Bean
类型 变量名 = new 类型();
Hello hello = new Hello();
id = 变量名
class = new的对象
property 相当于给对象中的属性设置一个值!
-->
<bean id="hello" class="com.dam.pojo.Hello">
<property name="str" value="Spring"/>
</bean>
</beans>
public class MyTest {
public static void main(String[] args) {
//获取Spring的上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
//我们的对象现在都在Spring中的管理了,我们需要使用,直接去里面取出来就可以!
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello.toString());
}
}
运行
Hello对象是谁创建的?
Hello对象是由Spring创建的。
Hello对象的属性是怎么设置的?
Hello对象的属性是由Spring容器设置的。
控制: 谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的。
反转: 程序本身不创建对象,而变成被动的接收对象。
依赖注入: 就是利用set方法来进行注入的。
IOC是一种编程思想,由主动的编程变成被动的接收。其实就是程序员使用Spring创建并管理的对象。
到了现在,我们彻底不用在程序中去改动了,要实现不同的操作,只需要在xml配置文件中进行修改,所谓的IOC,一句话搞定:对象由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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--使用Spring来创建对象,在Spring这些都称为Bean
类型 变量名 = new 类型();
Hello hello = new Hello();
id = 变量名
class = new的对象
property 相当于给对象中的属性设置一个值!
-->
<bean id="oracleImpl" class="com.dam.dao.UserDaoOracleImpl" />
<bean id="mysqlImpl" class="com.dam.dao.UserDaoMysqlImpl" />
<bean id="UserServiceImpl" class="com.dam.Service.UserServiceImpl">
<!--
ref: 引用Spring容器中创建好的对象
value:具体的值(基本数据类型)
-->
<property name="userDao" ref="oracleImpl"></property>
</bean>
</beans>
@Test
public void testSpringIOC() {
//拿到Spring容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
//需要什么,就get什么
UserServiceImpl userServiceImpl = (UserServiceImpl) applicationContext.getBean("UserServiceImpl");
userServiceImpl.getUser();
}
使用了控制反转之后,直接修改配置文件,就可以修改程序
在配置文件加载的时候,所有对象都已经初始化了,Spring创建对象是单例
<bean id="user" class="org.dam.pojo.User">
<property name="name" value="小明"/>
</bean>
@Test
public void testUserShow(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
//在执行getBean的时候, user已经创建好了 , 通过无参构造
User user = (User) applicationContext.getBean("user");
//调用对象的方法 .
user.show();
}
运行,从输出可以得知,Spring帮我们调用了构造方法
实体类要有相应的构造方法
public User(String name) {
System.out.println("执行 User 有参构造方法");
this.name = name;
}
<bean id="user" class="org.dam.pojo.User">
<constructor-arg index="0" value="小花" />
</bean>
@Test
public void testUserShow(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
//在执行getBean的时候, user已经创建好了 , 通过无参构造
User user = (User) applicationContext.getBean("user");
//调用对象的方法 .
user.show();
}
(缺点:如果有多个相同类型的变量,则无法使用)
<bean id="user" class="com.dam.pojo.User">
<constructor-arg type="java.lang.String" value="小明2号"/>
</bean>
<bean id="user" class="com.dam.pojo.User">
<constructor-arg name="name" value="小明三号" />
</bean>
作用就是给一个bean(类对象)起别名:这样的话我们测试执行时,user和userXiaoHua都可以访问创建的类对象
<bean id="user" class="org.dam.pojo.User">
<constructor-arg index="0" value="小花" />
</bean>
<alias name="user" alias="userXiaoHua"/>
使用示例
User user = (User) applicationContext.getBean("userXiaoHua");
<bean id="user" class="com.dam.pojo.User" name="user1, user2">
<property name="name" value="大壮"/>
</bean>
可以通过别名获取对象
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
User daZhuang = (User)context.getBean("user1");
如果不想要强转,可以使用如下方式
User user = context.getBean("user1",User.class);
一般用于团队开发使用,它可以将多个配置文件导入合并为一个总的,等到需要加载配置文件的时候只需要加载这一个总的就相当于加载所有配置类
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="beans1.xml" />
<import resource="beans2.xml" />
<import resource="beans3.xml" />
</beans>
合并之后,可以通过一个配置文件获取所有的bean
参照IOC创建对象的方式
【Address类】
public class Address {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
【Student类】
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 String wife;
private Properties info;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String[] getBooks() {
return books;
}
public void setBooks(String[] books) {
this.books = books;
}
public List<String> getHobbys() {
return hobbys;
}
public void setHobbys(List<String> hobbys) {
this.hobbys = hobbys;
}
public Map<String, String> getCard() {
return card;
}
public void setCard(Map<String, String> card) {
this.card = card;
}
public Set<String> getGames() {
return games;
}
public void setGames(Set<String> games) {
this.games = games;
}
public String getWife() {
return wife;
}
public void setWife(String wife) {
this.wife = wife;
}
public Properties getInfo() {
return info;
}
public void setInfo(Properties info) {
this.info = info;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", address=" + address +
", books=" + Arrays.toString(books) +
", hobbys=" + hobbys +
", card=" + card +
", games=" + games +
", wife='" + wife + '\'' +
", info=" + info +
'}';
}
}
【配置文件】
下面展示不同数据类型的注入方式
<bean id="address" class="com.dam.pojo.Address" >
<property name="address" value="河南省"/>
</bean>
<bean id="student" class="com.dam.pojo.Student">
<!-- 普通值注入 -->
<property name="name" value="小明" />
<!-- Bean注入 -->
<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>
<value>看电影</value>
</list>
</property>
<!-- Map注入 -->
<property name="card">
<map>
<entry key="身份证" value="1111000384" />
<entry key="银行卡" value="222222222222" />
</map>
</property>
<!-- Set注入 -->
<property name="games">
<set>
<value>LOL1</value>
<value>LOL2</value>
<value>LOL3</value>
</set>
</property>
<!-- Null注入 -->
<property name="wife">
<null></null>
</property>
<!-- Properties -->
<property name="info">
<props>
<prop key="学号">110101</prop>
<prop key="性别">男</prop>
<prop key="username">小明</prop>
<prop key="password">11119999</prop>
</props>
</property>
</bean>
要使用命令空间,需要在xml的头文件添加如下约束
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
如
不需要在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"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- p命名空间注入(无参构造) 可以直接注入属性 property -->
<bean id="user" class="com.dam.pojo.User" p:name="小明" p:age="18"></bean>
</beans>
必须要存在 有参构造方法,才可以使用c命名空间
public User(String name, int age) {
this.name = name;
this.age = age;
}
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- c命名空间注入(有参构造器) 可以直接注入属性 property -->
<bean id="user2" class="com.dam.pojo.User" c:name="小强" c:age="19" ></bean>
</beans>
给属性赋值的顺序和有参构造的参数顺序是要一致的
<bean id="user2" class="com.dam.pojo.User" c:name="小强" c:age="19" scope="singleton" ></bean>
<bean id="user2" class="com.dam.pojo.User" c:name="小强" c:age="19" scope="prototype" ></bean>
每次使用getBean,返回的都是一个新对象
其余的request、session、application等只能在web开发中使用到
有三个类People、Cat、Dog
【Cat】
public class Cat {
public void shout() {
System.out.println("miao~");
}
}
【Dog】
public class Dog {
public void shout(){
System.out.println("wow~");
}
}
【People】
public class People {
private Cat cat;
private Dog dog;
private String name;
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
【bean】
<bean id="cat" class="com.dam.pojo.Cat"></bean>
<bean id="dog" class="com.dam.pojo.Dog"></bean>
<bean id="people" class="com.dam.pojo.People">
<property name="name" value="小明"></property>
<property name="cat" ref="cat"></property>
<property name="dog" ref="dog"></property>
</bean>
cat 和 dog 对象会装配到 people 对象中
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
People people = context.getBean("people", People.class);
people.getCat().shout();
people.getDog().shout();
}
byName:会自动在容器上下文中查找和当前对象set方法后面的值对应的bean id的对象装配到当前对象中
弊端:bean标签里面的id只能取属性名Xx(setXx),其他的不能识别;也就是说一个实体类属性只能对应一个setXx方法,那么也就一个bean
<bean id="cat" class="com.dam.pojo.Cat"></bean>
<bean id="dog" class="com.dam.pojo.Dog"></bean>
<bean id="people" class="com.dam.pojo.People" autowire="byName">
<property name="name" value="小明"></property>
</bean>
People类中有setCat和setDog方法,会自动去找id为cat和dog的Bean
模拟id和属性值不匹配
通过byType可以注入成功
byType:会自动在容器上下文中查找,和自己对象属性类型相同的bean,需要保证属性类型全局唯一 。
弊端:通过实体类中的每个属性的类型来在容器中进行上下文的查找;也就是说一个属性类型只能对应一个bean(id可省略,因为根据属性的类型进行查找,不是根据id)
Cat和Dog的id省略也可以注入成功
<bean class="com.dam.pojo.Cat"></bean>
<bean class="com.dam.pojo.Dog"></bean>
<bean id="people" class="com.dam.pojo.People" autowire="byType">
<property name="name" value="小明"></property>
</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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--开启注解支持-->
<context:annotation-config/>
</beans>
在spring4之后,想要使用注解形式,必须得要引入aop的包,详情请看下一节
开启之后,可以直接通过注解来注入cat和dog
如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候、我们可以
使用@Qualifier(value=“xxx”)去配置@Autowired的使用,指定一个唯一的bean对象注入!
Resource注解:也可以实现装配,但是性能会差一点
【@Autowired和@Resource区别】
【注意】
当属性上面加了注解之后,不要再给实体类的属性添加set方法
当字段标记了这个注解,表明该字段可以为null
在Spring4之后,使用注解开发都要导入aop的jar包
使用注解需要导入约束,开启注解支持
<?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
https://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.dam.pojo"/>
<!--开启注解支持!-->
<context:annotation-config/>
</beans>
实体类中使用@Component之后,不需要在xml中写bean了
//@Component组件
//等价于<bean id="user" class="com.zhang.pojo.User"></bean>
@Component
public class User {
public String name="小明";
}
@Value注解用于给实体类的属性赋值,放在属性上或者属性对应的setXx方法上都可以
// @Value("小明")
public String name;
@Value("小明")
public void setName(String name) {
this.name = name;
}
@Value() 相当于
@Component注解有几个衍生注解,我们在web开发中,会按照mvc架构进行分层
这四个注解的功能都是一样的,都代表将某个类注册到Spring中,来装配bean
要让注解生效,需要扫描com.dam下所有的包
<!--指定要扫描的包,这个包下的注解就会生效-->
<context:component-scan base-package="com.dam"/>
@Scope注解:用来设置实体类的单例模式、原型模式等
@Component
@Scope("singleton")
public class User {
public String name;
@Value("小明")
public void setName(String name) {
this.name = name;
}
}
现在可以不使用Spring配置文件(applicationContext.xml),而是全部交给java来进行配置。
JavaConfig是Spring的一个子项目,在Spring4之后,它成为了一个核心功能
@Component
public class User {
@Value("阿花")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
@Configuration,类中出现这个表示该类是一个配置类,就相当于beans.xml配置文件
@Configuration
//扫描同一包下的bean
@ComponentScan("com.zhang.pojo")
public class DamConfig {
//相当于给Spring注册一个User的bean对象
//方法名(getUser):代表bean标签的id属性
//方法返回类型(User):代表bean的class属性
//方法返回值(new User()):要注入到bean的对象
@Bean
public User getUser(){
return new User();
}
}
public class MyTest {
@Test
public void test1(){
// 如果完全使用java来配置Spring,
// 那么需要AnnotationConfigApplicationContext来获取Spring容器,
// 通过加载配置类对象的方式
ApplicationContext context = new AnnotationConfigApplicationContext(DamConfig.class);
// “getUser”是java配置类里面对应bean的方法名称
User user = context.getBean("getUser",User.class);
System.out.println(user.getName());
}
}
@Configuration可以做xml做的所有事情
【设计模式——学习笔记】23种设计模式——代理模式Proxy(原理讲解+应用场景介绍+案例介绍+Java代码实现)_Hello Dam的博客-CSDN博客
优点:
缺点:
【租房接口】
public interface Rent {
public void rent();
}
【房东】
//房东
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东要出租房子");
}
}
【中介】
//中介
public class Proxy implements Rent{
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
//看房子
public void seeHouse(){
System.out.println("中介带领看房子");
}
//租赁合同
public void hetong(){
System.out.println("签合同");
}
//中介费
public void fare(){
System.out.println("收中介费");
}
@Override
public void rent() {
// 中介除了帮房东租房之外,还可以做一些其他事情
seeHouse();
host.rent();
hetong();
fare();
}
}
【客户】
public class Client {
public static void main(String[] args) {
//房东出租房子
Host host = new Host();
//代理帮中介出租,但 代理角色会有 附属操作
Proxy proxy = new Proxy(host);
//找中介租房
proxy.rent();
}
}
【接口】
public interface UserService {
public void add();
public void del();
public void update();
public void select();
}
【接口实现类】
//真实对象
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("增加对象");
}
@Override
public void del() {
System.out.println("删除对象");
}
@Override
public void update() {
System.out.println("更改对象");
}
@Override
public void select() {
System.out.println("查询对象");
}
}
【代理角色】
在继承UserService的基础上(不改变原有代码的基础上,增加了附加操作【log日志功能】)
public class UserServiceProxy implements UserService{
UserService userService;
public void setUserService(UserServiceImpl userService){
this.userService = userService;
}
@Override
public void add() {
log("add");
System.out.println("add方法");
}
@Override
public void del() {
log("del");
System.out.println("del方法");
}
@Override
public void update() {
log("update");
System.out.println("update方法");
}
@Override
public void select() {
log("select");
System.out.println("select方法");
}
public void log(String msg){
System.out.println("使用了"+msg+"方法");
}
}
【客户端访问】
public class Client {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(userService);
proxy.add();
}
}
两个重要的类
【接口】
public interface Rent {
public void rent();
}
【真实角色】
//房东
public class Host implements Rent {
@Override
public void rent() {
System.out.println("房东要出租房子");
}
}
【动态代理类】
//用这个类自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Rent rent;
public void setRent(Rent rent){
this.rent = rent;
}
//生成得到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(),this);
}
// 处理代理实例,返回结果
// 调用被代理对象的方法的时候,会默认执行这个方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的本质,就是使用反射机制实现
seeHouse();
Object result = method.invoke(rent,args);
return result;
}
public void seeHouse(){
System.out.println("中介带看房子");
}
}
【客户端】
public static void main(String[] args) {
//真实角色
Host host = new Host();
//代理角色
ProxyInvocationHandler handler = new ProxyInvocationHandler();
//通过调用程序处理需要调用的接口对象
handler.setRent(host);
//proxy就是动态生成,我们并没有写它
Rent proxy = (Rent) handler.getProxy();
proxy.rent();
}
//用这个类自动生成代理类
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);
}
//处理代理实例,返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//打印方法名称
log(method.getName());
//动态代理的本质,就是使用反射机制实现
Object result = method.invoke(target,args);
return result;
}
public void log(String msg){
System.out.println("执行了"+msg+"方法");
}
}
【客户端调用】
public static void main(String[] args) {
//真实角色
UserServiceImpl userService = new UserServiceImpl();
//代理角色,不存在
ProxyInvocationHandler phandler = new ProxyInvocationHandler();
//设置要代理的对象
phandler.setTarget(userService);
//动态生成代理类
UserService proxy = (UserService) phandler.getProxy();
proxy.add();
}
AOP的底层就是通过代理模式完成
AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
提供声明式事务;允许用户自定义切面
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:就是SpringAOP在不改变原来代码的情况下,去增加新的业务功能。
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.19</version>
<scope>runtime</scope>
</dependency>
【接口(UserService)】
public interface UserService {
public void add();
public void del();
public void update();
public void query();
}
【实现类(UserServiceImpl)】
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("add了一个方法");
}
@Override
public void del() {
System.out.println("del了一个方法");
}
@Override
public void update() {
System.out.println("update了一个方法");
}
@Override
public void query() {
System.out.println("query了一个方法");
}
}
【方法执行前日志】
public class Log implements MethodBeforeAdvice {
//method 要执行的目标对象的方法
//objects 参数
// target 目标对象
@Override
public void before(Method method, Object[] rags, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
}
}
【方法执行后日志】
public class AfterLog implements AfterReturningAdvice {
//returnValue 返回值
//method 要执行的目标对象的方法
//objects 参数
// target 目标对象
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了"+method.getName()+"方法被执行,返回结果为"+returnValue);
}
}
<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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--指定要扫描的包,这个包下的注解就会生效-->
<context:component-scan base-package="com.zhang"/>
<!--开启注解支持!-->
<context:annotation-config/>
<!-- 注册 bean -->
<bean id="userService" class="com.zhang.service.UserServiceImpl"/>
<bean id="log" class="com.zhang.log.Log"/>
<bean id="afterLog" class="com.zhang.log.AfterLog"/>
<!-- 配置AOP :需要导入AOP约束 -->
<aop:config>
<!-- 切入点 expression表达式 execution(要执行的位置 修饰词 返回值 类名 方法名 参数) -->
<aop:pointcut id="pointcut" expression="execution(* com.zhang.service.UserServiceImpl.*(..))"/>
<!-- 执行环绕增加日志方法 -->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"></aop:advisor>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"></aop:advisor>
</aop:config>
</beans>
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//!!!!!!!!!!!!!!!动态代理的是接口:!!!!!!!!!!!!!!!!!!!!
//所以应该是UserService,而不是UserServiceImpl
UserService userService = context.getBean("userService", UserService.class);
userService.query();
}
【DiyPointCut类】
public void before(){
System.out.println("方法执行前");
}
public void after(){
System.out.println("方法执行后");
}
【applicationContext.xml】
<!-- 方式二:自定义来实现AOP【主要是切面定义】 -->
<bean id="diy" class="com.zhang.diy.DiyPointCut">
</bean>
<aop:config>
<!-- 切面 ref 引用的类 -->
<aop:aspect ref="diy">
<!-- 切入点 -->
<aop:pointcut id="point" expression="execution(* com.zhang.service.UserServiceImpl.*(..))"/>
<!-- 通知 -->
<aop:before method="before" pointcut-ref="point"></aop:before>
<aop:after method="after" pointcut-ref="point"></aop:after>
</aop:aspect>
</aop:config>
【测试】
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//!!!!!!!!!!!!!!!动态代理的是接口:!!!!!!!!!!!!!!!!!!!!
//所以应该是UserService,而不是UserServiceImpl
UserService userService = context.getBean("userService", UserService.class);
userService.query();
}
<!-- https://mvnrepository.com/artifact/aspectj/aspectjrt -->
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.5.4</version>
</dependency>
【AnnotationPointCut】
导包别导错了
@Aspect 标注这个类是一个切面
@Aspect
public class AnnotationPointCut {
@Before("execution(* com.zhang.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("======方法执行前=====");
}
@After("execution(* com.zhang.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("======方法执行后=====");
}
//在环绕增强中,可以给定一个参数,代表我们要获取处理切入的点
@Around("execution(* com.zhang.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
// 获得签名
// Signature signature = jp.getSignature();
// System.out.println("signature::::"+signature);
// System.out.println(jp.proceed());
//执行方法
Object proceed = jp.proceed(); //执行方法
System.out.println("环绕后");
}
}
【applicationContext.xml】
<!-- 方式三:使用注解来实现 -->
<bean id="annotationPointCut" class="com.zhang.diy.AnnotationPointCut"></bean>
<!-- 开启注解支持 默认JDK(proxy-target-class="false") cglib(proxy-target-class="true") -->
<aop:aspectj-autoproxy proxy-target-class="true" />
【执行结果】
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.dam</groupId>
<artifactId>spring-study</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- Spring相关-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.6</version>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
<!-- AOP -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<!-- Spring整合Mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
</dependencies>
<build>
<!--静态资源过滤-->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<!--不写下面的配置,无法获取到mybatis-config.xml-->
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
</project>
MyBatis-Spring会帮助你将MyBatis代码无缝地整合到Spring中。它将允许MyBatis参与到Spring的事务管理之中,创建映射器mapper和Sql Session并注入到bean中,以及将Mybatis的异常转换为Spring的DataAccessException。最终,可以做到应用代码不依赖于MyBatis、Spring或MyBatis-Spring。
Mybatis的事务管理很笨重,可以用Spring的AOP,使用MyBatis-Spring需要版本匹配
这个配置文件并不需要是一个完整的 MyBatis 配置。
<?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>
<!--取别名-->
<typeAliases>
<package name="org.dam.pojo"/>
</typeAliases>
</configuration>
最好是让spring-dao.xml
专门处理数据库,其他类的注入放在applicationContext.xml
或bean.xml
中,如下图的方式
要使用 Spring 整合 MyBatis,需要在 Spring 应用上下文中定义至少两样东西
在 Mybatis-Spring 中,可使用SqlSessionFactoryBean来创建 SqlSessionFactory。要配置这个工厂 bean,只需要把下面代码放在 Spring 的 XML 配置文件中:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
public class UserDaoImpl implements UserDao {
private SqlSession sqlSession;
public void setSqlSession(SqlSession sqlSession) {
this.sqlSession = sqlSession;
}
public User getUser(String userId) {
return sqlSession.getMapper...;
}
}
<bean id="userDao" class="org.mybatis.spring.sample.dao.UserDaoImpl">
<property name="sqlSession" ref="sqlSession" />
</bean>
最终的完整Spring-dao.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的-->
<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-study?useSSL=true&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="12345678"/>
</bean>
<!--配置SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--关联Mybatis配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--指定mapper的xml文件所在地址-->
<property name="mapperLocations" value="classpath:org/dam/dao/xml/*.xml"/>
</bean>
<!--注册sqlSessionTemplate , 关联sqlSessionFactory-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--利用构造器注入-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<bean id="userMapper" class="org.dam.dao.impl.UserMapperImpl">
<!--将sqlSession注入到UserMapper实现类中,供其使用-->
<property name="sqlSession" ref="sqlSession"/>
</bean>
</beans>
package org.dam.pojo;
import lombok.ToString;
/**
* @Author dam
* @create 2024/1/5 19:54
*/
@ToString
public class User {
private String name;
public User() {
System.out.println("执行 User 构造方法");
}
public User(String name) {
System.out.println("执行 User 有参构造方法");
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("name="+ name );
}
}
package org.dam.dao;
import org.dam.pojo.User;
import java.util.List;
/**
* @Author dam
* @create 2024/1/6 10:54
*/
public interface UserMapper {
public List<User> selectUser();
}
<?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="org.dam.dao.UserMapper">
<select id="selectUser" resultType="User">
select * from user
</select>
</mapper>
package org.dam.dao.impl;
import org.dam.dao.UserMapper;
import org.dam.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import java.util.List;
/**
* @Author dam
* @create 2024/1/6 11:03
*/
public class UserMapperImpl implements UserMapper {
//sqlSession不用我们自己创建了,Spring来管理
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public List<User> selectUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.selectUser();
}
}
@Test
public void testSpringMybatis1(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
UserMapper mapper = (UserMapper) context.getBean("userMapper");
List<User> user = mapper.selectUser();
System.out.println(user);
}
运行
mybatis-spring1.2.3版以上的才可以使用这种方式:
dao继承Support类 ,直接利用 getSqlSession() 获得 ,然后直接注入SqlSessionFactory。比起方式1 , 不需要管理SqlSessionTemplate ,而且对事务的支持更加友好
不需要再注册sqlSessionTemplate
<?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的-->
<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-study?useSSL=true&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="12345678"/>
</bean>
<!--配置SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--关联Mybatis配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--指定mapper的xml文件所在地址-->
<property name="mapperLocations" value="classpath:org/dam/dao/xml/*.xml"/>
</bean>
<bean id="userMapper2" class="org.dam.dao.impl.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
</beans>
直接继承SqlSessionDaoSupport
,然后调用其getSqlSession()
方法就可以使用sqlSession
package org.dam.dao.impl;
import org.dam.dao.UserMapper;
import org.dam.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import java.util.List;
/**
* @Author dam
* @create 2024/1/6 11:03
*/
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
@Override
public List<User> selectUser() {
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
return mapper.selectUser();
}
}
@Test
public void testSpringMybatis2(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
UserMapper mapper = (UserMapper) context.getBean("userMapper2");
List<User> user = mapper.selectUser();
System.out.println(user);
}
事务就是把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用。
Spring在不同的事务管理API之上定义了一个抽象层,使得开发人员不必了解底层的事务管理API就可以使用Spring的事务管理机制。Spring支持编程式事务管理和声明式的事务管理。
session.commit();
使用Spring管理事务,注意头文件的约束导入 : tx
xmlns:tx=“http://www.springframework.org/schema/tx”
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd"
因为还要使用aop来织入事务,还需要导入aop相关约束
xmlns:aop=“http://www.springframework.org/schema/aop”
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
最终的spring-dao.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:tx="http://www.springframework.org/schema/tx"
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
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd"
>
<!--配置数据源:数据源有非常多,可以使用第三方的,也可使使用Spring的-->
<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-study?useSSL=true&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="12345678"/>
</bean>
<!--配置SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--关联Mybatis配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--指定mapper的xml文件所在地址-->
<property name="mapperLocations" value="classpath:org/dam/dao/xml/*.xml"/>
</bean>
<bean id="userMapper2" class="org.dam.dao.impl.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事务通知-->
<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="search*" propagation="REQUIRED"/>
<tx:method name="get" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--配置aop织入事务 org.dam.dao包下所有类的所有方法中。
这样,在调用这些方法时,会自动实现事务的管理,确保数据的一致性和完整性。-->
<aop:config>
<!--切入点是 org.dam.dao包下的所有类的所有方法-->
<aop:pointcut id="txPointcut" expression="execution(* org.dam.dao.*.*(..))"/>
<!--定义一个advisor(通知器),将事务通知(txAdvice)织入到切入点-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
</beans>
事务管理器的Bean
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
配置好事务管理器后我们需要去配置事务的通知
<!--配置事务通知-->
<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="search*" propagation="REQUIRED"/>
<tx:method name="get" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
spring事务传播特性:
事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。spring支持7种事务传播行为:
propagation_requierd
:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。propagation_supports
:支持当前事务,如果没有当前事务,就以非事务方法执行。propagation_mandatory
:使用当前事务,如果没有当前事务,就抛出异常。propagation_required_new
:新建事务,如果当前存在事务,把当前事务挂起。propagation_not_supported
:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。propagation_never
:以非事务方式执行操作,如果当前事务存在则抛出异常。propagation_nested
:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。
假设 ServiceX#methodX() 都工作在事务环境下(即都被 Spring 事务增强了),假设程序中存在如下的调用链:Service1#method1()->Service2#method2()->Service3#method3(),那么这 3 个服务类的 3 个方法通过 Spring 的事务传播机制都工作在同一个事务中。几个方法存在调用,会被放在一组事务当中!
导入aop的头文件!
<!--配置aop织入事务 org.dam.dao包下所有类的所有方法中。
这样,在调用这些方法时,会自动实现事务的管理,确保数据的一致性和完整性。-->
<aop:config>
<!--切入点是 org.dam.dao包下的所有类的所有方法-->
<aop:pointcut id="txPointcut" expression="execution(* org.dam.dao.*.*(..))"/>
<!--定义一个advisor(通知器),将事务通知(txAdvice)织入到切入点-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
package org.dam.pojo;
import lombok.AllArgsConstructor;
import lombok.ToString;
/**
* @Author dam
* @create 2024/1/5 19:54
*/
@ToString
@AllArgsConstructor
public class User {
private int id;
private String name;
private String pwd;
public User() {
System.out.println("执行 User 构造方法");
}
public User(String name) {
System.out.println("执行 User 有参构造方法");
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("name="+ name );
}
}
package org.dam.dao;
import org.dam.pojo.User;
import java.util.List;
/**
* @Author dam
* @create 2024/1/6 10:54
*/
public interface UserMapper {
public List<User> selectUser();
//添加一个用户
int addUser(User user);
//根据id删除用户
int deleteUser(int id);
void testTransaction();
}
这里故意将删除语句delete
错误写成deletes
,这样就可以模拟删除动作的错误
<?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="org.dam.dao.UserMapper">
<select id="selectUser" resultType="User">
select * from user
</select>
<insert id="addUser" parameterType="user">
insert into user (id,name,pwd) values (#{id},#{name},#{pwd})
</insert>
<delete id="deleteUser" parameterType="int">
deletes from user where id = #{id}
</delete>
</mapper>
textTransaction
方法同时调用了
package org.dam.dao.impl;
import org.dam.dao.UserMapper;
import org.dam.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import java.util.List;
/**
* @Author dam
* @create 2024/1/6 11:03
*/
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
@Override
public List<User> selectUser() {
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
return mapper.selectUser();
}
//新增
@Override
public int addUser(User user) {
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
return mapper.addUser(user);
}
//删除
@Override
public int deleteUser(int id) {
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
return mapper.deleteUser(id);
}
/**
* 事务测试
*/
@Override
public void testTransaction(){
User user = new User(10,"dam","123456");
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
mapper.addUser(user);
mapper.deleteUser(4);
}
}
/**
* 事务测试
*/
@Test
public void testSpringMybatis2Transaction(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
UserMapper mapper = (UserMapper) context.getBean("userMapper2");
mapper.textTransaction();
}
执行之后,sql报错了
数据表中也没有增加相应的数据,说明事务管理成功
为什么需要配置事务?
本文章为本人学习 遇见狂神说 的学习笔记,文章中大部分内容来源于狂神的视频【狂神说Java】Spring5最新完整教程IDEA版通俗易懂,也有部分内容来自于自己的思考,发布文章是想帮助其他学习的人更方便地整理自己的笔记或者直接通过文章学习相关知识,如有侵权请联系删除,最后对狂神的优质课程表示感谢。