# Spring Framework
# 系统架构
- Data Access:数据访问
- Data Integration:数据集成
- Web:Web 开发
- AOP:面向切面编程
- Aspects:AOP 思想实现
- Core Container:核心容器
- Test:单元测试与集成测试
# Spring 核心概念
# IoC(Inversion of Control)控制反转
- 使用对象时,由于主动 new 产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部,此思想为控制反转
- Spring 技术对 IoC 思想进行了实现
- Spring 提供了一个容器,称为 IoC,用来充当 IoC 思想中的外部
- IoC 容器负责对象的创建、初始化等一些列工作,被创建或被管理的对象在 IoC 容器中统称为 Bean
- DI(Dependency Injection)依赖注入
- 在容器中建立 bean 与 bean 之间的依赖关系的整个过程,称为依赖注入
- 充分解耦
- 使用 IoC 管理 bean
- 在 IoC 容器内将有依赖关系的 bean 进行关系绑定(DI)
# IoC 入门(XML 版)
# 导入 Spring 坐标
| <dependency> |
| <groupId>org.springframework</groupId> |
| <artifactId>spring-context</artifactId> |
| <version>5.0.5.RELEASE</version> |
| </dependency> |
# 定义 Spring 管理的类(接口)
| public interface BookService { |
| public void save(); |
| } |
| public class BookServiceImpl implements BookService { |
| private BookDao bookDao = new BookDaoImpl(); |
| public void save() { |
| bookDao.save(); |
| } |
| } |
# 创建 Spring 配置文件,配置对应类作为 Spring 管理的 bean
| <?xml version="1.0" encoding="UTF-8"?> |
| <beans xmlns="http://www.springframework.org/schema/beans" |
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> |
| <bean id="bookDao" class="com.baozi.dao.impl.BookDaoImpl"></bean> |
| <bean id="bookService" class="com.baozi.service.impl.BookServiceImpl"></bean> |
| </beans> |
# 初始化 IoC 容器(Spring 核心容器 / Spring 容器),通过容器获取 bean
| public class App { |
| public static void main(String[] args) { |
| ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); |
| BookService bookService = (BookService) ctx.getBean("bookService"); |
| bookService.save(); |
| } |
| } |
# DT 入门案例
# 删除使用 new 形式创建的代码
| public class BookServiceImpl implements BookService { |
| |
| private BookDao bookDao = new BookDaoImpl(); |
| public void save() { |
| bookDao.save(); |
| } |
| } |
# 提供依赖对象对应的 setter 方法
| public class BookServiceImpl implements BookService { |
| private BookDao bookDao; |
| public void save() { |
| bookDao.save(); |
| } |
| public void setBookDao(BookDao bookDao) { |
| this.bookDao = bookDao; |
| } |
| } |
# 配置 service 与 dao 之间的关系
| <?xml version="1.0" encoding="UTF-8"?> |
| <beans xmlns="http://www.springframework.org/schema/beans" |
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> |
| <bean id="bookDao" class="com.baozi.dao.impl.BookDaoImpl"></bean> |
| <bean id="bookService" class="com.baozi.service.impl.BookServiceImpl"> |
| |
| <property name="bookDao" ref="bookDao"></property> |
| </bean> |
| </beans> |
# bean 配置
# bean 的别名配置
类别 |
描述 |
名称 |
name |
类别 |
属性 |
所属 |
bean 标签 |
功能 |
定义 bean 的别名,可定义多个,使用逗号,分号,空格分隔 |
范例 |
<bean id="bookDao" name="dao bbookDaoImpl" class="com.baozi.dao.impl.BookDaoImpl"> |
- 获取 bean 无论通过 id 还是 name 获取,如果无法获取到,将抛出异常
NoSuchBeanDefinitionException
NoSuchBeanDefinitionException: No Bean named 'bookServiceImpl' available
# bean 作用范围配置
类别 |
描述 |
名称 |
scope |
类型 |
属性 |
所属 |
bean 标签 |
功能 |
定义 bean 的作用范围:1. singleton:单例(默认) 2. prototype:非单例 |
范例 |
<bean id="bookDao" class="com.baozi.dao.impl.BookDaoImpl" scope="prototype"> |
- 适合交给容易进行管理的 bean
- 表现层对象
- 业务层对象
- 数据层对象
- 工具对象
- 不适合交给容器进行管理的 bean
- 封装实体的域对象
# bean 的实例化
# 构造方法
# 提供可访问的无参构造方法
| public class BookDaoImpl implements BookDao { |
| public BookDaoImpl { |
| |
| } |
| public void save() { |
| System.out.println("book dao save ..."); |
| } |
| } |
# 配置
| <bean id="bookDao" class="com.baozi.dao.impl.BooKDaoImpl"></bean> |
无参方法如果不存在则抛出异常 BeanCreationException
# 静态工厂
# 工厂类
| public class OrderDaoFactory { |
| public static OrderDao getOrderDao() { |
| return new OrderDaoImpl(); |
| } |
| } |
# 配置
| <bean id="orderDao" class="com.baozi.factory.OrderDaoFactory" factory-method="getOrderDao"></bean> |
# 实例工厂
# 工厂类
| public class UserDaoFactory { |
| public UserDao getUserDao() { |
| return new UserDaoImpl(); |
| } |
| } |
# 配置
| <bean id="userDaoFactory" class="com.baozi.factory.UserDaoFactory"></bean> |
| <bean id="userDao" factory-method="getUserDao" factory-bean="userDaoFactory"></bean> |
# FactoryBean
# 创建 FactoryBean 类
| public class UserDaoFactoryBean implements FactoryBean<UserDao> { |
| public UserDao getObject() throws Exception { |
| return new UserDaoImpl(); |
| } |
| public Class<?> getObjectType() { |
| return UserDao.class; |
| } |
| } |
# 配置
| <bean id="userDao" class="com.baozi.factory.UserDaoFactoryBean"></bean> |
# Bean 的生命周期
# 提供生命周期控制方法
| public class BookDaoImpl implements BookDao { |
| public void save() { |
| System.out.println("book dao is running..."); |
| } |
| public void init() { |
| System.out.println("init ..."); |
| } |
| public void destory() { |
| System.out.println("destory ..."); |
| } |
| } |
# 配置生命周期控制方法
| <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" init-method="init" destory-method="destory"></bean> |
# 实现 InitializingBean,DisposableBean 接口
| public class BookServiceImpl implements BookService, InitializingBean, DisposableBean { |
| public void save() { |
| System.out.println("book dao is running..."); |
| } |
| public void afterPropertiesSet() throws Exception { |
| System.out.println("afterPropertiesSet..."); |
| } |
| public void destroy() throws Exception { |
| System.out.println("destory..."); |
| } |
| } |
# 生命周期流程
# 初始化容器
- 创建对象(内存分配)
- 执行构造方法
- 执行属性注入(set 操作)
- 执行 bean 初始化方法
# 使用 bean
- 执行业务操作
# 关闭 / 销毁容器
- 执行 bean 销毁操作
# 容器关闭前触发 bean 的销毁
# 关闭容器的方式
- 手工关闭容器:
ConfigurationApplicationContext接口close()操作
- 注册关闭钩子,在虚拟机退出前先关闭容器再退出虚拟机:
ConfigurableApplicationContext
接口 registerShutdownHook()
操作
| public class AppForLifeCycle { |
| public static void main(String[] args) { |
| ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); |
| ctx.close(); |
| } |
| } |
# 依赖注入
# setter 注入 —— 引用类型
# 在 bean 中定义引用类型属性并提供可访问的 set 方法
| public class BookServiceImpl implements BookService { |
| private BookDao bookDao; |
| public void setBookDao(BookDao bookDao) { |
| this.bookDao = bookDao; |
| } |
| } |
# 配置中使用 property 标签 ref 属性注入引用类型对象
| <bean id="bookService" class="com.baozi.service.impl.BookServiceImpl"> |
| <property name="bookDao" ref="bookDao"></property> |
| </bean> |
| <bean id="bookDao" class="com.baozi.dao.impl.BookDaoImpl"></bean> |
# setter 注入 —— 简单类型
# 在 bean 中定义引用类型属性并提供可访问的 set 方法
| public class BookDaoImpl implements BookDao { |
| private int connectionNumber; |
| public void setConnectionNumber(int connectionNumber) { |
| this.connectionNumber = connectionNumber; |
| } |
| } |
# 配置使用 property 标签 value 属性注入简单类型数据
| <bean id="bookDao" class="com.baozi.dao.impl.BookDaoImpl"> |
| <property name="connectionNumber" value="10"></property> |
| </bean> |
# 构造器注入 —— 引用类型
# 在 bean 中定义引用类型属性并提供可访问的构造方法
| public class BookServiceImpl implements BookService { |
| private BookDao bookDao; |
| public BookServiceImpl(BookDao bookDao) { |
| this.bookDao = bookDao; |
| } |
| } |
# 配置中使用 constructor-arg 标签 ref 属性引入引用类型对象
| <bean id="bookService" class="com.baozi.service.impl.BookServiceImpl"> |
| <constructor-arg name="bookDao" ref="bookDao"></constructor-arg> |
| </bean> |
| <bean id="bookDao" class="com.baozi.dao.impl.BookDaoImpl"></bean> |
# 构造器注入 —— 简单类型
# 在 bean 中定义引用类型属性并提供可访问的 set 方法
| public class BookDaoImpl implements BookDao { |
| private int connectionNumber; |
| public void setConnectionNumber(int connectionNumber) { |
| this.connectionNumber = connectionNumber; |
| } |
| } |
# 配置中使用 constructor-arg 标签 value 属性注入简单数据类型
| <bean id="bookDao" class="com.baozi.dao.impl.BookDaoImpl"> |
| <constructor-arg name="connectionNumber" value="10"></constructor-arg> |
| </bean> |
# 构造器注入 —— 参数适配
# 配置中使用 constructor-arg 标签 type 属性设置按参数类型注入
| <bean id="bookDao" class="com.baozi.dao.impl.BookDaoImpl"> |
| <construct-arg type="int" value="10"></construct-arg> |
| </bean> |
# 配置中使用 constructor-arg 标签 index 属性设置按形参位置注入
| <bean id="bookDao" class="com.baozi.dao.impl.BookDaoImpl"> |
| <constructor-arg index="0" value="10"></constructor-arg> |
| </bean> |
# 自动装配注入
- 自动装配用于引用类型依赖注入,不能对简单类型进行操作
- 使用按类型装配时(byType)必须保障容器中相同类型的 bean 唯一,推荐使用
- 使用按名称装配时(byType)必须保障容器中具有指定名称的 bean,因变量名与配置耦合,不推荐使用
- 自动装配优先级低于 setter 注入与构造器注入,同时出现时自动装配配置失效
# 集合注入
# 注入数组对象
| <property name="array"> |
| <array> |
| <value>100</value> |
| <value>200</value> |
| <value>300</value> |
| </array> |
| </property> |
# 注入 List 对象
| <property name="list"> |
| <list> |
| <value>baozi</value> |
| <value>baobao</value> |
| <value>xiaobaozi</value> |
| </list> |
| </property> |
# 注入 Set 对象
| <property name="set"> |
| <set> |
| <value>baozi</value> |
| <value>baobao</value> |
| <value>xiaobaozi</value> |
| </set> |
| </property> |
# 注入 Map 对象
| <property name="map"> |
| <map> |
| <entry key="1" value="baozi"/> |
| <entry key="2" value="xiaobaozi"/> |
| <entry key="3" value="baobao"/> |
| </map> |
| </property> |
# 注入 Properties 对象
| <property name="properties"> |
| <props> |
| <prop key="1">baozi</prop> |
| <prop key="2">xiaobaozi</prop> |
| <prop key="3">baobao</prop> |
| </props> |
| </property> |
# 数据源对象管理 bean
| <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> |
| <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> |
| <property name="url" value="jdbc:mysql://localhost:3306/student"></property> |
| <property name="username" value="root"></property> |
| <property name="password" value="root"></property> |
| </bean> |
# 加载 properties 文件
# 开启 context 命名空间
| <?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 http://www.springframework.org/schema/beans/spring-beans.xsd |
| http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd |
| "> |
| <context:property-placeholder location="jdbc.properties" /> |
| </beans> |
# 使用 context 命名空间,加载指定 properties 文件
| <?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 http://www.springframework.org/schema/beans/spring-beans.xsd |
| http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd |
| "> |
| <context:property-placeholder location="jdbc.properties" /> |
| </beans> |
# 使用 ${} 读取加载的属性值
| <?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 http://www.springframework.org/schema/beans/spring-beans.xsd |
| http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd |
| "> |
| <context:property-placeholder location="jdbc.properties" /> |
| <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> |
| <property name="driverClassName" value="${jdbc.driver}"/> |
| <property name="url" value="${jdbc.url}"/> |
| <property name="username" value="${jdbc.username}"/> |
| <property name="password" value="${jdbc.password}"/> |
| </bean> |
| </beans> |
# 容器
# 创建容器
类路径加载配置文件
| public class App { |
| public static void main(String[] args) { |
| ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); |
| } |
| } |
# 文件路径加载配置文件
| public class App { |
| public static void main(String[] args) { |
| ApplicationContext ctx = new FileSystemXmlApplicationContext("D:\\applicationContext.xml"); |
| } |
| } |
# 加载多个配置文件
| public class App { |
| public static void main(String[] args) { |
| ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml", "applicationContext2.xml"); |
| } |
| } |
# 获取 bean
# 使用 bean 名称获取
| public class App { |
| public static void main(String[] args) { |
| ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); |
| BookDao bookDao = (BookDao) ctx.getBean("bookDao"); |
| } |
| } |
# 使用 bean 名称获取指定类型
| public class App { |
| public static void main(String[] args) { |
| ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); |
| BookDao bookDao = ctx.getBean("bookDao", BookDao.class); |
| } |
| } |
# 使用 bean 类型获取
| public class App { |
| public static void main(String[] args) { |
| ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); |
| BookDao bookDao = ctx.getBean(BookDao.class); |
| } |
| } |
# 容器类层次结构图
# 注解开发
# 注解开发定义 bean
# 使用 @Component
定义 bean
| @Component("bookDao") |
| public class BookDaoImpl implements BookDao { |
| } |
| @Component |
| public class BookServiceImpl implements BookService { |
| } |
# 核心配置文件中通过组件扫描加载 bean
| <context:component-scan base-package="com.baozi"/> |
# Spring 提供了 @Component
注解的三个衍生注解
@Controller
:用于表现层 bean 定义
@Service
:用于业务层 bean 定义
@Repository
:用于数据层 bean 定义
| @Repository("bookDao") |
| public class BookDaoImpl implements BookDao { |
| } |
| @Service |
| public class BookServiceImpl implements BookService { |
| } |
# 纯注解开发
- Spring3.0 开启了纯注解开发模式,使用 Java 类替代配置文件,开启了 Spring 快速开发赛道
- Java 类代替 Spring 核心配置文件(代替 xml 配置文件)
@Configuration
注解用于设定当前类为配置类
@ComponentSacn
注解用于扫描路径,此注解只能添加一次,多个数据可以使用数组形式
| @Configuration |
| |
| @ComponentScan({"com.baozi.service", "com.baozi.dao"}) |
| public class SpringConfig { |
| } |
| public class App { |
| public static void main(String[] args) { |
| |
| AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); |
| } |
| } |
# bean 管理
# bean 作用范围
- 使用
@Scope
定义 bean 作用范围
- bean 的生命周期
| @Reponsitory |
| @Scope("singleton") |
| public class BookServiceImpl implements BookDao { |
| public BookDaoImpl() { |
| System.out.println("book dao constructor ..."); |
| } |
| @PostConstruct |
| public void init() { |
| System.out.println("book init..."); |
| } |
| @PreDestroy |
| public void destory() { |
| System.out.println("book Destory..."); |
| } |
| } |
# 依赖注入
# 使用 Autowired
注解开启自动装配模式
| @Service |
| public class BookServiceImpl implements BookService { |
| @Autowired |
| private BookDao bookDao; |
| public void save() { |
| System.out.println("book service save ..."); |
| bookDao.save(); |
| } |
| } |
- 自动装备基于反射设计创建对象并且暴力反射对应属性为私有属性初始化数据,因此无需提供 setter 方法
- 自动装配建议使用无参构造方法创建对象(默认),如果不提供无参构造房啊,请提供唯一构造方法
# 使用 @Qualifier
注解开启指定名称装配 bean
| @Service |
| public class BookServiceImpl implements BookService { |
| @Autowired |
| @Qualifier("bookDao") |
| private BookDao bookDao; |
| } |
@Qualifier
注解无法单独使用,必须配合 @Autowired
注解使用
# 使用 @Value
实现简单类型注入
| @Repository("bookDao") |
| public class BookDaoImpl implements BookDao { |
| @Value("100") |
| private String connectionNum; |
| } |
# 使用 @PropertySource
注解加载 properties 文件
| @Configuration |
| @ComponentScan("com.baozi") |
| @PropertySource("classpath:jdbc.properties") |
| public class SpringConfig { |
| } |
- 路径仅支持单一文件配置,多文件配置使用数组格式配置,不允许使用通配符 *
# 第三方 bean 管理
# 使用 @Bean
配置第三方 bean
| @Configuration |
| public class SpringConfig { |
| @Bean |
| public DataSource dataSource() { |
| DruidDataSource ds = new DruidDataSource(); |
| ds.setDriverClassName("com.mysql.jdbc.Driver"); |
| ds.setUrl("jdbc:mysql://localhost:3306/sstudent"); |
| ds.setUsername("root"); |
| ds.setPassword("root"); |
| return ds; |
| } |
| } |
# 使用独立的配置类管理第三方 bean
| public class JdbcConfig { |
| @Bean |
| public DataSource dataSource() { |
| DruidDataSource ds = new DruidDataSource(); |
| ds.setDriverClassName("com.mysql.jdbc.Driver"); |
| ds.setUrl("jdbc:mysql://localhost:3306/student"); |
| ds.setUsername("root"); |
| ds.setPassword("root"); |
| return ds; |
| } |
| } |
# 将独立配置类加入核心配置
方式一:使用 @Import
注解手动加入配置类到核心配置,此注解只能添加一次,多个数据采用数组格式
| public class JdbcConfig { |
| @Bean |
| public DataSource dataSource() { |
| DruidDataSource ds = new DruidDataSource(); |
| ds.setDriverClassName("com.mysql.jdbc.Driver"); |
| ds.setUrl("jdbc:mysql://localhost:3306/student"); |
| ds.setUsername("root"); |
| ds.setPassword("root"); |
| return ds; |
| } |
| } |
| @Configuration |
| @Import(JdbcConfig.class) |
| public class SpringConfig { |
| } |
方式二:使用 @ComponentScan
注解扫描配置所在的包,加载对应的配置类信息
| @Configuration |
| public class JdbcConfig { |
| @Bean |
| public DataSource dataSource() { |
| DruidDataSource ds = new DruidDataSource(); |
| ds.setDriverClassName("com.mysql.jdbc.Driver"); |
| ds.setUrl("jdbc:mysql://localhost:3306/student"); |
| ds.setUsername("root"); |
| ds.setPassword("root"); |
| return ds; |
| } |
| } |
| @Configuration |
| @ComponentScan("com.baozi.config") |
| public class SpringConfig { |
| } |
# 第三方 bean 依赖注入
# 简单类型注入
| public class JdbcConfig { |
| @Value("com.mysql.jdbc.Driver") |
| private String driver; |
| @Value("jdbc:mysql://localhost:3306/student") |
| private String url; |
| @Value("root") |
| private String username; |
| @Value("root") |
| private String password; |
| @Bean |
| public DataSource dataSource() { |
| DruidDataSource ds = new DruidDataSource(); |
| ds.setDriverClassName(driver); |
| ds.setUrl(url); |
| ds.setUsername(username); |
| ds.setPassword(password); |
| return ds; |
| } |
| } |
# 引用类型注入
引用类型注入只需要为 bean 定义方法设置形参即可,容器会根据类型自动装配对象
| public class JdbcConfig { |
| @Bean |
| public DataSource dataSource(BookService bookService) { |
| System.out.println(bookService); |
| DruidDataSource ds = new DruidDataSource(); |
| ds.setDriverClassName("com.mysql.jdbc.Driver"); |
| ds.setUrl("jdbc:mysql://localhost:3306/student"); |
| ds.setUsername("root"); |
| ds.setPassword("root"); |
| return ds; |
| } |
| } |
# XML 配置与注解配置
功能 |
XML 配置 |
注解 |
定义 bean |
bean 标签:id 属性,class 属性 |
@Component @Controller @Service @Repository @ComponentScan |
设置依赖注入 |
setter 注入,构造器注入 |
@Autowired @Qualifier @Value |
配置第三方 bean |
bean 标签,静态工厂,实例工厂,FactoryBean |
@Bean |
作用范围 |
scope 属性 |
@Scope |
生命周期 |
标准接口:init-method destory-method |
@PostController PreDestory |
# AOP
# AOP 简介
- AOP(Aspect Oriented Programming)面向切面编程,一种编程范式,指导开发者如何阻止程序结构
- 作用:在不惊动原始设计的基础上对其进行增强
- Spring 理念:无入侵式 / 无侵入式
# AOP 核心概念
- 连接点(JoinPoint):程序执行过程中的任意位置,粒度为执行方法,抛出异常,设置变量等
- 在 SpringAOP 中,理解为方法的执行
- 切入点(Pointcut):匹配连接点的式子
- 在 SpringAOP 中,一个切入点可以描述一个具体的方法,也可以匹配多个方
- 通知(Advice):在切入点处执行的操作,也就是共性功能
- 在 SpringAOP 中,功能最终以方法的形式呈现
- 通知类:定义通知的类
- 切面(Aspect):描述通知与切入点的对应关系
# AOP 入门案例
# 导入 aop 相关坐标
| <dependency> |
| <groupId>org.aspectj</groupId> |
| <artifactId>aspectjweaver</artifactId> |
| <version>1.9.4</version> |
| </dependency> |
# 定义 dao 接口与实现类
| public interface BookDao { |
| public void save(); |
| public void update(); |
| } |
| @Repository |
| public class BookDaoImpl implements BookDao { |
| public void save() { |
| System.out.println(System.currentTimeMillis()); |
| System.out.println("book dao save ..."); |
| } |
| public void update() { |
| System.out.println("book dao update ..."); |
| } |
| } |
# 定义通知类,制作通知
| public class Advice { |
| public void before { |
| System.out.println(System.currentTimeMillis()); |
| } |
| } |
# 定义切点
| public class Advice { |
| @Pointcut("excution(void com.baozi.dao.BookDao.update())") |
| private void pt(){} |
| } |
# 帮抵挡切入点与通知关系
| public class MyAdvice { |
| @Pointcut("excution(void com.baozi.dao.BookDao.update())") |
| private void pt(){} |
| @Before("pt()") |
| public void before() { |
| System.out.println(System.currentTimeMillis()); |
| } |
| } |
# 定义通知类接受 Spring 容器管理
| @Compoent |
| @Aspect |
| public class MyAdvice { |
| @Pointcut("excution(void com.baozi.dao.BookDao.update())") |
| private void pt(){} |
| @Before("pt()") |
| public void before() { |
| System.out.println(System.currentTimeMillis()); |
| } |
| } |
# 开启 Spring 对 AOP 注解驱动支持
| @Congiguration |
| @CompoentScan("com.baozi") |
| @EnableAspectJAutoProxy |
| public class SpringConfig { |
| } |
# AOP 工作流程
- Spring 容器启动
- 读取所有切面配置中的切入点
- 初始化 bean,判定 bean 对应的类中的方法是否匹配到任意切入点
- 匹配失败,创建对象
- 匹配成功,创建原始对象(目标对象)的代理对象
- 获取 bean 执行方法
- 获取 bean,调用方法并执行,完成操作
- 获取的 bean 是代理对象,根据代理对象的运行模式运行原始方法与增强的内容,完成操作
核心概念
- 目标对象(Target):原始功能去掉共性功能对应的类产生的对象,这种对象是无法直接完成最终工作的
- 代理(Proxy):目标对象无法直接完成工作,需要对其进行功能回填,通过原始对象的代理对象实现
# AOP 切入点表达式
# 表达式格式
切入点表达式标准格式:动作关键词(访问修饰符 返回值 包名。类名 / 接口名。方法名(参数)异常名)
| execution (public User com.baozi.service.UserService.findById(int)) |
- 动作关键字:描述切入点的行为动作
- 访问修饰符:public private 等
- 返回值
- 包名
- 类 / 接口名
- 方法名
- 参数
- 异常名:方法定义中抛出指定异常
# 通配符
- *:单个独立的任意符号,可以独立出现,也可以作为前缀或后缀的匹配符出现
- ..:多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写
- +:专用于匹配子类类型
# AOP 通知类型
# 前置通知
- 名称:
@Before
- 类型:方法注解
- 位置:通知方法定义上方
- 作用:设置当前通知方法与切入点之间的绑定关系,,当前通知方法在原始切入点方法前运行
- 相关属性:value(默认):切入点方法名,格式为类名。方法名 ()
| @Before("pt()") |
| public void before() { |
| System.out.println("before advice ..."); |
| } |
# 后置通知
- 名称:
@After
- 类型:方法注解
- 位置:通知方法定义上方
- 作用:设置当前通知方法与切入点之间的绑定关系,,当前通知方法在原始切入点方法后运行
- 相关属性:value(默认):切入点方法名,格式为类名。方法名 ()
| @After("pt()") |
| public void after() { |
| System.out.println("after advice ..."); |
| } |
# 环绕通知
- 名称:
@Around
- 类型:方法注解
- 位置:通知方法定义上方
- 作用:设置当前通知方法与切入点之间的绑定关系,,当前通知方法在原始切入点方法前后运行
| @Around("pt()") |
| public Object around(ProceedingJoinPoint pjp) throws Throwable { |
| System.out.println("around before advice ..."); |
| Object ret = pjp.proceed(); |
| System.out.println("around after advice ..."); |
| return ret; |
| } |
# 返回后通知
- 名称:
@AfterReturning
- 类型:方法注解
- 位置:通知方法定义上方
- 作用:设置当前通知方法与切入点之间的绑定关系,,当前通知方法在原始切入点方法正常执行完毕后运行
- 相关属性:value(默认):切入点方法名,格式为类名。方法名 ()
| @AfterReturning("pt()") |
| public void afterReturning() { |
| System.out.println("afterReturning advice ..."); |
| } |
# 抛出异常后通知
-
名称: @AfterThrowing
-
类型:方法注解
-
位置:通知方法定义上方
-
作用:设置当前通知方法与切入点之间的绑定关系,,当前通知方法在原始切入点方法运行抛出异常后执行
-
相关属性:value(默认):切入点方法名,格式为类名。方法名 ()
| @AfterThrowing("pt()") |
| public void afterThrowing() { |
| System.out.println("afterThrowing advice ..."); |
| } |
# AOP 通知获取数据
# 方法
- 获取切入点方法的参数
- JoinPoint:适用于前置、后置、返回后、抛出异常后通知
- ProceedJoinPoint:适用于环绕通知
- 获取切入点方法返回值
- 返回后通知
- 环绕通知
- 获取切入点方法运行异常信息
- 抛出异常后通知
- 环绕通知
# 实现
JoinPoint 对象描述了连接点方法的运行状态,可以获取到原始方法的适用参数
| @Before("pt()") |
| public void before(JoinPoint jp) { |
| Object[] args = jp.getArgs(); |
| System.out.println(Arrays.toString(args)); |
| } |
ProceedJointPoint 是 JoinPoint 的子类
| @Around("pt()") |
| public Object around(ProceedingJoinPoint pjp) throws Throwable { |
| Object[] args = pjp.getArgs(); |
| System.out.println(Arrays.toString(args)); |
| Object ret = pjp.proceed(); |
| return ret; |
| } |
抛出异常后通知可以获取切入点方法中出现的异常信息,适用形参可以接收对应的异常对象
| @AfterReturning(value = "pt()", returning = "ret") |
| public void afterReturning(String ret) { |
| System.out.println("afterReturning advice ..." + ret); |
| } |
环绕通知中可以手工书写对原始方法的调用,得到结果为原始方法的返回值
| @Around("pt()") |
| public Object around(ProceedingJoinPoint pjp) throws Throwable { |
| Object ret = pjp.proceed(); |
| return ret; |
| } |
# Spring 事务
# 1. Spring 事务简介
- 事物作用:在数据层保障一系列的数据库操作同成功同失败
- Spring 事务作用:在数据层或业务层保障一系列的数据库操作同成功同失败
# 早业务层接口上添加 Spring 事务管理
| public class AccountService { |
| @Transactional |
| public void transfer(String out, String in, Double money); |
| } |
# 设置平台事务管理器
| @Bean |
| public PlatformTransactionManager transactionManager(DataSource dataSource) { |
| DataSourceTransactionManager ptm = new DataSourceTransactionManager(); |
| ptm.setDataSource(dataSource); |
| return ptm; |
| } |
# 开启注解式事物驱动
| @Configuration |
| @ComponentScan("com.baozi") |
| @PropertySource("classpath:jdbc.properties") |
| @Import({JdbcConfig.class, MyBatisConfig.class}) |
| @EnableTransactionManagement |
| public class SpringConfig { |
| } |
# Spring 事务角色
- 事务管理员:发起事务方,在 Spring 中通常指代业务层开启事务的方法
- 事务协调员:加入事务方,在 Spring 中通常指代数据层方法,也可以是业务层方法
# Spring 事务属性
# 设置事务基本属性
属性 |
作用 |
示例 |
readOnly |
设置是否为只读事务 |
readOnly=true 只读事务 |
timeout |
设置事务超时时间 |
timeout=-1 永不超时 |
rollbackFor |
设置事务回滚异常(class) |
rollbackFor={NullPointException.class} |
rollbackForClassName |
设置事务回滚异常(String) |
同上为字符串格式 |
noRollbackFor |
设置事务不回滚异常(class) |
noRollbackFor={NullPointException.class} |
noRollbackForClassName |
设置事务不回滚异常(String) |
同上格式为字符串 |
propagation |
设置事务传播行为 |
... |
# 事务的传播行为
事务协调员对事物管理员所携带事务处理的态度