1、Spring
1.1、简介
-
Spring:春天---------->给软件行业带来了春天!
-
2002年,首次推出了Spring框架的雏形:interface21框架
-
Spring框架即以interface21框架为基础经过重新设计,并不断丰富其内涵,2004年3月24日发布了1.0正式版
-
Rod Johnson,Spring Framework创始人,悉尼大学的博士,然而他的专业不是计算机,而是音乐学
-
Spring的理念:使现有的技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架
-
过时的框架SHH: Struct2 + Spring + hibernate
-
流行框架SSM: SpringMVC + Spring + Mybatis
jar包导入
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
1.2、优点
- Spring是一个开源的免费的框架(容器)
- Spring是一个轻量级的,非入侵式的框架
- 控制反转(IOC),面向切面编程(AOP)
- 支持事务的处理,对框架整合的支持
总结一句话:Spring就是一个轻量级的控制反转(IOC)和面向切面(AOP)的框架
1.3、组成
1.4、拓展
在Spring官网有这个介绍:现代化的java开发,说白了就是基于Spring开发
- Spring Boot
- 一个快速开发的脚手架
- 基于SpringBoot可以快速的开发单个微服务
- 约定大于配置!
- Spring Clound
- SpringClound是基于SpringBoot实现的
因为现在大多数公司都在使用SpringBoot进行快速开发,学习SpringBoot的前提,需要完全掌握Spring及SpringMVC,承上启下的作用
弊端:发展了太久之后,违背了原来的理念,配置十分繁琐,人称“配置地狱”!
2、IOC理论指导
前期我们的代码是这样写的
- UserDao接口
- UserDaoImpl实现类
- UserService接口
- UserServiceImpl实现类
主动权在程序员手上
在我们之前的业务中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求去修改源代码,如果程序代码量十分大,修改一次的成本代价十分昂贵!
我们使用一个Set接口实现(其实下面的代码发生了革命性的变化,认真思考下)
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
这样写有什么好处?
- 之前程序是主动创建对象!控制权在程序员手上
- 使用了set注入后,程序不再具有主动性,而是变成了被动的接受对象!
这种思想,从本质上解决了问题,我们程序员不再去管理对象的创建了,系统的耦合性大大降低了,可以更加专注的在业务的实现上,这是IOC的原型!!
IOC本质
控制反转IOC(Inversion of Control)是一种设计思想,DI(依赖注入)是实现IOC的一种方法,也有认为IOCDI只是IOC的另一种说法,没有IOC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓的控制反转就是:获得依赖对象的方式反转了。
采取XML方式配置Bean的时候,Bean的定义信息和实现是分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式在实现类中,从而达到了零配置的目的
控制反转是一种通过描述(XML或注解)并通过第三方生产或获取特定对象的方式,在Spring中实现控制反转的是IOC容器,其实现方法是依赖注入(Dependency Injection)。
3、HelloSpring
试着使用Spring写一个helloworld程序
- 编写实体类(重点set方法)
package com.ryan.pojo;
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 + '\'' +
'}';
}
}
- 编写配置文件(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">
<!--
以前创建对象的方式
Hello hello = new Hello()
使用spring
id==>变量名
class==>要创建对象的类
property(属性)
str==>属性名称
value==>设置值
-->
<bean id="hello" class="com.ryan.pojo.Hello">
<property name="str" value="Spring"/>
</bean>
</beans>
- 测试
import com.ryan.pojo.Hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
//1.实例化容器
//参数可以是多个
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//2.从容器中获取对象(bean),参数为对象的id,也就是通过id获取对象
Hello hello = (Hello) context.getBean("hello");
//3.调用方法
System.out.println(hello.getStr());//Spring
System.out.println(hello.toString());//Hello{str='Spring'}
//可以看到,我们在这里再也不需要new对象,全都交给ioc容器来创建管理了
//写完hello代码之后,我们再回到spring-01-ioc1的模块优化之前的代码试试
}
}
思考问题?
-
Hello对象是谁创建的?
hello对象是由Spring创建的
-
Hello对象的属性是怎么设置的
hello对象的属性是由Spring容器设置的
这个过程就叫控制反转:
控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象的是由Spring来创建的。
反转:程序本身不创建对象,而变成被动的接收对象。
依赖注入:就是利用set方法来进行注入的。
IOC是一种变成思想,由主动的变成编程被动的接收
可以通过new ClassPathXmlApplicationContext去浏览一下底层原码。
Ok,到了现在,我们彻底不用在程序中去改动了,要实现不同的操作,只需要在xml配置文件中进行修改
所谓的IOC,一句话搞定:对象由Spring来创建,管理,装配!
4、IOC创建对象的方式
-
默认使用无参构造创建,如果在实体类中没有无参构造方法了,则会报错
No default constructor found; nested exception is java.lang.NoSuchMethodException: com.ryan.pojo.User.()
-
如果不想使用无参构造方法,也可以使用有参构造方法
-
方式一:下标
<bean id="user" class="com.ryan.pojo.User"> <constructor-arg index="0" value="kobe"/> </bean>
-
方式二:数据类型(不建议使用)
<bean id="user" class="com.ryan.pojo.User"> <constructor-arg type="java.lang.String" value="bryant"/> </bean>v
-
方式三:属性名称(一般都是用这个)
<bean id="user" class="com.ryan.pojo.User"> <constructor-arg name="name" value="甘志鹏"/> </bean>
总结:在配置文件加载的时候,容器中管理的对象就已经初始化了
-
5、Spring配置
5.1、alias:取别名
<!--取别名,然后也可以通过别名来获取对象,一次只能起一个别名这个功能基本上没什么用-->
<alias name="user" alias="user2"/>
5.2、bean
<!--
2、bean
id:相当于对象名称
class:对象相对应的数据类型
name:别名,可取多个别名,可用空格或者,或者;分割
-->
<bean id="user" class="com.ryan.pojo.User" name="user3,user4;user5 user6">
<constructor-arg name="name" value="甘志鹏"/>
</bean>
5.3、import
这个import,一般用于团队开发使用,他可以将多个配置文件,导入合并为一个
假设,现在项目中有多个人开发,这三个人负责不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的beans.xml合并为一个总的
<!--3、import,导入其他配置文件(用于团队协作开发)-->
<import resource="beans.xml"/>
<import resource="beans1.xml"/>
6、依赖注入
6.1、构造器注入
前面已经说过了
6.2、Set方式注入(重点)
- 依赖注入:Set注入
- 依赖:bean对象的创建依赖于容器
- 注入:bean对象中的所有属性,由容器来注入
环境搭建:
-
复杂类型
package com.ryan.pojo; public class Address { private String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
-
真实测试对象
public class Student { private String name; private Address address; private String[] books; private List<String> hobbies; private Map<String, String> card; private Set<String> games; private String girlfriend; private Properties info; }
-
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.ryan.pojo.Address"> <property name="address" value="广州"/> </bean> <bean id="student" class="com.ryan.pojo.Student"> <!--普通注入,直接使用value赋值就行--> <property name="name" value="甘志鹏"/> <!--引用注入--> <property name="address" ref="address"/> <!--数组注入--> <property name="books"> <array> <value>java编程思想</value> <value>java从入门到放弃</value> <value>狂神说</value> </array> </property> <!--list注入--> <property name="hobbies"> <list> <value>唱</value> <value>跳</value> <value>rap</value> <value>篮球</value> </list> </property> <!--map注入--> <property name="card"> <map> <entry key="学生卡" value="120202"/> <entry key="银行卡" value="8923823979382390"/> </map> </property> <!--set注入--> <property name="games"> <set> <value>LOL</value> <value>王者荣耀</value> <value>吃鸡</value> </set> </property> <!--null注入--> <property name="girlfriend"> <null/> </property> <!--properties注入--> <property name="info"> <props> <prop key="性别">男</prop> <prop key="班级">14旅2</prop> </props> </property> </bean> </beans>
-
测试类
import com.ryan.pojo.Student; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); Student student = (Student) context.getBean("student"); System.out.println(student.toString()); /* Student{name='甘志鹏', address=Address{address='广州'}, books=[java编程思想, java从入门到放弃, 狂神说], hobbies=[唱, 跳, rap, 篮球], card={学生卡=120202, 银行卡=8923823979382390}, games=[LOL, 王者荣耀, 吃鸡], girlfriend='null', info={班级=14旅2, 性别=男} } */ } }
用的比较多的就是普通注入和引用注入,但是其他的也要有所掌握
6.3、拓展方式注入
通过官方文档可以看到,我们还可以使用p命名空间和c命名空间注入
使用:
<?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命名空间-->
<bean id="user" class="com.ryan.pojo.User" p:name="甘志鹏" p:age="18"/>
<!--c命名空间-->
<bean id="user2" class="com.ryan.pojo.User" c:name="ryan" c:age="20"/>
</beans>
测试:
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
//小技巧,getBean方法可以传第二个参数,指定返回值类型,这样就不需要每次都去强转了
User user = context.getBean("user", User.class);
System.out.println(user.toString());//User{name='甘志鹏', age=18}
User user2 = context.getBean("user2", User.class);
System.out.println(user2.toString());//User{name='ryan', age=20}
}
注意:p命名空间和c命名空间可以直接使用,需要导入约束
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
6.4、beans的作用域
-
singleton单例模式(Spring默认机制):每次从容器get的都是同一个对象
<bean id="user2" class="com.ryan.pojo.User" c:name="ryan" c:age="20" scope="singleton"/>
-
prototype原型模式:每次从容器中get的时候,都会产生一个新的对象
<bean id="user2" class="com.ryan.pojo.User" c:name="ryan" c:age="20" scope="prototype"/>
-
其余的request、session、application这些都只能在web开发中使用到,目前就先不讲了
7、beans的自动装配
实体类
public class Person {
private String name;
private Dog dog;
private Cat cat;
}
public class Cat {
public void shout(){
System.out.println("miao~");
}
}
public class Dog {
public void shout(){
System.out.println("wang~");
}
}
自动装配前写代码
容器
<?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">
<!--先把所有实体类都放到容器中-->
<bean id="dog" class="com.ryan.pojo.Dog"/>
<bean id="cat" class="com.ryan.pojo.Cat"/>
<bean id="person" class="com.ryan.pojo.Person">
<property name="name" value="甘志鹏"/>
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
</bean>
</beans>
自动装配后的代码
-
byName自动装配
<!--先把所有实体类都放到容器中--> <bean id="dog" class="com.ryan.pojo.Dog"/> <bean id="cat" class="com.ryan.pojo.Cat"/> <!--看下面的代码,其实有些代码是重复的,因此spring还提供了自动装配功能 1.byName自动装配:会自动在容器上下文中查找,和自己对象set方法后面的值对应的bean 2.byType自动装配:会自动再容器上下文中查找,和自己对象属性类型相同的bean --> <bean id="person" class="com.ryan.pojo.Person" autowire="byName"> <property name="name" value="甘志鹏"/> </bean>
-
byType自动装配
<!--先把所有实体类都放到容器中--> <bean id="dog" class="com.ryan.pojo.Dog"/> <bean id="cat" class="com.ryan.pojo.Cat"/> <!--看下面的代码,其实有些代码是重复的,因此spring还提供了自动装配功能 1.byName自动装配:会自动在容器上下文中查找,和自己对象set方法后面的值对应的bean 2.byType自动装配:会自动再容器上下文中查找,和自己对象属性类型相同的bean --> <bean id="person" class="com.ryan.pojo.Person" autowire="byType"> <property name="name" value="甘志鹏"/> </bean>
测试:
import com.ryan.pojo.Person;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Person person = context.getBean("person", Person.class);
person.getCat().shout();//miao~
person.getDog().shout();//wang~
}
}
最终都是可以运行并且得出相同的结果的
注意点:
- byName的时候,需要保证所有的bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致
- byType的时候,需要保证所有的bean的class唯一,并且这个bean需要和注入的属性的类型一致
8、注解自动装配
除了上面两种byType和byName自动装配方式,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" 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/> <!--将实体类都放到容器中--> <!-- 这里做个测试,探索@Autowired,是先通过什么方式去自动装配的 1.将id名字改为和实体类属性不同的名字,结果还是可以运行 2.增加两个相同类型的bean(dog1和dog2,cat1和cat2),结果报错 3.分别将两个id名改回和属性名一样的,结果又可以运行了 说明@Autowired是优先通过类型(byType)来自动装配的,如果有相同类型的,再通过id名称来自动装配 那么问题来了,上面的第二个测试的问题,有办法解决吗? 答案是可以的,这时候可以配合使用注解@Qualifier("id名称")即可 --> <bean id="dog1" class="com.ryan.pojo.Dog"/> <bean id="dog2" class="com.ryan.pojo.Dog"/> <bean id="cat1" class="com.ryan.pojo.Cat"/> <bean id="cat2" class="com.ryan.pojo.Cat"/> <bean id="person" class="com.ryan.pojo.Person"/> </beans>
-
在相应的实体类中添加注解
package com.ryan.pojo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; public class Person { private String name; @Qualifier("dog1") @Autowired private Dog dog; @Qualifier("cat1") @Autowired private Cat cat; public String getName() { return name; } public void setName(String name) { this.name = name; } public Dog getDog() { return dog; } public void setDog(Dog dog) { this.dog = dog; } public Cat getCat() { return cat; } public void setCat(Cat cat) { this.cat = cat; } }
-
测试:略
小结:
-
如果使用注解自动装配的话,注意需要修改约束,还要开启支持注解自动装配
<?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>
-
@Autowired:自动装配先通过类型,然后通过名字,如果还是不行的话则再添加注解@Qualifier(“id名称”)
除了@Autowired和@Qualifier,还有个注解也可以了解一下,叫**@Resource**,相当于@Autowired和@Qualifier的组合体,但是在jdk11中移除了这个注解,正常是不能使用的,如果想要使用,可以另外导入依赖jar包
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
使用
public class Person {
private String name;
@Resource(name = "dog1")
private Dog dog;
@Resource(name = "cat1")
private Cat cat;
public String getName() {
return name;
}
}
补充:还有个注解可以了解一下:
@Nullable 字段标注了这个注解,说明这个字段可以为null
@Resource和@Autowired的区别:
- 都是用来自动装配的,都可以放在属性字段上
- @Autowired默认通过Type的方式实现,而且必须要求这个对象存在!(常用)
- @Resource默认通过byNam的方式实现,如果找不名字,则通过byType实现,如果两个都找不到的情况下,就报错(常用)
- 执行顺序不同:@Autowired默认通过byType方式实现,@Resource默认通过byName方式实现
9、使用注解开发
环境搭建
-
导入aop的jar包
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Us2ALzv8-1589763982252)(C:\Users\86131\AppData\Roaming\Typora\typora-user-images\image-20200516211301286.png)]
-
导入约束和开启 注解支持(扫描或者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 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>
使用步骤:
-
编写实体类
-
编写bean
-
在实体类中使用注解
package com.ryan.pojo; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; //在实体类上面使用@Component注解,相当于beans的<bean id="user" class="com.ryan.pojo.User" /> //也叫组件 @Component public class User { @Value("甘志鹏") private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
在其他层对应的组件,虽然注解名字不一样,但是功能却是完全一样的,都是将相应的实体类注入到容器中
- dao层:【@Repository】
- service层:【@Service】
- controller层:【@Controller】
-
属性的注入(可以写在属性上,也可以写在set方法上)
@Value("甘志鹏") private String name;
-
测试
-
小结
xml与注解:
- xml更加万能,适用于任何场合!维护简单方便
- 注解不是自己的类使用不了,维护相对复杂
xml与注解的最佳时间:
- xml用来管理bean
- 注解只负责完成属性的注入
- 我们在使用的过程中,只需要注意一个问题,必须要让注解生效,就是开启注解的支持
10、使用java的方式注解Spring
我们现在要完全不使用Spring的xml配置了,全权交给java来做!
JavaConfig是Spring的一个子项目,在Spring4之后,他称为了一个核心功能!
步骤:
-
创建一个实体类
package com.ryan.pojo; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; //这里这个注解的意思,就是说有这个类被Spring接管了,注入到了容器中 @Component public class User { @Value("Ryan") private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "name='" + name + '\'' + '}'; } }
-
创建一个配置类
package com.ryan.config; import com.ryan.pojo.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Component; //这个会被Spring容器托管,注册到容器中,因为他本来就是一个@Component //@Configuration代表这是一个配置类,就和我们之前看的beans.xml一样 @Configuration //在这里还可以添加一个扫描器,这样其他有@Component注解的实体类也会自动添加到容器中被托管(当然这个看每个人的想法) @ComponentScan("com.ryan.pojo") public class AppConfig { //注入一个bean,就相当于我们之前写的bean标签 //这个方法的名字,就相当于bean标签的id属性 //这个方法的返回值,就相当于bean标签中的class属性 @Bean public User getUser(){ return new User();//就是返回要注入到bean的对象 } }
-
分别在配置类和实体类使用注解完成Spring功能
-
测试
import com.ryan.config.AppConfig; import com.ryan.pojo.User; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class MyTest { @Test public void test01(){ //由于这里我们完全由java来配置Spring,不再写beans.xml了,所以这里实例化的是注解容器,传的参数是配置类 ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); User user = context.getBean("getUser", User.class); System.out.println(user.getName());//Ryan } }
提醒:这种纯java的配置方式,在SpringBoot中随处可见!
11、代理模式
为什么要学习代理模式?因为这就是SpringAOP的底层!(SpringAOP和SpringMVC)
代理模式的分类:
-
静态代理
-
动态代理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fHTCfOcW-1589763982253)(C:\Users\86131\AppData\Roaming\Typora\typora-user-images\image-20200516233206026.png)]
11.1、静态代理
步骤:
-
抽象角色
package com.ryan.proxy1; public interface Rent { void rent(); }
-
真实角色
package com.ryan.proxy1; public class Host implements Rent{ @Override public void rent() { System.out.println("房东要出租房子"); } }
-
代理角色
package com.ryan.proxy1; public class Proxy implements Rent { private Host host; public Proxy(Host host) { this.host = host; } @Override public void rent() { host.rent(); seeHouse(); sign(); money(); } public void seeHouse(){ System.out.println("带客户看房子"); } public void sign(){ System.out.println("跟客户签合同"); } public void money(){ System.out.println("收房租"); } }
-
客户端访问代理角色
package com.ryan.proxy1; public class Client { public static void main(String[] args) { //客户访问代理,租房子 Proxy proxy = new Proxy(new Host()); proxy.rent(); //分析:房东host只想出租房子,不想干其他活,所以找了中介代理,代理一般都要做一些附属操作 } }
静态代理模式的好处:
- 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
- 公共业务就交给代理角色,实现了业务的分工
- 公共业务发生扩展的时候,方便几种管理
缺点:
- 一个真实角色就会产生一个代理角色,代码会翻倍,导致开发效率变低
聊聊AOP,加深理解
11.2、动态代理
- 动态代理和静态代理的角色一样
- 动态代理的代理类是动态生成的,不是我们直接写死的
- 动态代理分为两大类:基于接口的动态代理和基于类的动态代理
- 基于接口—JDK动态代理(我们在这里使用)
- 基于类:cglib
- java字节码实现:javaasist
需要了解两个类:
- Proxy(代理):
- 提供了创建动态代理类和实例的静态方法,它也是有这些方法创建的所有动态代理类的超类
- InvocationHandler(调用处理程序):
- 每个代理实例都有一个关联的调用处理程序,当在代理实例上调用方法时,方法调用将被编码并分派到其他调用处理程序的invoke方法
使用步骤:
-
抽象角色
在动态代理类中使用万能的Object,客户端访问时再传实例就行
-
真实角色(被代理的对象)
在动态代理类中使用万能的Object,客户端访问时再传实例就行
-
动态代理类并实现InvocationHandler接口(关键)
package com.ryan.proxy4; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; //我们用这个类来自动生成代理类,我们不要写死代理 public class ProxyInvocationHandler implements InvocationHandler { //抽象角色(被代理的接口) private Object target; //真实角色实例 public void setTarget(Object target) { this.target = target; } //获取代理类方法 public Object getProxy(){ /* 参数分析: loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载 interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口, 如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了 h:一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上 */ return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(),this); } //处理代理实例,并返回结果 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { seeHouse(); //动态代理的本质,就是通过反射实现的 Object result = method.invoke(target, args); money(); return result; } public void seeHouse(){ System.out.println("看房子"); } public void money(){ System.out.println("收租"); } }
-
客户端通过动态代理类生成动态代理,然后实现其真实角色的功能及代理的附加功能
package com.ryan.proxy4; import com.ryan.proxy3.Host; import com.ryan.proxy3.Rent; public class Client { public static void main(String[] args) { //被代理的对象,比如我要租房,可以使用多态 Rent rent = new Host(); //代理对象,我们没有提供,但是我们可以通过动态代理类动态生成一个代理对象 //首先获取动态代理动态生成器 ProxyInvocationHandler pih = new ProxyInvocationHandler(); //宣称要代理谁,我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的 pih.setTarget(rent); //获取代理,并强转一下 Rent proxy = (Rent) pih.getProxy(); //然后租调用方法即可 proxy.rent();//房东需要出租房子 //此时如果客户想要其他操作,都可以在动态代理生成类中添加即可,比如租房之前,看房,之后收租 /* 看房子 房东需要出租房子 收租 */ //每个代理实例都有一个关联的调用处理程序,当在代理实例上调用方法时,方法调用将被编码并分派到其他调用处理程序的invoke方法 //也就是说如果有其他代理实例想要通过代理执行相关操作的话,也去关联一个调用处理程序即可 //比如说我再写一个调用处理程序,让其关联即可 } }
12、AOP
12.1、什么是AOP
AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,AOP是OOP的延续,是软件开发的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各个部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
12.2、AOP在Spring中的作用
提供声明式事务:允许用户自定义切面
- 横切关注点:跨越应用程序多个模块的方法或功能,即是,与我们的业务逻辑无关的,但是我们需要关注的部分,就是横切关注点,如日志,安全,缓存,事务等等…
- 切面(ASPECT):横切关注点被模块化的特殊对象,集,它是一个类
- 通知(Advice):切面必须要完成的工作,即,它是类中的一个方法
- 目标(Target):被通知对象
- 代理(Proxy):向目标对象应用通知之后创建的对象
- 切入点(PointCut):切面通知执行的“地点”的定义
- 连接点(JoinPoint):与切入点匹配的执行点
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice
12.3、使用Spring实现AOP的
方式一:使用Spring的API接口
-
环境搭建:
-
导入AOP的依赖jar包
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency>
-
在xml中导入约束
-
-
编写j接口和实体类
package com.ryan.service; public interface UserService { void add(); void delete(); void update(); void query(); }
package com.ryan.service; public class UserServiceImpl implements UserService{ @Override public void add() { System.out.println("增加用户"); } @Override public void delete() { System.out.println("删除用户"); } @Override public void update() { System.out.println("修改用户"); } @Override public void query() { System.out.println("查询用户"); } }
-
编写切面类(横切关注点被模块化的特殊对象),例如这里关注的是日志
package com.ryan.log; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; public class LogBefore implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println(target.getClass().getSimpleName() + "执行了"+ method.getName() + "方法"); } }
package com.ryan.log; import org.springframework.aop.AfterReturningAdvice; import java.lang.reflect.Method; public class LogAfter implements AfterReturningAdvice { @Override public void afterReturning(Object o, Method method, Object[] args, Object target) throws Throwable { System.out.println(method.getName() + "方法执行了,返回结果为:" + o); } }
-
Spring配置,这里使用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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--将所有的类注入到spring中--> <!--id建议写接口,class写具体实现类,也就是多态--> <bean id="userService" class="com.ryan.service.UserServiceImpl"/> <bean id="logAfter" class="com.ryan.log.LogAfter"/> <bean id="logBefore" class="com.ryan.log.LogBefore"/> <!--方式一:使用原生Spring API接口--> <!--配置aop,需要导入aop约束,输入<aop:config>后会自动导入--> <aop:config> <!--切入点:expression="execution(要执行的位置)"--> <aop:pointcut id="userServiceImpl" expression="execution(* com.ryan.service.UserServiceImpl.*(..))"/> <!--执行环绕增加--> <aop:advisor advice-ref="logBefore" pointcut-ref="userServiceImpl"/> <aop:advisor advice-ref="logAfter" pointcut-ref="userServiceImpl"/> </aop:config> </beans>
-
测试和结果
import com.ryan.service.UserService; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { @Test public void test01(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //注意,动态代理的是接口,而不是实现类 UserService userService = context.getBean("userService", UserService.class); userService.add(); } }
UserServiceImpl执行了add方法 增加用户 add方法执行了,返回结果为:null
方式二:自定义来实现AOP
与方式一的区别,方式以主要是SpringAPI接口实现,这里是自定义实现API,切面定义
-
编写实体类
还是用上面的userService
-
自定义一个切面和通知(需要关注的类和方法)
package com.ryan.diy; public class DiyPointcut { public void before(){ System.out.println("=======执行方法前======="); } public void after(){ System.out.println("========执行方法后========="); } }
-
编写Spring配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns: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"> <bean id="userService" class="com.ryan.service.UserServiceImpl"/> <bean id="diy" class="com.ryan.diy.DiyPointcut"/> <!--方式二:自定义的类--> <aop:config> <!--切面,并引入自定义的类--> <aop:aspect ref="diy"> <!--切入点--> <aop:pointcut id="point" expression="execution(* com.ryan.service.UserServiceImpl.*(..))"/> <!--通知--> <aop:before method="before" pointcut-ref="point"/> <aop:after method="after" pointcut-ref="point"/> </aop:aspect> </aop:config> </beans>
-
测试结果
=======执行方法前======= 增加用户 ========执行方法后=========
方式三:使用注解
说白了就是将aop的相关配置,在实体类中使用注解来实现,当然实体类注入Spring这个不变
注意:使用注解也需要开启注解支持,这个跟IOC那个不太一样,不要混淆了,详细可以看下面的代码
-
编写接口和实体类,还是用上面的例子
-
配置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 http://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.ryan.service.UserServiceImpl"/> <bean id="diy" class="com.ryan.diy.DiyPointcut"/> <!--方式三:使用注解,得开启注解支持,注意和IOC的开启不太一样,不要混淆了--> <aop:aspectj-autoproxy/> </beans>
-
自定一个实体类,也是用上面的diy,并加上AOP注解
package com.ryan.diy; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect//标注这个类是一个切面 public class DiyPointcut { @Before("execution(* com.ryan.service.UserServiceImpl.*(..))") public void before(){ System.out.println("=======执行方法前======="); } @After("execution(* com.ryan.service.UserServiceImpl.*(..))") public void after(){ System.out.println("========执行方法后========="); } }
-
测试结果:略
总体上是比较推荐方式二的,不过仁者见仁,智者见智
13、整合mybatis
13.1、回顾mybatis
使用步骤
-
导入jar包
-
junit
-
log4j
-
lombok
-
mybatis
-
mysql
-
spring相关的
-
aop织入
-
mybatis-spring(新的包)
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.4</version> </dependency>
注意点:版本要对应
-
```xml
<dependencies>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--spring-webmvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--aop织入-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
<!--spring-jdbc,spring操作数据库的-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--mybatis和spring整合-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.4</version>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--lombok-->
<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>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
</build>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!--修改Language level-->
<maven.compiler.source>11</maven.compiler.source>
<!--修改Java Compiler-->
<maven.compiler.target>11</maven.compiler.target>
</properties>
```
-
编写核心配置文件,记得注册mapper
<?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"> <!--mybatis的主配置文件--> <configuration> <properties resource="db.properties"/> <settings> <!--标准的日志工厂--> <!--<setting name="logImpl" value="STDOUT_LOGGING"/>--> <!--log4j日志工厂--> <setting name="logImpl" value="LOG4J"/> <!--是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn--> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <typeAliases> <package name="com.ryan.pojo" /> </typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <mappers> <mapper class="com.ryan.dao.UserMapper"/> </mappers> </configuration>
-
编写mybatis工具类
package com.ryan.utils; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; public class MybatisUtils { private static SqlSessionFactory sqlSessionFactory; static{ try { String resource = "mybatis-config.xml"; InputStream is = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); } catch (IOException e) { e.printStackTrace(); } } //获取sqlSession public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(); } }
-
编写实体类和mapper
package com.ryan.pojo; import lombok.Data; @Data public class User { private int id; private String username; private String gender; private String address; }
package com.ryan.dao; import com.ryan.pojo.User; import java.util.List; public interface UserMapper { 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="com.ryan.dao.UserMapper"> <select id="selectUser" resultType="user"> select * from mybatis.user </select> </mapper>
-
测试
import com.ryan.dao.UserMapper; import com.ryan.pojo.User; import com.ryan.utils.MybatisUtils; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import java.util.List; public class MyTest { @Test public void test01(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> users = mapper.selectUser(); for (User user : users) { System.out.println(user); } } }
13.2、Mybatis-Spring
什么是Mybatis-Spring?
- MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。
- 它将允许 MyBatis 参与到 Spring 的事务管理之中,创建映射器 mapper 和
SqlSession
并注入到 bean 中 - 将 Mybatis 的异常转换为 Spring 的
DataAccessException
- 最终,可以做到应用代码不依赖于 MyBatis,Spring 或 MyBatis-Spring。
使用:
-
导入jar包
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.4</version> </dependency>
Mybatis-Spring方式一
-
编写pojo
package com.ryan.pojo; import lombok.Data; @Data public class User { private int id; private String username; private String gender; private String address; }
-
编写接口和mapper.xml
package com.ryan.mapper; import com.ryan.pojo.User; import java.util.List; public interface UserMapper { 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="com.ryan.mapper.UserMapper"> <select id="selectUser" resultType="user"> select * from mybatis.user </select> </mapper>
-
编写配置文件
- dataSource
- sqlSessionFactory
- 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"> <!-- dataSource,使用spring提供的数据源替换原来mybatis的配置,这里使用的是spring提供的jdbc 这里就相当于mybatis-config.xml中的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/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="1227"/> </bean> <!-- sqlSessionFactory 在 MyBatis-Spring 中,可使用 SqlSessionFactoryBean来创建 SqlSessionFactory 注意:SqlSessionFactory 需要一个 DataSource(数据源)。 这可以是任意的 DataSource,只需要和配置其它 Spring 数据库连接一样配置它就可以了。也就是上面的dataSource 除了配置dataSource,其实还可以配置其他核心配置文件里面的东西,比如关联核心配置文件,mapper等,这样核心配置文件中的东西更加精简了 当然这注册mapper既可以在核心配置文件中配置也可以在此配置,看个人偏好,比较推荐在这里配置,核心配置文件只剩下别名和一些设置就行一般 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <!--关联核心配置文件--> <property name="configLocation" value="classpath:mybatis-config.xml"/> <!--注册mapper--> <property name="mapperLocations" value="classpath:com/ryan/mapper/*.xml"/> </bean> <!-- SqlSessionTemplate :是 MyBatis-Spring 的核心。 作为 SqlSession 的一个实现,这意味着可以使用它无缝代替你代码中已经在使用的 SqlSession。 --> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <!--只能使用构造器注入sqlSessionFactory,因为他没有set方法(看源码)--> <constructor-arg index="0" ref="sqlSessionFactory"/> </bean> <!-- 配置好之后,你就可以像 Spring 中普通的 bean 注入方法那样,将映射器注入到你的业务或服务对象中。 我在这里就不加入其他bean了,以上三个是固定通用的,然后我再新建一个总的配置文件,然后将其import即可 --> </beans>
小技巧:既然前三个是固定的,可将其作为一个固定的xml文件,然后另外写一个总的,再将其import即可
-
编写实现类
package com.ryan.mapper; import com.ryan.pojo.User; import org.mybatis.spring.SqlSessionTemplate; import java.util.List; public class UserMapperImpl implements UserMapper { //在原来,我们所有的操作,都是用sqlSession来执行,现在我们都使用sqlSessionTemplate private SqlSessionTemplate sqlSessionTemplate; public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) { this.sqlSessionTemplate = sqlSessionTemplate; } @Override public List<User> selectUser() { UserMapper mapper = sqlSessionTemplate.getMapper(UserMapper.class); return mapper.selectUser(); } }
-
将实现类注入到spring中
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns: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"> <import resource="spring-dao.xml"/> <!--注入mapper实现类--> <bean id="userMapper" class="com.ryan.mapper.UserMapperImpl"> <property name="sqlSessionTemplate" ref="sqlSession"/> </bean> </beans>
-
测试
import com.ryan.mapper.UserMapper; import com.ryan.pojo.User; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { @Test public void test(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationConfig.xml"); UserMapper userMapper = context.getBean("userMapper", UserMapper.class); for (User user : userMapper.selectUser()) { System.out.println(user); } } }
发现在测试中,不需要new对象,也不需要写获取sqlSession等操作,这些都交给spring去做了
Mybatis-Spring方式二:
在实现类中继承SqlSessionDaoSupport,然后注入spring即可,其他的基本保持不变
package com.ryan.mapper;
import com.ryan.pojo.User;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import java.util.List;
/*
方式二:继承SqlSessionDaoSupport
SqlSessionDaoSupport 是一个抽象的支持类,用来为你提供 SqlSession。
调用 getSqlSession() 方法你会得到一个 SqlSessionTemplate,之后可以用于执行 SQL 方法
*/
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
@Override
public List<User> selectUser() {
return getSqlSession().getMapper(UserMapper.class).selectUser();
}
}
14、事务
14.1、事务回顾
- 把一组业务当成一个业务来做:要么都成功,要么都失败
- 事务在项目开发中,十分的重要,涉及到数据的一致性问题,不能马虎
- 确保完整性和一致性
事务ACID原则:
- 原子性
- 一致性
- 隔离性
- 多个业务可能操作同一个资源,防止数据损坏
- 持久性
- 事务一旦提交,无论系统发生什么问题,结果都不会再被影响,被持久化的写到存储器中。
14.2、spring中的事务管理
-
声明式事务:AOP
<!-- 配置声明式事务 要开启 Spring 的事务处理功能,在 Spring 的配置文件中创建一个 DataSourceTransactionManager 对象: --> <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="selectUser"/> </tx:attributes> </tx:advice> <!--集合aop织入事务--> <aop:config> <aop:pointcut id="txPointcut" expression="execution(* com.ryan.mapper.UserMapperImpl.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/> </aop:config>
-
编程式事务:需要在代码 中,进行事务的管理(不推荐)
思考:为什么需要事务?
- 如果不配置事务,可能存在数据提交不一致的情况
- 如果我们不在spring中去配置声明式事务,我们就需要在代码中手动配置事务
- 事务在项目的开发中十分重要,涉及到数据的一致性和完整性问题,不容马虎
学习网站推荐:哔哩哔哩
推荐up主:狂神说
祝各位跟我一样在自学的java早日成功🍻