/**
- 通过反射获取私有的成员变量.
*/
private Object getPrivateValue(Person person, String fieldName)
{
try
{
Field field = person.getClass().getDeclaredField(fieldName);
// 主要就是这里,需要将属性的 accessible 设置为 true
field.setAccessible(true);
return field.get(person);
}
catch(Exception e)
{
e.printStackTrace();
}
return null;
}
AOP
AOP 的内部原理其实就是动态代理和反射了。主要涉及到的反射类:
动态代理相关原理的话,你需要了解什么是代理模式、静态代理的不足、动态代理的实现原理。Spring 中实现动态代理有两种方式可选,这两种动态代理的实现方式的一个对比也是面试中常问的。
JDK 动态代理
必须实现 InvocationHandler 接口,然后通过** Proxy.newProxyInstance(ClassLoader**
loader, Class<?>[] interfaces, InvocationHandler h) 获得动态代理对象。
CGLIB 动态代理
使用 CGLIB 动态代理,被代理类不需要强制实现接口。CGLIB 不能对声明为 final的方法进行代理,因为 CGLIB 原理是动态生成被代理类的子类。
OK,AOP 讲了。其实讲到这里,可能会有一个延伸的面试问题。我们知道,Spring事物也是 通 过 AOP 来 实 现的 , 我们使用的时候 一 般就是在方法上 加@Tranactional 注解,那么你有没有遇到过事物不生效的情况呢?这是为什么?这个问题我们在后面的面试题中会讲。
这只是个大体流程,内部的具体行为太多,需要自行去看看代码。
3.1. 什么是循环依赖,有啥问题?
循环依赖就是 N 个类中循环嵌套引用,这样会导致内存溢出。循环依赖主要分两种:
- 构造器循环依赖
- setter 循环依赖
3.2. Spring 解决循环依赖问题
- 构造器循环依赖问题
无解,直接抛出 BeanCurrentlyInCreatingException 异常。
- setter 循环依赖问题
单例模式下,通过“三级缓存”来处理。非单例模式的话,问题无解。
Spring 初始化单例对象大体是分为如下三个步骤的:
- createBeanInstance:调用构造函数创建对象
- populateBean:调用类的 setter 方法填充对象属性
- initializeBean:调用定义的 Bean 初始化 init 方法
可以看出,循环依赖主要发生在 1、2 步,当然如果发生在第一步的话,Spring 也是无法解决该问题的。那么就剩下第二步 populateBean 中出现的循环依赖问题。通过“三级缓存”来处理,三级缓存如下:
- **singletonObjects:**Cache of singleton objects: bean name --> bean instance,完成初始化的单例对象的 cache(一级缓存)
- **earlySingletonObjects:**Cache of early singleton objects: bean name–> bean instance ,完成实例化但是尚未初始化的,提前暴光的单例对象的 cache (二级缓存)
- singletonFactories : Cache of singleton factories: bean name -->ObjectFactory,进入实例化阶段的单例对象工厂的 cache (三级缓存)
我们看下获取单例对象的方法:
protected Object getSingleton(String beanName, boolean allowEarlyReference)
{
Object singletonObject = this.singletonObjects.get(beanName);
// isSingletonCurrentlyInCreation:判断当前单例 bean 是否正在创建中
if(singletonObject == null && isSingletonCurrentlyInCreation(beanName))
{
synchronized(this.singletonObjects)
{
singletonObject = this.earlySingletonObjects.get(beanName);
// allowEarlyReference:是否允许从 singletonFactories 中通过 getObject 拿到
对象
if(singletonObject == null && allowEarlyReference)
{
ObjectFactory <? > singletonFactory = this.singletonFactories.get(beanName);
if(singletonFactory != null)
{
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return(singletonObject != NULL_OBJECT ? singletonObject : null);
}
其中解决循环依赖问题的关键点就在 singletonFactory.getObject() 这一步,getObject 这是 ObjectFactory 接口的方法。Spring 通过对该方法的实现,在createBeanInstance 之后,populateBean 之前,通过将创建好但还没完成属性设置和初始化的对象提前曝光,然后再获取 Bean 的时候去看是否有提前曝光的对象实例来判断是否要走创建流程。
protected void addSingletonFactory(String beanName, ObjectFactory <? > singletonFactory)
{
Assert.notNull(singletonFactory, “Singleton factory must not be null”);
synchronized(this.singletonObjects)
{
if(!this.singletonObjects.containsKey(beanName))
{
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
可能的原因:
小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
[外链图片转存中…(img-4cskc3nk-1710869499491)]
[外链图片转存中…(img-G3prLvEw-1710869499492)]
[外链图片转存中…(img-mncqQinK-1710869499492)]
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
[外链图片转存中…(img-miIxr2kV-1710869499493)]