Spring(十四):继续创建单例Bean,提前曝光,阿里大牛纯手码7W字

protected String[] unsatisfiedNonSimpleProperties(AbstractBeanDefinition mbd, BeanWrapper bw) {

//创建一个treeSet进行去重

Set result = new TreeSet<>();

//获取所有的依赖

PropertyValues pvs = mbd.getPropertyValues();

PropertyDescriptor[] pds = bw.getPropertyDescriptors();

//

for (PropertyDescriptor pd : pds) {

//只有写了set方法

//不在不检查依赖的集合中

//不是简单类型

if (pd.getWriteMethod() != null && !isExcludedFromDependencyCheck(pd) && !pvs.contains(pd.getName()) &&

!BeanUtils.isSimpleProperty(pd.getPropertyType())) {

//满足条件之后添加进里面

result.add(pd.getName());

}

}

return StringUtils.toStringArray(result);

}

看代码比较抽象,所以决定举个栗子

配置文件只是简单的一个注入类型

在这里插入图片描述

类也很简单,只是简单地提供了一个set方法注入

在这里插入图片描述

下面我们直接看那两个集合是什么

PropertyDescriptor[] pds = bw.getPropertyDescriptors();

结果如下

在这里插入图片描述

里面有两个对象

一个是bean自己的class,没有writeMethod

在这里插入图片描述

另外一个,关于twoPojo的,而且里面存称了writeMethod正式set方法!!!!

在这里插入图片描述

所以可以推测这个集合获取的是需要依赖的属性详情

PropertyValues pvs = mbd.getPropertyValues();

里面的东西如下所示

在这里插入图片描述

可以看到,里面完全就是配置文件里面的信息。。。。

所以,这个集合里面存储的是配置文件里面的依赖属性的信息

那么现在,这个for循环来剔除什么,就很容易看出了

回归到我们的for循环

for (PropertyDescriptor pd : pds) {

//只有写了set方法

//不在不检查依赖的集合中

//不是简单类型

//并且配置文件里面没有这个beanName,

//如果配置文件里有那就去IOC容器里面拿就好了,不需要自动注入

//而不是采用注入的方式

if (pd.getWriteMethod() != null && !isExcludedFromDependencyCheck(pd) && !pvs.contains(pd.getName()) &&

!BeanUtils.isSimpleProperty(pd.getPropertyType())) {

//满足条件之后添加进里面

result.add(pd.getName());

}

}

在这里插入图片描述

可以看到结果,result为0

假如我们去掉配置文件的property

在这里插入图片描述

可以看到结果带上了twoPOJO

下面再来看一下BeanUtils过滤了什么东西出来

在这里插入图片描述

在这里插入图片描述

可以看到其过滤一些类型

  • void类型

  • isPrimitiveOrWrapper:判断是不是包装类,也就是过滤包装类

  • Enum类型

  • Date类型

  • URI类型

  • URL类型

  • Locale类型

  • Class类型

  • CharSequence类型

总结一下哪种依赖bean会被

  • 没有写Set方法的依赖会被过滤

  • 位于不进行检查的集合中依赖会被过滤

  • 配置文件中已经指定注入的类型会被过滤

  • 属于简单类型的也会被过滤

AUTOWIRE_BY_TYPE

还有一种自动注入类型就是根据类型去寻找

对应的代码如下

在这里插入图片描述

对应的方法是autowireByType

源码如下

protected void autowireByType(

String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {

//获取类型处理器

TypeConverter converter = getCustomTypeConverter();

if (converter == null) {

converter = bw;

}

//使用一个LinkedHashSet来存储需要自动注入的bean的beanName

//之前存储beanName也是使用一个set进行去重,然后转化成一个字符串数组

Set autowiredBeanNames = new LinkedHashSet<>(4);

//可以看到这里同样要进行过滤,就不赘述了

String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);

//遍历需要注入的依赖

for (String propertyName : propertyNames) {

try {

PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);

// Don’t try autowiring by type for type Object: never makes sense,

// even if it technically is a unsatisfied, non-simple property.

//判断该依赖是不是Object类型,(注意:过滤并没有将Object类型过滤)

if (Object.class != pd.getPropertyType()) {

//获取该bean的set方法的参数。。

//相当于就是获取类型了。。体现出了ByType

MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);

// Do not allow eager init for type matching in case of a prioritized post-processor.

boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered);

DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);

//解析指定beanName的属性所匹配的至,并把解析到的属性名称存储在autowiredBeanNames中

Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);

