SpringloC容器的依赖注入源码解析(10)—,2024年最新java面试题汇总及答案

return null;

}

相对于注解的这种情况,并没有实现该方法。


之后尝试从依赖描述符实例里面去获取目标实例的属性

Class<?> type = descriptor.getDependencyType();

这里的目标实例是girlfriend,之后就会调用注解候选解析器的getSuggestedValue方法尝试获取属性值,但是对于@Autowired修饰的属性来说,这一步无法获取到值

@Override

@Nullable

public Object getSuggestedValue(DependencyDescriptor descriptor) {

Object value = findValue(descriptor.getAnnotations());

if (value == null) {

MethodParameter methodParam = descriptor.getMethodParameter();

if (methodParam != null) {

value = findValue(methodParam.getMethodAnnotations());

}

}

return value;

}

该方法调用findValue方法

@Nullable

protected Object findValue(Annotation[] annotationsToSearch) {

if (annotationsToSearch.length > 0) { // qualifier annotations have to be local

AnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes(

AnnotatedElementUtils.forAnnotations(annotationsToSearch), this.valueAnnotationType);

if (attr != null) {

return extractValue(attr);

}

}

return null;

}

该方法主要是提取@Value修饰的属性值的


回到doResolveDependency方法

请添加图片描述

在if里会视情况对获取到的value进行类型转换。

之后来到resolveMultipleBeans方法

// 如果标识@Autowired注解的成员变量是复合类型,如Array,Collection,Map

// 从这个方法获取@Autowired里的值

Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);

@Nullable

private Object resolveMultipleBeans(DependencyDescriptor descriptor, @Nullable String beanName,

@Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) {

final Class<?> type = descriptor.getDependencyType();

// 首先是stream类型的处理

if (descriptor instanceof StreamDependencyDescriptor) {

Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);

if (autowiredBeanNames != null) {

autowiredBeanNames.addAll(matchingBeans.keySet());

}

Stream stream = matchingBeans.keySet().stream()

.map(name -> descriptor.resolveCandidate(name, type, this))

.filter(bean -> !(bean instanceof NullBean));

if (((StreamDependencyDescriptor) descriptor).isOrdered()) {

stream = stream.sorted(adaptOrderComparator(matchingBeans));

}

return stream;

}

// 判断要注入的是不是一个数组,可见除了集合注入外,也可以以数组的形式注入bean

else if (type.isArray()) {

Class<?> componentType = type.getComponentType();

ResolvableType resolvableType = descriptor.getResolvableType();

Class<?> resolvedArrayType = resolvableType.resolve(type);

if (resolvedArrayType != type) {

componentType = resolvableType.getComponentType().resolve();

}

if (componentType == null) {

return null;

}

Map<String, Object> matchingBeans = findAutowireCandidates(beanName, componentType,

new MultiElementDescriptor(descriptor));

if (matchingBeans.isEmpty()) {

return null;

}

if (autowiredBeanNames != null) {

autowiredBeanNames.addAll(matchingBeans.keySet());

}

TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());

Object result = converter.convertIfNecessary(matchingBeans.values(), resolvedArrayType);

if (result instanceof Object[]) {

Comparator comparator = adaptDependencyComparator(matchingBeans);

if (comparator != null) {

Arrays.sort((Object[]) result, comparator);

}

}

return result;

}

// 判断注入的是不是集合的接口类(List,Set等)

else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {

Class<?> elementType = descriptor.getResolvableType().asCollection().resolveGeneric();

if (elementType == null) {

return null;

}

Map<String, Object> matchingBeans = findAutowireCandidates(beanName, elementType,

new MultiElementDescriptor(descriptor));

if (matchingBeans.isEmpty()) {

return null;

}

if (autowiredBeanNames != null) {

autowiredBeanNames.addAll(matchingBeans.keySet());

}

TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());

Object result = converter.convertIfNecessary(matchingBeans.values(), type);

if (result instanceof List) {

Comparator comparator = adaptDependencyComparator(matchingBeans);

if (comparator != null) {

((List<?>) result).sort(comparator);

}

}

return result;

}

// 判断要注入的是不是map类

