Spring
1、简介
? Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。
官网: https://spring.io/projects/spring-framework#overview
官方下载: https://repo.spring.io/release/org/springframework/spring/
GitHub: https://github.com/spring-projects/spring-framework
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
2、优点
- Spring是一个开源的免费的框架(容器)!
- Spring是一个轻量级的、非入侵式的框架!
- 控制反转(IOC),面向切面编程(AOP)!
- 支持事务的处理,对框架整合的支持!
总结一句话:Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架!
3、组成
- 核心容器SpringCode: 核心容器提供Spring框架的基本功能。—-它主要的组件就是BeanFactory,是工厂模式的实现。同时BeanFactory适用控制反转(IOC)思想将应用程序的配置和依赖性规范与实际的应用程序分开。
- Spring Context: Spring上下文是一个配置文件,主要向框架提供上下文信息。
- SpringAop: 通过配置管理特性,SpringAOP模块直接将面向切面地编程功能集成到了Spring框架中,所以,它可以很容易地使Spring框架管理的任何对象支持AOP。SpringAOP模块也是基于Spring的应用程序中的对象提供了事务管理服务。—–比较强大的功能
- SpringDAO: 它主要和dao层相关联,可以用该结构来管理异常处理和不同数据库供应商抛出的错误信息。其中异常层次结构简化了错误处理,并且极大地降低了需要编写地异常代码数据(例如打开和关闭连接)。
- Spring ORM : Spring框架中插入了若干个ORM框架,从而提供了ORM的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
- SpringWEB模块: Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
- SpringMVC:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、FreeMarker、Velocity、Tiles(jsp布局)、iText(报表处理) 和 poi。
4、ICO本质
控制反转IOC(Inversion of Control),是一种设计思想,Di(依赖注入)是实现IOC的一种方法,也有人认为DI只是IOC的另一种说法。没有IOC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓的控制反转就是:获得依赖对象的方式反转了
? 控制反转是一种通过描述(XML或注解)并通过第三方产生或获取特定对象的方式。在Spring中实现控制反转的是IOC容器,其实现方式是依赖注入(Dependency Injection,ID)
5、Hello Spring
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
- 编写spring文件,这里我们命名文beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="hello" class="com.morant.spring.entity.hello">
<property name="name" value="morant"/>
</bean>
</beans>
package Test;
import com.morant.spring.entity.hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
hello hello1= (hello) context.getBean("hello");
System.out.println(hello1.toString());
}
}
思考
- Hello 对象是谁创建的?【hello对象是由Spring创建的】
- Hello 对象的属性是怎么设置的?hello对象的属性是由Spring容器设置的,这个过程就叫控制反转:
- 控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的
- 反转:程序本身不创建对象,而变成被动的接收对象
依赖注入:就是利用set方法来进行注入的
IOC是一种编程思想,由主动的变成编程变成被动的接收
可以通过newClassPathXmlApplicationContext去浏览一次底层代码
4.IOC创建对象的方式
1.通过无参构造来创建对象
public class User {
private String name;
public User() {
System.out.println("调用无参构造");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println(this.name+"在学java");
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
2.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.morant.spring.User">
<property name="name" value="morant"></property>
</bean>
</beans>
3.测试
public class Test {
public static void main(String[] args) {
ApplicationContext contest = new ClassPathXmlApplicationContext("beans.xml");
}
}
结果可以发现,在new出ApplicationContext对象之后,无需getBean()方法,Spring已经为我们创建好了一个实例对象
总结:在配置文件加载的时候,容器中管理的对象已经初始化了
5.IOC创建的对象调用有参构造函数的三种方式
构造方法
public User(String name){
this.name = name;
}
1.利用参数索引
<bean id="user" class="com.morant.spring.User">
<constructor-arg index="0" value="morant"></constructor-arg>
</bean>
2.利用参数类型(不推荐,可能有同类型参数,基础类型直接写,引用类型需要些全名)
<bean id="user" class="com.morant.spring.User">
<constructor-arg type="java.lang.String" value="mo"></constructor-arg>
</bean>
3.通过参数名字调用
<bean id="user" class="com.morant.spring.User">
<constructor-arg name="name" value="moo"></constructor-arg>
</bean>
6、Spring配置
6.1、别名(alias)
<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="user" class="com.morant.spring.User">
<constructor-arg name="name" value="moo"></constructor-arg>
<property name="name" value="morant"></property>
</bean>
<alias name="user" alias="ufhdsajkhfkjsa"></alias>
</beans>
给bean取别名 其中的name对应bean的id,alias对应别名
6.2、Bean的配置
<bean id="user" class="com.morant.spring.User" name="user2 u2;u1,u4">
<constructor-arg name="name" value="moo"></constructor-arg>
<property name="name" value="morant"></property>
</bean>
6.3、import
这个import,一般用于团队开发使用,它可以将多个配置文件合并为一个。
假设,现在项目中有多个人开发,这三个人负责不同的开发,不同的类需要注册在不同的bean中,我们可以利用import将所有的beans.xml合并为一个总的
<import resource="beans.xml"/>
<import resource="beans1.xml"/>
<import resource="beans2.xml"/>
使用的时候,直接使用总的配置就可以了
7、DI依赖注入
7.1、构造器注入
之前说过
7.2、Set方式注入【重点】
- 依赖注入:Set注入!
- 依赖:bean对象的创建依赖于容器
- 注入:bean对象中的所有属性,由容器来注入!
8种不同类型bean属性的注入方法
public class Adress {
private String street;
@Override
public String toString() {
return "Adress{" +
"street='" + street + '\'' +
'}';
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
}
public class Student {
private String name;
private Adress adress;
private String [] books;
private List<String> hobby;
private Map<String,String> cord;
private Set<String> friends;
private Properties info;
private String dog;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", adress=" + adress +
", books=" + Arrays.toString(books) +
", hobby=" + hobby +
", cord=" + cord +
", friends=" + friends +
", info=" + info +
", dog='" + dog + '\'' +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Adress getAdress() {
return adress;
}
public void setAdress(Adress adress) {
this.adress = adress;
}
public String[] getBooks() {
return books;
}
public void setBooks(String[] books) {
this.books = books;
}
public List<String> getHobby() {
return hobby;
}
public void setHobby(List<String> hobby) {
this.hobby = hobby;
}
public Map<String, String> getCord() {
return cord;
}
public void setCord(Map<String, String> cord) {
this.cord = cord;
}
public Set<String> getFriends() {
return friends;
}
public void setFriends(Set<String> friends) {
this.friends = friends;
}
public Properties getInfo() {
return info;
}
public void setInfo(Properties info) {
this.info = info;
}
public String getDog() {
return dog;
}
public void setDog(String dog) {
this.dog = dog;
}
}
- applicationContext配置文件配置并注入
<?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 name="adress" class="com.morant.spring.entity.Adress">
<property name="street" value="浙江省"/>
</bean>
<bean name="student" class="com.morant.spring.entity.Student">
<property name="name" value="moran"/>
<property name="adress" ref="adress"/>
<property name="books">
<array>
<value>java编程思想</value>
<value>java核心卷I</value>
<value>SpringBoot</value>
<value>SpringCloud</value>
</array>
</property>
<property name="hobby">
<list>
<value>打篮球</value>
<value>踢足球</value>
</list>
</property>
<property name="cord">
<map>
<entry key="身份证" value="028391489718937498137"/>
<entry key="银行卡" value="978787984375893748957"/>
</map>
</property>
<property name="friends">
<set>
<value>Mike</value>
<value>John</value>
</set>
</property>
<property name="info">
<props>
<prop key="姓名">morant</prop>
<prop key="性别">男</prop>
</props>
</property>
<property name="dog">
<null></null>
</property>
</bean>
</beans>
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Student student = (Student) context.getBean("student");
System.out.println(student.toString());
}
7.3、拓展方式
1、P命名空间注入
xmlns:p="http://www.springframework.org/schema/p"
<bean id="user" class="com.kuang.pojo.User" p:name="狂神" p:age="18"/>
2、C命名空间注入:
xmlns:c="http://www.springframework.org/schema/c"
<bean id="user" class="com.kuang.pojo.User" c:name="狂神" c:age="18"/>
8、bean的作用域
? 在Spring中,那些组成应用程序的主题由Spring IoC容器所管理的对象,被称之为bean。简单的讲,bean就是由IOC容器初始化、装配以及管理的对象。
8.1、四种作用域:
-
singleton
- 在Spring Ioc容器中仅存在一个Bean实例,Bean以单例方式存在,默认值
-
prototype
- 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行new XxxBean()
-
request
- 每次HTTP请求都会创建一个新的Bean,该作用于仅适用于WebApplicationContext环境
-
session
- 同一个HTTP Session共享一个Bean,不同的Session使用不同的Bean,仅适用于WebApplicationContext环境
9、Bean的自动装配
自动装配说明
- 自动装配是使用Spring满足bean依赖的一种方法
- spring会在应用上下文中为某个bean寻找其依赖的bean
Spring中bean有三种装配机制,分别是:
? 1.在xml中显式配置
? 2.在java中显式配置
? 3.隐式的bean发现机制和自动装配。
这里我们主要讲第三种:自动化的装配bean
Spring的自动装配需要从两个角度来实现,或者说是两个操作:
? 1.组件扫描(component scanning):spring会自动发现应用上下文所创建的bean
? 2.自动装配(autowiring):spring自动满足bean之间的依赖,也是我们说的Ioc/DI;
组件扫描和自动装配组合发挥巨大威力,使用显式的配置降低到最少。
推荐不使用自动装配xml配置,而是用注解
? 1、新建一个项目
? 2、新建两个实体类,Cat,Dog都有一个shout的方法
public class Cat {
public void shout(){
System.out.println("miao~");
}
}
public class Dog {
public void shout(){
System.out.println("Wang~");
}
}
? 3、新建一个用户Person类
public class Person {
private String name;
private Cat cat;
private Dog dog;
public Person(String name, Cat cat, Dog dog) {
this.name = name;
this.cat = cat;
this.dog = dog;
}
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = 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;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", cat=" + cat +
", dog=" + dog +
'}';
}
}
? 4、编写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">
<bean class="com.morant.spring.entity.Cat" id="cat"/>
<bean class="com.morant.spring.entity.Dog" id="dog"/>
<bean class="com.morant.spring.entity.Person" id="person">
<property name="name" value="morant"/>
<property name="cat" value="cat"/>
<property name="dog" value="dog"/>
</bean>
</beans>
? 5、测试
public class PersonTest {
@Test
public void a(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = context.getBean("person", Person.class);
System.out.println(person);
}
}
? 结果正常输出,环境OK(这种方法时显式的在xml文件中配置)
9.1、使用xml设置自动装配
byName
autowire byName(按名称自动装配)
由于在手动配置xml过程中,常常发生字母缺漏和大小写等错误,而无法对齐进行检查,是的开发效率降低。
采用自动装配将避免这些错误,并且是配置简单化。
测试:
1、修改bean配置,增加一个属性 autowire=”byName“
<bean class="com.morant.spring.entity.Person" id="person" autowire="byName">
<property name="name" value="morant"/>
</bean>
2、再次测试,结果依旧成功输出!
3、我们将cat的bean id改为catXXX
4、再次测试,执行时报空指针错误。因为byName规则找不到对应的set方法,真正的setCat就没执行,对象就没有初始化,所以调用就会报空指针错误
小结:
当一个bean节点带有autowire byName属性时。
? 1.将查找其类中的所有set方法名,例如setCat,获得将set去掉并且首字母小写的字符串,即cat。
? 2.去spring容器中寻找是否有此字符串名称的id的对象
? 3.如果有,就取出并注入;如果没有,就报空指针异常
byType
autowire byType(按类型自动装配)
使用autowire byType首先需要保证:同意类型的对象,在Spring容器中唯一。如果不唯一,就会报唯一异常
NoUniqueBeanDefinitionExceprion
测试:
? 1、将user的bean配置修改一下 autowire ="byType"
? 2、测试,正常输出
? 3、再注册一个cat的bean对象!
xml会报错
? 4、测试,报错NoUniqueBeanDefinitionExceprion
? 5、删掉cat2,将cat的bean名字改掉,测试!因为是按类型装配,所以并不会报异常,以不影响结果。甚至id属性去掉也不行项结果。这就是按照类型自动装配!
9.2、使用注解自动装配
使用须知:
? 1、导入约束,context约束
? 2、配置注解的支持:
<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>
1、@Autowired
直接在属性上使用即可!也可以在set方式上面用
@Autowired用的是byType类型的自动装配
@Autowired可以配合@Qualifier(value = “dog”)一同使用,来指定对象bean的名字
2、@Resouse
直接在属性上使用即可!也可以在set方式上面用
@Resouse用的是byName类型的自动装配
@Resouse(name=“dog1”)可以指定自动配置的bean名字
小结:
@Resource和@Autowried的区别:
-
都是用来自动装配的,都可以放在字段属性上 -
@Autowried通过byType的方式实现,而且必须要求这个对象存在【常用】 -
@Resource 默认通过byName的方式实现,如果找不到名字,自通过byType实现!如果两个都找不到的情况,就报错 -
执行顺序不同:@Autowried通过byType的方式实现。@Resource通过byName的方式实现
10、使用直接开发
- 在spring4之后,要是用注解开发,必须要导入aop的包是否导入
- 使用注解需要导入context约束,增加注解的支持
<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>
1.bean
2.属性如何注入
- 可以不使用set方法,直接在属性名上添加@Value(“值”)
@Value("morant")private String name;
- 如果提供了set方法,在set方法上添加@Value(“值”);
@Value("morant")public void setName(String name) { this.name = name;}
3.衍生的注解
我们这些注解,就替代了在配置文件当中配置的步骤而已,更加的方便快捷
@Component三个衍生注解:
为了更好的进行分层,Spring可以使用其他的三个注解,功能一样,目前使用哪一个功能都一样
- Controller:web层
- Service:service层
- Repository:dao层
写上这些注解,就相当于这个类交给Spring管理装配了!
4.自动装配注解
在上面提到过
5.作用域
@Scope
- singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
- prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收
6.小结
XML与注解比较
- XML可以使用任何场景,结构清晰,维护方便
- 注解不是字节提供的类是用不了,开发简单方便
xml与注解整合开发:推荐最佳实践
- xml管理Bean
- 注解完成属性注入
- 使用过程中,可以不用扫描,扫描是为了类上的注解
<context:annotation-config/>
作用:
- 进行注解驱动注册,从而使注解生效
- 用于激活那些已经在spring容器里注册过的bean上面的注解,也就是显式的向spring注册
- 如果不扫描包,就要手动配置bean
- 如果不注册驱动,则注入值为null
11、使用java的方式配置Spring
我们现在要完全使用Spring的xml配置了,全全交给Java来做!
JavaConfig原来是Spring的一个子项目,他通过Java类的方式提供Bean的定义信息,在Spring4的版本,JavaConfig已经正式成为spring4的核心功能
测试:
1、编写一个实体类,User
public class User { @Value("morant") private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "name='" + name + '\'' + '}'; }
2、新建一个config配置包,编写一个MyConfig的配置类
@Configurationpublic class morantConfig { @Bean
其中@Import(MyConfig2.class)注解个已导入其他配置
3、测试
public class UserTest { @Test public void a(){ ApplicationContext context = new AnnotationConfigApplicationContext(morantConfig.class); User getUser = (User) context.getBean("getUser"); System.out.println(getUser.toString()); }}
关于这种Java类的配置方式,我们在之后的SpringBoot 和 SpringCloud中还会大量看到,我们需要知道这些注解的作用即可!
12、代理模式
12.1、静态代理
为什么要学习代理模式?因为这就是SpringAOP的底层!【SpringAOP 和 SpringMVC】
静态代理角色分析
- 抽象角色 : 一般使用接口或者抽象类来实现
- 真实角色 : 被代理的角色
- 代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .
- 客户 : 使用代理角色来进行一些操作 .
Rent . java 即抽象角色
Host . java 即真实角色
Proxy . java 即代理角色
Client . java 即客户
分析:在这个过程中,你直接接触的就是中介,就如同现实生活中的样子,你看不到房东,但是你依旧租到了房东的房子通过代理,这就是所谓的代理模式,程序源自于生活,所以学编程的人,一般能够更加抽象的看待生活中发生的事情。
静态代理的好处:
- 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
- 公共的业务由代理来完成 . 实现了业务的分工 ,
- 公共业务发生扩展时变得更加集中和方便 .
缺点 :
- 类多了 , 多了代理类 , 工作量变大了 . 开发效率降低 .
我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理 !
OK,到了现在代理模式大家应该都没有什么问题了,重点大家需要理解其中的思想;
我们在不改变原来的代码的情况下,实现了对原有功能的增强,这是AOP中最核心的思想
聊聊AOP:纵向开发,横向开发
12.2、动态代理
- 动态代理和静态代理角色一样
- 动态代理的代理类是动态生成的。静态代理的代理类是我们提前写好的
- 动态代理分为两类:一类是基于接口动态代理,一类是基于类的动态代理
- 基于接口的动态代理—JDK动态代理
- 基于类的动态代理—cglib
- 现在用的比较多的是javasist来生成动态代理
- 我们这里使用JDK的原生代码来实现,其余道理都是一样的
JDK动态代理需要了解两个类
核心:InvocationHandler 和 Proxy,
【InvocationHandler:调用处理程序】
Object invoke(Object proxy, 方法 method, Object[] args);
【Proxy:代理】
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
rent.getClass().getInterfaces(),this);
}
代码实现
抽象角色和真是角色和之前的一样!
Rent.java即抽象角色
public interface Rent {
public void rent();
}
Host.java即真实角色
public class Host implements Rent{
public void rent() {
System.out.println("房屋出租");
}
}
ProxyInvocationHandler. java 即代理角色
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);
fare();
return result;
}
public void seeHouse(){
System.out.println("带房客看房");
}
public void fare(){
System.out.println("收中介费");
}
}
Client . java
public class Client {
public static void main(String[] args) {
Host host = new Host();
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setRent(host);
Rent proxy = (Rent)pih.getProxy();
proxy.rent();
}
}
核心:一个动态代理,一般代理某一类业务,一个动态代理也可以代理多个类,代理的是接口
深化理解
我们来使用动态代理实现代理我们后面写的UserService!
我们也可以编写一个通用的动态代理实现的类!所有的代理对象设置为Object即可!
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 {
log(method.getName());
Object result = method.invoke(target, args);
return result;
}
public void log(String methodName){
System.out.println("执行了"+methodName+"方法");
}
}
测试!
public class Test {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setTarget(userService);
UserService proxy = (UserService)pih.getProxy();
proxy.delete();
}
}
动态代理的好处
静态代理有的他都有,静态代理没有的 他也有
- 可以使用真实角色更加纯粹,不再去关注一些公共的事情
- 公共的业务由代理来完成。实现了业务的分工
- 公共的业务发生扩展时变得更加集中方便
- 一个动态代理可以实现多个类,代理的接口!
静态代理的接口是写死的,只能代理该接口的实现类,实现其对应的方法,真是角色都属于同一类,动态代理通过反射获取被代理对象的接口类型,所以代理的对象可以是任意的,代理类都可以通过反射获取接口类型,被代理的对象的方法也可以通过反射获取,所以动态代理比静态代理最大的区别就是能够代理的类型可以使任意的(Object)的,而静态代理只能代理一类对象(实现相同的接口),静态代理实现了和被代理类相同的接口,而动态代理实现的是InvocationHandler,可以通过反射实现任意类型的接口,更具灵活性。
13、AOP就是这么简单
什么是AOP
AOP(Aspect Oriented Programming):意为:面向切面编程,通过预编译和运行期运行期动态代理实现程序功能的唯一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生泛型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可用性,同时提高了开发的效率。
AOP在Spring中的作用
提供声明式的事务,语序用户自定义切面
以下名词需要了解:
- 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 …
- 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
- 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
- 目标(Target):被通知对象。
- 代理(Proxy):向目标对象应用通知之后创建的对象。
- 切入点(PointCut):切面通知 执行的 “地点”的定义。
- 连接点(JointPoint):与切入点匹配的执行点。
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:
即 Aop 在 不改变原有代码的情况下 , 去增加新的功能 .
13.1、使用Spring实现AOP
使用Spring实现AOP
【重点】使用AOP织入,需要导入一个依赖包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
第一种方式
通过Spring API实现
首先编写我们的业务接口和实现类
public interface UserService {
public void add();
public void delete();
public void update();
public void search();
}
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 search() {
System.out.println("查询用户");
}
}
然后去写我们的增强类 , 我们编写两个 , 一个前置增强 一个后置增强
public class Log implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println( o.getClass().getName() + "的" + method.getName() + "方法被执行了");
}
}
public class AfterLog implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了" + target.getClass().getName()
+"的"+method.getName()+"方法,"
+"返回值:"+returnValue);
}
}
最后去spring的文件中注册 , 并实现aop切入实现 , 注意导入约束
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.kuang.service.UserServiceImpl"/>
<bean id="log" class="com.kuang.log.Log"/>
<bean id="afterLog" class="com.kuang.log.AfterLog"/>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.kuang.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 test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService) context.getBean("userService");
userService.search();
}
}
Aop的重要性 : 很重要 . 一定要理解其中的思路 , 主要是思想的理解这一块 .
Spring的Aop就是将公共的业务 (日志 , 安全等) 和领域业务结合起来 , 当执行领域业务时 , 将会把公共业务加进来 . 实现公共业务的重复利用 . 领域业务更纯粹 , 程序猿专注领域业务 , 其本质还是动态代理
第二种方式
自定义类来实现Aop
目标业务不变依旧是userServiceImpl
第一步:写出我们自己的切入类
public class DiyPointcut {
public void before(){
System.out.println("---------方法执行前---------");
}
public void after(){
System.out.println("---------方法执行后---------");
}
}
第二步:去spring中配置
<!--第二种方式自定义实现-->
<!--注册bean-->
<bean id="diy" class="com.kuang.config.DiyPointcut"/>
<!--aop的配置-->
<aop:config>
<!--第二种方式:使用AOP的标签实现-->
<aop:aspect ref="diy">
<aop:pointcut id="diyPonitcut" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
<aop:before pointcut-ref="diyPonitcut" method="before"/>
<aop:after pointcut-ref="diyPonitcut" method="after"/>
</aop:aspect>
</aop:config>
第三步:测试
public class MyTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService) context.getBean("userService");
userService.add();
}
}
第三种实现方式
使用注解
第一步:编写一个注解实现的增强类
package com.kuang.config;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class AnnotationPointcut {
@Before("execution(* com.kuang.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("---------方法执行前---------");
}
@After("execution(* com.kuang.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("---------方法执行后---------");
}
@Around("execution(* com.kuang.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
System.out.println("签名:"+jp.getSignature());
Object proceed = jp.proceed();
System.out.println("环绕后");
System.out.println(proceed);
}
}
第二步:在Spring配置文件中,注册bean,并增加支持注解的配置
<!--第三种方式:注解实现-->
<bean id="annotationPointcut" class="com.kuang.config.AnnotationPointcut"/>
<aop:aspectj-autoproxy/>
aop:aspectj-autoproxy:说明
通过aop命名空间的<aop:aspectj-autoproxy />声明自动为spring容器中那些配置@aspectJ切面的bean创建代理,织入切面。
当然,spring 在内部依旧采用AnnotationAwareAspectJAutoProxyCreator进行自动代理的创建工作,但具体实现的细节已经被<aop:aspectj-autoproxy />隐藏起来了
<aop:aspectj-autoproxy />有一个proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强,
当配为<aop:aspectj-autoproxy poxy-target-class="true"/>时,表示使用CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false,
如果目标类没有声明接口,则spring将自动使用CGLib动态代理。
14、整合Mybatis
步骤:
1、导入相关jar包
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
配置Maven静态资源过滤问题
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
2、编写配置文件
3、代码实现
回忆Mybatis
编写entity实体类
@Data
public class User {
private int id;
private String name;
private String password;
}
实现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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.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"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/morant/spring/Mapper/UserMapper.xml"/>
</mappers>
</configuration>
UserDao接口编写
public interface UserMapper {
public List<User> selectUser();
}
接口对应的Mapper映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.morant.spring.Mapper.UserMapper">
<select id="selectUser" resultType="com.morant.spring.entity.User">
select * from user
</select>
</mapper>
4、测试
public class UserMapperTest {
public static void main(String[] args) throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
for (User user : mapper.selectUser()) {
System.out.println(user);
}
sqlSession.close();
}
}
mybatis-Spring学习
引入Spring之前需要了解mybatis-spring包中的一些重要类;
http://www.mybatis.org/spring/zh/index.html
什么是 MyBatis-Spring?
MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。
知识基础
在开始使用 MyBatis-Spring 之前,你需要先熟悉 Spring 和 MyBatis 这两个框架和有关它们的术语。这很重要
MyBatis-Spring 需要以下版本:
MyBatis-Spring | MyBatis | Spring 框架 | Spring Batch | Java |
---|
2.0 | 3.5+ | 5.0+ | 4.0+ | Java 8+ | 1.3 | 3.4+ | 3.2.2+ | 2.1+ | Java 6+ |
如果使用 Maven 作为构建工具,仅需要在 pom.xml 中加入以下代码即可:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
要和 Spring 一起使用 MyBatis,需要在 Spring 应用上下文中定义至少两样东西:一个 SqlSessionFactory 和至少一个数据映射器类。
在 MyBatis-Spring 中,可使用SqlSessionFactoryBean来创建 SqlSessionFactory。要配置这个工厂 bean,只需要把下面代码放在 Spring 的 XML 配置文件中:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>
注意:SqlSessionFactory需要一个 DataSource(数据源)。这可以是任意的 DataSource,只需要和配置其它 Spring 数据库连接一样配置它就可以了。
在基础的 MyBatis 用法中,是通过 SqlSessionFactoryBuilder 来创建 SqlSessionFactory 的。而在 MyBatis-Spring 中,则使用 SqlSessionFactoryBean 来创建。
在 MyBatis 中,你可以使用 SqlSessionFactory 来创建 SqlSession。一旦你获得一个 session 之后,你可以使用它来执行映射了的语句,提交或回滚连接,最后,当不再需要它的时候,你可以关闭 session。
SqlSessionFactory有一个唯一的必要属性:用于 JDBC 的 DataSource。这可以是任意的 DataSource 对象,它的配置方法和其它 Spring 数据库连接是一样的。
一个常用的属性是 configLocation,它用来指定 MyBatis 的 XML 配置文件路径。它在需要修改 MyBatis 的基础配置非常有用。通常,基础配置指的是 < settings> 或 < typeAliases>元素。
需要注意的是,这个配置文件并不需要是一个完整的 MyBatis 配置。确切地说,任何环境配置(),数据源()和 MyBatis 的事务管理器()都会被忽略。SqlSessionFactoryBean 会创建它自有的 MyBatis 环境配置(Environment),并按要求设置自定义环境的值。
SqlSessionTemplate 是 MyBatis-Spring 的核心。作为 SqlSession 的一个实现,这意味着可以使用它无缝代替你代码中已经在使用的 SqlSession。
模板可以参与到 Spring 的事务管理中,并且由于其是线程安全的,可以供多个映射器类使用,你应该总是用 SqlSessionTemplate 来替换 MyBatis 默认的 DefaultSqlSession 实现。在同一应用程序中的不同类之间混杂使用可能会引起数据一致性的问题。
可以使用 SqlSessionFactory 作为构造方法的参数来创建 SqlSessionTemplate 对象。
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
现在,这个 bean 就可以直接注入到你的 DAO bean 中了。你需要在你的 bean 中添加一个 SqlSession 属性,就像下面这样:
public class UserDaoImpl implements UserDao {
private SqlSession sqlSession;
public void setSqlSession(SqlSession sqlSession) {
this.sqlSession = sqlSession;
}
public User getUser(String userId) {
return sqlSession.getMapper...;
}
}
按下面这样,注入 SqlSessionTemplate:
<bean id="userDao" class="org.mybatis.spring.sample.dao.UserDaoImpl">
<property name="sqlSession" ref="sqlSession" />
</bean>
14.1整理实现一
1、引入Spring配置文件beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
2、配置数据源替换mybaits的数据源
<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=false&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
3、配置SqlSessionFactory,关联MyBatis
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/morant/spring/Mapper/*.xml"/>
</bean>
4、注册sqlSessionTemplate,关联sqlSessionFactory;
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
5、增加Dao接口的实现类;私有化sqlSessionTemplate
public class UserMapperImpl implements UserMapper{
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();
}
}
6、注册bean实现
<bean id="userMapperImpl" class="com.morant.spring.Mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
7、测试
@Test
public void b(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserMapper mapper = (UserMapper) context.getBean("userMapperImpl");
List<User> user = mapper.selectUser();
System.out.println(user);
}
结果成功输出!现在我们的Mybatis配置文件的状态!发现都可以被Spring整合!
<?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="com.morant.spring.entity"/>
</typeAliases>
</configuration>
14.2、整合实现二
mybatis-spring1.2.3版以上的才有这个 .
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jRyvrVhj-1630919723332)(C:\Users\mohai\AppData\Roaming\Typora\typora-user-images\image-20210829183056580.png)]
测试:
1、将我们上面写的UserDaoImpl修改一下
public class UserDaoImpl extends SqlSessionDaoSupport implements UserMapper {
public List<User> selectUser() {
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
return mapper.selectUser();
}
}
2、修改beans.xml 的配置不需要注册sqlSession了
<?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="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=false&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/morant/spring/Mapper/*.xml"/>
</bean>
<bean id="userMapperImpl" class="com.morant.spring.Mapper.UserMapperImpl">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
</beans>
3、测试
@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserMapper mapper = (UserMapper) context.getBean("userMapperImpl");
List<User> user = mapper.selectUser();
System.out.println(user);
}
15、声明式事务
15.1、回顾事务
事务在项目开发过程非常重要,涉及到数据的一致性的问题,不容马虎!
事务管理是企业级应用程序开发中必备技术,用来确保数据的完整性和一致性。
事务就是把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用。
15.2、事务的四个属性ACID
原子性(atomicity)
事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用
一致性(consistency)
一旦所有事务动作完成,事务就要被提交。数据和资源处于一种满足业务规则的一致性状态中
隔离性(isolation)
可能多个事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏
持久性(durability)
事务一旦完成,无论系统发生什么错误,结果都不会受到影响。通常情况下,事务的结果被写到持久化存储器中
15.3、测试
将上面的代码拷贝到一个新项目中
在之前的案例中,我们给userDao接口新增两个方法,删除和增加用户;
/添加一个用户
int addUser(User user);
int deleteUser(int id);
mapper文件,我们故意把 deletes 写错,测试!
<insert id="addUser" parameterType="com.kuang.pojo.User">
insert into user (id,name,pwd) values (#{id},#{name},#{pwd})
</insert>
<delete id="deleteUser" parameterType="int">
deletes from user where id = #{id}
</delete>
编写接口的实现类,在实现类中,我们去操作一波
public class UserDaoImpl extends SqlSessionDaoSupport implements UserMapper {
public List<User> selectUser() {
User user = new User(4,"小明","123456");
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
mapper.addUser(user);
mapper.deleteUser(4);
return mapper.selectUser();
}
public int addUser(User user) {
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
return mapper.addUser(user);
}
public int deleteUser(int id) {
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
return mapper.deleteUser(id);
}
}
测试
@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserMapper mapper = (UserMapper) context.getBean("userDao");
List<User> user = mapper.selectUser();
System.out.println(user);
}
报错:sql异常,delete写错了
结果 :插入成功!
没有进行事务的管理;我们想让他们都成功才成功,有一个失败,就都失败,我们就应该需要事务!
以前我们都需要自己手动管理事务,十分麻烦!
但是Spring给我们提供了事务管理,我们只需要配置即可;
Spring中的事务管理
Spring在不同的事务管理API之上定义了一个抽象层,使得开发人员不必了解底层的事务管理API就可以使用Spring的事务管理机制。Spring支持编程式事务管理和声明式的事务管理。
编程式事务管理
将事务管理代码嵌到业务方法中来控制事务的提交和回滚
缺点:必须在每个事务操作业务逻辑中包含额外的事务管理代码
声明式事务管理
一般情况下比编程式事务好用。
将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。
将事务管理作为横切关注点,通过aop方法模块化。Spring中通过Spring AOP框架支持声明式事务管理。
使用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">
事务管理器
- 无论使用Spring的哪种事务管理策略(编程式或者声明式)事务管理器都是必须的。
- 就是 Spring的核心事务管理抽象,管理封装了一组独立于技术的方法。
JDBC事务
<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,它适合于绝大多数的情况。
假设 ServiveX#methodX() 都工作在事务环境下(即都被 Spring 事务增强了),假设程序中存在如下的调用链:Service1#method1()->Service2#method2()->Service3#method3(),那么这 3 个服务类的 3 个方法通过 Spring 的事务传播机制都工作在同一个事务中。
就好比,我们刚才的几个方法存在调用,所以会被放在一组事务当中!
配置AOP
导入aop的头文件!
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.kuang.dao.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
进行测试
删掉刚才插入的数据,再次测试!
@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserMapper mapper = (UserMapper) context.getBean("userDao");
List<User> user = mapper.selectUser();
System.out.println(user);
}
为什么需要配置事务?
- 如果不配置,就需要我们手动提交控制事务;
- 事务在项目开发过程非常重要,涉及到数据的一致性的问题,不容马虎!
此时真个项目的架构如下
依赖
<?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.example</groupId>
<artifactId>Spring</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>spring-01-ioc1</module>
<module>spring-02-hellospring</module>
<module>spring-03-di</module>
<module>spring-04-wire</module>
<module>spring-05-aop</module>
<module>spring-06-Rent</module>
<module>spring-07-spingMybatis</module>
</modules>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
</dependencies>
</project>
<?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">
<parent>
<artifactId>Spring</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-07-spingMybatis</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</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>
</project>
资源文件
mybatis-config.xml
<?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>
<properties resource="db.properties"/>
<typeAliases>
<package name="com.bupt.pojo"/>
</typeAliases>
</configuration>
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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
">
<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/student?userSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="datasource"/>
<property name="configLocation" value="mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/bupt/dao/*.xml"/>
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<bean id="transactionmanager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="datasource"/>
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionmanager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.bupt.dao.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
</beans>
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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<import resource="spring-dao.xml"/>
<bean id="userMapper" class="com.bupt.dao.UserMapperImpl">
<property name="sqlSessionTemplate" ref="sqlSession"/>
</bean>
<bean id="userDao" class="com.bupt.dao.UserDaoImpl">
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
</beans>
pojo实体类User
package com.bupt.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class User {
private int id;
private String username;
private String pwd;
}
utils类,整合后就不再使用了
package com.bupt.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 {
static InputStream resourceAsStream = null;
static SqlSessionFactory build = null;
static {
try {
String resource = "mybatis-config.xml";
resourceAsStream = Resources.getResourceAsStream(resource);
build = new SqlSessionFactoryBuilder().build(resourceAsStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
SqlSession sqlSession = build.openSession(true);
return sqlSession;
}
}
UserMapper接口
package com.bupt.dao;
import com.bupt.pojo.User;
import java.util.List;
public interface UserMapper {
public List<User> selectUser();
int addUser(User user);
int deleteUser(int id);
}
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.bupt.dao.UserMapper">
<select id="selectUser" resultType="User">
select * from user
</select>
<insert id="addUser" parameterType="com.bupt.pojo.User">
insert into user (id,username) values (#{id},#{username})
</insert>
<delete id="deleteUser" parameterType="int">
delete from user where id = #{id}
</delete>
</mapper>
UserMapperImpl
package com.bupt.dao;
import com.bupt.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import java.util.List;
public class UserMapperImpl implements UserMapper{
private SqlSessionTemplate sqlSessionTemplate;
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSessionTemplate = sqlSessionTemplate;
}
@Override
public List<User> selectUser() {
return sqlSessionTemplate.getMapper(UserMapper.class).selectUser();
}
@Override
public int addUser(User user) {
return 0;
}
@Override
public int deleteUser(int id) {
return 0;
}
}
UserDaoImpl
package com.bupt.dao;
import com.bupt.pojo.User;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import java.util.List;
public class UserDaoImpl extends SqlSessionDaoSupport implements UserMapper {
@Override
public List<User> selectUser() {
User user = new User(445,"小明","123456");
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
mapper.addUser(user);
mapper.deleteUser(444);
return mapper.selectUser();
}
@Override
public int addUser(User user) {
return getSqlSession().getMapper(UserMapper.class).addUser(user);
}
@Override
public int deleteUser(int id) {
return getSqlSession().getMapper(UserMapper.class).deleteUser(id);
}
}
MyTest
import com.bupt.dao.UserMapper;
import com.bupt.pojo.User;
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 org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class MyTest {
@Test
public void test() throws IOException {
String Resource = "mybatis-config.xml";
InputStream resourceAsStream = Resources.getResourceAsStream(Resource);
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = build.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.selectUser();
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
}
@Test
public void test02() {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring-dao.xml");
UserMapper userMapper = classPathXmlApplicationContext.getBean("userMapper", UserMapper.class);
for (User user : userMapper.selectUser()) {
System.out.println(user);
}
}
@Test
public void test03() {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
UserMapper userMapper = classPathXmlApplicationContext.getBean("userMapper", UserMapper.class);
for (User user : userMapper.selectUser()) {
System.out.println(user);
}
}
@Test
public void test04(){
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
UserMapper userMapper = classPathXmlApplicationContext.getBean("userMapper", UserMapper.class);
for (User user : userMapper.selectUser()) {
System.out.println(user);
}
}
@Test
public void test05(){
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
UserMapper userMapper = classPathXmlApplicationContext.getBean("userDao", UserMapper.class);
for (User user : userMapper.selectUser()) {
System.out.println(user);
}
}
@Test
public void test06(){
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
UserMapper userDao = classPathXmlApplicationContext.getBean("userDao", UserMapper.class);
List<User> users = userDao.selectUser();
System.out.println(users);
}
}
|