if (autowiredArgument != null) {

pvs.add(propertyName, autowiredArgument);

}

//遍历需要注入的bean

for (String autowiredBeanName : autowiredBeanNames) {

//注册依赖

registerDependentBean(autowiredBeanName, beanName);

if (logger.isTraceEnabled()) {

logger.trace(“Autowiring by type from bean name '” + beanName + “’ via property '” +

propertyName + “’ to bean named '” + autowiredBeanName + “'”);

}

}

autowiredBeanNames.clear();

}

}

catch (BeansException ex) {

throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);

}

}

}

从这里可以看到两个细节

  • Spring对于Object类型的,使用ByType是不会进行注入的

  • 获取类型是通过过去Set方法的参数来实现的,参数其实就是Type了,可以看出是ByType的体现

  • 通过resolveDependency来进行创建依赖

resolveDependency

在这里插入图片描述

可以看到,对ByType的依赖进行创建是交由DefaultListableBeanFactory来实现的,而ByName的依赖进行创建交由AbstractAutowireCapableFactory来实现的

源码如下

public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,

@Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

//

descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());

//扩展了对Optional类的支持

if (Optional.class == descriptor.getDependencyType()) {

//Optional类单独使用方法创建

return createOptionalDependency(descriptor, requestingBeanName);

}

//对于ObjectFactory和ObjectProvider也是单独方法创建

else if (ObjectFactory.class == descriptor.getDependencyType() ||

ObjectProvider.class == descriptor.getDependencyType()) {

return new DependencyObjectProvider(descriptor, requestingBeanName);

}

//对JavaInjectProviderClass进行单独处理

else if (javaxInjectProviderClass == descriptor.getDependencyType()) {

return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);

}

else {

//对于其他一般类型

//使用AutowireCandidateResolver来进行注入

Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(

descriptor, requestingBeanName);

if (result == null) {

//如果AutpwireCandidateResolver注入失败,使用doResolveDependency进行注入

result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);

}

return result;

}

}

可以看到,对于一些特定类型单独处理之外,其他的类型都会走相同的步骤(大部分)

getLazyResollutionProxyIfNecessary

对于一般类型,首先会经过这个方法进行处理

从这个方法的注释来看,感觉像是对切入点进行添加代理的。。

不过其默认实现直接返回Null,那就先不看了。

在这里插入图片描述

doResolveDependency

接下来执行这个方法进行处理

源码如下,可以看到十分恐怖。。。

public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,

@Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);

try {

Object shortcut = descriptor.resolveShortcut(this);

if (shortcut != null) {

return shortcut;

}

//获取类对象,也就是前面通过set方法拿到的参数

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

//支持新增的@Value注解

//@Value注解是可以直接用在类上的,所以这里是根据类获取其注解

//然后去相应配置中进行注入

Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);

//判断是不是@Value注入了

//所以ByName不会被@Value注入????

if (value != null) {

//如果是,会执行下面这些操作

if (value instanceof String) {

String strVal = resolveEmbeddedValue((String) value);

BeanDefinition bd = (beanName != null && containsBean(beanName) ?

getMergedBeanDefinition(beanName) : null);

value = evaluateBeanDefinitionString(strVal, bd);

}

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

try {

return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());

}

catch (UnsupportedOperationException ex) {

// A custom TypeConverter which does not support TypeDescriptor resolution…

return (descriptor.getField() != null ?

converter.convertIfNecessary(value, type, descriptor.getField()) :

converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));

}

}

//考虑这个属性是不是多个Bean的,也就是处理集合类型

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

if (multipleBeans != null) {

return multipleBeans;

}

//查找出所有的候选者

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

if (matchingBeans.isEmpty()) {

if (isRequired(descriptor)) {

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

}

return null;

}

String autowiredBeanName;

Object instanceCandidate;

//判断候选者的数量

if (matchingBeans.size() > 1) {

//如果存在两个或两个意思,执行下面操作

//决定使用哪个候选者

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);

}

else {

//如果只有一个候选值,进行下面处理

// We have exactly one match.

//去获取唯一的一个候选者

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

//使用候选者的属性

//beanName为key

//依赖的对象就是候选者的value

autowiredBeanName = entry.getKey();

instanceCandidate = entry.getValue();

}

//添加进集合

if (autowiredBeanNames != null) {

autowiredBeanNames.add(autowiredBeanName);

}

if (instanceCandidate instanceof Class) {

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

}

//此时让result为得出来的候选者

Object result = instanceCandidate;

//再判断是不是NullBean类型