else if (Map.class == type) {

ResolvableType mapType = descriptor.getResolvableType().asMap();

Class<?> keyType = mapType.resolveGeneric(0);

if (String.class != keyType) {

return null;

}

Class<?> valueType = mapType.resolveGeneric(1);

if (valueType == null) {

return null;

}

Map<String, Object> matchingBeans = findAutowireCandidates(beanName, valueType,

new MultiElementDescriptor(descriptor));

if (matchingBeans.isEmpty()) {

return null;

}

if (autowiredBeanNames != null) {

autowiredBeanNames.addAll(matchingBeans.keySet());

}

return matchingBeans;

}

else {

return null;

}

}

该方法主要是针对复合类型(stream类型、数组类型、集合类型、map类型)的值进行解析处理,无论是哪种复合类型,最终都会调用findAutowireCandidates方法去获取注入的候选者列表(可能有多个)

由于测试时并非用的复合类型的数据,所以此处的multipleBeans为空


回到doResolveDependency,继续执行后续的逻辑

// 如果标识@Autowired注解的属性是非复合类型

// 从这个方法获取@Autowired里的值

Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);

if (matchingBeans.isEmpty()) {

// 没有找到,检验@Autowire的require是否为true

if (isRequired(descriptor)) {

raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);

}

return null;

}

处理被@Autowired标记的非复合类型的逻辑,同样会调用处理复合逻辑类型里的findAutowireCandidates方法尝试从容器里面根据属性的类型获取到对应的bean名字,

protected Map<String, Object> findAutowireCandidates(

@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {

// 查找出所有符合类型的beanName

String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(

this, requiredType, true, descriptor.isEager());

Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);

// resolvableDependencies记录了属性类型–值的映射

for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {

Class<?> autowiringType = classObjectEntry.getKey();

if (autowiringType.isAssignableFrom(requiredType)) {

Object autowiringValue = classObjectEntry.getValue();

// key值是我们需要的类型

autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);

if (requiredType.isInstance(autowiringValue)) {

result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);

break;

}

}

}

// 遍历候选数组

for (String candidate : candidateNames) {

// 候选Bean不是自引用(即要注入的类不能是类本身,会触发无限递归注入)

if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {

addCandidateEntry(result, candidate, descriptor, requiredType);

}

}

if (result.isEmpty()) {

boolean multiple = indicatesMultipleBeans(requiredType);

// Consider fallback matches if the first pass failed to find anything…

DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();

for (String candidate : candidateNames) {

if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor) &&

(!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) {

addCandidateEntry(result, candidate, descriptor, requiredType);

}

}

if (result.isEmpty() && !multiple) {

// Consider self references as a final pass…

// but in the case of a dependency collection, not the very same bean itself.

for (String candidate : candidateNames) {

if (isSelfReference(beanName, candidate) &&

(!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&

isAutowireCandidate(candidate, fallbackDescriptor)) {

addCandidateEntry(result, candidate, descriptor, requiredType);

}

}

}

}

return result;

}

首先调用BeanFactoryUtils.beanNamesForTypeIncludingAncestors,尝试获取针对于该属性的候选名单,beanNamesForTypeIncludingAncestors会尝试从各层容器里寻找符合该属性类型的bean的名字。

Map<String, Object> result会存放girlfriend。

之后会尝试去从已经注册到容器里的依赖关系里,去寻找先前有没有注入过girlfriend属性的依赖,这里相当于缓存的作用,由于是第一次所以肯定没有。

之后for循环会轮询候选列表做一些必要的检查,过滤掉自己依赖于自己的,再看看是否是支持注入的,满足条件就将其添加到候选人的列表当中

之后如果result为空的话会对依赖描述符做一个降级处理(一般进不来这个逻辑里)

DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();

这一步可以理解为在容器里找不到和当前类型匹配的bean时,会尝试去到可以替代该类的类中查找,比如girlfriend的父类看看能不能找到候选者


回到doResolveDependency,如果选出的bean个数大于1的话,就会选出最佳的候选者

if (matchingBeans.size() > 1) {

// 如果被@Autowired标记的有两个以上同样的类型。

// 就需要依据@Primary以及@Priority注解标签选择了

autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);

if (autowiredBeanName == null) {

if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {

// 无法选出唯一的,则直接报错

return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);

}

else {

// In case of an optional Collection/Map, silently ignore a non-unique case:

// possibly it was meant to be an empty collection of multiple regular beans

// (before 4.3 in particular when we didn’t even look for collection beans).

return null;

}

}

instanceCandidate = matchingBeans.get(autowiredBeanName);

}

进入到determineAutowireCandidate方法,用于选择最佳的候选bean:

@Nullable

protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {

Class<?> requiredType = descriptor.getDependencyType();

// 根据@Primary注解标签来选择最优解

String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);

if (primaryCandidate != null) {

return primaryCandidate;

}

// 根据@Order,@PriorityOrder,及实现Order接口的序号来选择最优解,选序号最小的

String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);

if (priorityCandidate != null) {

return priorityCandidate;

}

// Fallback

for (Map.Entry<String, Object> entry : candidates.entrySet()) {

String candidateName = entry.getKey();

Object beanInstance = entry.getValue();

if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||

matchesBeanName(candidateName, descriptor.getDependencyName())) {

return candidateName;

}

}

return null;

}

  1. 先找候选bean里是否有被@Primary标记的,有则直接选择;

  2. 没有的话再据@Order,@PriorityOrder,及实现Order接口的序号来选择最优解,选序号最小的;

  3. 没有的话降级看看有没有合适的bean,用属性的名字或别名找有没有相关的bean返回。

从上面的逻辑可以看出,@Autowired并非一定按照类型来查找。


回到doResolveDependency,由于在我们的例子里只有一个候选者,所以会来到else

else {

// We have exactly one match.

Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();

autowiredBeanName = entry.getKey();

instanceCandidate = entry.getValue();

}

在这里会获取到对应的属性名,以及属性对应的class对象,此时只是拿到了girlfriend的名字和class,还没有创建出其实例,因为在最终确认之前没必要创建多余的bean实例。

之后调用resolveCandidate去解析类的实例:

if (instanceCandidate instanceof Class) {

instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);

}

public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)

throws BeansException {

return beanFactory.getBean(beanName);

}

所谓的解析其实就是根据bean的名字去调用容器的getBean方法去获取/创建出bean实例,如果经过aop包装的话,获取出的属性也会是经过aop包装的。

剩下后续就是一些验证了逻辑了,验证完之后就执行完成整个doResolveDependency方法了,此时resolveDependency方法就获得了girlfriend对应的bean实例了,再上一层回到AutowiredAnnotationBeanPostProcessor的inject方法执行的地方,获取到girlfriend的bean实例之后,就会去注册相关的依赖关系

在这里插入图片描述

synchronized (this) {

if (!this.cached) {

// 成员变量的值不为null,并且required属性为true

if (value != null || this.required) {

this.cachedFieldValue = desc;

// 为指定Bean注册依赖Bean

registerDependentBeans(beanName, autowiredBeanNames);

if (autowiredBeanNames.size() == 1) {

String autowiredBeanName = autowiredBeanNames.iterator().next();

// 如果容器中有指定名称的Bean对象

if (beanFactory.containsBean(autowiredBeanName) &&

// 依赖对象类型和字段类型匹配,默认按类型注入

beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {

this.cachedFieldValue = new ShortcutDependencyDescriptor(

desc, autowiredBeanName, field.getType());

}

}

}

else {

this.cachedFieldValue = null;

}

this.cached = true;

}

}

注册完依赖关系后就会将bean实例放入到缓存里,在inject方法最后就是调用反射机制来给field设置上该bean实例了

// 如果字段值不为null

if (value != null) {

// 使用反射机制来赋值

ReflectionUtils.makeAccessible(field);

field.set(bean, value);

}


之后回到AbstractAutowireCapableBeanFactory的populateBean方法

// 在这里会对@Autowired标记的属性进行依赖注入

PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);

这时这里执行完毕了,boyfriend就会设置上girlfriend的bean实例,接下来来到依赖检查,

// 依赖检查,对应depend-on属性,3.0已经弃用此属性

if (needsDepCheck) {

// 过滤出所有需要进行依赖检查的属性编辑器

if (filteredPds == null) {

filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);

}

checkDependencies(beanName, mbd, filteredPds, pvs);

}

这里是对显式指定depends-on来检查的,默认不开启,所以会来到

if (pvs != null) {

// 最终将属性注入到Bean的Wrapper实例里,这里的注入主要是供

// 显式配置了autowiredbyName或者ByType的属性注入,

// 针对注解来讲,由于在AutowiredAnnotationBeanPostProcessor已经完成了注入,

// 所以此处不执行

applyPropertyValues(beanName, mbd, bw, pvs);

}

进入到applyPropertyValues方法里:

protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {

if (pvs.isEmpty()) {

return;

}

if (System.getSecurityManager() != null && bw instanceof BeanWrapperImpl) {

// 设置安全上下文,JDK安全机制

((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());

}

MutablePropertyValues mpvs = null;

List original;

if (pvs instanceof MutablePropertyValues) {

mpvs = (MutablePropertyValues) pvs;

if (mpvs.isConverted()) {

// Shortcut: use the pre-converted values as-is.

// 若属性值已经转换了,则直接赋值

try {

bw.setPropertyValues(mpvs);

return;

}

catch (BeansException ex) {

throw new BeanCreationException(

mbd.getResourceDescription(), beanName, “Error setting property values”, ex);

}

}

// 若没有被转换,先将没转换前的原始类型值给记录下来

original = mpvs.getPropertyValueList();

}

else {

// 如果pvs不是MutablePropertyValues类型,则直接使用原始类型

original = Arrays.asList(pvs.getPropertyValues());

}

// 获取用户的自定义类型转换

TypeConverter converter = getCustomTypeConverter();

if (converter == null) {

converter = bw;

}

// 创建一个Bean定义属性值解析器,将BeanDefinition中的属性值解析为Bean实例对象的实际值

BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);

// Create a deep copy, resolving any references for values.

// 为属性需要解析的值创建一个副本,将副本的数据注入Bean实例

List deepCopy = new ArrayList<>(original.size());

boolean resolveNecessary = false;

for (PropertyValue pv : original) {

// 不需要转换的属性值直接添加

if (pv.isConverted()) {

deepCopy.add(pv);

}

else {

String propertyName = pv.getName();

// 保留转换前的属性值

Object originalValue = pv.getValue();

// 如果是被Autowired标记的实例,则把Bean里面关于set此属性的方法给记录下来,

if (originalValue == AutowiredPropertyMarker.INSTANCE) {

Method writeMethod = bw.getPropertyDescriptor(propertyName).getWriteMethod();

if (writeMethod == null) {

throw new IllegalArgumentException("Autowire marker for property without write method: " + pv);

}

originalValue = new DependencyDescriptor(new MethodParameter(writeMethod, 0), true);

}

Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);

Object convertedValue = resolvedValue;

boolean convertible = bw.isWritableProperty(propertyName) &&

!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);

if (convertible) {

convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);

}

// Possibly store converted value in merged bean definition,

// in order to avoid re-conversion for every created bean instance.

if (resolvedValue == originalValue) {

if (convertible) {

pv.setConvertedValue(convertedValue);

}

deepCopy.add(pv);

}

else if (convertible && originalValue instanceof TypedStringValue &&

!((TypedStringValue) originalValue).isDynamic() &&

!(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {

pv.setConvertedValue(convertedValue);

deepCopy.add(pv);

}

else {

resolveNecessary = true;

deepCopy.add(new PropertyValue(pv, convertedValue));

}

}

}

if (mpvs != null && !resolveNecessary) {

mpvs.setConverted();

}

// Set our (possibly massaged) deep copy.

try {

// 进行属性的依赖注入

bw.setPropertyValues(new MutablePropertyValues(deepCopy));

}

catch (BeansException ex) {

throw new BeanCreationException(

mbd.getResourceDescription(), beanName, “Error setting property values”, ex);

}

}

针对注解的情况,直接返回

if (pvs.isEmpty()) {

return;

}

该方法主要是视情况做各种属性值的类型转换,毕竟获取到的是字符串类型的Type,需要这些字符串的type转换成对应的类型,首先看一下缓存里有没有,如果没有再做转换,转换完成后最终会调用

bw.setPropertyValues(new MutablePropertyValues(deepCopy));

给bean设置上属性值。


回到AbstractAutowireCapableBeanFactory的doCreateBean方法,执行完

populateBean(beanName, mbd, instanceWrapper);

之后调用

// 初始化bean,过程如下:

// 1. 判断是否实现了BeanNameAware,BeanClassLoaderAware

// BeanFactoryAware方法,如果有,则设置相关的属性

// 2. 调用bean初始化的前置(BeanPostProcessor)操作

// 3. 执行初始化的方法

// 如果有initializingBean,则调用afterPropertiesSet

// 如果有InitMethod,则调用初始方法

// 4. 调用bean初始化的后置(BeanPostProcessor)操作

exposedObject = initializeBean(beanName, exposedObject, mbd);

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {

if (System.getSecurityManager() != null) {

AccessController.doPrivileged((PrivilegedAction) () -> {

invokeAwareMethods(beanName, bean);

return null;

}, getAccessControlContext());

}

else {

invokeAwareMethods(beanName, bean);

}

Object wrappedBean = bean;

if (mbd == null || !mbd.isSynthetic()) {

wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);

}

