Spring
文章目录
1、Spring 概述
Spring 是什么
- Spring 是分层的 Java SE/EE 应用 full-stack 轻量级开源框架,以 IOC(Inverse Of Control:反转控制)和 AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层 SpringMVC 和持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的 Java EE 企业应用开源框架
Spring 的优势
- 方便解耦,简化开发
通过 Spring 提供的 IOC 容器,可以将对象间的依赖关系交由 Spring 进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。 - AOP 编程的支持
通过 Spring 的 AOP 功能,方便进行面向切面的编程,许多不容易用传统 OOP 实现的功能可以通过 AOP 轻松应付 - 声明式事务的支持
可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量 - 方便程序的测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。 - 方便集成各种优秀框架
Spring 可以降低各种框架的使用难度,提供了对各种优秀框架( Struts、 Hibernate、 Hessian、 Quartz等)的直接支持。 - 降低 JavaEE API 的使用难度
Spring 对 JavaEE API(如 JDBC、 JavaMail、远程调用等)进行了薄薄的封装层,使这些 API 的使用难度大为降低
2、IOC(控制反转)
程序的耦合和解耦
耦合和解耦概念
- 耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差( 降低耦合性,可以提高其独立性)。 耦合性存在于各个领域,而非软件设计中独有的,但是我们只讨论软件工程中的耦合。
- 在软件工程中, 耦合指的就是就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。因此对象的设计应使类和构件之间的耦合最小。 软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。 划分模块的一个准则就是高内聚低耦合
- 分类:
- (1) 内容耦合。当一个模块直接修改或操作另一个模块的数据时,或一个模块不通过正常入口而转入另一个模块时,这样的耦合被称为内容耦合。内容耦合是最高程度的耦合,应该避免使用之。
- (2) 公共耦合。两个或两个以上的模块共同引用一个全局数据项,这种耦合被称为公共耦合。在具有大量公共耦合的结构中,确定究竟是哪个模块给全局变量赋了一个特定的值是十分困难的。
- (3) 外部耦合 。一组模块都访问同一全局简单变量而不是同一全局数据结构,而且不是通过参数表传递该全局变量的信息,则称之为外部耦合。
- (4) 控制耦合 。一个模块通过接口向另一个模块传递一个控制信号,接受信号的模块根据信号值而进行适当的动作,这种耦合被称为控制耦合。
- (5) 标记耦合 。若一个模块 A 通过接口向两个模块 B 和 C 传递一个公共参数,那么称模块 B 和 C 之间存在一个标记耦合。
- (6) 数据耦合。模块之间通过参数来传递数据,那么被称为数据耦合。数据耦合是最低的一种耦合形式,系统中一般都存在这种类型的耦合,因为为了完成一些有意义的功能,往往需要将某些模块的输出数据作为另一些模块的输入数据。
- (7) 非直接耦合 。两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现的
- 总结
耦合是影响软件复杂程度和设计质量的一个重要因素,在设计上我们应采用以下原则:如果模块间必须存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的范围,尽量避免使用内容耦合 - 内聚与耦合
- 内聚标志一个模块内各个元素彼此结合的紧密程度,它是信息隐蔽和局部化概念的自然扩展。 内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事。它描述的是模块内的功能联系。耦合是软件结构中各模块之间相互连接的一种度量,耦合强弱取决于模块间接口的复杂程度、进入或访问一个模块的点以及通过接口的数据。 程序讲究的是低耦合,高内聚。就是同一个模块内的各个元素之间要高度紧密,但是各个模块之间的相互依存度却要不那么紧密。
- 内聚和耦合是密切相关的,同其他模块存在高耦合的模块意味着低内聚,而高内聚的模块意味着该模块同其他
模块之间是低耦合。在进行软件设计时,应力争做到高内聚,低耦合
工厂模式解耦
-
在实际开发中我们可以把三层的对象都使用配置文件配置起来,当启动服务器应用加载的时候, 让一个类中的方法通过读取配置文件,把这些对象创建出来并存起来。在接下来的使用的时候,直接拿过来用就好了。那么,这个读取配置文件, 创建和获取三层对象的类就是工厂
-
实例
properties配置文件
accountService=com.itfzk.service.impl.AccouontServiceImpl accountDao=com.itfzk.dao.impl.AccountDaoImpl
Bean 对象的工厂模式类
public class BeanFuctory { //定义一个 Properties 对象 private static Properties properties; //定义一个 Map,用于存放我们创建的对象,我们称之为容器 private static Map<String, Object> beans; //使用静态代码块对 Properties 对象赋值 static { try { //实例化对象 properties = new Properties(); //获取 Properties 的流对象 InputStream inputStream = BeanFuctory.class.getClassLoader().getResourceAsStream("bean.properties"); properties.load(inputStream); //实例化容器对象 beans = new HashMap<String, Object>(); //取出配置文件中所有的 key Enumeration<Object> keys = properties.keys(); //遍历枚举 while (keys.hasMoreElements()){ //取出每个 key String key = keys.nextElement().toString(); //通过 key 获取 value String beanPath = properties.getProperty(key); //利用反射机制实例化对象 Object value = Class.forName(beanPath).newInstance(); //将 key 和 value 存储在 Map 中 beans.put(key, value); } } catch (Exception e) { e.printStackTrace(); } } /** * 根据 Bean 的名称获取 bean 对象 * @param beanName * @return */ public static Object getBean(String beanName){ return beans.get(beanName); } }
IOC 的概念和作用
IOC 控制反转
- 控制反转(Inversion of Control,英文缩写为IoC)把创建对象的权利交给框架,是框架的重要特征,并非面向对象编程的专用术语。它包括依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup)。
- IoC 的作用 :削减计算机程序的耦合(解除我们代码中的依赖关系)
基于 XML 的配置实现 IOC
步骤(四步)
-
第一步:导入必备的 jar 包到工程中
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.0.RELEASE</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-core</artifactId> <version>1.3</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.27</version> </dependency> <dependency> <groupId>commons-dbutils</groupId> <artifactId>commons-dbutils</artifactId> <version>1.6</version> </dependency> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.1_3</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.5</version> </dependency> </dependencies>
-
第二步:在 resources 路径下创建一个任意名称的 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"> </beans>
-
第三步: 在刚创建的 xml 文件中,配置 spring 管理资源,在配置文件中配置 service 和 dao
<!-- bean 标签:用于配置让 spring 创建对象,并且存入 ioc 容器之中 id 属性:对象的唯一标识。 class 属性:指定要创建对象的全限定类名 --> <!-- 配置 service --> <bean id="accountService" class="cn.itfzk.spring.service.impl.AccouontServiceImpl"></bean> <!-- 配置 dao --> <bean id="accountDao" class="cn.itfzk.spring.dao.impl.AccountDaoImpl"></bean>
-
第四步:测试配置是否成功
/** * 获取 Spring 的 IOC 核心容器,并根据 id 获取对象 * * ApplicationContext 三种常用实现类 * ClassPathXmlApplicationContext :加载类路径下的配置文件,要求配置文件必须在类路径下,不在的话,加载不了(更常用) * FileSystemXmlApplicationContext :加载磁盘任意路径下的配置文件(必须有访问权限) * AnnotationConfigApplicationContext :读取注解创建容器 * @param args */ public static void main(String[] args) { //获取核心容器对象 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml"); //根据 id 获取 Bean 对象 IAccountService as = (IAccountService) applicationContext.getBean("accountService"); IAccountDao ad = (IAccountDao) applicationContext.getBean("accountDao"); System.out.println(as); System.out.println(ad); }
IOC 中 bean 标签和管理对象
bean 标签
-
作用:
- 用于配置对象让 spring 来创建的。
- 默认情况下它调用的是类中的无参构造函数。如果没有无参构造函数则不能创建成功。
-
属性:
- id: 给对象在容器中提供一个唯一标识。用于获取对象。
- class: 指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数。
- scope: 指定对象的作用范围。
- singleton:单例的(默认值)(常用)
- prototype:多例的(常用)
- request:作用于 Web 应用的请求范围
- session:作用于 Web 应用的会话范围
- global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,他就是 session
- init-method: 指定类中的初始化方法名称。
- destroy-method: 指定类中销毁方法名称。
-
配置 bean 标签的属性(xml配置文件)
<bean id="accountService" class="cn.itfzk.spring02.service.impl.AccouontServiceImpl" scope="singleton" init-method="init" destroy-method="destroy"></bean>
bean 的作用范围和生命周期
-
单例对象: scope=“singleton”
-
一个应用只有一个对象的实例。它的作用范围就是整个引用。
-
生命周期:
-
init-method: 指定类中的初始化方法名称
-
destroy-method: 指定类中销毁方法名称
对象出生:当应用加载,创建容器时,对象就被创建了。
对象活着:只要容器在,对象一直活着。
对象死亡:当应用卸载,销毁容器时,对象就被销毁了。 -
-
-
多例对象: scope=“prototype”
-
每次访问对象时,都会重新创建对象实例。
-
生命周期:
-
init-method: 指定类中的初始化方法名称
-
destroy-method: 指定类中销毁方法名称
对象出生:当使用对象时,创建新的对象实例。
对象活着:只要对象在使用中,就一直活着。
对象死亡:当对象长时间不用时,被 java 的垃圾回收器回收了 -
-
实例化 Bean 的三种方式
-
第一种方式:使用默认构造函数创建
在 Spring 的配置文件中使用 Bean 对象,配以 id 和 class 属性之后,且没有其他属性和标签时,采用的就是默认构造函数创建 Bean 对象,此时如果类中没有默认构造函数,则对象无法创建。<bean id="accountService" class="cn.itfzk.spring01.service.impl.AccouontServiceImpl"></bean>
-
第二种方式:使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入 Spring 容器)
服务层实现类public class AccouontServiceImpl { public AccouontServiceImpl(String str){ System.out.println("创建 AccouontServiceImpl 对象"); } public void saveAccount() { System.out.println("service 中的 saceAccount 方法执行力。。。"); } }
模拟一个工厂类(将这个类看成是 jar 包中的类)
public class InstanceFactory { public IAccountService getAccountService(){ return new AccouontServiceImpl("naem"); } }
xml配置文件
<bean id="instanceFactory" class="cn.itfzk.spring01.factory.InstanceFactory" ></bean> <bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService" ></bean>
-
第三种方法:使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入 Spring 容器)
服务层实现类
public class AccouontServiceImpl { public AccouontServiceImpl(String str){ System.out.println("创建 AccouontServiceImpl 对象"); } public void saveAccount() { System.out.println("service 中的 saceAccount 方法执行力。。。"); } }
模拟一个工厂类(将这个类看成是 jar 包中的类)
public class StaticFactory { //静态方法 public static IAccountService getAccountService(){ return new AccouontServiceImpl("naem"); } }
xml配置文件
<bean id="accountService" class="cn.itfzk.spring01.factory.StaticFactory" factory-method="getAccountService"></bean>
Spring 的依赖注入
依赖注入的概念
- 依赖注入: Dependency Injection。 它是 spring 框架核心 ioc 的具体实现。
- 注入的数据:三类
- 第一类:基本类型和 String
- 第二类:其他 Bean 类型(在配置文件中或者配置过的 Bean)
- 第三类:复杂类型/集合类型
- 注入的方式:三种
- 第一种:使用构造函数提供
- 第二种:使用 set 方法提供
- 第三种:使用注解提供
构造函数注入
-
使用的标签:constructor-arg
-
标签的位置:bean 标签的内部
-
标签的属性:
-
name:用于指定给构造函数中指定名称的参数赋值(常用的)
-
type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
-
index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值,索引的位置从 0 开始
-
以上三个用于指定给构造函数中哪个参数赋值
-
value:用于提供基本类型和 String 类型的数据
-
ref:用于指定其他的 bean 类型数据,它指的就是在 Spring 的 IOC 核心容器中出现过的 bean
-
-
对象优势:在获取 Bean 对象时,注入数据是必须的操作,否则对象无法创建成功
-
弊端:改变了 bean 对象的实例化方式,使我们在创建对象时,如果用不上这些数据,也必须提供
//构造函数注入类
public class AccouontServiceImpl {
private String name;
private int age;
private Date birthday;
public AccouontServiceImpl(String name, int age, Date birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
}
}
<!-- xml文件配置 -->
<bean id="accountService" class="cn.itfzk.spring03.service.impl.AccouontServiceImpl" >
<constructor-arg name="name" value="fzk" ></constructor-arg>
<constructor-arg name="age" value="20"></constructor-arg>
<constructor-arg name="birthday" ref="now" ></constructor-arg> <!-- ref标签的使用 -->
</bean>
<!--匹配一个日期对象-->
<bean id="now" class="java.util.Date" ></bean>
set 方法的注入:(更常用)
- 使用的标签:property
- 标签的位置:bean 标签的内部
- 标签的属性:
- name:用于指定注入时所调用的 set 方法名称
- value:用于提供基本类型和 String 类型的数据
- ref:用于指定其他的 bean 类型数据,它指的就是在 Spring 的 IOC 核心容器中出现过的 bean 对象
- 优势:创建对象时没有明确的限制,可以直接使用默认构造函数
- 弊端:如果有某个成员必须有值,则获取对象有可能 set 方法没有执行
//set方法的注入类
public class AccouontServiceImpl01 {
private String name;
private int age;
private Date birthday;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
<!-- xml文件配置 -->
<bean id="accountService01" class="cn.itfzk.spring03.service.impl.AccouontServiceImpl01" >
<property name="name" value="fzk" ></property>
<property name="age" value="20" ></property>
<property name="birthday" ref="now" ></property>
</bean>
<!--匹配一个日期对象-->
<bean id="now" class="java.util.Date" ></bean>
复杂类型的注入/集合类型的注入
//复杂类型的注入/集合类型的注入
public class AccouontServiceImpl02 implements IAccountService {
private Object[] myStrs;
private List<Object> myList;
private Set<Object> mySet;
private Map<Object, Object> myMap;
private Properties myPros;
public void setMyStrs(Object[] myStrs) {
this.myStrs = myStrs;
}
public void setMyList(List<Object> myList) {
this.myList = myList;
}
public void setMySet(Set<Object> mySet) {
this.mySet = mySet;
}
public void setMyMap(Map<Object, Object> myMap) {
this.myMap = myMap;
}
public void setMyPros(Properties myPros) {
this.myPros = myPros;
}
}
<!-- xml文件配置 -->
<bean id="accountService02" class="cn.itfzk.spring03.service.impl.AccouontServiceImpl02" >
<!--数组-->
<property name="myStrs" >
<array>
<value>fff</value>
<value>zzz</value>
</array>
</property>
<!--List-->
<property name="myList" >
<list>
<value>fff</value>
<value>zzz</value>
</list>
</property>
<!--Set-->
<property name="mySet" >
<set>
<value>fff</value>
<value>zzz</value>
</set>
</property>
<!--Map-->
<property name="myMap" >
<map>
<entry key="fff" value="fff"></entry>
<entry key="zzz" value="zzz"></entry>
</map>
</property>
<!--Properties-->
<property name="myPros" >
<props>
<prop key="fff">fff</prop>
<prop key="zzz">zzz</prop>
</props>
</property>
</bean>
使用 spring 的 IoC 的实现账户的CRUD (增删改查)
步骤
- 创建数据库
- 编写Javabean类
- 业务层接口及业务层实现类
- 持久层接口及持久层实现类
- xml 配置文件
- 测试类
创建数据库
CREATE TABLE `caogao` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=latin1;
Javabean类
public class Account implements Serializable {
private int id;
private String username;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
业务层接口及业务层实现类
/**
* 业务层接口
*/
public interface IAccountService {
//查询所有
public List<Account> findAll();
//根据 id 查询信息
public Account findById(int id);
//保存信息
public void save(Account account);
//修改信息
public void update(Account account);
//删除信息
public void delete(int id);
}
/**
* 业务层实现类
*/
public class AccountServiceImpl implements IAccountService {
private IAccountDao accountDao;
public void setAccountDao(IAccountDao accountDao) {
this.accountDao = accountDao;
}
//查询所有
public List<Account> findAll() {
return accountDao.findAll();
}
//根据 id 查询信息
public Account findById(int id) {
return accountDao.findById(id);
}
//保存信息
public void save(Account account) {
accountDao.save(account);
}
//保存信息
public void update(Account account) {
accountDao.update(account);
}
//删除信息
public void delete(int id) {
accountDao.delete(id);
}
}
持久层接口及持久层实现类
/**
* 持久层接口
*/
public interface IAccountDao {
//查询所有
public List<Account> findAll();
//根据 id 查询信息
public Account findById(int id);
//保存信息
public void save(Account account);
//修改信息
public void update(Account account);
//删除信息
public void delete(int id);
}
/**
* 持久层实现类
*/
public class AccountDaoImpl implements IAccountDao {
private QueryRunner queryRunner;
public void setQueryRunner(QueryRunner queryRunner) {
this.queryRunner = queryRunner;
}
//查询所有
public List<Account> findAll() {
List<Account> accounts = null;
try {
String sql = "select * from caogao";
accounts = queryRunner.query(sql, new BeanListHandler<Account>(Account.class));
} catch (SQLException e) {
e.printStackTrace();
}
return accounts;
}
//按照id查询
public Account findById(int id) {
Account account = null;
try {
String sql = "select * from caogao where id=?";
account = queryRunner.query(sql, new BeanHandler<Account>(Account.class), id);
} catch (SQLException e) {
e.printStackTrace();
}
return account;
}
//保存信息
public void save(Account account) {
try {
String sql = "insert into caogao(username, password) values(?,?)";
queryRunner.update(sql, account.getUsername(), account.getPassword());
} catch (SQLException e) {
e.printStackTrace();
}
}
//修改信息
public void update(Account account) {
try {
String sql = "update caogao set username=?, password=? where id=?";
queryRunner.update(sql, account.getUsername(), account.getPassword(), account.getId());
} catch (SQLException e) {
e.printStackTrace();
}
}
//删除信息
public void delete(int id) {
try {
String sql = "delete from caogao where id=?";
queryRunner.update(sql, id);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
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">
<!-- 配置AccountServiceImpl -->
<bean id="accountService" class="net.memory.Spring01.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置AccountDaoImpl -->
<bean id="accountDao" class="net.memory.Spring01.dao.impl.AccountDaoImpl">
<property name="queryRunner" ref="queryRunner"></property>
</bean>
<!-- 配置QueryRunner -->
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
<constructor-arg name="ds" ref="dataSource"></constructor-arg>
</bean>
<!-- 配置ComboPooledDataSource -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/fzk"></property>
<property name="user" value="root"></property>
<property name="password" value="123456"></property>
</bean>
</beans>
测试类
public class accountTest {
private ApplicationContext applicationContext = new ClassPathXmlApplicationContext("testBean.xml");
private IAccountService accountService = (IAccountService) applicationContext.getBean("accountService");
//查询所有测试
@Test
public void findAllTest(){
List<Account> accounts = accountService.findAll();
for(Account account : accounts){
System.out.println(account);
}
}
//按照id查询测试
@Test
public void findByIdTest(){
Account account = accountService.findById(2);
System.out.println(account);
}
//保存信息测试
@Test
public void saveTest(){
Account account = new Account();
account.setUsername("123");
account.setPassword("123");
accountService.save(account);
}
//修改信息测试
@Test
public void update(){
Account account = new Account(8, "psq", "123");
accountService.update(account);
}
//删除信息测试
@Test
public void deleteTest(){
accountService.delete(8);
}
}
基于注解的配置 实现 IOC
步骤(四步)
-
第一步:导入必备的 jar 包到工程中
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.0.RELEASE</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-core</artifactId> <version>1.3</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.27</version> </dependency> <dependency> <groupId>commons-dbutils</groupId> <artifactId>commons-dbutils</artifactId> <version>1.6</version> </dependency> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.1_3</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.5</version> </dependency> </dependencies>
-
第二步:使用@Component 注解配置管理的资源
@Component("accountService") public class AccountServiceImpl implements IAccountService { private IAccountDao accountDao; public void setAccountDao(IAccountDao accountDao) { this.accountDao = accountDao; } }
-
第三步: 创建 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: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"> <!--告知 Spring 在创建容器时要扫描的包 --> <context:component-scan base-package="net.memory"></context:component-scan> </beans>
-
第四步:测试配置是否成功
public class Client { public static void main(String[] args) { ApplicationContext application = new ClassPathXmlApplicationContext("testBean.xml"); IAccountService accountService = (IAccountService) application.getBean("accountService"); System.out.println(accountService); } }
常用注解
用于创建对象
- 相当于 xml: <bean id="" class="">
- @Component
- 作用:用于把当前类对象存入 Spring 容器中
- 属性:
- value:用于指定 Bean 的 id,当我们不写时,他的默认值是当前类名,且首字母改小写
- @Controller @Service @Repository
- 作用和属性与 @Component 是一模一样的,他们三个是 Spring 框架为我们提供明确的三层架构使用的注解,使我们的三层架构对象更加清晰
- @Controller:一般用在表现层
- @Service:一般用在业务层
- @Repository:一般用在持久层
用于改变作用范围
- 相当于xml: <bean id="" class="" scope="">
- @Scope:
- 作用:用于指定 bean 的作用范围
- 属性:
- value:指定范围的取值。常用取值:singleton(单例,默认) prototype(多例)
和生命周期相关
- 相当于xml: <bean id="" class="" init-method="" destroy-method="" />
- @PostConstruct
- 作用:用于指定初始化方法
- @PreDestroy
- 作用:用于指定销毁方法
用于注入数据
-
相当于xml:
-
<bean><property name="" ref=""></bean>
-
<bean><property name="" value=""></bean>
-
-
@Autowired
-
作用:自动按照类型注入,只要容器中有唯一的一个 Bean 对象类型和要注入的变量类型匹配,就注入成功如果 IOC 容器中没有任何 Bean 类型和要注入的变量类型匹配则报错
- 出现位置:可以是变量上,也可以是方法上
- 细节:在使用注解注入时,set 方法不是必须的
-
@Qualifier
-
作用:在按照类中注入的基础之上再按照名称注入。它在给类成员注入时不能单独使用,但是在给方法参数注入时可以
- 属性:
- value:用于指定注入 bean 的 id
- 属性:
-
@Resource
-
作用:直接按照 bean 的 id 注入,可以单独使用
- 属性:
- name:用于指定 bean 的 id
- 属性:
-
以上三个注解都只能注入其他 bean 类型的数据,而基本类型和 String 类型无法使用,另外,集合类型的注入只能通过 XML 来实现
-
@Value()
-
作用:用于注入基本类型和 String 类型
- 属性:
- value:用于指定数据的值,它可以使用 Spring 中的 SpEL(Spring 的 EL 表达式)
- SpEL的写法:${表达式}
- value:用于指定数据的值,它可以使用 Spring 中的 SpEL(Spring 的 EL 表达式)
- 属性:
Spring 注解和 XML 的选择问题
-
注解的优势:
- 配置简单,维护方便(我们找到类,就相当于找到了对应的配置)。
-
XML 的优势:
- 修改时,不用改源码。不涉及重新编译和部署。
-
Spring 管理 Bean 方式的比较:
基于XML配置 基于注解配置 Bean定义 <bean id="" class="" /> @Component
衍生类@Repository @Service @ControllerBean名称 通过id或name指定 @Component("**") Bean注入 <property name="" ref="">
或<property name="" value="">@Autowired 按类型注入
@Qualifer 按名称注入
@Resource 按bean的id注入
@Value 注入基本类型和 String 类型生命过程、Bean作用范围 init-method destroy-method
范围scope属性@PostConstruct初始化 @PreDestroy 销毀
@Scope设置作用范围适合场景 Bean来自第三方,使用其它 Bean的实现类由用户自己开发
Spring 的纯注解配置
-
@Configuration
- 作用:
- 用于指定当前类是一个 spring 配置类, 当创建容器时会从该类上加载注解。
- 获取容器时需要使用 AnnotationApplicationContext (有@Configuration 注解的类.class)。
- 属性:
- value:用于指定配置类的字节码
//该类是一个 spring 配置类 @Configuration public class Config { }
- 作用:
-
@ComponentScan
- 相当于xml:<context:component-scan base-package=“cn.itfzk.spring05”></context:component-scan>
- 作用:
- 用于指定 spring 在初始化容器时要扫描的包
- 属性:
- basePackages:用于指定要扫描的包。和该注解中的 value 属性作用一样
//扫描指定的包:cn.itfzk.spring05 @Configuration @ComponentScan(basePackages = {"cn.itfzk.spring05"}) public class Config { }
-
@Bean
- 作用:用于把当前方法的返回值作为 bean 对象存入 Spring 的 IOC 容器中
- 属性:
- name:用于指定 bean 的 id。当不写时,默认值是当前方法的名称
- 细节:
- 当我们使用注解配置方法时,如果方法有参数,Spring 框架会去容器中查找有没有可以用的 bean 对象,查找的方式和 Autowired 注解的作用是一样的
public class JdbcConfig { /** * 创建 QueryRunner 对象 * @param dataSource * @return * @Bean(name = "runner") 和 createQueryRunner 方法相当于: * <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" > * <constructor-arg name="ds" ref="dataSource" ></constructor-arg> * </bean> */ @Bean(name = "queryRunner") public QueryRunner createQueryRunner(DataSource dataSource){ return new QueryRunner(dataSource); } /** * 创建 DataSource 对象 * @return * @Bean(name = "dataSource") 和 createDataSource 方法相当于: * <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> * <property name="driverClass" value="com.mysql.jdbc.Driver" ></property> * <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/fzk" ></property> * <property name="user" value="root" ></property> * <property name="password" value="123456" ></property> * </bean> */ @Bean(name = "dataSource") public DataSource createDataSource(){ ComboPooledDataSource ds = new ComboPooledDataSource(); try { ds.setDriverClass("com.mysql.jdbc.Driver"); ds.setJdbcUrl("jdbc:mysql://localhost:3306/fzk"); ds.setUser("root"); ds.setPassword("123456"); } catch (PropertyVetoException e) { e.printStackTrace(); } return ds; } }
-
@Import
- 作用:用于导入其他配置的类
- 属性:
- value[]:用于指定其他配置类的字节码
- 细节:当我们使用 @Import 注解之后,有 @Import 注解的类就是父配置类,而导入的都是子类配置
@Configuration @Import(value = {JdbcConfig.class}) //用于导入其他配置的类 public class Config { }
-
@PropertySource
- 作用: 用于加载.properties 文件中的配置
- 属性:
- value[]:用于指定 properties 文件位置。如果是在类路径下,需要写上 classpath:
//properties 文件中的配置 jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/fzk jdbc.name=root jdbc.password=123456
@Configuration @PropertySource(value = {"classpath:jdbc.properties"}) //加载 properties 文件中的配置 public class Config { @Value("${jdbc.driver}") //取出 properties 文件中的 jdbc.driver 数据 private String driver; }
-
通过注解获取容器
ApplicationContext application = new AnnotationConfigApplicationContext(SpringConfiguration.class);
3、Spring 整合 Junit
步骤(四步)
-
第一步:导入必备的 jar 包到工程中
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.0.RELEASE</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-core</artifactId> <version>1.3</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.27</version> </dependency> <dependency> <groupId>commons-dbutils</groupId> <artifactId>commons-dbutils</artifactId> <version>1.6</version> </dependency> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.1_3</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.5</version> </dependency> </dependencies>
-
第二步:使用@RunWith 注解替换原有运行器
@RunWith(SpringJUnit4ClassRunner.class) //替换原有运行器 public class accountTest { }
-
第三步:使用@ContextConfiguration 指定 spring 配置文件的位置
-
xml 方法来指定 spring 配置文件的位置
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:testBean.xml") //指定 spring 配置文件的位置 public class accountTest {
-
注解 方法来指定 spring 配置文件的位置
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfiguration.class) //指定 spring 配置文件的位置 public class accountTest {
-
-
第四步:使用@Autowired 给测试类中的变量注入数据
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfiguration.class) @ContextConfiguration(locations = "classpath:testBean.xml") public class accountTest { @Autowired //给测试类中的变量注入数据 private IAccountService accountService; }
4、AOP(面向切面编程)
AOP 概述
AOP 概念
- AOP: 全称是 Aspect Oriented Programming(面向切面编程)
- 把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对我们的已有方法进行增强。
AOP 的作用及优势
- 作用:
- 在程序运行期间,不修改源码对已有方法进行增强。
- 优势:
- 减少重复代码
- 提高开发效率
- 维护方便
动态代理技术实现(CRUD)
-
业务层(接口,类)
//业务层接口 public interface IAccountService { //查询所有 List<Account> findAll(); //按 id 查询 Account findById(int id); //按 username 查询 Account findByName(String username); //添加信息 void saveAccount(Account account); //修改信息 void updateAccount(Account account); //按 id 删除信息 void deleteById(int id); //交换信息 void changeMessage(String username1, String username2); } //业务层实现类 public class AccountServiceImpl implements IAccountService { private AccountDaoImpl accountDao; public void setAccountDao(AccountDaoImpl accountDao) { this.accountDao = accountDao; } //查询所有信息 public List<Account> findAll() { return accountDao.findAll(); } //按 id 查询信息 public Account findById(int id) { return accountDao.findById(id); } //按 username 查询信息 public Account findByName(String username) { return accountDao.findByName(username); } //添加信息 public void saveAccount(Account account) { accountDao.saveAccount(account); } //修改信息= public void updateAccount(Account account) { accountDao.updateAccount(account); } //删除信息= public void deleteById(int id) { accountDao.deleteById(id); } //交换信息,将 username1 和 username2 的名字交换= public void changeMessage(String username1, String username2) { accountDao.changeMessage(username1, username2); } }
-
//持久层接口 public interface IAccountDao { //查询所有 List<Account> findAll(); //按 id 查询 Account findById(int id); //按 username 查询 Account findByName(String username); //添加信息 void saveAccount(Account account); //修改信息 void updateAccount(Account account); //按 id 删除信息 void deleteById(int id); //交换信息 void changeMessage(String username1, String username2); } //持久层实现类 public class AccountDaoImpl implements IAccountService { private QueryRunner queryRunner; private ConnectionUtils connectionUtils; public void setConnectionUtils(ConnectionUtils connectionUtils) { this.connectionUtils = connectionUtils; } public void setQueryRunner(QueryRunner queryRunner) { this.queryRunner = queryRunner; } //查询所有信息 public List<Account> findAll() { try { return queryRunner.query(connectionUtils.getThreadConnection() ,"select * from caogao", new BeanListHandler<Account>(Account.class)); } catch (SQLException e) { e.printStackTrace(); } return null; } //按 id 查询信息 public Account findById(int id) { try { return queryRunner.query(connectionUtils.getThreadConnection() ,"select * from caogao where id=?", new BeanHandler<Account>(Account.class), id); } catch (SQLException e) { e.printStackTrace(); } return null; } //按 username 查询信息 public Account findByName(String username) { try { return queryRunner.query(connectionUtils.getThreadConnection() ,"select * from caogao where username=?", new BeanHandler<Account>(Account.class), username); } catch (SQLException e) { e.printStackTrace(); } return null; } //添加信息 public void saveAccount(Account account) { try { queryRunner.update(connectionUtils.getThreadConnection() ,"insert into caogao(username, password) values(?,?)", account.getUsername(), account.getPassword()); } catch (SQLException e) { e.printStackTrace(); } } //修改信息 public void updateAccount(Account account) { try { queryRunner.update(connectionUtils.getThreadConnection() ,"update caogao set username=?, password=? where id=?", account.getUsername(), account.getPassword(), account.getId()); } catch (SQLException e) { e.printStackTrace(); } } //删除信息 public void deleteById(int id) { try { queryRunner.update(connectionUtils.getThreadConnection() ,"delete from caogao where id=?", id); } catch (SQLException e) { e.printStackTrace(); } } //交换信息,将 username1 和 username2 的信息交换 public void changeMessage(String username1, String username2) { //1、查询 username1 的信息 Account user1 = findByName(username1); //2、查询 username2 的信息 Account user2 = findByName(username2); //3、修改 username1 的信息 user1.setUsername(username2); //4、修改 username2 的信息 user2.setUsername(username1); //5、更新 username1 的信息 updateAccount(user1); //模拟网络错误 int i = 10 / 0; //6、更新 username2 的信息 updateAccount(user2); } }
-
连接的工具类
public class ConnectionUtils { private ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>(); private DataSource dataSource; public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } //获取连接 public Connection getThreadConnection(){ //从 ThreadLocal 获取连接 Connection connection = threadLocal.get(); //判断是否有连接 if(connection == null){ try { //从数据源中获取一个连接,并存入 ThreadLocal 中 connection = dataSource.getConnection(); threadLocal.set(connection); } catch (SQLException e) { e.printStackTrace(); } } return connection; } //把连接和线程解绑 public void removeConnection(){ threadLocal.remove(); } }
-
事务相关的工具类
public class TransactionManager { private ConnectionUtils connectionUtils; public void setConnectionUtils(ConnectionUtils connectionUtils) { this.connectionUtils = connectionUtils; } /** * 开启事务 */ public void beginTransation(){ try { connectionUtils.getThreadConnection().setAutoCommit(false); } catch (SQLException e) { e.printStackTrace(); } } /** * 提交事务 */ public void commit(){ try { connectionUtils.getThreadConnection().commit(); } catch (SQLException e) { e.printStackTrace(); } } /** * 回滚事务 */ public void rollback(){ try { connectionUtils.getThreadConnection().rollback(); } catch (SQLException e) { e.printStackTrace(); } } /** * 释放事务 */ public void close(){ try { connectionUtils.getThreadConnection().close(); //还回线程池 connectionUtils.removeConnection(); } catch (SQLException e) { e.printStackTrace(); } } }
-
AccountServiceImpl 的代理工厂类
public class BeanFactory { private IAccountService accountService; private TransactionManager transactionManager; public void setTransactionManager(TransactionManager transactionManager) { this.transactionManager = transactionManager; } public void setAccountService(IAccountService accountService) { this.accountService = accountService; } public IAccountService getAccountService() { return (IAccountService) Proxy.newProxyInstance(accountService.getClass().getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object invoke; try { //1、开启事务 transactionManager.beginTransation(); //2、执行操作 invoke = method.invoke(accountService, args); //3、提交事务 transactionManager.commit(); //4、返回结果 return invoke; }catch (Exception e){ //5、回滚事务 transactionManager.rollback(); throw new RuntimeException(e); }finally{ //6、释放连接 transactionManager.close(); } } }); } }
-
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"> <!--AccountServiceImpl--> <bean id="accountService" class="itfzk.project.service.impl.AccountServiceImpl" > <property name="accountDao" ref="accountDao" ></property> </bean> <!--AccountDaoImpl--> <bean id="accountDao" class="itfzk.project.dao.impl.AccountDaoImpl" > <property name="queryRunner" ref="queryRunner" ></property> <property name="connectionUtils" ref="connectionUtils" ></property> </bean> <!--QueryRunner--> <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype" > </bean> <!--DataSource--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" > <property name="driverClass" value="com.mysql.jdbc.Driver" ></property> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/fzk" ></property> <property name="user" value="root" ></property> <property name="password" value="123456" ></property> </bean> <!--ConnectionUtils--> <bean id="connectionUtils" class="itfzk.project.utils.ConnectionUtils" > <property name="dataSource" ref="dataSource" ></property> </bean> <!--TransactionManager--> <bean id="transactionManager" class="itfzk.project.utils.TransactionManager" > <property name="connectionUtils" ref="connectionUtils" ></property> </bean> <!--BeanFactory--> <bean id="beanFactory" class="itfzk.project.factory.BeanFactory" > <property name="accountService" ref="accountService" ></property> <property name="transactionManager" ref="transactionManager" ></property> </bean> <!--AccountDaoImpl_BeanFactory--> <bean id="accountDaoImpl_BeanFactory" factory-bean="beanFactory" factory-method="getAccountService" ></bean> </beans>
Spring 中的 AOP
AOP 相关术语
- Joinpoint(连接点):
- 所谓连接点是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为 spring 只支持方法类型的连接点。
- Pointcut(切入点):
- 所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。
- Advice(通知/增强):
- 所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。
- 通知的类型:
- 前置通知,后置通知,异常通知,最终通知,环绕通知。
- Introduction(引介):
- 引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方法或 Field。
- Target(目标对象):
- 代理的目标对象。
- Weaving(织入):
- 是指把增强应用到目标对象来创建新的代理对象的过程。
- spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。
- Proxy(代理) :
- 一个类被 AOP 织入增强后,就产生一个结果代理类。
- Aspect(切面):
- 是切入点和通知(引介)的结合
基于 XML 的 AOP 配置
步骤(七步)
-
第一步:导入必备的 jar 包到工程中
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.0.RELEASE</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-core</artifactId> <version>1.3</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.27</version> </dependency> <dependency> <groupId>commons-dbutils</groupId> <artifactId>commons-dbutils</artifactId> <version>1.6</version> </dependency> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.1_3</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.5</version> </dependency> </dependencies>
-
第二步:创建 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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd"> </beans>
-
第三步:把通知类用 bean 标签配置起来
<!-- 配置通知 --> <bean id="txManager" class="cn.itfzk.spring07.log.Logger"></bean>
-
第四步:使用 aop:config 声明 aop 配置
<aop:config> <!-- 配置的代码 --> </aop:config>
-
第五步:使用 aop:aspect 配置切面
<aop:config> <aop:aspect id="txAdvice" ref="txManager"> <!--配置通知的类型--> </aop:aspect> </aop:config>
-
第六步:使用 aop:pointcut 配置切入点表达式
<aop:aspect id="txAdvice" ref="txManager"> <aop:pointcut id="pt1" expression="execution(* cn.itfzk.spring07.service.impl.AccountServiceImpl.save())"/> </aop:aspect>
-
第七步:使用 aop:xxx 配置对应的通知类型
<aop:pointcut id="pt1" expression="execution(* cn.itfzk.spring07.service.impl.AccountServiceImpl.save())"/> <aop:before method="beforeLog" pointcut-ref="pt1"></aop:before> <aop:after-returning method="afterReturningLog" pointcut-ref="pt1"></aop:after-returning> <aop:after-throwing method="afterThrowingLog" pointcut-ref="pt1"></aop:after-throwing> <aop:after method="afterLog" pointcut-ref="pt1"></aop:after>
标签
aop:config
-
作用: 用于声明开始 aop 的配置
<aop:config> <!-- 配置的代码 --> </aop:config>
aop:aspect
-
作用:
- 用于配置切面。
-
属性:
- id: 给切面提供一个唯一标识。
- ref: 引用配置好的通知类 bean 的 id。
<aop:config> <aop:aspect id="txAdvice" ref="txManager"> <!--配置通知的类型--> </aop:aspect> </aop:config>
aop:before、aop:after-returning、aop:after-throwing、aop:after
-
aop:before :配置前置通知
-
aop:after-returning :配置后置通知
-
aop:after-throwing :配置异常通知
-
aop:after :配置最终通知
-
以上四个标签的属性:
-
method 属性:用于指定前置通知类的方法
-
pointcut 属性:用于指定切入点的表达式
-
pointcut 属性切入点表达式的写法:
-
关键字:execution
-
标准写法:
- 方法类型 包名.类名.方法名(参数)
void(int | String | …) cn.itfzk.spring07.service.impl.AccountServiceImpl.save(…)
- 方法类型 包名.类名.方法名(参数)
-
全通配写法(* 代表任意):
* *..*.*(..)
-
-
-
-
不使用 aop:pointcut
<!--前置通知,后置通知,异常通知,最终通知的使用-->
<!--前置通知:在切入点方法执行前执行-->
<aop:before method="beforeLog" pointcut="execution(* cn.itfzk.spring07.service.impl.AccountServiceImpl.save())"></aop:before>
<!--后置通知:在切入点方法执行后执行-->
<aop:after-returning method="afterReturningLog" pointcut="execution(* cn.itfzk.spring07.service.impl.AccountServiceImpl.save())"></aop:after-returning>
<!--异常通知:在切入点方法执行产生异常执行-->
<aop:after-throwing method="afterThrowingLog" pointcut="execution(* cn.itfzk.spring07.service.impl.AccountServiceImpl.save())"></aop:after-throwing>
<!--最终通知:无论切入点是否正常执行最终通知依然执行-->
<aop:after method="afterLog" pointcut="execution(* cn.itfzk.spring07.service.impl.AccountServiceImpl.save())"></aop:after>
- 使用 aop:pointcut
<aop:pointcut id="pt1" expression="execution(* cn.itfzk.spring07.service.impl.AccountServiceImpl.save())"/>
<!--前置通知:在切入点方法执行前执行-->
<aop:before method="beforeLog" pointcut-ref="pt1"></aop:before>
<!--后置通知:在切入点方法执行后执行-->
<aop:after-returning method="afterReturningLog" pointcut-ref="pt1"></aop:after-returning>
<!--异常通知:在切入点方法执行产生异常执行-->
<aop:after-throwing method="afterThrowingLog" pointcut-ref="pt1"></aop:after-throwing>
<!--最终通知:无论切入点是否正常执行最终通知依然执行-->
<aop:after method="afterLog" pointcut-ref="pt1"></aop:after>
aop:around(环绕通知)
-
作用:
- 用于配置环绕通知
-
属性:
- method:指定通知中方法的名称。
- pointct:定义切入点表达式
- pointcut-ref:指定切入点表达式的引用
-
说明:
- 它是 spring 框架为我们提供的一种可以在代码中手动控制增强代码什么时候执行的方式。
-
注意:
- 通常情况下,环绕通知都是独立使用的
<!-- 环绕通知配置 --> <aop:config> <aop:aspect id="txAdvice" ref="transactionManaget"> <aop:pointcut id="pt" expression="execution(* net.memory.Spring01.service.impl.AccountServiceImpl.*(..))"/> <!-- 环绕通知 --> <aop:around method="aroundAccount" pointcut-ref="pt"></aop:around> </aop:aspect> </aop:config>
//环绕通知实现方法 public Object aroundAccount(ProceedingJoinPoint proceedingJoinPoint){ Object proceed = null; try { //前置通知 beginTransaction(); Object[] args = proceedingJoinPoint.getArgs(); proceed = proceedingJoinPoint.proceed(); //后置通知 commit(); } catch (Throwable throwable) { //异常通知 rollback(); throwable.printStackTrace(); } finally { //最终通知 close(); } return proceed; }
aop:pointcut
-
作用:
- 用于配置切入点表达式。就是指定对哪些类的哪些方法进行增强
-
属性:
- id: 用于给切入点表达式提供一个唯一标识
- expression:用于定义切入点表达式
<aop:pointcut id="pt1" expression="execution(* cn.itfzk.spring07.service.impl.AccountServiceImpl.save())"/>
基于 注解 的 AOP 配置
步骤(八步)
-
第一步:导入必备的 jar 包到工程中
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.0.RELEASE</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-core</artifactId> <version>1.3</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.27</version> </dependency> <dependency> <groupId>commons-dbutils</groupId> <artifactId>commons-dbutils</artifactId> <version>1.6</version> </dependency> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.1_3</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.5</version> </dependency> </dependencies>
-
第二步:创建 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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd"> </beans>
-
第三步:把资源使用注解配置
@Service("accountService") public class AccountServiceImpl implements IAccountService { }
-
第四步:在配置文件中指定 spring 要扫描的包
<!-- 告知 spring,在创建容器时要扫描的包 --> <context:component-scan base-package="net.memory"></context:component-scan>
-
第五步:把通知类也使用注解配置 @Component
@Component("transactionManager") //使用注解配置 public class TransactionManager { }
-
第六步:在通知类上使用 @Aspect 注解声明为切面
@Component("transactionManager") @Aspect //使用@Aspect 注解声明为切面 public class TransactionManager { }
-
第七步:在增强的方法上使用注解配置通知
-
@Before、@AfterReturning、@AfterThrowing、@After(前置、后置、异常、最终通知)
@Component("transactionManaget") @Aspect public class TransactionManaget { @Autowired private ConnectionUtils connectionUtils; //前置通知 @Before("execution(* net.memory.Spring01.service.impl.AccountServiceImpl.*(..))") public void beginTransaction(){ try { connectionUtils.getConnection().setAutoCommit(false); } catch (SQLException e) { e.printStackTrace(); } } //后置通知 @AfterReturning("execution(* net.memory.Spring01.service.impl.AccountServiceImpl.*(..))") public void commit(){ try { connectionUtils.getConnection().commit(); } catch (SQLException e) { e.printStackTrace(); } } //异常通知 @AfterThrowing("execution(* net.memory.Spring01.service.impl.AccountServiceImpl.*(..))") public void rollback(){ try { connectionUtils.getConnection().rollback(); } catch (SQLException e) { e.printStackTrace(); } } //最终通知 @After("execution(* net.memory.Spring01.service.impl.AccountServiceImpl.*(..))") public void close(){ try { connectionUtils.getConnection().close(); } catch (SQLException e) { e.printStackTrace(); } connectionUtils.removeConnection(); } }
-
@Around(环绕通知)
@Component("transactionManaget") @Aspect public class TransactionManaget { @Autowired private ConnectionUtils connectionUtils; //前置通知 public void beginTransaction(){ try { connectionUtils.getConnection().setAutoCommit(false); } catch (SQLException e) { e.printStackTrace(); } } //后置通知 public void commit(){ try { connectionUtils.getConnection().commit(); } catch (SQLException e) { e.printStackTrace(); } } //异常通知 public void rollback(){ try { connectionUtils.getConnection().rollback(); } catch (SQLException e) { e.printStackTrace(); } } //最终通知 public void close(){ try { connectionUtils.getConnection().close(); } catch (SQLException e) { e.printStackTrace(); } connectionUtils.removeConnection(); } //环绕通知 @Around("execution(* net.memory.Spring01.service.impl.AccountServiceImpl.*(..))") public Object aroundAccount(ProceedingJoinPoint proceedingJoinPoint){ Object proceed = null; try { beginTransaction(); //前置通知 Object[] args = proceedingJoinPoint.getArgs(); proceed = proceedingJoinPoint.proceed(); commit(); //后置通知 } catch (Throwable throwable) { rollback(); //异常通知 throwable.printStackTrace(); } finally { close(); //最终通知 } return proceed; } }
-
-
第八步:在 spring 配置文件中开启 spring 对注解 AOP 的支持
<context:component-scan base-package="net.memory"></context:component-scan> <!-- 开启 spring 对注解 AOP 的支持 --> <aop:aspectj-autoproxy />
注解
@Aspect
-
作用:
- 把当前类声明为切面类。
@Component("transactionManager") @Aspect //使用@Aspect 注解声明为切面 public class TransactionManager { }
@Before 、 @AfterReturning 、 @AfterThrowing 、@After
-
@Before :把当前方法看成是前置通知。
-
@AfterReturning :把当前方法看成是后置通知。
-
@AfterThrowing :把当前方法看成是异常通知。
-
@After :把当前方法看成是最终通知
@Before("execution(* net.memory.Spring01.service.impl.AccountServiceImpl.*(..))") @AfterReturning("execution(* net.memory.Spring01.service.impl.AccountServiceImpl.*(..))") @AfterThrowing("execution(* net.memory.Spring01.service.impl.AccountServiceImpl.*(..))") @After("execution(* net.memory.Spring01.service.impl.AccountServiceImpl.*(..))")
@Around
-
作用:
- 把当前方法看成是环绕通知
@Around("execution(* net.memory.Spring01.service.impl.AccountServiceImpl.*(..))")
@Pointcut
-
作用:指定切入点表达式
-
属性:
- value:指定表达式的内容
@Pointcut("execution(* net.memory.Spring01.service.impl.AccountServiceImpl.*(..))") public void pt1(){ } @Before("pt1()") @AfterReturning("pt1()") @AfterThrowing("pt1()") @After("pt1()") @Around("pt1()")
@EnableAspectJAutoProxy(纯注解的配置方式 )
@Configuration
@ComponentScan(basePackages = {"net.memory"}) //扫描指定的包:cn.itfzk.spring05
@Import(JdbcConfiguration.class)
@EnableAspectJAutoProxy //开启 spring 对注解 AOP 的支持
public class SpringConfiguration {
}
5、Spring 中的 JdbcTemplate
概述
- spring 框架中提供的一个对象,是对原始 Jdbc API 对象的简单封装。
- spring 框架为我们提供了很多的操作模板类。
- 操作关系型数据的:
- JdbcTemplate
- HibernateTemplate
- 操作 nosql 数据库的:
- RedisTemplate
- 操作消息队列的:
- JmsTemplate
- 操作关系型数据的:
JdbcTemplate 的增删改查操作
不在 dao 中使用 JdbcTemplate
-
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" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd"> <!--Spring 的 JdbcTemplate 的的配置--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置数据源 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/fzk"></property> <property name="username" value="root"></property> <property name="password" value="123456"></property> </bean> </beans>
-
JdbcTemplate使用类
public class JdbcTemplateDemo { private ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean08.xml"); private JdbcTemplate jdbcTemplate = (JdbcTemplate) applicationContext.getBean("jdbcTemplate"); /** * 查询所有 */ @Test public void findAll(){ String sql = "select * from caogao"; List<Account> accounts = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Account>(Account.class)); for(Account account : accounts){ System.out.println(account); } } /** * 获取其他类型的数据 */ @Test public void find(){ String sql = "select count(*) from caogao"; Long fzk = jdbcTemplate.queryForObject(sql, Long.class); System.out.println(fzk); } /** * 按条件查询 */ @Test public void findById(){ String sql = "select * from caogao where id = ?"; List<Account> accounts = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Account>(Account.class), 2); System.out.println(accounts.get(0)); } /** * 修改 */ @Test public void updateById(){ String sql = "update caogao set username = ?, password = ? where id = ?"; jdbcTemplate.update(sql, "fff123", "fff123", 2); } /** * 添加信息 */ @Test public void add(){ String sql = "insert into caogao(username, password) values(?,?)"; jdbcTemplate.update(sql, "psq", "123"); } /** * 删除信息 */ @Test public void delete(){ String sql = "delete from caogao where id = ?"; jdbcTemplate.update(sql, 7); } }
在 dao 中使用 JdbcTemplate
方法一
-
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" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd"> <bean id="jdbcTemplateDemo" class="cn.itfzk.spring08.JdbcTemplateDemo"> <property name="jdbcTemplate" ref="jdbcTemplate"></property> </bean> <!--Spring 的 JdbcTemplate 的的配置--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置数据源 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/fzk"></property> <property name="username" value="root"></property> <property name="password" value="123456"></property> </bean> </beans>
-
JdbcTemplate使用类
public class JdbcTemplateDemo { private JdbcTemplate jdbcTemplate; public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } /** * 查询所有 */ public List<Account> findAll(){ String sql = "select * from caogao"; List<Account> accounts = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Account>(Account.class)); return accounts; } /** * 获取其他类型的数据 */ public Integer find(){ String sql = "select count(*) from caogao"; Integer fzk = jdbcTemplate.queryForObject(sql, Long.class); return fzk; } /** * 按条件查询 */ public Account findById(){ String sql = "select * from caogao where id = ?"; List<Account> accounts = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Account>(Account.class), 2); return accounts.get(0); } /** * 修改 */ public void updateById(){ String sql = "update caogao set username = ?, password = ? where id = ?"; jdbcTemplate.update(sql, "fff123", "fff123", 2); } /** * 添加信息 */ public void add(){ String sql = "insert into caogao(username, password) values(?,?)"; jdbcTemplate.update(sql, "psq", "123"); } /** * 删除信息 */ public void delete(){ String sql = "delete from caogao where id = ?"; jdbcTemplate.update(sql, 7); } }
方法二:让 dao 继承 JdbcDaoSupport
-
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" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd"> <bean id="jdbcTemplateDemo" class="cn.itfzk.spring08.JdbcTemplateDemo"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置数据源 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/fzk"></property> <property name="username" value="root"></property> <property name="password" value="123456"></property> </bean> </beans>
-
继承JdbcDaoSupport类
public class JdbcTemplateDemo extends JdbcDaoSupport { /** * 查询所有 */ public List<Account> findAll(){ String sql = "select * from caogao"; List<Account> accounts = getJdbcTemplate().query(sql, new BeanPropertyRowMapper<Account>(Account.class)); return accounts; } /** * 获取其他类型的数据 */ public Integer find(){ String sql = "select count(*) from caogao"; Integer fzk = getJdbcTemplate().queryForObject(sql, Long.class); return fzk; } /** * 按条件查询 */ public Account findById(){ String sql = "select * from caogao where id = ?"; List<Account> accounts = getJdbcTemplate().query(sql, new BeanPropertyRowMapper<Account>(Account.class), 2); return accounts.get(0); } /** * 修改 */ public void updateById(){ String sql = "update caogao set username = ?, password = ? where id = ?"; getJdbcTemplate().update(sql, "fff123", "fff123", 2); } /** * 添加信息 */ public void add(){ String sql = "insert into caogao(username, password) values(?,?)"; getJdbcTemplate().update(sql, "psq", "123"); } /** * 删除信息 */ public void delete(){ String sql = "delete from caogao where id = ?"; getJdbcTemplate().update(sql, 7); } }
6、Spring 中的事务控制
Spring 中事务控制的 API 介绍
事务的隔离级别
- ISOLATION DEFAULT
- 默认级别,归属下列某一种
- ISOLATION READ UNCOMMITTED
- 可以读取未提交数据
- ISOLATION READ_ COMMITTED
- 只能读取已提交数据,解决脏读问题(Oracle默认级别)
- ISOLATION REPEATABLE READ
- 是否读取其他事务提交修改后的数据,解决不可重复读问题(MySQL默认级别)
- ISOLATION SERIALIZABLE
- 是否读取其他事务提交添加后的数据,解决幻影读问题
事务的传播行为
- REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在事务,加入到这个事务中。增删改的选择
- SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)查询可以的选择
- MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常
- REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起。
- NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
- NEVER:以非事务方式运行,如果当前存在事务,抛出异常
- NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行 REQUIRED 类似的操作。
超时时间
- 默认值是-1,没有超时限制。如果有,以秒为单位进行设置。
是否是只读事务
- 建议查询时设置为只读。
基于 XML 的声明式事务控制
步骤(十步)
-
第一步:导入必备的 jar 包到工程中
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.0.RELEASE</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-core</artifactId> <version>1.3</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.27</version> </dependency> <dependency> <groupId>commons-dbutils</groupId> <artifactId>commons-dbutils</artifactId> <version>1.6</version> </dependency> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.1_3</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.5</version> </dependency> </dependencies>
-
第二步:创建 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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd"> </beans>
-
第三步:编写业务层接口和实现类
//业务层接口 public interface IAccountService { //查询所有 List<Account> findAll(); //按 id 查询 Account findById(int id); //按 username 查询 Account findByName(String username); //添加信息 void saveAccount(Account account); //修改信息 void updateAccount(Account account); //按 id 删除信息 void deleteById(int id); //交换信息 void changeMessage(String username1, String username2); } //业务层实现类 public class AccountServiceImpl implements IAccountService { private AccountDaoImpl accountDao; public void setAccountDao(AccountDaoImpl accountDao) { this.accountDao = accountDao; } /** * 查询所有信息 */ public List<Account> findAll() { return accountDao.findAll(); } /** * 按 id 查询信息 */ public Account findById(int id) { return accountDao.findById(id); } /** * 按 username 查询信息 */ public Account findByName(String username) { return accountDao.findByName(username); } /** * 添加信息 */ public void saveAccount(Account account) { accountDao.saveAccount(account); } /** * 修改信息 */ public void updateAccount(Account account) { accountDao.updateAccount(account); } /** * 删除信息 */ public void deleteById(int id) { accountDao.deleteById(id); } /** * 交换信息,将 username1 和 username2 的名字交换 */ public void changeMessage(String username1, String username2) { accountDao.changeMessage(username1, username2); } }
-
第四步:编写持久层接口和实现类
//持久层接口 public interface IAccountDao { //查询所有 List<Account> findAll(); //按 id 查询 Account findById(int id); //按 username 查询 Account findByName(String username); //添加信息 void saveAccount(Account account); //修改信息 void updateAccount(Account account); //按 id 删除信息 void deleteById(int id); //交换信息 void changeMessage(String username1, String username2); } //持久层实现类 public class AccountDaoImpl extends JdbcDaoSupport implements IAccountService { /** * 查询所有信息 */ public List<Account> findAll() { List<Account> accounts = super.getJdbcTemplate().query("select * from caogao", new BeanPropertyRowMapper<Account>(Account.class)); return accounts; } /** * 按 id 查询信息 */ public Account findById(int id) { List<Account> accounts = super.getJdbcTemplate().query("select * from caogao where id=?", new BeanPropertyRowMapper<Account>(Account.class), 2); return accounts.isEmpty() ? null : accounts.get(0); } /** * 按 username 查询信息 */ public Account findByName(String username) { List<Account> accounts = super.getJdbcTemplate().query("select * from caogao where username=?", new BeanPropertyRowMapper<Account>(Account.class), username); return accounts.isEmpty() ? null : accounts.get(0); } /** * 添加信息 */ public void saveAccount(Account account) { super.getJdbcTemplate().update("insert into caogao(username, password) values(?,?)", account.getUsername(), account.getPassword()); } /** * 修改信息 */ public void updateAccount(Account account) { super.getJdbcTemplate().update("update caogao set username=?, password=? where id=?", account.getUsername(), account.getPassword(), account.getId()); } /** * 删除信息 */ public void deleteById(int id) { super.getJdbcTemplate().update("delete from caogao where id=?", id); } /** * 交换信息,将 username1 和 username2 的信息交换 */ public void changeMessage(String username1, String username2) { //1、查询 username1 的信息 Account user1 = findByName(username1); //2、查询 username2 的信息 Account user2 = findByName(username2); //3、修改 username1 的信息 user1.setUsername(username2); //4、修改 username2 的信息 user2.setUsername(username1); //5、更新 username1 的信息 updateAccount(user1); //模拟网络错误 int i = 10 / 0; //6、更新 username2 的信息 updateAccount(user2); } }
-
第五步:在配置文件中配置业务层和持久层对
<!--AccountServiceImpl--> <bean id="accountService" class="cn.itfzk.spring09.service.impl.AccountServiceImpl" > <property name="accountDao" ref="accountDao" ></property> </bean> <!--AccountDaoImpl--> <bean id="accountDao" class="cn.itfzk.spring09.dao.impl.AccountDaoImpl" > <property name="dataSource" ref="dataSource"></property> </bean> <!--DataSource--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" > <property name="driverClass" value="com.mysql.jdbc.Driver" ></property> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/fzk" ></property> <property name="user" value="root" ></property> <property name="password" value="123456" ></property> </bean>
-
第六步: 配置事务管理器
<!--配置事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean>
-
第七步:配置事务的通知引用事务管理器 tx:advice
<!--配置事务的通知--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> </tx:advice>
-
第八步:配置事务的属性 tx:attributes、tx:method
<!--配置事务的通知--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!--配置事务的属性--> <tx:attributes> <tx:method name="*" propagation="REQUIRED" read-only="false"/> <tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method> </tx:attributes> </tx:advice>
-
第九步:配置 AOP 切入点表达式 aop:config、aop:pointcut
<!--配置 AOP--> <aop:config> <aop:pointcut id="pt" expression="execution(* cn.itfzk.spring09.service.impl.AccountServiceImpl.*(..))"/> </aop:config>
-
第十步:配置切入点表达式和事务通知的对应关系 aop:advisor
<!--配置 AOP--> <aop:config> <aop:pointcut id="pt" expression="execution(* cn.itfzk.spring09.service.impl.AccountServiceImpl.*(..))"/> <!--建立事务通知和切入点表达式的对应关系--> <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"></aop:advisor> </aop:config>
标签
tx:advice
-
作用:事务的通知
-
属性:
- id :给事务通知唯一的标识
- transaction-manager :给事务通知提供一个事务管理器引用
<!--配置事务的通知--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> </tx:advice>
tx:attributes、tx:method
-
作用:事务的属性
-
属性:
- name :名称
- isolation :事务的隔离级别
- DEFAULT :默认值,表示数据库的默认隔离级别
- READ_COMMITTED :读取提交的数据
- READ_UNCOMMITTED :解决脏读
- REPEATABLE_READ :解决不可重复读
- SERIALIZABLE :解决幻读
- propagation :指定事务的传播行为
- REQUIRED :默认值,表示一定会有事务,增删改的选择
- SUPPORTS :查询可以的选择
- MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常
- REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起。
- NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
- NEVER:以非事务方式运行,如果当前存在事务,抛出异常
- NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行 REQUIRED 类似的操作。
- timeout :事务的超时时间,默认值是 -1 ,表示永不超时,如果指定数值则以秒为单位
- read-only :事务是否只读,只有查询方法才能设置为 true,默认值为 false 表示读写
- rollback-for :用于指定一个异常,当产生该异常时,事务回滚,其他异常时,事务不回滚,没有默认值。表示如何异常都回滚
- no-rollback-for :用于指定一个异常,当产生该异常时,事务不回滚,其他异常时,事务回滚,没有默认值。表示如何异常都回滚
<tx:attributes> <tx:method name="*" propagation="REQUIRED" read-only="false"/> <tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method> <tx:method name="" isolation="" propagation="" read-only="" timeout="" rollback-for="" no-rollback-for=""></tx:method> </tx:attributes>
aop:advisor
-
作用:建立事务通知和切入点表达式的对应关系
<aop:config> <aop:pointcut id="pt" expression="execution(* cn.itfzk.spring09.service.impl.AccountServiceImpl.*(..))"/> <!--建立事务通知和切入点表达式的对应关系--> <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"></aop:advisor> </aop:config>
基于 注解 的配置方式
步骤(七步)
-
第一步:导入必备的 jar 包到工程中
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.0.RELEASE</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-core</artifactId> <version>1.3</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.27</version> </dependency> <dependency> <groupId>commons-dbutils</groupId> <artifactId>commons-dbutils</artifactId> <version>1.6</version> </dependency> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.1_3</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.5</version> </dependency> </dependencies>
-
第二步:创建 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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd"> </beans>
-
第三步:创建业务层接口和实现类并使用注解让 spring 管理
@Service("accountService") //使用注解让 spring 管理 public class AccountServiceImpl implements IAccountService { @Autowired private IAccountDao accountDao; public void changeAccount(int id1, int id2){ Account account1 = accountDao.findById(id1); Account account2 = accountDao.findById(id2); String name = account1.getUsername(); account1.setUsername(account2.getUsername()); account2.setUsername(name); accountDao.update(account1); int i = 10 / 0; accountDao.update(account2); } }
-
第四步:创建持久层接口和实现类并使用注解让 spring 管理
@Repository("accountDao") //使用注解让 spring 管理 public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao { }
-
第五步:配置事务管理器并注入数据源
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean>
-
第六步:在业务层使用@Transactional 注解
@Service("accountService") @Transactional(propagation = Propagation.SUPPORTS, readOnly = false) //使用@Transactional 注解 public class AccountServiceImpl implements IAccountService { }
-
第七步:在配置文件中开启 spring 对注解事务的支持
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
不使用 xml 的配置方式
@Configuration
@EnableTransactionManagement //TransactionManagement
public class SpringTxConfiguration {
}
注解
@Transactional
- 作用
- 事务的通知
- 属性
- 和 xml 中的属性含义一致。
- 该注解可以出现在接口上,类上和方法上。
- 出现接口上,表示该接口的所有实现类都有事务支持。
- 出现在类上,表示类中所有方法有事务支持
- 出现在方法上,表示方法有事务支持。
- 以上三个位置的优先级:方法 > 类 > 接口