if (result instanceof NullBean) {

if (isRequired(descriptor)) {

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

}

result = null;

}

if (!ClassUtils.isAssignableValue(type, result)) {

throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());

}

//返回结果

return result;

}

finally {

ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);

}

}

好复杂,看的要吐了。。。。。。

总结一下大概的过程

  • 通过前面解析set方法得到的参数来获取Class类型

  • 通过这个Class类型去判断其有没有被@Value注解标识

  • 如果有,走另一个逻辑

  • 如果没有,代表还是需要去注入

  • 判断是不是集合类型的,如果是集合类型的,又走集合类型的处理

  • 如果不是集合类型的,去获取所有候选者

  • 判断候选者的数量

  • 如果候选者数量存在两个以上

  • 判断决定使用哪个候选者,如果决定不出来又是另外一个方法

  • 如果决定出来了,该候选者就是依赖Bean的实例了

  • 如果候选者只有一个,直接使用该候选者

  • 然后对产生出来的候选者做一些判空处理

  • 返回结果

大概的思路就是这样。。。。下次再仔细研究,这太难了

到了这里之后,我们就已经完成了对所有注入属性的获取,但是获取了属性是以PropertyValue形式存在的,还没有应用到已经实例化的bean中

注入属性

回到populate方法中,现在所有的依赖就已经创建好了,等待注入bean里面了,对应的执行的方法为applyPropertyValues方法,同时也是populateBean最后的执行方法

在这里插入图片描述

源码如下

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

//如果依赖为空直接返回

if (pvs.isEmpty()) {

return;

}

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

((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 {

//如果转化成对应的类型,直接装入BeanWrapper中

bw.setPropertyValues(mpvs);

//结束返回

return;

}

catch (BeansException ex) {

throw new BeanCreationException(

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

}

}

original = mpvs.getPropertyValueList();

}

else {

//将依赖放进original中

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

}

//

TypeConverter converter = getCustomTypeConverter();

if (converter == null) {

converter = bw;

}

//获取对应的解析器

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

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

//使用一个ArrayList去存储依赖的深拷贝对象

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();

//如果值是AutowiredPropertyMarker…

//不太清楚这什么情况

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);

//将解析结果放在convertedValue变量中

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 {

//最后将所有深拷贝的依赖存进beanWrapper中

bw.setPropertyValues(new MutablePropertyValues(deepCopy));

}

catch (BeansException ex) {

throw new BeanCreationException(

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

}

}

注入属性也好难懂,大体的过程是

  • 判断依赖是不是都转化好了

  • 如果都转化好了就直接添加进去beanWrapper中

  • 如果没有转化好,就要进行对依赖深拷贝了

  • 获取依赖的变量名和依赖的值,值通常是一个Spring自定义的RuntimeBeanReference

  • 使用解析器进行转化

  • 将转化后的值添加进去deepCopy集合中,deepCopy是一个ArrayList

  • 最后将deepCopy集合存进beanWrapper中

所以,重点的部分在于解析和将解析后依赖存进beanWrapper这里,下面看看是如何进行解析的

valueResolver.resolveValueIfNecessary(pv, originalValue);

这个方法就是进行解析的

源码如下

@Nullable

