Spring5
Spring 5
Spring框架是一个开放源代码的J2EE应用程序框架,由[Rod Johnson](https://baike.baidu.com/item/Rod Johnson/1423612?fromModule=lemma_inlink)发起,是针对bean的生命周期进行管理的轻量级容器(lightweight container)。 Spring解决了开发者在J2EE开发中遇到的许多常见的问题,提供了功能强大IOC、AOP及Web MVC等功能。Spring可以单独应用于构筑应用程序,也可以和Struts、Webwork、Tapestry等众多Web框架组合使用,并且可以与 Swing等桌面应用程序AP组合。因此, Spring不仅仅能应用于J2EE应用程序之中,也可以应用于桌面应用程序以及小应用程序之中。Spring框架主要由七部分组成,分别是 Spring Core、 Spring AOP、 Spring ORM、 Spring DAO、Spring Context、 Spring Web和 Spring Web MVC
Spring 5 框架概述
- Spring 是轻量级的开源的 JavaEE 框架
- Spring 可以解决 企业应用开发的复杂性
- Spring 有两个核心部分:IOC 和  AOP - IOC :控制反转,把创建对象过程交给 Spring进行管理
- AOP:面向切面,不修改源代码进行功能增强
 
- Spring 特点- 方便解耦,简化开发
- AOP 编程支持
- 方便程序测试
- 方便和其他框架进行整合
- 方便进行事务操作
- 降低 API 开发难度
 
IOC
Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下:
IOC 理解
- 谁控制谁,控制什么: - 传统 JavaSE 程序设计,就直接在对象内部通过 new,来创建对象,是程序主动去创建对象的。而 IOC 是专门有一个容器来创建这些对象,即由 IOC 容器来控制对象的创建。 - 谁控制谁: 当然是 IOC 容器控制了对象。
- **控制什么:**只要控制了外部资源获取(不仅是对象,包括比如文件等)
 
- 为什么是反转,那些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取对象依赖对象,也就是正转。而反转则是由容器来帮忙创建及注入依赖对象。 - 为什么是反转:因为容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转
- **那些方面反转了:**依赖对象的获取被反转了
 
 
以上是传统的应用程序示意图
 
以上为有了 IOC 之后的程序结构图
IOC 能做什么
IOC 不是一种技术,只是一种思想,一种重要的 OOP 的法则,能够指导我们如何设计低耦合、更优良的程序。传统的应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了 IOC 容器之后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象和对象之间是松散耦合,这样也方便测试,易于功能复用,更重要的是 使得程序的整个体系结构变得非常灵活
其实 IOC 对编程带来的最大改变不是从代码上,而是从思想上,发生了 “主从换位” 的变化。程序原本是老大,要获取什么资源都是 主动出击,但是在 IOC/DI 思想中,应用程序就变成被动的了,被动的 等待 IOC 容器来创建并注入它所需要的资源了
IOC 很好的体现了 面向对象设计法则之一 —– 好莱坞法则: 别找我们,我们找你
即:由 IOC 容器帮对象找响应的依赖对象并注入,而不是由对象主动去找
IOC 底层原理
xml 解析、工厂模式、反射

IOC 接口(BeanFactory)
- IOC 思想基于 IOC 容器完成,IOC 容器底层就是对象工厂
- Spring 提供 IOC 容器实现两个方式(两个接口)- BeanFactory: IOC 容器基本实现,是 Spring 内部使用的 接口,不提供开发人员进行使用,加载配置文件的时候不会创建对象,在获取对象(使用)的时候才会去创建对象
- ApplicationContext: BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人员进行使用,加载配置问价你的时候就会把在配置文件中 的对象进行创建
 
- ApplicationContext 接口有实现类
 
IOC操作 Bean 管理
什么是 Bean 管理
- Bean 管理指的是两个操作- Spring 创建对象
- Spring 注入属性
 
- Bean 管理操作有两种操作- 基于 xml 配置文件方式实现
- 基于 注解方式实现
 
基于 xml
基于 xml 方式创建对象

- 在 spring 配置文件中,使用 bean 标签,标签里面添加对应属性,就可以实现对象创建
- 在 bean 标签中 有很多属性,常用属性如下- id 属性:唯一标识
- class 属性:类的全路径(包类路径)
 
- 创建对象时候,默认也是执行无参构造方法完成对象创建
基于 xml 方式 注入属性
- DI : 依赖注入,就是注入属性
set 方法进行注入
- 创建类,定义属性和对应的set 方法
 
- 在 spring 配置文件 配置对象创建,配置属性注入
 
使用 有参构造函数
- 创建类,定义属性,创建属性对应有参构造函数 
 
- 在 spring 配置文件中 进行配置 
 
p 名称空间注入
- 使用 p 名称空间注入,可以简化 基于 xml 配置方式 - 第一步 添加 p 名称空间在配置文件中 
- <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">- 1 
 2
 3
 4
 5
 6
 7
 `xmlns:p="http://www.springframework.org/schema/p"` 添加以上配置
 - 进行属性注入,在 bean 标签中进行操作
 - ```xml
 <bean id="orders" class="com.cs7eric.ioc.bean.Orders" p:name="diannao" p:address="zhengzhou"></bean>
 
xml 注入其他类型属性
- 字面量 - null 值 - 1 
 2
 3
 4
 5- <bean id="orders" class="com.cs7eric.ioc.bean.Orders"> 
 <property name="address">
 <null/>
 </property>
 </bean>
- 属性值包含特殊符号 - 1 
 2
 3- <property name="name" > 
 <value><![CDATA[<<南京>>]]></value>
 </property>
 
- 注入属性-外部 bean - 创建两个类 service 和 dao - 在 service 调用 dao 里面的方法 - 在 spring 配置文件中进行配置 - <!-- service 和 dao 对象的创建 --> <bean id="userService" class="com.cs7eric.ioc.service.UserService"> <!-- 注入 userDao 对象 name 属性: 类里面属性名称 ref 属性: 创建 userDao 对象 bean 标签的 id 值 --> <property name="userDao" ref="userDaoImpl"></property> </bean> <bean id="userDaoImpl" class="com.cs7eric.ioc.dao.UserDaoImpl"></bean>- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 - 注入属性 - 内部 bean
  一对多关系:部门和员工:一个部门有多个员工,一个员工属于一个部门,部门是一,员工是多
  在实体类之间表示一对多的关系,员工表示所属部门,使用对象类型属性进行表示
 - ```xml
 <bean id="emp" class="com.cs7eric.ioc.bean.Emp">
 <property name="ename" value="lucky"></property>
 <property name="gender" value="male"></property>
 
 <property name="dept">
 <bean id="dept" class="com.cs7eric.ioc.bean.Dept">
 <property name="dname" value="技术部"></property>
 </bean>
 </property>
 </bean>
 
- 级联赋值 - 第一种 - 1 
 2
 3
 4
 5
 6- <bean id="emp" class="com.cs7eric.ioc.bean.Emp"> 
 <property name="dept" ref="dept"></property>
 </bean>
 <bean id="dept" class="com.cs7eric.ioc.bean.Dept">
 <property name="dname" value="xxx"></property>
 </bean>
- 第二种 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13- <!-- 级联赋值 --> 
 <bean id="emp" class="com.cs7eric.ioc.bean.Emp">
 <!-- 设置两个普通属性 -->
 <property name="ename" value="lucky"></property>
 <property name="gender" value="male"></property>
 <!-- 级联赋值 -->
 <property name="dept" ref="dept"></property>
 <property name="dept.dname" value="技术部"></property>
 </bean>
 <bean id="dept" class="com.cs7eric.ioc.bean.Dept">
 <property name="dname" value="财务部"></property>
 </bean>
 
xml 注入集合属性
创建 类,定义数组、list、map、set 类型属性,生成 对应 set 方法
| 1 | public class Stu { | 
在 spring 配置文件中进行配置
| 1 | <bean id="stu" class="com.cs7eric.ioc.bean.Stu"> | 
集合里设置对象类型值
 
把 集合注入部分提取出来
在 spring 配置文件中引入 名称空间
| 1 | <beans xmlns="http://www.springframework.org/schema/beans" | 
使用 util 标签完成 list 集合注入提取
| 1 | <!-- 提取 list 集合 类型属性注入 --> | 
FactoryBean
- Spring 由两种类型 bean,一种普通 bean,另外一个工厂 bean (FactoryBean)
- 普通 bean :在配置文件中定义 bean 类型就是返回类型
- 工厂 bean :在配置文件定义 bean 类型可以和返回类型不一样- 第一步:创建类,让这个类作为 工厂 bean,实现接口 FactoryBean
- 第二步:实现接口里面的方法,在实现的方法中定义返回的 bean 类型
 
| 1 | public class MyBean implements FactoryBean<Course> { | 
| 1 | <bean id="myBean" class="com.cs7eric.ioc.factorybean.MyBean"></bean> | 
bean 作用域
- 在 spring 里面,设置创建 bean 实例是单实例还是多实例
- 在 spring 里面,默认情况下,bean 是单实例对象
- 如何设置单实例还是多实例- 在 spring 配置文件中 bean 标签里面有属性(scope)用于设置单实例还是多实例
- scope 属性值- 第一个值 默认值:singleton 表示是单实例对象
- 第二个值 prototype 表示是多实例对象
 
- singleton 和 prototype 的区别- singleton 是单实例 prototype 是多实例
- 设置 scope 值是 singleton 的时候,加载 spring 配置文件的时候就会创建单实例对象
- 设置 scope 值是 prototype 的时候,不是在加载 spring 配置文件的时候创建 对象,在调用 getBean 方法的时候创建 多实例对象
 
 
| 1 | <bean id="book" class="com.cs7eric.ioc.collectiontype.Book" scope="prototype"></bean> | 
bean 生命周期
- 生命周期- 从对象创建到对象销毁的过程
 
- bean 生命周期- 通过 构造器 创建 bean 实例(无参数构造)
- 为 bean 的属性设置值 和 对其他 bean 引用(调用 set 方法)
- 调用 bean 的初始化的方法 (需要进行配置初始化的方法)
- bean 可以使用了(对象获取到了)
- 当容器关闭的时候,调用 bean 的销毁方法(需要进行配置销毁的方法)
 
| 1 | public class Orders { | 
| 1 | <bean id="orders" class="com.cs7eric.ioc.bean.Orders" init-method="initMethod" destroy-method="destroyMethod"> | 
| 1 | 
 | 
 
- bean 的后置处理器,bean 的生命周期有 7 步 - 通过 构造器创建 bean 实例(无参数构造)
- 为 bean 的属性设置值 和 对其他 bean 引用 (调用 set 方法)
- **把 bean 实例传递给 bean 后置处理器的方法 postProcessBeforeInitialization **
- 调用 bean 的初始化方法(需要到配置文件中配置)
- **把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization **
- bean 可以使用了(对象获取到了
- 当容器关闭的时候,调用 bean的 销毁方法 (需要 在配置文件中配置销毁的方法
 
- 演示 添加 后置处理器效果 - 创建类,实现接口 BeanPostProcessor ,创建后置处理器 
- public class MyBeanPost implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("在初始化之前执行的方法"); return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName); } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("在初始化之后执行的方法"); return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName); } }- 1 
 2
 3
 4
 5
 - 配置后置处理器
 ```xml
 <bean id="myBeanPost" class="com.cs7eric.ioc.bean.MyBeanPost"></bean>
 
 
xml 自动装配
- 什么是自动装配- 根据指定装配规则(属性名称或者属性类型),Spring 自动将匹配的属性值进行注入
 
- 演示自动装配过程- 根据属性名称自动注入
- bean 标签属性 autowire 配置自动装配- byName :根据属性名称注入,注入值 bean 的 id 值 和 类属性名称一样
- byType : 根据 属性类型注入
 
 
byName
| 1 | <bean id="emp" class="com.cs7eric.ioc.bean.Emp" autowire="byName"></bean> | 
byType
| 1 | <bean id="emp2" class="com.cs7eric.ioc.bean.Emp" autowire="byType"></bean> | 
外部属性文件
- 直接配置数据库信息- 引入 druid 连接池
- 引入依赖 jar 包
 
| 1 | <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> | 
- 引入外部属性文件配置数据库连接池 - 创建外部属性文件,properties 格式文件,写数据信息 
- 把 外部 properties 属性文件 引入到 spring 配置文件中 - 引入 context 名称空间 
- <context:property-placeholder location="druid.properties"></context:property-placeholder>- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 ```xml
 <!-- 配置数据库连接池 -->
 <bean id="dataSource2" class="com.alibaba.druid.pool.DruidDataSource">
 <property name="driverClassName" value="${prop.driverClassName}"></property>
 <property name="url" value="${prop.url}"></property>
 <property name="username" value="${prop.username}"></property>
 <property name="password" value="${prop.password}"></property>
 </bean>
 
 
基于 注 解
什么是注解
- 注解是 代码特殊标记,格式:@注解名称(属性名称=值,属性名称=值…)
- 使用注解,直接作用在 类、方法、属性 上面
- 使用注解目的:简化 xml 配置
Spring 针对 bean 管理中 创建对象提供注解
- @Compoonent
- @Service
- @Controller
- @Reposity
上面四个注解功能是一样的 ,都可以用来创建 bean 实例
基于注解方式实现对象创建
- 第一步:引入依赖  
- 开启组件扫描 - 1 - <context:component-scan base-package="com.cs7eric"></context:component-scan> 
- 第三步 创建类,在类上面添加 创建对象注解 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 public class UserService {
 private UserDao userDao;
 public void setUserDao(UserDao userDao) {
 this.userDao = userDao;
 }
 public void add(){
 System.out.println("service add....");
 userDao.update();
 }
 }
开启组件扫描细节配置
- use-default-filters=”false” 表示现在不使用默认 filter ,自己配置 
- context:include-filter 设置扫描那些内容 - 1 
 2
 3
 4
 5- <context:component-scan base-package="com.cs7eric" use-default-filters="false"> 
 
 <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:include-filter>
 
 </context:component-scan>
- context:exclude-filter 设置那些内容不进行扫描 - 1 
 2
 3
 4
 5- <context:component-scan base-package="com.cs7eric" use-default-filters="false"> 
 
 <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:exclude-filter>
 
 </context:component-scan>
基于注解方式实现属性注入
- @AutoWired:根据属性类型进行自动装配 - 第一步:把 service 和 dao 对象进行创建,在 service 和 dao 类 添加创建对象注释
- 第二步:在 service 注入 dao 对象,在 service 类添加 dao 类型属性,在属性上面使用直接
 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 public class UserService {
 
 private UserDao userDao;
 public void setUserDao(UserDao userDao) {
 this.userDao = userDao;
 }
 public void add(){
 System.out.println("service add....");
 userDao.update();
 }
 }
- @Qualifier:根据名称进行注入 - 这个 @Qualifier 注解的使用,和上面 @Autowired 一起使用 
- @Autowired @Qualifier(value="userDaoImpl") private UserDao userDao;- 1 
 2
 3
 4
 5
 6
 7
 - @Resource:可以根据类型注入,可以根据名称注入
 - ```java
 @Resource //根据类型注入
 @Resource(name="userDaoImpl") //根据名称注入
 private UserDao userDao;
 
- @Value : 注入普通类型属性 - @Value(value="csq") private String name;- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 ##### 完全注解开发
 - 创建配置类,代替 xml配置文件
 - ```java
 @Configuration
 @ComponentScan(basePackages = "com.cs7eric")
 public class SpringConfig {
 }
 
- 编写测试类 - @Test public void testBean6(){ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); UserService userService = context.getBean("userService", UserService.class); System.out.println(userService); System.out.println(userService.getName()); userService.add(); }- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 
 ## AOP
 AOP为Aspect Oriented Programming的缩写,意为:[面向切面编程](https://baike.baidu.com/item/面向切面编程/6016335?fromModule=lemma_inlink),通过[预编译](https://baike.baidu.com/item/预编译/3191547?fromModule=lemma_inlink)方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是[OOP](https://baike.baidu.com/item/OOP?fromModule=lemma_inlink)的延续,是软件开发中的一个热点,也是[Spring](https://baike.baidu.com/item/Spring?fromModule=lemma_inlink)框架中的一个重要内容,是[函数式编程](https://baike.baidu.com/item/函数式编程/4035031?fromModule=lemma_inlink)的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的[耦合度](https://baike.baidu.com/item/耦合度/2603938?fromModule=lemma_inlink)降低,提高程序的可重用性,同时提高了开发的效率。
 可以通过[预编译](https://baike.baidu.com/item/预编译?fromModule=lemma_inlink)方式和运行期动态代理实现在不修改[源代码](https://baike.baidu.com/item/源代码?fromModule=lemma_inlink)的情况下给程序动态统一添加某种特定功能的一种技术。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,提高代码的灵活性和可扩展性,AOP可以说也是这种目标的一种实现。
 在Spring中提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和[事务](https://baike.baidu.com/item/事务?fromModule=lemma_inlink)(transaction)管理)进行[内聚性](https://baike.baidu.com/item/内聚性?fromModule=lemma_inlink)的开发。[应用对象](https://baike.baidu.com/item/应用对象?fromModule=lemma_inlink)只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如[日志](https://baike.baidu.com/item/日志?fromModule=lemma_inlink)或事务支持。
 ### 什么是 AOP
 ---
 面向 **切面** 编程,利用 AOP 可以对 业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
 **通俗描述**
 不通过修改源代码的方式,在主干功能里面添加新功能
 以登录示例
 <img src="https://cs7eric-image.oss-cn-hangzhou.aliyuncs.com/images/image-20221119205750988.png" alt="image-20221119205750988" style="zoom: 33%;" />
 ### AOP 底层原理
 ---
 AOP 底层使用 动态代理
 有两种情况动态代理
 - 有接口的情况 使用 JDK 动态代理
 - 创建接口实现类代理对象,增强类的方法
 - <img src="https://cs7eric-image.oss-cn-hangzhou.aliyuncs.com/images/image-20221119210454455.png" alt="image-20221119210454455" style="zoom:33%;" />
 - 没有 接口 的情况 使用 CGLIB 动态代理
 - 创建子类的代理对象,增强类的方法
 - <img src="https://cs7eric-image.oss-cn-hangzhou.aliyuncs.com/images/image-20221119210649055.png" alt="image-20221119210649055" style="zoom:33%;" />
 ### AOP - JDK 动态代理
 ---
 使用 JDK 动态代理,使用 Proxy 类里面的方法 创建代理对象
 <img src="https://cs7eric-image.oss-cn-hangzhou.aliyuncs.com/images/image-20221119210927637.png" alt="image-20221119210927637" style="zoom:25%;" />
 #### 调用 newProxyInstance 方法
 <img src="https://cs7eric-image.oss-cn-hangzhou.aliyuncs.com/images/image-20221119211017494.png" alt="image-20221119211017494" style="zoom: 50%;" />
 方法有三个参数
 - 第一参数:类加载器
 - 第二参数:增强方法所在的类,这个类实现的接口,支持多个接口
 - 第三参数:实现这个接口 InvocationHandler ,创建 代理对象,写增强的部分
 #### 编写 JDK 动态代理代码
 ```java
 public class JDKProxy {
 public static void main(String[] args) {
 //创建接口实现类代理对象
 Class[] interfaces = {UserDao.class};
 UserDaoImpl userDao = new UserDaoImpl();
 UserDao useDao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
 int result = userDao.add(1, 2);
 System.out.println(result);
 }
 }
 //创建代理对象代码
 class UserDaoProxy implements InvocationHandler {
 private Object obj;
 public UserDaoProxy(Object obj) {
 this.obj = obj;
 }
 //增强的逻辑
 @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 //方法之前
 System.out.println("方法之前执行..." + method.getName() + "传递的参数" + Arrays.toString(args));
 //被增强的方法执行
 Object res = method.invoke(obj, args);
 //方法之后
 System.out.println("方法之后执行..." + obj);
 return res;
 }
 public UserDaoProxy(UserDaoImpl userDao) {
 }
 }
 
AOP 术语
- 连接点- 类里面有哪些方法可以被增强,这些方法 称为 连接点
 
- 切入点- 实际被真正增强的方法,称为切入点
 
- 通知(增强)- 实际增强的逻辑部分称为通知(增强
- 通知有多种类型- 前置通知
- 后置通知
- 环绕通知
- 异常通知
- 最终通知
 
 
- 切面- 是动作,把通知应用到切入点的过程
 
AOP 操作(准备工作
- Spring 框架 一般都是基于 AspectJ 实现 AOP 操作 - AspectJ 不是 Spring 的组成部分,是独立的 AOP 框架,一般把 AspectJ 和 Spring 框架一起使用,进行 AOP 操作
 
- 基于 AspectJ 实现 AOP 操作 - 基于 xml 配置文件实现
- 基于注解方式实现
 
- 在项目工程里面引入相关依赖 
- 切入点表达式 - 切入点表达式作用:知道对哪个类里面的哪个方法进行增强 
- 语法结构: - execution([权限修饰符][返回类型][类全路径][方法名称]([参数列表]))- 对 com.cs7eric.dao.BookDao 类里面的 add 进行增强 - execution(* com.cs7eric.dao.BookDao.add(..))
- 对 com.cs7eric.dao.BookDao 类里面的 所有方法 进行增强 - execution(* com.cs7eric.dao.BookDao.*(..))
- 对 com.cs7eric.dao包里 所有 类里面的 所有方法 进行增强 - execution(* com.cs7eric.dao.*.*(..))
 
 
AOP 操作(AspectJ 注解
- 创建类,在类里面定义方法
| 1 | public class User { | 
- 创建增强类(编写增强逻辑 - 在增强类里面,创建方法,让不同方法代表不同通知类型 
- public class UserProxy { public void before(){ System.out.println("before ..."); } }- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 
 - 进行通知的配置
 - 在 Spring 配置文件中,开启注解扫描
 - ```xml
 <context:component-scan base-package="com.cs7eric.aop.aopanno"></context:component-scan>
- 使用 注解创建 User和 UserProxy 对象 
- 在 增强类上面添加注释 @Aspect 
- 在 spring 配置文件中 开启自动生成代理对象 - <!-- 开始 Aspect 生成代理对象 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 - 配置不同类型的通知
 - 在 增强类的里面,在作为 通知方法上面 添加通知类型注解,使用切入点表达式
 - ```java
 @Aspect
 @Component
 public class UserProxy {
 
 //前置通知
 @Before(value = "execution(* com.cs7eric.aop.aopanno.User.add(..))")
 public void before(){
 System.out.println("before ...");
 }
 
 //后置通知
 @After(value = "execution(* com.cs7eric.aop.aopanno.User.add(..))")
 public void after(){
 System.out.println("after...");
 }
 
 //后置通知(返回通知)
 @AfterReturning(value = "execution(* com.cs7eric.aop.aopanno.User.add(..))")
 public void afterReturning(){
 System.out.println("afterReturning...");
 }
 
 //异常通知
 @AfterThrowing(value = "execution(* com.cs7eric.aop.aopanno.User.add(..))")
 public void afterThrowing(){
 
 System.out.println("afterThrowing...");
 }
 
 //环绕通知
 @Around(value = "execution(* com.cs7eric.aop.aopanno.User.add(..))")
 public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
 
 System.out.println("环绕之前");
 proceedingJoinPoint.proceed();
 System.out.println("环绕之后");
 }
 }
 
 
- 相同切入点抽取 - @Pointcut(value = "execution(* com.cs7eric.aop.aopanno.User.add(..))") public void pointDemo(){ } //前置通知 @Before(value = "pointDemo()") public void before(){ System.out.println("before ..."); }- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 - 有多个增强类对同一个方法进行增强,设置增强优先级
 - 在增强类上面添加注解,**@Order(数字类型值)** ,数字类型值越小 优先级越高
 - <img src="https://cs7eric-image.oss-cn-hangzhou.aliyuncs.com/images/image-20221119231055663.png" alt="image-20221119231055663" style="zoom:67%;" />
 - 完全注解开发
 - 创建配置类,不需要创建 xml 配置文件
 - ```java
 @Configuration
 @ComponentScan(basePackages = {"com.cs7eric"})
 @EnableAspectJAutoProxy(proxyTargetClass = true)
 public class ConfigAop {
 }
 
AOP 操作(AspectJ 配置文件
- 创建两个类,增强类和被增强类,创建方法 
- 在 Spring 配置文件中创建两个类对象 - <bean id="book" class="com.cs7eric.aop.aopxml.Book"></bean> <bean id="bookProxy" class="com.cs7eric.aop.aopxml.BookProxy"></bean>- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 - 在 Spring 配置文件中 配置切入点
 - ```xml
 <aop:config>
 <!-- 配置切入点 -->
 <aop:pointcut id="p" expression="execution(* com.cs7eric.aop.aopxml.Book.add(..))"></aop:pointcut>
 <!-- 配置切面 -->
 <aop:aspect ref="bookProxy">
 <!-- 增强作用在具体的方法上-->
 <aop:before method="before" pointcut-ref="p"></aop:before>
 </aop:aspect>
 </aop:config>
 
JdbcTemplate
Spring 框架对 JDBC 进行封装,使用 JdbcTemplate 方便实现对数据库操作
- 引入相关依赖  
- 在 Spring 配置文件配置数据库连接池 - <!-- 配置数据库连接--> <context:property-placeholder location="druid.properties"></context:property-placeholder> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="url" value="${prop.url}"></property> <property name="driverClassName" value="${prop.driverClassName}"></property> <property name="username" value="${prop.username}"></property> <property name="password" value="${prop.password}"></property> </bean>- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 - 配置 JdbcTemplate 对象,注入 DataSource
 - ```xml
 <!-- 配置 jdbctemplate 对象-->
 <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
 <!-- 注入 dataSource -->
 <property name="dataSource" ref="dataSource"></property>
 </bean>
 
- 创建 Service 类,创建 dao 类,在 dao 注入 jdbcTemplate 对象 - 配置文件 - <!-- 组件扫描--> <context:component-scan base-package="com.cs7eric"></context:component-scan>- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 - Service
 - ```java
 @Service
 public class BookService {
 
 //注入 dao
 @Autowired
 private BookDao bookDao;
 
 }
 
- BookDao - @Repository public class BookDaoImpl implements BookDao{ //注入 jdbcTemplate @Autowired private JdbcTemplate jdbcTemplate; @Override public void add() { } }- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 ### JdbcTemplate 操作 数据库
 ---
 对应数据库创建实体类
 ```java
 public class Book {
 
 private Integer id;
 private String name;
 public Book(Integer id, String name) {
 this.id = id;
 this.name = name;
 }
 public Book() {
 }
 public Integer getId() {
 return id;
 }
 public void setId(Integer id) {
 this.id = id;
 }
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 }
 @Override
 public String toString() {
 return "Book{" +
 "id=" + id +
 ", name='" + name + '\'' +
 '}';
 }
 }
 
 
添加操作
- 编写 service 和 dao - 在 dao 进行数据库添加操作 
- 调用 JdbcTemplate 对象里面 update 方法  
- 有两个参数- 第一个参数:sql 语句
- 第二个参数:可变参数,设置 SQL 语句值
 
 
- @Repository public class BookDaoImpl implements BookDao{ //注入 jdbcTemplate @Autowired private JdbcTemplate jdbcTemplate; @Override public void add() { } @Override public void addBook(Book book) { String sql = "INSERT INTO t_book VALUES(?,?)"; Object[] args = {book.getId(),book.getName()}; int update = jdbcTemplate.update(sql, args); System.out.println(update); } }- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 - 测试类
 - ```java
 @Test
 public void addBook() {
 
 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("jdbctemplate.xml");
 BookService bookService = context.getBean("bookService", BookService.class);
 bookService.addBook(new Book(1,"csq"));
 }
 
修改
| 1 | /** | 
删除
| 1 | 
 | 
查询返回某个值
- 查询表里面有多少条记录,返回是某个值 
- 使用 JdbcTemplate 实现查询返回某个值  - 有两个参数- 第一个参数:SQL 语句
- 第二个参数:返回类型 Class
 
 
- 有两个参数
- @Override public void selectCount() { String sql = "SELECT COUNT(*) FROM t_book"; Integer count = jdbcTemplate.queryForObject(sql, Integer.class); System.out.println(count); }- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 #### 查询返回对象
 - 场景:查询图书详情
 - JdbcTemplate 实现查询返回对象
 - 
 - 有三个参数
 - 第一个参数:SQL 语句
 - 第二个参数:RowMapper 是接口,针对返回不同类型数据,使用这个接口里面实现类完成数据封装
 - 第三个参数:SQL 语句填充值
 - ```java
 @Override
 public Book queryForBook(Integer id) {
 
 String sql = "SELECT * FROM t_book WHERE id=?";
 Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class), id);
 return book;
 }
 
查询返回集合
- 场景:查询图书列表分页 
- 调用 JdbcTemplate 方法实现查询返回集合  
- 有三个参数 - 第一个参数:SQL 语句
- 第二个参数:RowMapper 是接口,针对返回不同类型数据,使用这个接口里面实现类完成数据封装
- 第三个参数:SQL 语句值
 
- @Override public List<Book> queryBooks() { String sql = "SELECT * FROM t_book"; return jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class)); }- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 #### 批量操作
 - 批量操作:操作表里面多条记录
 - JdbcTemplate 实现批量添加操作
 - 
 - 有两个参数
 - SQL 语句
 - List 集合,添加多条数据
 - ```java
 @Override
 public void batchAddBook(List<Object[]> batchArgs) {
 String sql = "INSERT INTO t_book VALUES(?,?)";
 int[] ints = jdbcTemplate.batchUpdate(sql,batchArgs);
 System.out.println(Arrays.toString(ints));
 }
 
批量修改和批量删除操作和批量添加一样,更改一下SQL 语句即可
事务操作
事务相关概念在 MySQL 和 JDBC 里
搭建事务操作环境
模拟事务环境
Spring 事务管理介绍
- 事务添加到 JavaEE 三层结构里面 Service 层(业务逻辑层)
- 在 Spring 进行 事务管理操作- 有两种方式- 编程式事务管理
- 声明式事务管理
 
 
- 有两种方式
- 声明式事务管理- 基于 注解方式
- 基于xml 方式
 
- 在 Spring 进行声明式事务管理,底层使用 AOP 原理
- Spring 事务管理 API- 提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类
 
注解声明式事务管理
- 在 Spring 配置文件配置事务管理器 - <!-- 创建事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 注入数据源--> <property name="dataSource" ref="dataSource"></property> </bean>- 1 
 2
 3
 4
 5
 6
 7
 8
 - 在 Spring 配置文件 开启事务注解
 - 在 Spirng 配置文件中 引入名称空间
 - ```xml
 xmlns:tx="http://www.springframework.org/schema/tx"
 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