try {

invokeInitMethods(beanName, wrappedBean, mbd);

}

catch (Throwable ex) {

throw new BeanCreationException(

(mbd != null ? mbd.getResourceDescription() : null),

beanName, “Invocation of init method failed”, ex);

}

if (mbd == null || !mbd.isSynthetic()) {

wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

}

return wrappedBean;

}

跳过前面检查的逻辑之后执行

invokeAwareMethods(beanName, bean);

private void invokeAwareMethods(final String beanName, final Object bean) {

if (bean instanceof Aware) {

if (bean instanceof BeanNameAware) {

((BeanNameAware) bean).setBeanName(beanName);

}

if (bean instanceof BeanClassLoaderAware) {

ClassLoader bcl = getBeanClassLoader();

if (bcl != null) {

((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);

}

}

if (bean instanceof BeanFactoryAware) {

((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);

}

}

}

该方法判断bean是否实现了相关的一些bean级别的Aware接口的set方法,如果实现了就将其set进去


回到initializeBean,执行完

invokeAwareMethods(beanName, bean);

之后就会判断

if (mbd == null || !mbd.isSynthetic()) {

wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

}

如果bean实例不是spring内置的一些特殊的bean,就会调用applyBeanPostProcessorsAfterInitialization方法去处理

@Override

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)

throws BeansException {

Object result = existingBean;

for (BeanPostProcessor processor : getBeanPostProcessors()) {

Object current = processor.postProcessAfterInitialization(result, beanName);

if (current == null) {

return result;

}

result = current;

}

return result;

}

发现该方法又使用了责任链模式,此时调用的是通用的bean后置处理器

Object current = processor.postProcessAfterInitialization(result, beanName);

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
img

我的面试宝典:一线互联网大厂Java核心面试题库

以下是我个人的一些做法,希望可以给各位提供一些帮助:

整理了很长一段时间,拿来复习面试刷题非常合适,其中包括了Java基础、异常、集合、并发编程、JVM、Spring全家桶、MyBatis、Redis、数据库、中间件MQ、Dubbo、Linux、Tomcat、ZooKeeper、Netty等等,且还会持续的更新…可star一下!

image

283页的Java进阶核心pdf文档

Java部分:Java基础,集合,并发,多线程,JVM,设计模式

数据结构算法:Java算法,数据结构

开源框架部分:Spring,MyBatis,MVC,netty,tomcat

分布式部分:架构设计,Redis缓存,Zookeeper,kafka,RabbitMQ,负载均衡等

微服务部分:SpringBoot,SpringCloud,Dubbo,Docker

image

还有源码相关的阅读学习

image

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
img

曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-iw1NEyOr-1712812281047)]
[外链图片转存中…(img-ItxU6Ve0-1712812281047)]
[外链图片转存中…(img-HmgIhZQW-1712812281048)]
[外链图片转存中…(img-M2IWx8gX-1712812281048)]
[外链图片转存中…(img-jgHHSgi4-1712812281048)]
[外链图片转存中…(img-KANvDVRo-1712812281049)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-9FV4Q5UM-1712812281049)]

我的面试宝典:一线互联网大厂Java核心面试题库

以下是我个人的一些做法,希望可以给各位提供一些帮助:

整理了很长一段时间,拿来复习面试刷题非常合适,其中包括了Java基础、异常、集合、并发编程、JVM、Spring全家桶、MyBatis、Redis、数据库、中间件MQ、Dubbo、Linux、Tomcat、ZooKeeper、Netty等等,且还会持续的更新…可star一下!

[外链图片转存中…(img-h831PTlA-1712812281049)]

283页的Java进阶核心pdf文档

Java部分:Java基础,集合,并发,多线程,JVM,设计模式

数据结构算法:Java算法,数据结构

开源框架部分:Spring,MyBatis,MVC,netty,tomcat

分布式部分:架构设计,Redis缓存,Zookeeper,kafka,RabbitMQ,负载均衡等

微服务部分:SpringBoot,SpringCloud,Dubbo,Docker

[外链图片转存中…(img-CzwXvrgV-1712812281049)]

还有源码相关的阅读学习

[外链图片转存中…(img-gCZbY4sO-1712812281050)]

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-xAEaiaOz-1712812281050)]

  • 19
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值