public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {

// We must check each value to see whether it requires a runtime reference

// to another bean to be resolved.

//判断是不是RuntimeBeanReference

if (value instanceof RuntimeBeanReference) {

RuntimeBeanReference ref = (RuntimeBeanReference) value;

return resolveReference(argName, ref);

}

//判断是不是RuntimeBeanNameReference

else if (value instanceof RuntimeBeanNameReference) {

String refName = ((RuntimeBeanNameReference) value).getBeanName();

refName = String.valueOf(doEvaluate(refName));

if (!this.beanFactory.containsBean(refName)) {

throw new BeanDefinitionStoreException(

“Invalid bean name '” + refName + "’ in bean reference for " + argName);

}

return refName;

}

//判断是不是BeanDefinitinHolder

else if (value instanceof BeanDefinitionHolder) {

// Resolve BeanDefinitionHolder: contains BeanDefinition with name and aliases.

BeanDefinitionHolder bdHolder = (BeanDefinitionHolder) value;

return resolveInnerBean(argName, bdHolder.getBeanName(), bdHolder.getBeanDefinition());

}

//判断是不是BeanDefinition

else if (value instanceof BeanDefinition) {

// Resolve plain BeanDefinition, without contained name: use dummy name.

BeanDefinition bd = (BeanDefinition) value;

String innerBeanName = “(inner bean)” + BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR +

ObjectUtils.getIdentityHexString(bd);

return resolveInnerBean(argName, innerBeanName, bd);

}

//判断是不是DependencyDescriptor

else if (value instanceof DependencyDescriptor) {

Set autowiredBeanNames = new LinkedHashSet<>(4);

Object result = this.beanFactory.resolveDependency(

(DependencyDescriptor) value, this.beanName, autowiredBeanNames, this.typeConverter);

for (String autowiredBeanName : autowiredBeanNames) {

if (this.beanFactory.containsBean(autowiredBeanName)) {

this.beanFactory.registerDependentBean(autowiredBeanName, this.beanName);

}

}

return result;

}

//判断是不是ManagedArray

else if (value instanceof ManagedArray) {

// May need to resolve contained runtime references.

ManagedArray array = (ManagedArray) value;

Class<?> elementType = array.resolvedElementType;

if (elementType == null) {

String elementTypeName = array.getElementTypeName();

if (StringUtils.hasText(elementTypeName)) {

try {

elementType = ClassUtils.forName(elementTypeName, this.beanFactory.getBeanClassLoader());

array.resolvedElementType = elementType;

}

catch (Throwable ex) {

// Improve the message by showing the context.

throw new BeanCreationException(

this.beanDefinition.getResourceDescription(), this.beanName,

"Error resolving array type for " + argName, ex);

}

}

else {

elementType = Object.class;

}

}

return resolveManagedArray(argName, (List<?>) value, elementType);

}

//判断是不是ManagedList

else if (value instanceof ManagedList) {

// May need to resolve contained runtime references.

return resolveManagedList(argName, (List<?>) value);

}

//判断是不是ManagedSet

else if (value instanceof ManagedSet) {

// May need to resolve contained runtime references.

return resolveManagedSet(argName, (Set<?>) value);

}

//判断是不是ManagedMap

else if (value instanceof ManagedMap) {

// May need to resolve contained runtime references.

return resolveManagedMap(argName, (Map<?, ?>) value);

}

//判断是不是ManagedProperties

else if (value instanceof ManagedProperties) {

Properties original = (Properties) value;

Properties copy = new Properties();

original.forEach((propKey, propValue) -> {

if (propKey instanceof TypedStringValue) {

propKey = evaluate((TypedStringValue) propKey);

}

if (propValue instanceof TypedStringValue) {

propValue = evaluate((TypedStringValue) propValue);

}

if (propKey == null || propValue == null) {

throw new BeanCreationException(

this.beanDefinition.getResourceDescription(), this.beanName,

"Error converting Properties key/value pair for " + argName + “: resolved to null”);

}

copy.put(propKey, propValue);

});

return copy;

}

//判断是不是TypeStringValue

else if (value instanceof TypedStringValue) {

// Convert value to target type here.

TypedStringValue typedStringValue = (TypedStringValue) value;

Object valueObject = evaluate(typedStringValue);

try {

Class<?> resolvedTargetType = resolveTargetType(typedStringValue);

if (resolvedTargetType != null) {

return this.typeConverter.convertIfNecessary(valueObject, resolvedTargetType);

}

else {

return valueObject;

}

}

catch (Throwable ex) {

// Improve the message by showing the context.

throw new BeanCreationException(

this.beanDefinition.getResourceDescription(), this.beanName,

"Error converting typed String value for " + argName, ex);

}

}

//判断是不是NullBean

else if (value instanceof NullBean) {

return null;

}

//如果都不是上述类型

else {

return evaluate(value);

}

}

可以看到,这里注入属性根据传进来的值是什么类型做了区分处理

  • RuntimeBeanReference类型的

  • RuntimeBeanNameReference类型的

  • BeanDefinitionHolder类型的

  • BeanDefiniton类型的

  • DependencyDescriptor类型的

  • ManagedArray类型的

  • ManagedList类型的额

  • ManagedSet类型的

  • ManagedMap类型的

  • ManagedProperty类型的

  • NullBean类型的

  • 其他类型,这里的其他类型已经就是对应的class类型了

一般传进来的是一个已经是对应的类型了

在这里插入图片描述

那就会直接对应最后的evaluate方法

源码如下

@Nullable

protected Object evaluate(@Nullable Object value) {

//对字符串进行处理

//主要的处理是将字符串解析为一个Bean对象

if (value instanceof String) {

return this.doEvaluate((String)value);

}

//对字符串数组进行处理

else if (value instanceof String[]) {

String[] values = (String[])((String[])value);

boolean actuallyResolved = false;

Object[] resolvedValues = new Object[values.length];

//其实就是遍历字符串数组,对每个字符串进行处理

for(int i = 0; i < values.length; ++i) {

String originalValue = values[i];

Object resolvedValue = this.doEvaluate(originalValue);

if (resolvedValue != originalValue) {

actuallyResolved = true;

}

resolvedValues[i] = resolvedValue;

}

return actuallyResolved ? resolvedValues : values;

}

//不是字符串也不是字符串数组,直接返回value,也就是创建好的对象

//一般直接在这里就返回了

else {

return value;

}

}

可以看到这个方法对字符串和字符串数组做了特殊处理,但这里是不可能出现String类型和字符串数组类型的。。。因为前面已经过滤掉了

bw.setPropertyValues

这个方法就是将依赖存进beanWrapper中了

在这里插入图片描述

可以看到,此时要注入的依赖都存进了deepCopy中

进来这个方法之后,可以看到这个方法是交由AbstractPropertyAccessor实现的,并且调用了重载方法

在这里插入图片描述

源码如下

@Override

public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)

