先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
正文
try {
ReflectionUtils.makeAccessible(ctor);
return (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?
KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));
}
catch (InstantiationException ex) {
throw new BeanInstantiationException(ctor, “Is it an abstract class?”, ex);
}
// …
}
最终调用 ctor.newInstance()
实例化用户定制类LightMgrService,而默认构造器在类实例化时被自动调用,Spring 也无法控制。
而此时负责自动装配的 populateBean 方法还没有执行,LightMgrService 的属性 LightService 还是 null,导致NPE。
问题在于使用 @Autowired 直接标记在成员属性引发的装配行为发生在构造器执行后。
所以可通过如下方案解决:
构造器注入
当使用上述代码,构造器参数 LightService 会被自动注入LightService 的 Bean,从而在构造器执行时,避免NPE。
Spring 在类属性完成注入之后,会回调我们定义的初始化方法。即在 populateBean 方法之后,会调用
AbstractAutowireCapableBeanFactory#initializeBean
-
applyBeanPostProcessorsBeforeInitialization处理 @PostConstruct
-
invokeInitMethods处理InitializingBean 接口
两种不同的初始化方案的逻辑
applyBeanPostProcessorsBeforeInitialization与 @PostConstruct
applyBeanPostProcessorsBeforeInitialization 方法最终执行到
InitDestroyAnnotationBeanPostProcessor#buildLifecycleMetadata:
private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
// …
do {
// …
final List currDestroyMethods = new ArrayList<>();
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
// initAnnotationType 即 PostConstruct.class
if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
LifecycleElement element = new LifecycleElement(method);
currInitMethods.add(element);
// …
}
在这个方法里,Spring 将遍历查找被 PostConstruct.class 注解过的方法,返回到上层,并最终调用此方法。
invokeInitMethods 与 InitializingBean 接口
给bean一个机会去响应现在它的所有属性都已设置,并有机会了解它拥有的bean工厂(这个对象)。 这意味着检查 bean 是否实现了 InitializingBean 或自定义了 init 方法。
若是,则调用必要的回调。
invokeInitMethods会判断当前 Bean 是否实现了 InitializingBean 接口,只有实现该接口时,Spring 才会调用该 Bean 的接口实现方法 afterPropertiesSet()。
还有两种方式:
init 方法 && @PostConstruct
实现 InitializingBean 接口,回调afterPropertiesSet()
对于本案例,后两种方案并非最优。
但在一些场景下,这两种方案各有所长。
=================================================================================
类销毁时,也容易写出一堆 bug。
LightService#shutdown,负责关灯:
之前的案例中,若宿管系统重启,灯是不会被关闭的。但随着业务变化,可能会去掉 @Service ,而使用另外一种产生 Bean 的方式:创建一个配置类 BeanConfiguration(标记 @Configuration)来创建一堆 Bean,其中就包含了创建 LightService 类型的 Bean,并将其注册到 Spring 容器:
让 Spring 启动完成后立马关闭当前 Spring 上下文,这就能模拟模拟宿管系统的启停:
以上代码没有其他任何方法的调用,仅是将所有符合约定的类初始化并加载到 Spring 容器,完成后再关闭当前 Spring 容器。
预期:运行后不会有任何log,只改变 Bean 的产生方式。
运行后,控制台打印:
显然 shutdown 方法未按照预期,被执行了,这就导致一个有意思的 bug:
-
在使用新的 Bean 生成方式之前,每一次宿舍管理服务被重启时,宿舍里所有的灯都不会被关闭
-
但修改后,只要服务重启,灯都被意外关闭
你能理解这个bug吗?
发现:
-
只有通过使用 Bean 注解注册到 Spring 容器的对象,才会在 Spring 容器被关闭时自动调用 shutdown
-
使用 @Component将当前类自动注入到 Spring 容器时,shutdown 方法则不会被自动执行
可尝试到 Bean 注解类的代码中去寻找一些线索,可看到属性 destroyMethod。
使用 Bean 注解的方法所注册的 Bean 对象,如果用户不设置 destroyMethod 属性,则其属性值为 AbstractBeanDefinition.INFER_METHOD。
此时 Spring 会检查当前 Bean 对象的原始类中是否有名为 shutdown 或 close 的方法:
-
有,此方法会被 Spring 记录下来,并在容器被销毁时自动执行
-
没有,安然无事
查找 INFER_METHOD 枚举值的引用,很容易就找到了使用该枚举值的方法
DisposableBeanAdapter#inferDestroyMethodIfNecessary
private String inferDestroyMethodIfNecessary(Object bean, RootBeanDefinition beanDefinition) {
String destroyMethodName = beanDefinition.getDestroyMethodName();
if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName) ||(destroyMethodName == null && bean instanceof AutoCloseable)) {
if (!(bean instanceof DisposableBean)) {
try {
// 尝试查找 close 方法
return bean.getClass().getMethod(CLOSE_METHOD_NAME).getName();
}
catch (NoSuchMethodException ex) {
try {
// 尝试查找 shutdown 方法
return bean.getClass().getMethod(SHUTDOWN_METHOD_NAME).getName();
}
catch (NoSuchMethodException ex2) {
// no candidate destroy method found
}
}
}
return null;
}
return (StringUtils.hasLength(destroyMethodName) ? destroyMethodName : null);
}
代码逻辑和
Bean 注解类中对于 destroyMethod 属性的注释:
完全一致。
- destroyMethodName==INFER_METHOD&&当前类没有实现DisposableBean接口
则先查找类的 close 方法:
- 找不到
就在抛出异常后继续查找 shutdown 方法
- 找到
则返回其方法名(close 或者 shutdown)
接着,继续逐级查找引用,最终得到的调用链从上到下为:
-
doCreateBean
-
registerDisposableBeanIfNecessary
-
registerDisposableBean(new DisposableBeanAdapter)
-
inferDestroyMethodIfNecessary
然后,我们追溯到了顶层的 doCreateBean:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// 实例化 bean
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// …
// 初始化 bean 实例.
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
// …
// Register bean as disposable.
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, “Invalid destruction signature”, ex);
}
return exposedObject;
}
doCreateBean 管理了Bean的整个生命周期中几乎所有的关键节点,直接负责了 Bean 对象的生老病死,其主要功能包括:
-
Bean 实例的创建
-
Bean 对象依赖的注入
-
定制类初始化方法的回调
-
Disposable 方法的注册
接着,继续查看 registerDisposableBean:
public void registerDisposableBean(String beanName, DisposableBean bean) {
synchronized (this.disposableBeans) {
this.disposableBeans.put(beanName, bean);
}
}
DisposableBeanAdapter 类(其属性destroyMethodName 记录了使用哪种 destory 方法)被实例化
并添加到 DefaultSingletonBeanRegistry#disposableBeans 属性内,disposableBeans 将暂存这些 DisposableBeanAdapter 实例,直到 AnnotationConfigApplicationContext#close被调用。
而当 AnnotationConfigApplicationContext#close被调用时,即当 Spring 容器被销毁时,最终会调用到 DefaultSingletonBeanRegistry#destroySingleton:
-
遍历 disposableBeans 属性
-
逐一获取 DisposableBean
-
依次调用其 close 或 shutdown
public void destroySingleton(String beanName) {
// Remove a registered singleton of the given name, if any.
removeSingleton(beanName);
// Destroy the corresponding DisposableBean instance.
DisposableBean disposableBean;
synchronized (this.disposableBeans) {
disposableBean = (DisposableBean) this.disposableBeans.remove(beanName);
}
destroyBean(beanName, disposableBean);
}
案例调用了 LightService#shutdown 方法,将所有的灯关闭了。
避免在Java类中定义一些带有特殊意义动词的方法来解决。
如果一定要定义名为 close 或者 shutdown 方法,可以将 Bean 注解内 destroyMethod 属性设置为空。如下:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
总结
面试建议是,一定要自信,敢于表达,面试的时候我们对知识的掌握有时候很难面面俱到,把自己的思路说出来,而不是直接告诉面试官自己不懂,这也是可以加分的。
以上就是蚂蚁技术四面和HR面试题目,以下最新总结的最全,范围包含最全MySQL、Spring、Redis、JVM等最全面试题和答案,仅用于参考
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
;
import org.springframework.context.annotation.Configuration;
总结
面试建议是,一定要自信,敢于表达,面试的时候我们对知识的掌握有时候很难面面俱到,把自己的思路说出来,而不是直接告诉面试官自己不懂,这也是可以加分的。
以上就是蚂蚁技术四面和HR面试题目,以下最新总结的最全,范围包含最全MySQL、Spring、Redis、JVM等最全面试题和答案,仅用于参考
[外链图片转存中…(img-OELtruan-1713072114568)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-Fu7P4wDl-1713072114568)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!