- 开启 事务注解 - <!-- 开启事务注解--> <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 - 在 service 类上面(或者 service 类里面的方法上) 添加事务注解
 - @Transactional ,这个注解添加到类上面,也可以添加到方法上面
 - 如果把这个注解添加到类上面,这个类里面的所有方法都添加事务
 - 如果把这个注解添加到方法上面,就是为这个方法添加事务
 - ```java
 @Service
 @Transactional
 public class UserService {
 
 
声明式事务管理参数配置
- 在 service 类上面添加注解 @Transactional,在这个注解里面配置事务相关参数 
- propagation :事务传播行为 - 当一个事务被另一个事务调用的时候,这个事务如何进行 
- 事务的传播行为可以由传播属性指定,Spring 定义了 7 种类传播行为 
- @Service @Transactional(propagation = Propagation.REQUIRED) public class UserService {- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 - ioslation 事务隔离级别
 - 事务有特性隔离性,多事务操作之间不会产生影响,不考虑隔离性产生很多问题
 - 有三个读问题: 脏读,不可重复读,虚(幻)读
 - 脏读:一个未提交事务读取到另一个未提交事务的数据
 - 
 - 不可重复读:一个未提交事务读取到另一个事务修改数据
 - 
 - 虚读:一个未提交事务读取到另一个提交事务添加数据
 - **解决:通过设置事务隔离级别 解决读问题**
 - 
 - ```java
 @Service
 @Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)
 public class UserService {
 
- timeout 超时时间 - 事务需要在一定时间内提交,如果不提交进行回滚
- 默认为 -1,设置时间以秒为单位
 
- readOnly 是否只读 - 读:查询操作,写:添加修改删除操作
- readOnly 默认值为false 表示可以查询,可以添加修改删除操作
- 设置 readOnly 值为 true ,设置成 true 之后,只能查询
 
- rollbackFor 回滚 - 设置那些 异常进行回滚
 
- noRollbackFor - 设置那些 异常不回滚
 
xml 声明式事务管理
- 在 spring 配置文件中进行配置 - 第一步:配置 事务管理器
- 第二步:配置通知
- 第三步:配置切入点和切面
 
- <!-- 创建事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 注入数据源--> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置通知--> <tx:advice id="txadvice"> <!-- 配置事务参数--> <tx:attributes> <!-- 指定那种规则的方法上面添加事务--> <tx:method name="accountMoney" propagation="REQUIRED"></tx:method> </tx:attributes> </tx:advice> <!-- 配置切入点和切面--> <aop:config> <!-- 配置切入点--> <aop:pointcut id="pt" expression="execution(* com.cs7eric.transaction.service.UserService.*(..))"></aop:pointcut> <!-- 配置切面--> <aop:advisor advice-ref="txadvice" pointcut-ref="pt"></aop:advisor> </aop:config>- ```java @Test public void accountMoney() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TransactionConfig.class); UserService userService = context.getBean("userService", UserService.class); userService.accountMoney(); }- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 ### 完全注解声明式事务管理
 ---
 - 创建配置类,使用配置类代替 xml 配置文件
 - ```java
 @Configuration //配置类
 @ComponentScan(basePackages = "com.cs7eric") //组件扫描
 @EnableTransactionManagement //开启事务
 public class TransactionConfig {
 
 //创建数据库连接
 @Bean
 public DruidDataSource getDruidDataSource(){
 
 DruidDataSource dataSource = new DruidDataSource();
 dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
 dataSource.setUrl("jdbc:mysql:///t_book");
 dataSource.setUsername("root");
 dataSource.setPassword("C020611.");
 return dataSource;
 }
 
 //创建 JdbcTemplate 对象
 @Bean
 public JdbcTemplate getJdbcTemplate(DataSource dataSource){
 //到 ioc 容器中根据类型找到 dataSource
 JdbcTemplate jdbcTemplate = new JdbcTemplate();
 //注入 dataSource
 jdbcTemplate.setDataSource(dataSource);
 return jdbcTemplate;
 }
 
 //创建 事务管理器
 @Bean
 public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
 DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
 dataSourceTransactionManager.setDataSource(dataSource);
 return dataSourceTransactionManager;
 }
 }
 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 ## Spring 5 框架新功能
 - 整个 Spring 5 框架的代码基于 Java 8 ,运行时兼容 JDK 9,许多不建议使用的类和方法在代码库中删除
 - Spring 5 框架自带了 通用的日志封装
 - Spring 5 已经移除了 Log4jConfigListener ,官方建议使用 Log4j2
 - Spring 5 框架已经整合了 Log4j2
 - 第一步:引入依赖 jar 包
 - 
 - 第二步:创建 log4j2.xml 配置文件
 - ```xml
 <?xml version="1.0" encoding="UTF-8"?>
 <!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE> ALL-->
 <!-- Configuration 后面的 status 用于设置 log4j2 自身内部的信息输出,可以不设置
 当设置成 trace 时,可以看到 log4j2 内部各种详细输出-->
 <configuration status="INFO">
 <!-- 先定义所有的 appender -->
 <appenders>
 <!-- 输出日志信息到控制台-->
 <console name="Console" target="SYSTEM_OUT">
 <!-- 控制日志输出的格式-->
 <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
 </console>
 </appenders>
 
 <!-- 然后定义 logger ,只有定义 logger 并引入 到 appender , appender 才会生效-->
 <!-- root : 用于 指定项目的根日志,如果没有单独指定 logger 则会使用 root 作为默认的日志输出-->
 <loggers>
 <root level="info">
 <appender-ref ref="Console"></appender-ref>
 </root>
 </loggers>
 </configuration>
- Spring 5 框架 核心容器支持 @Nullable 注解 - @Nullable 注解可以使用在方法, 属性, 参数上面,表示方法返回可以为空,属性值可以为空,参数值可以为空 - 方法 参数: - @Nullable public void testNullable(@Nullable String name){ System.out.println(name); }- 1 
 2
 3
 4
 5
 6
 - 属性
 - ```java
 @Nullable
 private String test;
 
 
 