throws BeansException {

//存储异常的

List propertyAccessExceptions = null;

//将传进来的依赖转为List

List propertyValues = (pvs instanceof MutablePropertyValues ?

((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));

//这个参数为false,不考虑下面这步

if (ignoreUnknown) {

this.suppressNotWritablePropertyException = true;

}

try {

//遍历传进来的所有依赖

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

深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

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

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

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
img

言尽于此,完结

无论是一个初级的 coder,高级的程序员,还是顶级的系统架构师,应该都有深刻的领会到设计模式的重要性。

  • 第一,设计模式能让专业人之间交流方便,如下:

程序员A:这里我用了XXX设计模式

程序员B:那我大致了解你程序的设计思路了

  • 第二,易维护

项目经理:今天客户有这样一个需求…

程序员:明白了,这里我使用了XXX设计模式,所以改起来很快

  • 第三,设计模式是编程经验的总结

程序员A:B,你怎么想到要这样去构建你的代码

程序员B:在我学习了XXX设计模式之后,好像自然而然就感觉这样写能避免一些问题

  • 第四,学习设计模式并不是必须的

程序员A:B,你这段代码使用的是XXX设计模式对吗?

程序员B:不好意思,我没有学习过设计模式,但是我的经验告诉我是这样写的

image

从设计思想解读开源框架,一步一步到Spring、Spring5、SpringMVC、MyBatis等源码解读,我都已收集整理全套,篇幅有限,这块只是详细的解说了23种设计模式,整理的文件如下图一览无余!

image

搜集费时费力,能看到此处的都是真爱!

id)

throws BeansException {

//存储异常的

List propertyAccessExceptions = null;

//将传进来的依赖转为List

List propertyValues = (pvs instanceof MutablePropertyValues ?

((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));

//这个参数为false,不考虑下面这步

if (ignoreUnknown) {

this.suppressNotWritablePropertyException = true;

}

try {

//遍历传进来的所有依赖

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

深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

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

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

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
[外链图片转存中…(img-Tv6TLtcz-1711082482911)]

言尽于此,完结

无论是一个初级的 coder,高级的程序员,还是顶级的系统架构师,应该都有深刻的领会到设计模式的重要性。

  • 第一,设计模式能让专业人之间交流方便,如下:

程序员A:这里我用了XXX设计模式

程序员B:那我大致了解你程序的设计思路了

  • 第二,易维护

项目经理:今天客户有这样一个需求…

程序员:明白了,这里我使用了XXX设计模式,所以改起来很快

  • 第三,设计模式是编程经验的总结

程序员A:B,你怎么想到要这样去构建你的代码

程序员B:在我学习了XXX设计模式之后,好像自然而然就感觉这样写能避免一些问题

  • 第四,学习设计模式并不是必须的

程序员A:B,你这段代码使用的是XXX设计模式对吗?

程序员B:不好意思,我没有学习过设计模式,但是我的经验告诉我是这样写的

[外链图片转存中…(img-ol5fngwF-1711082482912)]

从设计思想解读开源框架,一步一步到Spring、Spring5、SpringMVC、MyBatis等源码解读,我都已收集整理全套,篇幅有限,这块只是详细的解说了23种设计模式,整理的文件如下图一览无余!

[外链图片转存中…(img-YDsNK2ct-1711082482912)]

搜集费时费力,能看到此处的都是真爱!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值