一、概念
- 轻量级的开源JavaEE框架
- IOC:控制反转,把创建对象过程交给Spring进行管理
- Aop:面向切面,不修改源代码进行功能增强
二、IOC容器
2.1 IOC底层原理
- XML解析、工厂模式、反射
2.2 IOC容器实现方式
2.2.1 两个接口
- BeanFactory:IOC容器基本实现,是Spring内部的使用接口,不提供开发人员使用
- ApplicationContext:BeanFactory的子接口,提供更多更强大的功能,一般由开发人员进行使用
- ApplicationContext实现类:FileSystemApplicationContext/ClassPathXmlApplicationContext
2.3 Bean管理
2.3.1 两个操作
- Spring创建对象
- Spring注入属性
2.3.2 操作Bean管理对象(基础操作/基于XML)
(1)创建/获取对象
1 | <bean id="user" class="com.company.spring5.User"></bean> |
- 在Spring配置文件中,使用bean标签,标签里面加对应属性,就可以实现对象创建
- bean标签属性
- id:唯一标识,不可以加特殊符号
- class:类全路径(包类路径)
- 创建对象时候,默认也是执行无参数构造方法完成对象创建
1 | // 获取XML文件名获取应用上下文 |
(2)注入属性
- DI:依赖注入,就是注入对象属性值
(3)注入方式
- 第一种:使用set方法进行注入
1 | // 创建类 |
1 | <!-- |
- 第二种:使用有参数构造进行注入
1 | // 创建类 |
1 | <!-- |
- 第三种:p名称空间注入(了解)
1 | <!-- |
(4) 注入其他类型属性
- 字面量
- null值
- 属性值包含特殊符号
1 | <!-- |
(4) 注入外部bean
- 比如Service层调用Dao层对象,需要在Bean中进行注入
1 | <!-- |
(5) 注入内部bean和级联赋值
一对多关系:部门和员工
- 一个部门有多个员工,一个员工属于一个部门
- 在实体类之间表示一对多关系,员工表示所属部门,使用对象类型属性
实体类
1 | // 部门类,生成set方法方便注入 |
- 注入内部bean
1 | <bean id="emp" class="com.company.Emp"> |
- 级联赋值
1 | <!--第一种写法--> |
2.3.3 操作Bean管理对象(注入集合类型)
(1)注入数组/List/Map/Set类型
- 为学生类注入集合属性
1 | public class Stu { |
1 | <!-- |
(2)集合中注入对象类型值
1 | public class Stu { |
1 | <!-- |
(4)把集合注入部分提取出来(公共属性)
- 在spring配置文件中引入util名称空间
- 使用util:list创建属性
1 | <!-- |
2.3.4 操作Bean管理对象(工厂Bean)
- Spring有两种类型bean,一种普通bean,另外一种工厂bean
- 普通bean:在配置文件中定义bean类型就是返回类型
- 工厂bean:在配置文件定义bean类型可以和返回类型不一样
- 第一步:创建类,让这个类作为工厂bean,实现接口FactoryBean
- 第二步:实现接口里面的方法,在实现的方法中定义返回的bean类型
1 | // 创建工厂类,实现接口FactoryBean |
1 | <!-- |
1 | // 测试方法,根据applicationContext获取到的对象应该使用具体的类,这里使用的是User,所以根据注入的工厂bean获取到的的就是User对象 |
2.3.5 操作Bean管理对象(Bean作用域)
(1)bean的性质
- Spring中,默认情况下,创建的bean是单实例对象,也就是注入的一个bean被两个引用变量获取到后,引用的地址都是相同的
(2)设置单实例/多实例
- 在spring配置文件标签里面有属性scope用于设置bean是单实例还是多实例
- scope属性值
- 第一个:默认值,singleton,表示实例为单实例对象
- 第二个:prototype,表示是多实例对象
1 | <bean id="book" class="com.company.spring5.User" scope="prototype"> |
(3)singleton和prototype区别
- singleton表示单实例,prototype表示多实例
- 设置scope值是singleton时候,加载spring配置文件(java的ApplicationContext对象创建的时候)时,就会创建单实例对象,单实例对象不管获取多少次地址都是相同的
- 设置scope值是prototype时,不是在加载spring配置文件时创建,而是在调用getBean方法的时候才创建多实例对象,多实例对象的地址都是不一样的
2.3.6 操作Bean管理对象(Bean生命周期)
(1)生命周期
- 从对象创建到对象销毁的过程
(2)bean生命周期
- 通过构造器创建bean实例(无参数构造)
- 为bean的属性设置值和对其他bean的引用(调用set方法)
- 把bean实例传递bean前置处理器
- 调用bean的初始化的方法(需要进行配置)
- 把bean实例传递bean后置处理器
- bean可以被获取到了
- 当容器关闭时候,调用bean的销毁的方法(需要进行配置销毁的方法)
(3)演示bean生命周期
- 实体类,配置无参构造,初始化方法initMethod,销毁方法destory
1 | public class Order { |
- bean注入
1 | <!-- |
- 测试
1 | ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); |
- 结果
(4)后置处理器
- bean实例初始化前后的操作,具体操作如下
- 创建类,实现接口BeanPostProcessor,创建后置处理器
1 | // 继承BeanPostProcessor的类可以重写方法 |
- 配置bean
1 | <bean id="myOrder" class="com.company.Order" init-method="initMethod" destroy-method="destory"> |
2.4 xml自动装配
- 根据指定装配规则(属性名称或者属性类型),Spring自动将匹配的属性值进行注入
1 | <!-- |
2.5 IOC操作bean管理(外部属性文件)
- 使用情况:一般在spring项目要配置properties配置文件,填写数据库信息,这里就需要使用xml以引入外部文件的方式,配置连接数据库的bean
直接配置数据库信息
- 配置德鲁伊druid连接池
引入外部属性文件配置数据库连接池
1 | <!-- |
2.6 IOC操作Bean管理(基于注解方式)
2.6.1 Spring提供的注解
- @Component
- @Service
- @Controller
- @Repository
- 以上的注解功能都是一样的,都可以用来创建bean实例,主要是方面理解
2.6.2 注解方式创建对象
- 第一步:引入spring-aop包
- 第二步:在bean配置文件中,开启组件扫描
1 | <context:component-scan base-package="包地址 包地址 ..." /> |
第三步:在类上添加注解,spring即可扫描到该类并完成对象的注入
默认注入bean的id为类名称首字母小写:如UserService=>userService
- 注解Repository(value=”userSerivice”)中的value值等同于bean的id值
2.6.3 注解方式注入属性
- 提供的注解
- @Autowired:根据属性类型进行自动装配
- @Qualifier:根据属性名称进行注入,@Qualifier(value=”注入类的value/id值”),需要和@Autowired一起使用,就是叠加在一起作用在属性上
- @Resource:可以根据类型/名称注入,@Resource默认按照属性类型进行自动装配,@Resource(name=””)即可以按照属性名称进行注入
- @Value:完成普通属性的注入,如@Value(value=”123”),可以为String类型属性注入属性值
- 以上操作都需要在bean配置文件中开启扫描才能运行,意思是在获取注入的对象时,仍然要用ApplicationContext获取到bean配置文件
2.6.4 完全注解开发
- 第一步:创建配置类config/,这一步完成后就可以删除bean配置文件,因为这一步的作用和bean配置文件是一样的
1 |
|
- 第二步:获取对象
1 | ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class) |
三、AOP
3.1 概念
- 面向切面编程:在不修改源代码的情况下在主干功能里添加新的功能模块
3.2 AOP底层原理
3.2.1 动态代理
Spring5已经对动态代理做了封装,可以直接调用,需要的知识点:反射
第一种:有接口情况,使用JDK动态代理
- 创建接口实现类代理对象,增强类的方法
- 第二种:没有接口情况,使用CGLIB动态代理
3.2.2 AOP(JDK动态代理)
- JDK动态代理,使用Proxy类里面的方法创建代理对象
- 调用newProxyInstance方法
- newProxyInstance方法包含的三个参数
- 第一个参数:类加载器
- 第二个参数:增强方法所在的类,这个类实现的接口,支持多个接口
- 第三个参数:实现接口InvocationHandler,创建代理对象,写增强的方法
- 实现步骤:
- 创建接口,定义方法,如UserDao
- 创建接口实现类,实现方法,如UserDaoImpl
1 | public class JDKProxy{ |
3.3 AOP专业术语
- 连接点:类中可以被增强的方法,这些方法称为连接点
- 切入点:真正被增强的方法,称为切入点
- 通知(增强):实际增强的逻辑部分
- 前置通知
- 后置通知
- 环绕通知
- 异常通知
- 最终通知 finally
- 切面:动作,如权限判断
- 把通知应用到切入点的过程
3.4 AOP操作准备
3.4.1 概述
- Spring框架一般都是基于AspectJ实现AOP操作
- AspectJ不是Spring组成部分,独立的AOP框架,一般把AspectJ和Spring框架一起使用,进行AOP操作
- 基于AspectJ实现AOP操作
- 基于xml配置文件实现
- 基于注解方式实现(使用)
- 需要引入依赖包
3.4.2 切入点表达式
1 | // execution([权限修饰符][返回类型][类全路径][方法名称]([参数列表])) |
3.5 AspectJ注解
3.5.1 操作步骤
- 创建类,定义方法,如User类
- 创建增强类,编写增强逻辑,如UserProxy
- 在增强类中,创建方法,让不同方法代表不同通知类型
- 进行通知的配置
- 在Spring配置文件中,开启注解扫描
- 使用注解创建User(@Component)和UserProxy对象
- 在增强类上面添加注解@Aspect
- 在Spring配置文件中开启生成代理对象
1 | <!--开启注解扫描--> |
- 配置不同类型的通知
- 在增强类中,在通知方法上添加通知类型注解,使用切入点表达式进行配置
1 |
|
3.5.2 相同切入点抽取
- 将方法增强的切入点写成公共的部分,方便调用和切入点的统一修改
1 |
|
3.5.3 增强类设置优先级
- 多个增强类可以增强同一个方法,因此可以为增强类设置优先级,
- 方法:在增强类上面添加注解@Order(数值型),数字类型值越小,优先级越高
1 |
|
3.6 AspectJ配置文件
1 | <aop:config> |
3.7 完全注解开发
1 |
|
四、JdbcTemplate
4.1 准备工作
- 引入依赖包
- mysql-connect
- spring-jdbc
- spring-tx:处理事务的包
- 配置数据库连接池(druid)
- 注入JdbcTemplate对象
1 | <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> |
- 开启组件扫描
4.3 CURD实现
4.3.1 添加操作
- 创建实体类User,创建Dao层UserDao,创建Service层UserService
1 | public class UserDaoImpl implements UserDao{ |
4.3.2 修改/删除操作
1 | public class UserDaoImpl implements UserDao{ |
4.3.3 查询操作
1 | public class UserDaoImpl implements UserDao{ |
4.3.4 批量操作
1 | public class UserDaoImpl implements UserDao{ |
五、事务管理
5.1 概念
- 一组操作的组合
- 事务的一些问题请转到数据库笔记中进行学习
5.2 事务特性ACID
- 原子性
- 一致性
- 隔离性
- 持久性
5.3 准备工作
- 以银行转账操作为例
- 创建数据库表,记录用户资金
- 创建service、dao完成对象创建和注入
- 创建jdbcTemplate对象
- dao创建两个方法,某用户资金增加,另外用户资金减少
- service创建转账方法,实现转账操作
5.4 事务操作
5.4 1 声明式事务管理
- 基于注解方式
- 基于xml方式
5.4.2 Spring事务管理API
- Spring提供一个接口,代表事务管理器,这个接口针对不同框架提供不同的实现类
5.4.2 注解方式
- 注入事务管理对象
1 | <!--由于使用的式jdbc框架,所以注入DataSourceTransactionManager对象--> |
- 开启扫描
- xml引入名称空间tx,同前面的引入操作
- 开启事务注解
1 | <tx:annotation-driven transacntion-manager="transactionManager"></tx:annotation-driven> |
- 在Service层添加事务注解@Transactional
- @Transactional可以加到类上,也可以加到方法上
- 加到类上,表示作用到类中的所有方法
5.4.3 注解的参数
(1)propagation:事务传播行为
- 多事务方法直接进行调用,这个过程中事务是如何进行管理的,必须是对数据库中的数据产生变化的操作
- 简单来说就是,就是添加了@Transaction事务管理的方法去调用未添加事务管理的方法,事务的操作过程是怎样的
- 下表均已上图为例,在事务管理方法中调用非事务管理方法,非事务管理方法运行的情况
- 配置方法:@Transactional(propagation = Propagation.REQUIRED)
传播属性 | 描述 |
---|---|
REQUIRED(default) | 有事务就在当前事务运行,没有事务就创建事务运行 |
REQUIRED_NEW | 无论有没有事务,都会创建一个事务在里面运行 |
SUPPORTS | 如果有事务在运行,当前的方法就在这个事务内运行,否则它可以不运行在事务中 |
NOT_SUPPORT | 当前的方法不应该运行在事务中,如果有运行的事务,将它挂起 |
MANDATORY | 当前的方法必须运行在事务内部,如果没有正在运行的事务,就抛出异常 |
NEVER | 当前的方法不应该运行在事务中,如果有运行的事务,就抛出异常 |
NESTED | 如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行,否则,就启动一个新的事务,并在它自己的事务内运行 |
(2)ioslation:事务隔离级别
- 隔离性:多事务操作之间不会产生影响
- 多事务带来的问题:脏读、不可重复读、虚读
- 脏读:未提交的事务读取另一个未提交事务的数据
- 不可重复读:两个事务同时读到一条数据,一个事务读到了另一个事务提交之前的数据(数据不一致了)
- 虚读:一个未提交的事务读取到另一个提交事务的添加数据
- 配置方式:@Transactional(ioslation= Ioslation.READ_UNCOMMITTED)
脏读 | 不可重复读 | 虚读 | |
---|---|---|---|
READ UNCOMMITTED(读未提交) | 有 | 有 | 有 |
READ COMMITTED(读已提交) | 无 | 有 | 有 |
REPEATABLE READ(可重复读/default) | 无 | 无 | 有 |
SERIALIZABLE(串行化) | 无 | 无 | 无 |
(3)timeout:超时时间
- 事务需要在一定时间内进行提交,如果不提交就进行回滚,默认是-1,单位是秒
(4)readOnly:是否只读
读:查询操作,写:增删改
默认为false
(5)rollbackFor:回滚
- 设置出现哪些异常进行事务回滚
- 设置该属性值为异常的class即可
(6)noRollbackFor:不回滚
- 设置出现哪些异常不进行事务回滚
5.5 事务完全注解开发
- 创建配置类
1 |
|