注意:大部分内容均来自B站《遇见狂神说》,笔者也是基于视频做的笔记,方便日后复习与查看,有不懂的地方可观看视频讲解!
视频地址:https://www.bilibili.com/video/BV1WE411d7Dv
Spring翻译过来就是春天,你也可以理解为给Java现代化软件开发带来了春天。Spring适用于任何Java应用!为降低软件开发的复杂性而诞生!
Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。
Spring是一个轻量级控制反转(IOC)和面向切面(AOP)的容器框架。
Spring相关知识:
官方地址:https://spring.io/projects/spring-framework
官方下载地址:http://repo.spring.io/release/org/springframework/spring
maven坐标,导入spring-webmvc即可,maven会自动帮我们下载其他需要的依赖!
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.2.10.RELEASEversion>
dependency>
Spring框架为任何类型的部署平台上基于Java的现代企业应用程序提供了一个全面的编程和配置模型。它的优点不言而喻!
总结一句话: Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架!
在Spring的官网有这个介绍:现代化的Java开发!说白就是基于Spring的开发。
SpringCloud
因为现在大多数公司都在使用SpringBoot进行快速开发,学习SpringBoot的前提,需要完全掌握Spring及SpringMVC!
弊端:发展了太久之后,违背了原来的理念!配置十分繁琐,人称:“配置地狱!"
在以前dao与service层我们都是基于这样实现:
在我们之前的业务中,我们可能需要书写很多实现类,去实现业务逻辑,但是随着用户的需求增加,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求去修改原代码!如果程序代码量十分大,修改与维护的成本十分昂贵!
为了解决上面问题,我们可以使用set接口实现,让程序实现动态创建实现类!
private UserDao userDao;
// 利用set进行动态实现值的注入
public void setUserDao(UserDao userDaoImpl){
this.userDao = userDaoImpl;
// 这里的userDaoImpl可以是不同的实现类,这样就可动态的创建接口实现类
}
set注入分析:
这种思想,从本质上解决了问题,我们技术人员不用再去管理对象的创建了。系统耦合性大大降低,可以更加关注业务逻辑的实现!这就是IOC的原型!
控制反转IOC(Inversion Of Control),是一种设计思想,DI(依赖注入)是实现IOC的一种方法,也有人认为DI只是IOC的另一种说法。
没有IOC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方(工厂模式),个人认为所谓控制反转就是:获得依赖对象的方式反转了。
lOC是Spring框架的核心内容,使用多种方式完美的实现了loC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IOC。
Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从loc容器中取出需要的对象。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是loC容器,其实现方法是依赖注入(Dependency Injection,Dl)。
总结IOC的底层实现原理:xml 解析、工厂模式、反射。
上一期中我们理解了IOC的基本思想,我们现在来看下Spring的应用
1、新建maven普通项目,导入spring-webmvc的包,通过这个包maven会自动帮我们下载需要的依赖!
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.2.10.RELEASEversion>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
2、编写实体类Hello
/**
* @description: Hello实体类
* @author: laizhenghua
* @date: 2020/11/14 10:18
*/
public class Hello {
private String message;
public Hello() {
}
...
}
3、编写xml配置文件applicationContext.xml
<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.howie.pojo.Hello"/>
beans>
4、编写测试程序(实例化容器)
@Test
public void helloTest(){
// 测试第一个spring程序
// 1.加载Spring的配置文件即获取Spring的上下文对象
String value = "applicationContext.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(value);
// 2.获取创建的对象hello
Hello hello = context.getBean(Hello.class);
hello.setMessage("Hello world");
System.out.println(hello);
}
测试结果
1、Hello 对象是谁创建的?
2、这个过程就叫控制反转:
可以通过newClassPathXmlApplicationContext去浏览一下底层源码.
OK,到了现在,我们彻底不用再程序中去改动了,要实现不同的操作,只需要在xml配置文件中进行修改,所谓的lOC,一句话搞定:对象由Spring 来创建,管理,装配!
1、使用无参构造器创建对象,默认!
2、有参构造器创建对象
<bean id="user" class="com.howie.pojo.User">
<constructor-arg index="0" value="7500000"/>
<constructor-arg index="1" value="42"/>
bean>
<bean id="user" class="com.howie.pojo.User">
<constructor-arg type="int" value="1001"/>
<constructor-arg type="java.lang.String" value="Java"/>
bean>
<bean id="user" class="com.howie.pojo.User">
<constructor-arg name="id" value="1001"/>
<constructor-arg name="name" value="Java"/>
bean>
结论:在配置文件加载的时候。其中管理的对象都已经初始化了!并且默认是以单例的形式存在!
测试:
@Test
public void helloTest(){
// 测试第一个spring程序
// 1.加载Spring的配置文件即获取Spring的上下文对象
String value = "applicationContext.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(value);
// 2.获取创建的对象hello
Hello hello1 = context.getBean(Hello.class);
Hello hello2 = context.getBean(Hello.class);
System.out.println(hello1 == hello2); // true
}
1、配置文件里添加别名
<alias name="user" alias="userAAAA"/>
2、获取对象
String value = "applicationContext.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(value);
// 获取创建的对象
User user1 = (User) context.getBean("user");
// 也可以使用别名获取对象
User user2 = (User) context.getBean("userAAAA");
<bean id="hello" class="com.howie.pojo.Hello">
<property name="message" value="Hello World"/>
bean>
这个import,一般用于团队开发使用,他可以将多个配置文件,导入合并为一个。假设,现在项目中有多个人开发,这三个人负责不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的beans.xml合并为一个总的!
使用的时候,直接使用总的配置(applicationContext.xml)就可以了
<import resource="zsBean"/>
<import resource="lsBean"/>
<import resource="wwBean"/>
依赖注入(Dependency Injection,DI)。
依赖:指Bean对象的创建依赖于容器 , Bean对象的依赖资源 。
注入:指Bean对象所依赖的资源 , 由容器来设置和装配 。
前面已经讲过了,有参无参注入等。
要求被注入的属性 , 必须有set方法 , set方法的方法名由set + 属性首字母大写 , 如果属性是boolean类型 , 没有set方法,是is
搭建测试环境,我们创建一些pojo类
Address.java
/**
* @description: 地址类
* @author: laizhenghua
* @date: 2020/11/14 15:34
*/
public class Address {
private String address;
...
// setter方法一定要有,这里省略
}
Student.java
/**
* @description: 复杂的学生类,属性包含数组、基本数据类型与引用数据类型还有集合类型等
* @author: laizhenghua
* @date: 2020/11/14 15:34
*/
public class Student {
private String name;
private Address address; // 引用类型
private String[] books;
private List<String> hobby;
private Map<String,String> card;
private Set<String> games;
private Properties props; // 处理属性文件,k-v都是String
private String girlFiend; // 女朋友
...
// setter方法一定要有,这里省略
}
applicationContext.xml
<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="student" class="com.howie.pojo.Student">
bean>
beans>
1、属性名注入
<bean id="student" class="com.howie.pojo.Student">
<property name="name" value="alex"/>
bean>
测试:
@Test
public void test(){
String resource = "applicationContext.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(resource);
Student student = (Student) context.getBean("student");
System.out.println(student.getName()); // 输出alex
}
2、bean注入
注意点:这里的值是一个引用,ref
<bean id="address" class="com.howie.pojo.Address">
<property name="address" value="西南林业大学新校区"/>
bean>
<bean id="student" class="com.howie.pojo.Student">
<property name="name" value="alex"/>
<property name="address" ref="address"/>
bean>
测试
@Test
public void test(){
String resource = "applicationContext.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(resource);
Student student = (Student) context.getBean("student");
System.out.println(student.getAddress()); // Address{address='西南林业大学新校区'}
}
3、数组注入
<property name="books">
<array>
<value>Javavalue>
<value>Pythonvalue>
<value>C/C++value>
<value>JavaScriptvalue>
array>
property>
测试
String resource = "applicationContext.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(resource);
Student student = (Student) context.getBean("student");
System.out.println(Arrays.toString(student.getBooks())); // [Java, Python, C/C++, JavaScript]
4、List注入
<property name="hobby">
<list>
<value>写代码value>
<value>烫发value>
<value>王者荣耀value>
list>
property>
5、Map注入
<property name="card">
<map key-type="java.lang.String">
<entry value-type="java.lang.String" key="中国工商" value="122121212"/>
<entry value-type="java.lang.String" key="建设银行" value="00021333"/>
map>
property>
6、set集合注入
<property name="games">
<set value-type="java.lang.String">
<value>cfvalue>
<value>lolvalue>
<value>王者荣耀value>
set>
property>
7、Null注入
<property name="girlFiend">
<null/>
property>
测试
@Test
public void test(){
String resource = "applicationContext.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(resource);
Student student = (Student) context.getBean("student");
System.out.println(student.getGirlFiend()); // null
}
8、Properties注入
<property name="props">
<props>
<prop key="学号">1001prop>
<prop key="班级">2017prop>
<prop key="专业">计算机prop>
<prop key="jdbc.url">jdbcxxxprop>
props>
property>
1、p 命名空间注入(XML Shortcut with the p-namespace)
新建一个没有有参构造器的实体类User
/**
* @description: User实体类
* @author: laizhenghua
* @date: 2020/11/14 18:51
*/
public class User {
private String name;
private Integer age;
public String getName() {
return name;
}
// 注意没有有参实体类
...
}
编写配置文件,需要在头文件中加入约束文件
xmlns:p="http://www.springframework.org/schema/p"
userBean.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.howie.pojo.User" p:name="alex" p:age="22"/>
beans>
测试方法
@Test
public void userTest(){
ApplicationContext context = new ClassPathXmlApplicationContext("userBean.xml");
User user = (User) context.getBean("user");
System.out.println(user); // User{name='alex', age=22}
}
2、c 命名空间注入 (XML Shortcut with the c-namespace)
编写配置文件时,需要在头文件中加入约束文件!
xmlns:c="http://www.springframework.org/schema/c"
userBean.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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">
<bean id="user" class="com.howie.pojo.User" c:name="alex" c:age="21"/>
beans>
发现问题:爆红了,刚才我们没有写有参构造!
解决:把有参构造器加上,这里也能知道,c 就是所谓的构造器注入!
测试:
@Test
public void userTest(){
ApplicationContext context = new ClassPathXmlApplicationContext("userBean.xml");
User user = (User) context.getBean("user");
System.out.println(user); // User{name='alex', age=21}
}
小结:
p命名(set注入)和c命名(构造器注入)空间不能直接使用,需要导入xml约束!
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
在Spring中,那些组成应用程序的主体及由Spring IOC容器所管理的对象,被称之为bean。简单地讲,bean就是由IOC容器初始化、装配及管理的对象。
几种作用域中,request、session作用域仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架),只能用在基于web的Spring ApplicationContext环境。
1、singleton(单例)-spring默认机制
当一个bean的作用域为Singleton,那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。
Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。
注意,Singleton作用域是Spring中的缺省作用域。要在XML中将bean定义成singleton,可以这样配置:
<bean id="user" class="com.howie.pojo.User" c:name="alex" c:age="21" scope="singleton"/>
测试:
@Test
public void userTest(){
ApplicationContext context = new ClassPathXmlApplicationContext("userBean.xml");
User user1 = (User) context.getBean("user");
User user2 = (User) context.getBean("user");
System.out.println(user1 == user2); // true
}
2、Prototype(原型)
Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。
<bean id="user" class="com.howie.pojo.User" c:name="alex" c:age="21" scope="prototype"/>
或者
<bean id="user" class="com.howie.pojo.User" c:name="alex" c:age="21" singleton="false"/>
测试:
@Test
public void userTest(){
ApplicationContext context = new ClassPathXmlApplicationContext("userBean.xml");
User user1 = (User) context.getBean("user");
User user2 = (User) context.getBean("user");
System.out.println(user1 == user2); // false
}
3、其余的request、session、application、这些个只能在web开发中使用到!
Spring中bean有三种装配机制,分别是:
1.在xml中显式配置;
2.在java中显式配置;
3.隐式的bean发现机制和自动装配。
这里我们主要讲第三种:自动化的装配bean。
Spring的自动装配需要从两个角度来实现,或者说是两个操作:
1、组件扫描(component scanning):spring会自动发现应用上下文中所创建的bean;
2、自动装配(autowiring):spring自动满足bean之间的依赖,也就是我们说的IoC/DI;
组件扫描和自动装配组合发挥巨大威力,使得显示的配置降低到最少。
1、新建一个普通maven项目,注意导入需要的包
2、编写实体类
实体Cat
/**
* @description: Cat实体类
* @author: laizhenghua
* @date: 2020/11/15 13:33
*/
public class Cat {
public void shout(){
System.out.println("喵~");
}
}
实体类Dog
/**
* @description: Dog实体类
* @author: laizhenghua
* @date: 2020/11/15 13:35
*/
public class Dog {
public void shout(){
System.out.println("旺~");
}
}
实体类Person
/**
* @description: Person实体类
* @author: laizhenghua
* @date: 2020/11/15 13:36
*/
public class Person {
private Cat cat;
private Dog dog;
private String message;
public Cat getCat() {
return cat;
}
...
}
3、编写xml配置文件
<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="cat" class="com.howie.pojo.Cat"/>
<bean id="dog" class="com.howie.pojo.Dog"/>
<bean id="person" class="com.howie.pojo.Person">
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
<property name="message" value="hello spring"/>
bean>
beans>
4、测试
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("personBean.xml");
Person person = context.getBean("person", Person.class);
System.out.println(person);
person.getCat().shout();
person.getDog().shout();
}
一个人有两个宠物!结果正常输出,环境OK
autowire byName (按名称自动装配)
由于在手动配置xml过程中,常常发生字母缺漏和大小写等错误,而无法对其进行检查,使得开发效率降低。
采用自动装配将避免这些错误,并且使配置简单化。
测试:
1、修改bean配置,增加一个属性 autowire=“byName”
<bean id="person" class="com.howie.pojo.Person" autowire="byName">
<property name="dog" ref="dog"/>
<property name="message" value="hello spring"/>
bean>
2、再次测试,结果依旧成功输出!
3、我们将 cat 的bean id修改为 catXXX
4、再次测试, 执行时报空指针java.lang.NullPointerException。因为按byName规则找不对应set方法,真正的setCat就没执行,对象就没有初始化,所以调用时就会报空指针错误。
小结:
当一个bean节点带有 autowire byName的属性时。
① 将查找其类中所有的set方法名,例如setCat,获得将set去掉并且首字母小写的字符串,即cat。
② 去spring容器中寻找是否有此字符串名称id的对象。
③ 如果有,就取出注入;如果没有,就报空指针异常。
使用autowire byType首先需要保证:同一类型的对象,在spring容器中唯一。如果不唯一,会报不唯一的异常。
NoUniqueBeanDefinitionException
<bean id="person" class="com.howie.pojo.Person" autowire="byType">
<property name="dog" ref="dog"/>
<property name="message" value="hello spring"/>
bean>
1、测试,可以正常输出!ok
2、我们再来注册一个类型为 cat 的 bean。此时cat类型的bean在容器中已不唯一。
3、这时,我们发现,xml 配置文件已爆红!所以说使用byType自动装配时,对象属性的要注入的bean必须唯一才能注入成功!
jdk1.5开始支持注解,spring2.5开始全面支持注解。
准备工作:利用注解的方式注入属性。
1、在spring配置文件中引入context文件头
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
2、开启属性注解支持!
<context:annotation-config/>
3、完整xml配置文件实例!
<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的说明 ?
2、如何使用@Autowired ?
3、xml配置文件
<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">
<bean id="cat" class="com.howie.pojo.Cat"/>
<bean id="dog" class="com.howie.pojo.Dog"/>
<bean id="person" class="com.howie.pojo.Person"/>
<context:annotation-config/>
beans>
4、@Autowired的使用
/**
* @description: Person实体类
* @author: laizhenghua
* @date: 2020/11/15 13:36
*/
public class Person {
@Autowired
private Cat cat;
@Autowired
private Dog dog;
@Value("Java天下第一!")
private String message;
...
// 可以没有set方法
}
5、测试
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("personBean.xml");
Person person = context.getBean("person", Person.class);
// System.out.println(person);
person.getCat().shout(); // 喵~
person.getDog().shout(); // 旺~
System.out.println(person.getMessage()); // Java天下第一!
}
扩展:@Autowired(required=false) 说明:false,对象可以为null;true,对象必须存对象,不能为null。
//如果允许对象为null,设置required = false,默认为true
@Autowired(required = false)
private Cat cat;
测试实验步骤:
1、配置文件修改内容,保证类型存在对象。且名字不为类的默认名字!
<bean id="cats" class="com.howie.pojo.Cat"/>
<bean id="cat123" class="com.howie.pojo.Cat"/>
<bean id="dogs" class="com.howie.pojo.Dog"/>
<bean id="dog123" class="com.howie.pojo.Dog"/>
2、没有加Qualifier测试,直接报错
3、在属性上添加Qualifier注解
/**
* @description: Person实体类
* @author: laizhenghua
* @date: 2020/11/15 13:36
*/
public class Person {
@Autowired
@Qualifier(value = "cat123") // 指明此属性注入的bean是cat123
private Cat cat;
@Autowired
@Qualifier(value = "dog123") // 指明此属性注入的bean是dog123
private Dog dog;
...
}
测试,成功输出!
说明:此注解并不是Spring的,而是Java的(import javax.annotation.Resource)
实体类:
/**
* @description: Person实体类
* @author: laizhenghua
* @date: 2020/11/15 13:36
*/
public class Person {
//如果允许对象为null,设置required = false,默认为true
@Resource
private Cat cat;
@Resource(name = "dog123")
private Dog dog;
private String message;
...
}
xml配置文件
<bean id="cat" class="com.howie.pojo.Cat"/>
<bean id="cat123" class="com.howie.pojo.Cat"/>
<bean id="dog123" class="com.howie.pojo.Dog"/>
<bean id="dogs" class="com.howie.pojo.Dog"/>
测试:结果OK
同:
异:
说明:前面我们已经知道,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>
我们之前都是使用 bean 的标签进行bean注入,但是实际开发中,我们一般都会使用注解!
1、配置扫描哪些包下的注解
<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/>
<context:component-scan base-package="com.howie.pojo"/>
beans>
2、在指定包下编写类,增加注解
/**
* @description: User实体类
* @author: laizhenghua
* @date: 2020/11/14 18:51
*/
@Component // 此注解等价于
public class User {
@Value("alex")
private String name;
private Integer age;
...
}
3、测试
@Test
public void test(){
String path = "applicationContext.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(path);
User user = context.getBean(User.class);
System.out.println(user.getName()); // alex
}
使用注解注入属性
1、可以不用提供set方法,直接在属性名上添加@value(“值”)
/**
* @description: User实体类
* @author: laizhenghua
* @date: 2020/11/14 18:51
*/
@Component // 此注解等价于
public class User {
@Value("alex")
private String name;
@Value(value = "21")
private Integer age;
...
}
2、如果提供了set方法,在set方法上添加@value(“值”)即可;
3、测试
@Test
public void test(){
String path = "applicationContext.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(path);
User user = context.getBean(User.class);
System.out.println(user); // User{name='alex', age=21}
}
我们这些注解,就是替代了在配置文件当中配置步骤而已!更加的方便快捷!
1、@Component三个衍生注解
为了更好的进行分层,Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。
@Controller:web层
@Service:service层
@Repository:dao层
2、写上这些注解,就相当于将这个类交给Spring管理装配了!
3、使用这些注解时,我们还需要在配置文件中,修改扫描包的范围!
<context:component-scan base-package="com.howie"/>
@Autowired、@Qualifire等前面已经讲过了
@scope(value = “singleton”)
singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收。
/**
* @description: User实体类
* @author: laizhenghua
* @date: 2020/11/14 18:51
*/
@Component // 此注解等价于
@Scope(value = "singleton") // 设置作用域类别,这里设置为单例
public class User {
@Value("alex")
private String name;
...
}
XML与注解比较
XML可以适用任何场景 ,结构清晰,维护方便
注解不是自己提供的类使用不了,开发简单方便
xml与注解整合开发 :推荐最佳实践
xml管理Bean
注解完成属性注入
使用过程中, 可以不用扫描,扫描是为了类上的注解
<context:annotation-config/>
作用:
进行注解驱动注册,从而使注解生效
用于激活那些已经在spring容器里注册过的bean上面的注解,也就是显示的向Spring注册
如果不扫描包,就需要手动配置bean
如果不加注解驱动,则注入的值为null!
现在我们要舍弃Spring的xml配置,全权交给Java进行配置,实现xml零配置。
JavaConfig 原来是 Spring 的一个子项目,它通过 Java 类的方式提供 Bean 的定义信息,在 Spring4 的版本, JavaConfig 已正式成为 Spring4 的核心功能 。
@Configuration和@Bean是实现完全注解的关键!
1、新建实体类User
/**
* @description: User实体类
* @author: laizhenghua
* @date: 2020/11/15 19:29
*/
@Component // 将这个类标注为Spring的一个组件,放到容器中!
public class User {
public String name = "alex"; // 显示赋值
}
2、新建一个config配置包,编写MyConfig配置类,替代 xml 配置文件
/**
* @description: 配置文件类
* @author: laizhenghua
* @date: 2020/11/15 19:35
*/
/*@Configuration这个也会Spring容器托管,注册到容器中,因为他本来就是一个@Component*/
@Configuration // 代表这是一个配置类
@ComponentScan(value = "com.howie.pojo") // 配置扫描包,可写可不写
public class MyConfig {
// @Bean只写在方法上,返回的是一个对象,但是一般不获取已经在容器中存在的对象。
// 通过方法注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id!
@Bean
public User user(){
return new User();
}
}
3、测试
@Test
public void test(){
// 加载配置类,通过AnnotationConfig上下文来获取容器,通过配置类的cLass对象加载
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
User user = context.getBean("user", User.class);
// 打印user的name属性
System.out.println(user.name); // alex
}
小结:我们发现上面的例子,我们完全没有使用xml配置文件,而是通过@Configuration和@Bean注解创建配置类,实现了bean的注入与获取,这就是完全注解实现!
还有一个问题,我们想导入其他配置类该如何做呢?
1、我们再编写一个配置类!
@Configuration //代表这是一个配置类
public class MyConfig2 {
}
2、在之前的配置类中我们来选择导入这个配置类
@Configuration
@Import(MyConfig2.class) //导入合并其他配置类,类似于配置文件中的 inculde 标签
public class MyConfig {
@Bean
public User user(){
return new User();
}
}
关于这种Java类的配置方式,我们在之后的SpringBoot 和 SpringCloud中还会大量看到,我们需要知道这些注解的作用即可!
设计模式:是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。
设计模免去我们自己再思考和摸索。就像是经典的棋谱,不同的棋局,我们用不同的棋谱。”套路”
在之前的学习中,不知道小伙伴们有没有学习过代理模式呢?代理模式在Java开发中扮演很重要的角色!
代理设计就是为其他对象提供一种代理赖控制对这个对象的访问!
AOP的底层机制就是动态代理!在认识AOP之前我们先要了解代理模式!
代理模式又分为:
静态代理角色分析:
代码实现:
Rent.java 即抽象角色。
/**
* @description: // 抽象角色:租房接口
* @author: laizhenghua
* @date: 2020/11/15 22:11
*/
public interface Rent {
void rent(); // 出租房屋
}
Host.java 即真实角色(被代理的角色)
/**
* @description: 房东
* @author: laizhenghua
* @date: 2020/11/15 22:12
*/
public class Host implements Rent{
public void rent() {
System.out.println("房东要出租房子!");
}
}
Proxy.java 即代理角色,可以理解为中间商(增加附属操作,如打扫房间签合同)
/**
* @description: 代理类,中间商
* @author: laizhenghua
* @date: 2020/11/15 22:17
*/
public class Proxy implements Rent{
private Host host;
public Proxy(Host host){
this.host = host;
}
public void rent(){
clear();
host.rent();
signContract();
}
/*
* @description: 增加的附属操作,打扫房间
* @author: laizhenghua
* @date: 2020/11/16 12:30
* @param:
* @return: void
*/
public void clear(){
System.out.println("我是中间商,我已经帮你打扫好房子了,你要不要出租房子!");
}
/*
* @description: 签合同
* @author: laizhenghua
* @date: 2020/11/16 9:25
* @param:
* @return: void
*/
public void signContract(){
System.out.println("签合同!");
}
}
Client.java 即客户
/**
* @description: 客户类,一般客户都会去找代理!
* @author: laizhenghua
* @date: 2020/11/15 22:15
*/
public class Client {
public static void main(String[] args) {
// 房东要租房子
Host host = new Host();
// 中介(代理),帮房东出租房子,但是代理会有一些附属操作,比如打扫房间(clear)
Proxy proxy = new Proxy(host);
// 你不用面对房东,直接找中介
proxy.rent();
}
}
1、分析:
在这个过程中,你直接接触的就是中介,就如同现实生活中的样子,你看不到房东,但是你依旧租到了房东的房子通过代理,这就是所谓的代理模式,程序源自于生活,所以学编程的人,一般能够更加抽象的看待生活中发生的事情。
2、静态代理的好处:
3、缺点:
OK,到了现在代理模式大家应该都没有什么问题了,重点大家需要理解其中的思想:
我们在不改变原来的代码的情况下,实现了对原有功能的增强,这是AOP中最核心的思想
4、聊聊AOP:纵向开发,横向开发
我们已知道静态代理的好处与缺点,那么我们现在想要静态代理的好处,又不想要静态代理的缺点,有没有更好的解决方案呢?答案就是动态代理!
这里我们主要掌握JDK的动态代理!,JDK动态代理有两个核心类分别是 InvocationHandler(调用处理程序) 和 Proxy(代理类) 。
关于动态代理的学习不是很友好!这里建议反复观看视频自己学习。
视频讲解地址:https://www.bilibili.com/video/BV1WE411d7Dv?p=19
动态代理的好处:
上面我们了解l代理模式,这是AOP的基础,一定要先搞懂它!接下来我们就要进入正题,进行AOP的学习!
AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
提供声明式事务,允许用户自定义切面
以下名词需要了解下:
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:
即 Aop 在 不改变原有代码的情况下 , 去增加新的功能。
使用AOP织入,需要导入一个依赖包!
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.5version>
<scope>runtimescope>
dependency>
首先编写我们的业务接口和实现类
/**
* @description: userService接口
* @author: laizhenghua
* @date: 2020/11/16 18:25
*/
public interface UserService {
int add();
int delete();
int update();
void query();
}
实现类
/**
* @description: UserService接口实现类
* @author: laizhenghua
* @date: 2020/11/16 18:28
*/
public class UserServiceImpl implements UserService {
public int add() {
System.out.println("增加了一个用户");
return 0;
}
public int delete() {
System.out.println("删除了一个用户");
return 0;
}
public int update() {
System.out.println("更新了一个用户");
return 0;
}
public void query() {
System.out.println("查询了一个用户");
}
}
然后去写我们的增强类 , 我们编写两个 , 一个前置增强 一个后置增强。
1、前置增强类
/**
* @description: 增强类,前置通知
* @author: laizhenghua
* @date: 2020/11/16 18:32
*/
public class BeforeLog implements MethodBeforeAdvice {
/*
method: 要执行目标对象的方法
objects: 参数
o: 目标对象
*/
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(o.getClass().getName() + "的" + method.getName() + "被执行了!");
}
}
2、后置增强类
/**
* @description: 增强类,后置通知
* @author: laizhenghua
* @date: 2020/11/16 18:52
*/
public class AfterLog implements AfterReturningAdvice {
/*
returnValue: 返回值
method被调用的方法
args 被调用的方法的对象的参数
target 被调用的目标对象
*/
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName() + "类,执行了方法" + method.getName() + "返回了" + returnValue);
}
}
3、最后去spring的文件中注册,并实现aop切入实现 ,注意导入约束
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.howie.service.impl.UserServiceImpl"/>
<bean id="beforeLog" class="com.howie.log.BeforeLog"/>
<bean id="afterLog" class="com.howie.log.AfterLog"/>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.howie.service.impl.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
aop:config>
beans>
4、测试方法
@Test
public void test2(){
String path = "applicationContext.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(path);
// 注意点:动态代理代理的是接口
UserService userService = (UserService) context.getBean("userService");
userService.add();
}
5、结果
AOP的重要性:很重要一定要理解其中的思路,主要是思想的理解这一块。
Spring的AOP就是将公共的业务 (日志,安全等) 和领域业务结合起来,当执行领域业务时,将会把公共业务加进来实现公共业务的重复利用,领域业务更纯粹,技术员专注领域业务,其本质还是动态代理。
6、切入点表达式(expression)总结
(1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强
(2)语法结构:execution([权限修饰符] [返回类型] [类全路径] [方法名称](参数列表))
execution(* com.atguigu.dao.BookDao.add(..))
execution(* com.atguigu.dao.BookDao.* (..))
execution(* com.atguigu.dao.*.* (..))
目标业务类不变依旧是userServiceImpl
1、第一步 : 写我们自己的一个切入类
/**
* @description:
* @author: laizhenghua
* @date: 2020/11/16 20:24
*/
public class MyPointcut {
public void before(){
System.out.println("===方法执行前===");
}
public void after(){
System.out.println("===方法执行后===");
}
}
2、修改配置文件
<bean id="myPointcut" class="com.howie.mydi.MyPointcut"/>
<aop:config>
<aop:aspect ref="myPointcut">
<aop:pointcut id="point" expression="execution(* com.howie.service.impl.UserServiceImpl.*(..))"/>
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
aop:aspect>
aop:config>
3、测试
@Test
public void test2(){
String path = "applicationContext.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(path);
// 注意点:动态代理代理的是接口
UserService userService = (UserService) context.getBean("userService");
userService.add();
}
结果
相关注解介绍:
@Aspect:作用是把当前类标识为一个切面供容器读取
@Pointcut:Pointcut是植入Advice的触发条件。每个Pointcut的定义包括2部分,一是表达式,二是方法签名。方法签名必须是 public及void型。可以将Pointcut中的方法看作是一个被Advice引用的助记符,因为表达式不直观,因此我们可以通过方法签名的方式为 此表达式命名。因此Pointcut中的方法只需要方法签名,而不需要在方法体内编写实际代码。
@Around:环绕增强,相当于MethodInterceptor
@AfterReturning:后置增强,相当于AfterReturningAdvice,方法正常退出时执行
@Before:标识一个前置增强方法,相当于BeforeAdvice的功能,相似功能的还有
@AfterThrowing:异常抛出增强,相当于ThrowsAdvice
第一步:编写一个注解实现的增强类
@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());
//执行目标方法proceed
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动态代理。
1、junit
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
2、MyBatis
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.3version>
dependency>
3、mysql数据库
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.21version>
dependency>
4、spring相关
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.2.10.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.2.10.RELEASEversion>
dependency>
5、aspectJ AOP 织入器
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.5version>
dependency>
6、mybatis-spring整合包(重点)
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>2.0.2version>
dependency>
7、配置Maven静态资源过滤问题!
<build>
<resources>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>truefiltering>
resource>
resources>
build>
整合之前我们还需要了解一些MyBatis-Spring的知识!
MyBatis-Spring官方地址:http://mybatis.org/spring/zh/index.html
1、编写Spring配置文件 applicationContext.xml 的头文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
2、配置数据源替换mybaits的数据源
<context:property-placeholder location="classpath:db.properties"/>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${prop.driver}"/>
<property name="url" value="${prop.url}"/>
<property name="username" value="${prop.username}"/>
<property name="password" value="${prop.password}"/>
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/howie/dao/*.xml"/>
bean>
4、注册sqlSessionTemplate,关联sqlSessionFactory
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
bean>
5、增加Dao接口的实现类;私有化sqlSessionTemplate
/**
* @description: UserDao接口实现类
* @author: laizhenghua
* @date: 2020/11/17 13:03
*/
public class UserDaoImpl implements UserDao {
// 我们所有的操作都要使用sqlSession来完成,sqlSession不用我们自己创建了,Spring来管理
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
public List<User> getUserList() {
UserDao dao = sqlSession.getMapper(UserDao.class);
return dao.getUserList();
}
}
6、注册bean实现
<bean id="userDao" class="com.howie.dao.impl.UserDaoImpl">
<property name="sqlSession" ref="sqlSession"/>
bean>
7、测试
@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = context.getBean("userDao", UserDaoImpl.class);
List<User> userList = userDao.getUserList();
for (User user : userList){
System.out.println(user);
}
}
输出结果
结果成功输出!现在我们的Mybatis配置文件的状态!发现都可以被Spring整合!
<configuration>
<typeAliases>
<package name="com.howie.pojo"/>
typeAliases>
configuration>
mybatis-spring1.2.3版以上的才有这个
dao继承Support类 ,直接利用 getSqlSession() 获得 , 然后直接注入SqlSessionFactory 比起方式1 , 不需要管理SqlSessionTemplate , 而且对事务的支持更加友好 ,可跟踪源码查看。
官方地址:http://mybatis.org/spring/zh/sqlsession.html#SqlSessionDaoSupport
1、将我们上面写的UserDaoImpl修改一下
public class UserDaoImpl extends SqlSessionDaoSupport implements UserMapper {
public List<User> getUserList() {
UserDao userDao = getSqlSession().getMapper(UserDao.class);
return userDao.getUserList();
}
}
2、修改bean的配置
<bean id="userDao" class="com.howie.dao.impl.UserDaoImpl">
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
bean>
3、测试
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = context.getBean("userDao", UserDaoImpl.class);
List<User> userList = userDao.getUserList();
for (User user : userList){
System.out.println(user);
}
}
总结 : 整合到spring以后可以完全不要mybatis的配置文件,除了这些方式可以实现整合之外,我们还可以使用注解来实现,这个等我们后面学习SpringBoot的时候还会测试整合!
1、事务:一组逻辑操作单元,使数据从一种状态变换到另一种状态。
2、事务处理(事务操作):保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),那么这些修改就永久地保存下来;要么数据库管理系统将放弃所作的所有修改,整个事务回滚(rollback)到最初状态。-- 事务处理原则
3、为确保数据库中数据的一致性,数据的操纵应当是离散的成组的逻辑单元:当它全部完成时,数据的一致性可以保持,而当这个单元中的一部分操作失败,整个事务应全部视为错误,所有从起始点以后的操作应全部回退到开始状态。
4、事务的ACID(acid)属性
总结下来就是,事务就是要么都成功要么都失败!事务在项目开发过程非常重要,涉及到数据的一致性的问题,不容马虎!
在之前的案例(spring整合MyBatis)中,我们给userDao接口新增两个方法,删除和增加用户。
// 添加一个用户
int addUser(User user);
// 删除一个用户
int deleteUser(Integer id);
// 测试方法,分别要执行添加与删除
void test();
mapper映射文件中,我们故意把 deletes 写错。
<insert id="addUser" parameterType="User">
insert into test.user(id,name,password,perms,email,birthday)
value(#{id},#{name},#{password},#{perms},#{email},#{birthday})
insert>
<delete id="deleteUser" parameterType="int">
deletes from test.user where id = #{id}
delete>
编写接口的实现类
/**
* @description: UserDao接口实现类
* @author: laizhenghua
* @date: 2020/11/17 13:03
*/
public class UserDaoImpl implements UserDao {
// 我们所有的操作都要使用sqlSession来完成,sqlSession不用我们自己创建了,Spring来管理
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
public List<User> getUserList() {
UserDao dao = sqlSession.getMapper(UserDao.class);
return dao.getUserList();
}
// 添加用户
public int addUser(User user) {
UserDao dao = sqlSession.getMapper(UserDao.class);
return dao.addUser(user);
}
// 删除用户
public int deleteUser(Integer id) {
UserDao dao = sqlSession.getMapper(UserDao.class);
return dao.deleteUser(id);
}
// 测试方法
public void test() {
UserDao dao = sqlSession.getMapper(UserDao.class);
// 新建用户
User user = new User();
user.setId(4);
user.setName("Java");
user.setPassword("123");
dao.addUser(user); // 执行添加操作
dao.deleteUser(4); // 执行删除操作
}
}
编写测试方法
@Test
public void test3(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = context.getBean("userDao", UserDaoImpl.class);
userDao.test(); // 执行测试方法
}
报错:sql异常,delete写错了
结果 :新用户添加成功!
没有进行事务的管理;我们想让他们都成功才成功,有一个失败,就都失败,我们就应该需要事务!
以前我们都需要自己手动管理事务,十分麻烦!
但是Spring给我们提供了事务管理,我们只需要配置即可;
官方文档地址:http://mybatis.org/spring/zh/transactions.html
Spring在不同的事务管理API之上定义了一个抽象层,使得开发人员不必了解底层的事务管理API就可以使用Spring的事务管理机制。Spring支持编程式事务管理和声明式的事务管理。
1、编程式事务管理(这里不讲,可查看官方文档自行学习)
2、声明式事务管理(AOP风格)
3、在Spring中配置事务
使用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 的配置文件中创建一个 DataSourceTransactionManager 对象。
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
事务管理器(transactionManager)
配置好事务管理器后我们需要去配置事务的通知
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
tx:attributes>
tx:advice>
spring事务传播特性
事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。spring支持7种事务传播行为:
Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。
假设 ServiveX#methodX() 都工作在事务环境下(即都被 Spring 事务增强了),假设程序中存在如下的调用链:Service1#method1()->Service2#method2()->Service3#method3(),那么这 3 个服务类的 3 个方法通过 Spring 的事务传播机制都工作在同一个事务中。
就好比,我们刚才的几个方法存在调用,所以会被放在一组事务当中!
配置AOP
注意:导入aop的头文件!
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.howie.dao.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
aop:config>
测试:
删掉刚才插入的数据,再次测试!
@Test
public void test3(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = context.getBean("userDao", UserDaoImpl.class);
userDao.test();
}
我们发现,程序虽然报错了,但是数据并没有像刚才那样插入数据库!这就是我们希望看到的,当程序发生异常或产生错误,我们应该让事务回滚!回到最初的状态!
完整applicationContext.xml配置文件包括整合MyBatis和Spring事务管理!
<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: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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:property-placeholder location="classpath:db.properties"/>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${prop.driver}"/>
<property name="url" value="${prop.url}"/>
<property name="username" value="${prop.username}"/>
<property name="password" value="${prop.password}"/>
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/howie/dao/*.xml"/>
bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
bean>
<bean id="userDao" class="com.howie.dao.impl.UserDaoImpl">
<property name="sqlSession" ref="sqlSession"/>
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="*" propagation="REQUIRED"/>
tx:attributes>
tx:advice>
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.howie.dao.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
aop:config>
beans>
为什么需要事务?