- Spring 5 核心容器支持函数式风格 GenericApplicationContext - //函数式创建对象,交给 Spring 管理 @Test public void testGenericApplicationContext(){ //创建 GenericApplicationContext 对象 GenericApplicationContext context = new GenericApplicationContext(); //调用 context 方法 对象注册 context.refresh(); context.registerBean("user", User.class,()->new User()); //获取对象 User user = context.getBean("user", User.class); System.out.println(user); }- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 - Spring 支持整合 Junit5
 - 整合 Junit4
 - 第一步:引入相关依赖
 - 
 - 
 - 第二步:创建测试类,使用注解方式完成
 - ```java
 @RunWith(SpringJUnit4ClassRunner.class)
 @ContextConfiguration("classpath:tx.xml")
 public class TestJunit4 {
 
 @Autowired
 private UserService userService;
 
 @Test
 public void test1(){
 
 userService.accountMoney();
 }
 
 }
- 整合 Junit 5 - 第一步:创建测试类,使用注解完成 - @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:tx.xml") public class TestJunit5 { @Autowired private UserService userService; @Test public void testJunit5(){ } }- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 - 使用一个复合注解代替上面的两个注解完成整合
 - ```java
 @SpringJUnitConfig(locations = "classpath:tx.xml")
 public class TestJunit5 {
 
 @Autowired
 private UserService userService;
 
 @Test
 public void testJunit5(){
 }
 }
 
 
 
- Title: Spring5
- Author: cccs7
- Created at : 2022-11-10 00:00:00
- Updated at : 2024-09-13 11:07:59
- Link: https://cs7eric.github.io/2022/11/10/Spring5/
- License: This work is licensed under CC BY-NC-SA 4.0.
















