文章目录
一 、依赖
上层依赖下层 ,service类属性中 (业务逻辑层) 中有 dao类 (持久层) 对象。
依赖倒置原则(DIP):上层不再依赖下层。
二、控制反转IoC
控制反转的核心是:将对象的创建权交出去,将对象和对象之间关系的管理权交出去,由第三方容器来负责创建与维护。
对象和对象之间关系的管理权: 给上层对象中的属性(下层对象)赋值。
控制反转常见的实现方式:依赖注入(Dependency Injection,简称DI)。
依赖注入的实现又包括两种方式:set方法注入 和 构造注入。
三、创建对象的方式
默认是利用反射机制通过无参构造方法创建对象(其他后续再说),但是我发现当类中没有无参构造方法,当然也没有有参构造方法,也是可以的,这是为什么呢?
原因如下:
当一个类中没有显式定义任何构造方法(无论是无参还是有参)时,Java编译器会在编译时自动为该类生成一个默认的无参构造方法。
当你显式定义了任意一个有参构造方法,但没有显式定义无参构造方法时,Java编译器不会再自动生成默认的无参构造方法。此时如果Spring尝试通过无参构造方法创建对象,会抛出异常。
所以,为了可读性,尽量定义无参构造方法。
四、创建对象存储的位置
放到 HashMap<String,Object>中
String: bean的id
Object:创建的对象
五、Spring对IoC的实现
依赖注入实现了控制反转的思想。
Spring通过依赖注入的方式来完成Bean管理的。
Bean管理说的是:Bean对象的创建,以及Bean对象中属性的赋值(或者叫做Bean对象之间关系的维护)。
赋值有2种方式:构造方法与set方法。
再来理解依赖注入:
- 依赖指的是对象和对象之间的关联关系。
- 注入指的是一种数据传递行为,通过注入行为来让对象和对象产生关系。
重点:
强调一个东西,为什么说这个,后面用注解,现在养成好习惯,以后干什么事就很快,也不会乱。
bean id 该怎么写 类名第一个字母小写
property name 该怎么写 和属性名保持一致
以set注入为例(常用)
<bean id = "userDao" class="com.zhenbang.spring6.dao.UserDao"/>
<bean id = "userService" class="com.zhenbang.spring6.service.UserService">
<property name="userDao" ref="userDao"/>
<property name ="userName" vlaue="张三"/>
</bean>
细节:不给属性赋值时默认为null。
六、Bean的作用域
scope属性的值不止两个,它一共包括8个选项:
- singleton:默认的,单例。
- prototype:原型。每调用一次getBean()方法则获取一个新的Bean对象。或每次注入的时候都是新对象。
- request:一个请求对应一个Bean。仅限于在WEB应用中使用。
- session:一个会话对应一个Bean。仅限于在WEB应用中使用。
- global session:portlet应用中专用的。如果在Servlet的WEB应用中使用global session的话,和session一个效果。(portlet和servlet都是规范。servlet运行在servlet容器中,例如Tomcat。portlet运行在portlet容器中。)
- application:一个应用对应一个Bean。仅限于在WEB应用中使用。
- websocket:一个websocket生命周期对应一个Bean。仅限于在WEB应用中使用。
- 自定义scope:很少使用。
现在先知道单例和多例以及什么时候初始化的,其他以后遇到后再说!
七、Bean的实例化方式
1、无参构造方法
2、实现BeanFactory接口
3、简单工厂模式实现
4、factory-bean方式实现(利用了工厂方法模式)
就1和2常用一些,3、4了解一下即可。
FactoryBean在Spring中是一个接口,被称为“工厂Bean”。
“工厂Bean”是一种特殊的Bean。
所有的“工厂Bean”都是用来协助Spring框架来创建其他Bean对象的。
划重点: BeanFactory和FactoryBean的区别
BeanFactory: 它是Spring IoC容器的顶级对象,BeanFactory被翻译为“Bean工厂”,在Spring的IoC容器中,“Bean工厂”负责创建Bean对象。
BeanFactory是工厂。
FactoryBean:它是一个Bean,是一个能够辅助Spring实例化其它Bean对象的一个Bean。
在Spring中,Bean可以分为两类:
- 第一类:普通Bean
- 第二类:工厂Bean(记住:工厂Bean也是一种Bean,只不过这种Bean比较特殊,它可以辅助Spring实例化其它Bean对象。)
八、Bean生命周期(实现AOP切面编程的基础)
Bean生命周期:Bean对象从创建开始到最终销毁的整个过程。
1、实例化
2、属性赋值
3、Bean后处理器before方法(实现BeanPostProcessor接口)
4、初始化(在Bean中配置 init方法)
5、Bean后处理器after方法(同3,实现这个接口,要重写before、after方法,并在spring.xml文件中配置“Bean后处理器”)
6、使用Bean
7、销毁Bean(同4,在Bean中配置destory方法)
其实有10步,不过7步够用了!
注意:
- 对于singleton作用域的Bean,Spring 能够精确地知道该Bean何时被创建,何时初始化完成,以及何时被销毁;
- 而对于 prototype 作用域的 Bean,Spring 只负责创建,当容器创建了 Bean 的实例后,Bean 的实例就交给客户端代码管理,Spring 容器将不再跟踪其生命周期。
九、Bean循环依赖问题(三级缓存)
第一种:
2个Bean都是singleton + set注入 可以解决循环依赖问题
原理:
因为是单例的,把所有对象先创建出来,直接马上曝光,再给对象属性赋值。
第二种:
2个Bean都是prototype+set注入 出现异常
原因:
当一个A对象属性中有另一个B对象,因为是B是多例的,需要重新建立一个B对象,new对象B的时候,B中有属性A对象,因为A是多例的,所以又要新建立A对象,这样A对象永远建立不出来,会出现异常。
但是想一下如果B是单例的或者A是单例的,就能解套了,不会出现异常。
第三种:
一个Bean是单例的,另一个Bean是多例的,set注入:不会发生异常
第四种:
都是singleton + 构造注入,会出现异常。
原因:构造方法注入会导致实例化对象的过程和对象属性赋值的过程没有分离开导致的。
十、注解开发
实例化bean注解:,将其纳入spring容器管理,@Component @Controller @Service @Repository,注意不写id值,默认是类的名字然后第一个字母小写。
属性赋值注解:
-
@Value 简单类型,不依赖于set方法(可以不写)
常见的简单类型:
-
@Autowired 非简单类型属性赋值,默认根据类型装配,一般是接口变量,这时候就有一个问题,如果接口有多个实现类,用哪一个,所以此时应该配合@Qualifier根据名字装配使用,例如:
@Autowired
@Qualifier("userDaoForOracle") // 这个是bean的名字。
private UserDao userDao;
@Autowired 注解用在属性上、setter方法上、构造方法上、构造方法参数上
- @Qualifier 配合 @Autowired 使用
- @Resource 最常用,必须用这个!!!
@Resource 非简单类型,注解默认根据名称装配byName,未指定name时,使用属性名作为name。通过name找不到的话会自动启动通过类型byType装配。
示例:
@Resource(name = "userDaoForMySQL")
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
注意:@Resource注解用在属性上、setter方法上。
需要引入依赖,是jakarta
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>
十一、GoF代理模式(AOP底层的实现原理)
静态代理:不用,了解就行。
动态代理方式
1、JDK 动态代理:只能代理接口。
2、CGLIB:可以代理接口,又可以代理类,底层是通过继承的方式实现的。性能比JDK动态代理要好。
3、Javassist动态代理技术:MyBatis动态生成dao实现类(动态代理类)底层实现方式。
十二、面向切面编程AOP
切面就是 和业务逻辑不关系的流程化处理代码,具有通用性。 通过AOP,复用这些代码。
重点:
Spring的AOP使用的动态代理是:JDK动态代理 + CGLIB动态代理技术。
Spring在这两种动态代理中灵活切换,如果是代理接口,会默认使用JDK动态代理,如果要代理某个类,这个类没有实现接口,就会切换使用CGLIB。当然,你也可以强制通过一些配置让Spring只使用CGLIB。
spring boot 用的全是CGLIB。
AOP引入
一般一个系统当中都会有一些系统服务,日志、事务管理、安全等。这些系统服务被称为:交叉业务。这些交叉业务几乎是通用的,在每一个业务处理过程中,都需要这些内容。
用一句话总结AOP:将与核心业务无关的(交叉业务)代码独立的抽取出来,形成一个独立的组件,然后以横向交叉的方式应用到业务流程当中的过程被称为AOP。
AOP的优点:
- 第一:代码复用性增强。
- 第二:代码易维护。
- 第三:使开发者更关注业务逻辑。
AOP七大术语:
不要被吓到,其实很简单的,切点就是在哪个类上的什么方法搞aop,用一个切点表达式来表示,然后通知就是那个方法,放你增强代码的用的。
注意啦,对目标类生成动态代理类这个过程,我们不需要手动写,只要开启自动AOP就行。
十三、Spring对事务的支持(底层实现方式是AOP,写注解和配置就可以完成事务)
事务传播行为:
一共有七种传播行为:
-
REQUIRED:支持当前事务,如果不存在就新建一个(默认)【没有就新建,有就加入原来那个事务】
-
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行有就加入,没有就不管了
-
MANDATORY:必须运行在一个事务中,如果当前没有事务正在发生,将抛出一个异常 有就加入,没有就抛异常
-
REQUIRES_NEW:开启一个新的事务,如果一个事务已经存在,则将这个存在的事务挂起不管有没有,直接开启一个新事务,开启的新事务和之前的事务不存在嵌套关系,之前事务被挂起
-
NOT_SUPPORTED:以非事务方式运行,如果有事务存在,挂起当前事务不支持事务,存在就挂起
-
NEVER:以非事务方式运行,如果有事务存在,抛出异常不支持事务,存在就抛异常
-
NESTED:如果当前正有一个事务在进行中,则该方法应当运行在一个嵌套式事务中。被嵌套的事务可以独立于外层事务进行提交或回滚。如果外层事务不存在,行为就像REQUIRED一样。【有事务的话,就在这个事务里再嵌套一个完全独立的事务,嵌套的事务可以独立的提交和回滚。没有事务就和REQUIRED一样。】
事务的隔离级别:
数据库中读取数据存在的三大问题:(三大读问题) -
脏读:读取到没有提交到数据库的数据,叫做脏读。
-
不可重复读:在同一个事务当中,(同一行数据)第一次和第二次读取的数据不一样,前后属性值不同(修改了)。
-
幻读:读到的数据是假的,(由于插入删除的原因)只要事务并发,一定存在幻读的。
事务隔离级别包括四个级别:
- 读未提交:READ_UNCOMMITTED
- 这种隔离级别,存在脏读问题,所谓的脏读(dirty read)表示能够读取到其它事务未提交的数据。
- 读提交:READ_COMMITTED(orcle数据库默认级别)
- 解决了脏读问题,其它事务提交之后才能读到,但存在不可重复读问题。
- 可重复读:REPEATABLE_READ(mysql数据库默认级别)
- 解决了不可重复读,可以达到可重复读效果,只要当前事务不结束,读取到的数据一直都是一样的。但存在幻读问题。
- 序列化:SERIALIZABLE
- 解决了幻读问题,事务排队执行。不支持并发。
- 解决了幻读问题,事务排队执行。不支持并发。