双十一过去了,终于可以把这篇博客补上了。
本文将分析BeanWrapperImpl,出现了大量的源码。如果你对BeanWrapper有相当的了解,请略过;如果想稍微了解一下BeanWrapper,可能需要关注一下源码。
--------------------------------------------
学习 Spring 核心是一件比较耗费精力的过程,代码太多,代码的层次也比较深。每次我都是在上午看代码,把一天中精力比较集中的时间段留给了他。在【 Spring IOC - 依赖注入(概述 getBean )】一文中,我们了解了 Spring 依赖注入的大概过程。本次我们要着重说明一下依赖注入中的 Bean 实例化,以及自动注入的实现方式。
1. 本次分享需要解决的几个问题
不得不说的 BeanWrapper
实例化的经过的各层传递
常用的自动注入如何实现
2. 不得不说的 BeanWrapper
BeanWrapper 是 Spring 框架中十分重要的技术支持组件。他的实现类大概有 1 千多行代码,很长,但是功能也是比较内聚的。 BeanWrapper 的主要作用是把所有的 Bean 的属性都可以集中处理。这种集中处理的机制来自于 Java 的反射。通过 Java 的反射,可以把 Bean 的属性抽取出来,然后在通过反射的方式进行赋值。这种方式使 Spring 可以用同一种方式来处理 Bean 的属性。
2.1 BeanWrapper 的实现类图
上图中可以发现默认实现 BeanWrapper 的实现类 BeanWrapperImpl 通过继承 AbstractPropertyAccessor 来增强其属性的赋值功能。这个过程中你可以定义自己的类型处理器。在 org.springframework.beans.propertyeditors 包下面是 Spring 扩展自己定义的类型处理器,如果需要可以扩展自己处理器注册到容器中。这些不是这次讨论的主题,就不详细说明了。
2.2 构建 BeanWrapper 的实例
构建一个 BeanWrapper 实例有很多种方式,可以指定需要实例化的 Bean.class ,可以指定需要包装的 Bean 实例等。下面就 BeanWrapper 的属性的进行说明。
Object object;
被包装的 Bean 实例,如果传递了对应的 class ,通过 BeanUtils 实例化一个
String nestedPath = "";
嵌套路径。例如如果 BeanA 里面包含属性 BeanB ,那么访问 BeanA 的 BeanB 属性的路径是“ beanA.beanB ”。那 BeanB 的嵌套路径就是“ beanA. ”
Object rootObject;
嵌套上级 Bean 实例,如果没有上级,就是自己本身
TypeConverterDelegate typeConverterDelegate;
类型转换器代理实现
CachedIntrospectionResults cachedIntrospectionResults;
缓存 Bean 实例对应的 class 、属性等信息( beanInfo ),在使用的 Bean 的信息,可以从中获取。( Java 内省机制)
Map nestedBeanWrappers;
保存嵌套路径到 BeanWrapper 实例的映射关系保存。
构造方法重要的有两个:
/**
* Create new BeanWrapperImpl for the given object,
* registering a nested path that the object is in.
* @param object object wrapped by this BeanWrapper
* @param nestedPath the nested path of the object
* @param rootObject the root object at the top of the path
*/
public BeanWrapperImpl(Object object, String nestedPath, Object rootObject) {
registerDefaultEditors();
setWrappedInstance(object, nestedPath, rootObject);
}
/**
* Create new BeanWrapperImpl for the given object,
* registering a nested path that the object is in.
* @param object object wrapped by this BeanWrapper
* @param nestedPath the nested path of the object
* @param superBw the containing BeanWrapper (must not be null
)
*/
private BeanWrapperImpl(Object object, String nestedPath, BeanWrapperImpl superBw) {
setWrappedInstance(object, nestedPath, superBw.getWrappedInstance());
setExtractOldValueForEditor(superBw.isExtractOldValueForEditor());
setAutoGrowNestedPaths(superBw.isAutoGrowNestedPaths());
setAutoGrowCollectionLimit(superBw.getAutoGrowCollectionLimit());
setConversionService(superBw.getConversionService());
setSecurityContext(superBw.acc);
}
/**
* Switch the target object, replacing the cached introspection results only
* if the class of the new object is different to that of the replaced object.
* @param object the new target object
* @param nestedPath the nested path of the object
* @param rootObject the root object at the top of the path
*/
public void setWrappedInstance(Object object, String nestedPath, Object rootObject) {
Assert.notNull(object, "Bean object must not be null");
this.object = object;
this.nestedPath = (nestedPath != null ? nestedPath : "");
this.rootObject = (!"".equals(this.nestedPath) ? rootObject : object);
this.nestedBeanWrappers = null;
this.typeConverterDelegate = new TypeConverterDelegate(this, object);
setIntrospectionClass(object.getClass());
}
2.3 使用 BeanWrapper 设置&获取属性值 (包含嵌套)
在了解 BeanWrapper 设置和获取获取属性值前,我们先了解一下 PropertyTokenHolder 。 PropertyTokenHolder 是 BeanWrapperImpl 的内部类,他很简单,定义了属性命名描述。一个能标识 Bean 的唯一名称(可能包含嵌套的路径), Bean 真实名称(不包含嵌套路径),访问集合属性的索引数组。
private static class PropertyTokenHolder {
// 例如 BeanA 中有个 Set 属性,他们的名称是“xxxSet”,
// 我们可以通过“xxxSet[1]”访问这个属性值,需要获得BeanA.xxxSet的一个值
// 权威唯一名称,例如:xxxSet[1]
public String canonicalName;
// 真实名称,例如:xxxSet
public String actualName;
// 访问集合索引,例如:["1"]
public String[] keys;
}
设置属性值,设置属性值有两个重载的方法可供选择。在设置属性时,需要处理嵌套属性的设置。此种需要了解 PropertyTokenHolder ,他是一个内部简单类,定义了嵌套的名称、真实名称、对应的访问 KEY 三个属性。
@Override
public void setPropertyValue(String propertyName, Object value) throws BeansException {
BeanWrapperImpl nestedBw;
try {
// 此方法在下面介绍
nestedBw = getBeanWrapperForPropertyPath(propertyName);
}
catch (NotReadablePropertyException ex) {
throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,
"Nested property in path '" + propertyName + "' does not exist", ex);
}
// getFinalPath 的处理逻辑是如果 nestedBw 是自己情况,直接返回传入的 propertyName
PropertyTokenHolder tokens = getPropertyNameTokens(getFinalPath(nestedBw, propertyName));
// 后面介绍这个方法
nestedBw.setPropertyValue(tokens, new PropertyValue(propertyName, value));
}
@Override
public void setPropertyValue(PropertyValue pv) throws BeansException {
PropertyTokenHolder tokens = (PropertyTokenHolder) pv.resolvedTokens;
// 如果无法获取属性标识,重新计算一下
if (tokens == null) {
String propertyName = pv.getName();
BeanWrapperImpl nestedBw;
try {
// 如果是嵌套的 BeanWrapper,获取的是嵌套的实例,否则是自己本身
nestedBw = getBeanWrapperForPropertyPath(propertyName);
}
catch (NotReadablePropertyException ex) {
throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,
"Nested property in path '" + propertyName + "' does not exist", ex);
}
tokens = getPropertyNameTokens(getFinalPath(nestedBw, propertyName));
if (nestedBw == this) {
pv.getOriginalPropertyValue().resolvedTokens = tokens;
}
nestedBw.setPropertyValue(tokens, pv);
}
else {
setPropertyValue(tokens, pv);
}
}
@SuppressWarnings("unchecked")
private void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {
String propertyName = tokens.canonicalName;
String actualName = tokens.actualName;
// 如果标识中的 keys 不为空,说明属性是集合方式的,需要通过集合的方式处理
// 并且应该提供对应属性的 getter 方法,因为需要访问对应的集合
if (tokens.keys != null) {
// Apply indexes and map keys: fetch value for all keys but the last one.
PropertyTokenHolder getterTokens = new PropertyTokenHolder();
getterTokens.canonicalName = tokens.canonicalName;
getterTokens.actualName = tokens.actualName;
getterTokens.keys = new String[tokens.keys.length - 1];
System.arraycopy(tokens.keys, 0, getterTokens.keys, 0, tokens.keys.length - 1);
Object propValue;
try {
// 获取对应属性值
propValue = getPropertyValue(getterTokens);
}
catch (NotReadablePropertyException ex) {
throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,
"Cannot access indexed value in property referenced " +
"in indexed property path '" + propertyName + "'", ex);
}
// Set value for last key.
String key = tokens.keys[tokens.keys.length - 1];
if (propValue == null) {
// null map value case
if (this.autoGrowNestedPaths) {
// TODO: cleanup, this is pretty hacky
int lastKeyIndex = tokens.canonicalName.lastIndexOf('[');
getterTokens.canonicalName = tokens.canonicalName.substring(0, lastKeyIndex);
propValue = setDefaultValue(getterTokens);
}
else {
throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + propertyName,
"Cannot access indexed value in property referenced " +
"in indexed property path '" + propertyName + "': returned null");
}
}
// 如果属性值是数组类型
if (propValue.getClass().isArray()) {
PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName);
Class requiredType = propValue.getClass().getComponentType();
int arrayIndex = Integer.parseInt(key);
Object oldValue = null;
try {
if (isExtractOldValueForEditor() && arrayIndex < Array.getLength(propValue)) {
oldValue = Array.get(propValue, arrayIndex);
}
Object convertedValue = convertIfNecessary(propertyName, oldValue, pv.getValue(),
requiredType, TypeDescriptor.nested(property(pd), tokens.keys.length));
Array.set(propValue, arrayIndex, convertedValue);
}
catch (IndexOutOfBoundsException ex) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Invalid array index in property path '" + propertyName + "'", ex);
}
}
// 如果属性值是List类型
else if (propValue instanceof List) {
PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName);
Class requiredType = GenericCollectionTypeResolver.getCollectionReturnType(
pd.getReadMethod(), tokens.keys.length);
List list = (List) propValue;
int index = Integer.parseInt(key);
Object oldValue = null;
if (isExtractOldValueForEditor() && index < list.size()) {
oldValue = list.get(index);
}
Object convertedValue = convertIfNecessary(propertyName, oldValue, pv.getValue(),
requiredType, TypeDescriptor.nested(property(pd), tokens.keys.length));
int size = list.size();
if (index >= size && index < this.autoGrowCollectionLimit) {
for (int i = size; i < index; i++) {
try {
list.add(null);
}
catch (NullPointerException ex) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Cannot set element with index " + index + " in List of size " +
size + ", accessed using property path '" + propertyName +
"': List does not support filling up gaps with null elements");
}
}
list.add(convertedValue);
}
else {
try {
list.set(index, convertedValue);
}
catch (IndexOutOfBoundsException ex) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Invalid list index in property path '" + propertyName + "'", ex);
}
}
}
// 如果属性是Map类型
else if (propValue instanceof Map) {
PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName);
Class mapKeyType = GenericCollectionTypeResolver.getMapKeyReturnType(
pd.getReadMethod(), tokens.keys.length);
Class mapValueType = GenericCollectionTypeResolver.getMapValueReturnType(
pd.getReadMethod(), tokens.keys.length);
Map map = (Map) propValue;
// IMPORTANT: Do not pass full property name in here - property editors
// must not kick in for map keys but rather only for map values.
TypeDescriptor typeDescriptor = (mapKeyType != null ?
TypeDescriptor.valueOf(mapKeyType) : TypeDescriptor.valueOf(Object.class));
Object convertedMapKey = convertIfNecessary(null, null, key, mapKeyType, typeDescriptor);
Object oldValue = null;
if (isExtractOldValueForEditor()) {
oldValue = map.get(convertedMapKey);
}
// Pass full property name and old value in here, since we want full
// conversion ability for map values.
Object convertedMapValue = convertIfNecessary(propertyName, oldValue, pv.getValue(),
mapValueType, TypeDescriptor.nested(property(pd), tokens.keys.length));
map.put(convertedMapKey, convertedMapValue);
}
// 其他是非集合情况,抛出异常
else {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Property referenced in indexed property path '" + propertyName +
"' is neither an array nor a List nor a Map; returned value was [" + pv.getValue() + "]");
}
}
// 当标识中Key为空,此时需要直接设定属性的值
else {
// 获取对应的属性描述
PropertyDescriptor pd = pv.resolvedDescriptor;
// 如果无法获取属性描述,或者获得的属性描述对应的 class 不是被包装实例的class时,重新获取
if (pd == null || !pd.getWriteMethod().getDeclaringClass().isInstance(this.object)) {
pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName);
if (pd == null || pd.getWriteMethod() == null) {
if (pv.isOptional()) {
logger.debug("Ignoring optional value for property '" + actualName +
"' - property not found on bean class [" + getRootClass().getName() + "]");
return;
}
else {
PropertyMatches matches = PropertyMatches.forProperty(propertyName, getRootClass());
throw new NotWritablePropertyException(
getRootClass(), this.nestedPath + propertyName,
matches.buildErrorMessage(), matches.getPossibleMatches());
}
}
pv.getOriginalPropertyValue().resolvedDescriptor = pd;
}
Object oldValue = null;
try {
Object originalValue = pv.getValue();
Object valueToApply = originalValue;
// 如果需要类型转换,需要对输入的属性值进行类型转换
if (!Boolean.FALSE.equals(pv.conversionNecessary)) {
if (pv.isConverted()) {
valueToApply = pv.getConvertedValue();
}
else {
// 如果存在 getter,从 getter 中获取对应的属性值,为了某些个性化的编辑器使用
if (isExtractOldValueForEditor() && pd.getReadMethod() != null) {
final Method readMethod = pd.getReadMethod();
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers()) &&
!readMethod.isAccessible()) {
if (System.getSecurityManager()!= null) {
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
readMethod.setAccessible(true);
return null;
}
});
}
else {
readMethod.setAccessible(true);
}
}
try {
if (System.getSecurityManager() != null) {
oldValue = AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws Exception {
return readMethod.invoke(object);
}
}, acc);
}
else {
oldValue = readMethod.invoke(object);
}
}
catch (Exception ex) {
if (ex instanceof PrivilegedActionException) {
ex = ((PrivilegedActionException) ex).getException();
}
if (logger.isDebugEnabled()) {
logger.debug("Could not read previous value of property '" +
this.nestedPath + propertyName + "'", ex);
}
}
}
valueToApply = convertForProperty(propertyName, oldValue, originalValue, pd);
}
pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue);
}
// 获取 setter 方法
final Method writeMethod = (pd instanceof GenericTypeAwarePropertyDescriptor ?
((GenericTypeAwarePropertyDescriptor) pd).getWriteMethodForActualAccess() :
pd.getWriteMethod());
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers()) && !writeMethod.isAccessible()) {
if (System.getSecurityManager()!= null) {
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
writeMethod.setAccessible(true);
return null;
}
});
}
else {
writeMethod.setAccessible(true);
}
}
// 设置处理后的值
final Object value = valueToApply;
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws Exception {
writeMethod.invoke(object, value);
return null;
}
}, acc);
}
catch (PrivilegedActionException ex) {
throw ex.getException();
}
}
else {
writeMethod.invoke(this.object, value);
}
}
catch (TypeMismatchException ex) {
throw ex;
}
catch (InvocationTargetException ex) {
PropertyChangeEvent propertyChangeEvent =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue());
if (ex.getTargetException() instanceof ClassCastException) {
throw new TypeMismatchException(propertyChangeEvent, pd.getPropertyType(), ex.getTargetException());
}
else {
throw new MethodInvocationException(propertyChangeEvent, ex.getTargetException());
}
}
catch (Exception ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue());
throw new MethodInvocationException(pce, ex);
}
}
}
/**
* 通过嵌套的属性路径递归查询 BeanWrapper 实例。
* Recursively navigate to return a BeanWrapper for the nested property path.
* @param propertyPath property property path, which may be nested
* @return a BeanWrapper for the target bean
*/
protected BeanWrapperImpl getBeanWrapperForPropertyPath(String propertyPath) {
// 获取属性解析的位置,例如属性路径为“beanA.beanB”,此时获取的pos=5
// 如果参数中不包含“.”, pos=-1,这里取得的是第一个“.”的位置
int pos = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(propertyPath);
// Handle nested properties recursively.
// 如果存在嵌套的情况,就需要递归查询,直到找到对应的 BeanWrapper 实例为止。
if (pos > -1) {
// 得到的值为:beanA
String nestedProperty = propertyPath.substring(0, pos);
// 得到的值为:beanB
String nestedPath = propertyPath.substring(pos + 1);
// 通过 beanA 得到嵌套的 BeanWrapper 实例
BeanWrapperImpl nestedBw = getNestedBeanWrapper(nestedProperty);
// 递归查询通过 beanA 得到嵌套的 BeanWrapper 实例的属性值 beanB
return nestedBw.getBeanWrapperForPropertyPath(nestedPath);
}
// 如果不存在嵌套的情况,返回自己本身的引用
else {
return this;
}
}
/**
* 为指定的嵌套属性恢复一个 BeanWrapper 实例,如果没有缓存中找到,重新创建一个。
* Retrieve a BeanWrapper for the given nested property.
* Create a new one if not found in the cache.
* Note: Caching nested BeanWrappers is necessary now,
* to keep registered custom editors for nested properties.
* @param nestedProperty property to create the BeanWrapper for
* @return the BeanWrapper instance, either cached or newly created
*/
private BeanWrapperImpl getNestedBeanWrapper(String nestedProperty) {
// 如嵌套的 BeanWrapper 缓存为空,重建一个
if (this.nestedBeanWrappers == null) {
this.nestedBeanWrappers = new HashMap();
}
// Get value of bean property.
PropertyTokenHolder tokens = getPropertyNameTokens(nestedProperty);
String canonicalName = tokens.canonicalName;
// 获取对应的属性值(留在后面讲解)
Object propertyValue = getPropertyValue(tokens);
if (propertyValue == null) {
if (this.autoGrowNestedPaths) {
propertyValue = setDefaultValue(tokens);
}
else {
throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + canonicalName);
}
}
// Lookup cached sub-BeanWrapper, create new one if not found.
BeanWrapperImpl nestedBw = this.nestedBeanWrappers.get(canonicalName);
if (nestedBw == null || nestedBw.getWrappedInstance() != propertyValue) {
if (logger.isTraceEnabled()) {
logger.trace("Creating new nested BeanWrapper for property '" + canonicalName + "'");
}
// 构建一个新的 BeanWrapper 实例,把自己作为嵌套父类,嵌套路径=当前路径+嵌套Bean标识+“.”
nestedBw = newNestedBeanWrapper(propertyValue, this.nestedPath + canonicalName + NESTED_PROPERTY_SEPARATOR);
// Inherit all type-specific PropertyEditors.
// 复制属性注册器到嵌套的 BeanWrapper 中
copyDefaultEditorsTo(nestedBw);
copyCustomEditorsTo(nestedBw, canonicalName);
// 嵌套 BeanWrapper 缓存添加新建的 BeanWrapper 实例
this.nestedBeanWrappers.put(canonicalName, nestedBw);
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Using cached nested BeanWrapper for property '" + canonicalName + "'");
}
}
return nestedBw;
}
/**
* 解析指定的属性名称,并把它赋值到对应的属性标识中(即保存在 PropertyTokenHolder 中)
* Parse the given property name into the corresponding property name tokens.
* @param propertyName the property name to parse
* @return representation of the parsed property tokens
*/
private PropertyTokenHolder getPropertyNameTokens(String propertyName) {
// 例如输入“xxxSet[1]”
PropertyTokenHolder tokens = new PropertyTokenHolder();
String actualName = null;
List keys = new ArrayList(2);
int searchIndex = 0;
while (searchIndex != -1) {
// 从指定的索引位置查询“[”的位置
int keyStart = propertyName.indexOf(PROPERTY_KEY_PREFIX, searchIndex);
searchIndex = -1;
// 如果能找到,找对应的“]”的位置
if (keyStart != -1) {
int keyEnd = propertyName.indexOf(PROPERTY_KEY_SUFFIX, keyStart + PROPERTY_KEY_PREFIX.length());
if (keyEnd != -1) {
if (actualName == null) {
// actualName = xxxSet
actualName = propertyName.substring(0, keyStart);
}
// key = 1
String key = propertyName.substring(keyStart + PROPERTY_KEY_PREFIX.length(), keyEnd);
if ((key.startsWith("'") && key.endsWith("'")) || (key.startsWith("\"") && key.endsWith("\""))) {
key = key.substring(1, key.length() - 1);
}
keys.add(key);
// 重置查询索取到“]”符号后面
searchIndex = keyEnd + PROPERTY_KEY_SUFFIX.length();
}
}
}
// tokens.actualName = xxxSet
tokens.actualName = (actualName != null ? actualName : propertyName);
// tokens.canonicalName = xxxSet
tokens.canonicalName = tokens.actualName;
if (!keys.isEmpty()) {
// tokens.canonicalName = xxxSet[1]
tokens.canonicalName +=
PROPERTY_KEY_PREFIX +
StringUtils.collectionToDelimitedString(keys, PROPERTY_KEY_SUFFIX + PROPERTY_KEY_PREFIX) +
PROPERTY_KEY_SUFFIX;
// tokens.keys = [1]
tokens.keys = StringUtils.toStringArray(keys);
}
// 输入"xxxSet[1]",得到:
// tokens.actualName="xxxSet",tokens.canonicalName="xxxSet[1]",tokens.keys=["1"]
// 输入"xxxSet",得到:
// tokens.actualName="xxxSet",tokens.canonicalName="xxxSet",tokens.keys=null
return tokens;
}
再来说说获取属性值, BeanWrapperImpl 提供了 getPropertyValue(String propertyName) 来处理。下面看看源码,了解实现方式:
@Override
public Object getPropertyValue(String propertyName) throws BeansException {
// 获取 propertyName 对应的 BeanWrapper 实例。如果包含嵌套,获取最后一层嵌套的 BeanWrapper 实例
BeanWrapperImpl nestedBw = getBeanWrapperForPropertyPath(propertyName);
// 构建访问属性的 property 属性名称的标识
PropertyTokenHolder tokens = getPropertyNameTokens(getFinalPath(nestedBw, propertyName));
// 执行获取属性值
return nestedBw.getPropertyValue(tokens);
}
private Object getPropertyValue(PropertyTokenHolder tokens) throws BeansException {
String propertyName = tokens.canonicalName;
String actualName = tokens.actualName;
// 获取对应的属性描述定义
PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName);
// 如果无法获取getter方法,直接异常
if (pd == null || pd.getReadMethod() == null) {
throw new NotReadablePropertyException(getRootClass(), this.nestedPath + propertyName);
}
final Method readMethod = pd.getReadMethod();
try {
// 如果 getter 访问不是 pulbic 类型,并且不是可以获取的,设置成可以访问
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers()) && !readMethod.isAccessible()) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
readMethod.setAccessible(true);
return null;
}
});
}
else {
readMethod.setAccessible(true);
}
}
// 通过 getter 方法获取对应的属性值
Object value;
if (System.getSecurityManager() != null) {
try {
value = AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws Exception {
return readMethod.invoke(object, (Object[]) null);
}
}, acc);
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
value = readMethod.invoke(object, (Object[]) null);
}
// 如果需要获取具体集合中某项值,指定了keys
if (tokens.keys != null) {
if (value == null) {
// 如果设置了自动生成嵌套的值属性,设置默认值
if (this.autoGrowNestedPaths) {
value = setDefaultValue(tokens.actualName);
}
else {
throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + propertyName,
"Cannot access indexed value of property referenced in indexed " +
"property path '" + propertyName + "': returned null");
}
}
String indexedPropertyName = tokens.actualName;
// apply indexes and map keys
for (int i = 0; i < tokens.keys.length; i++) {
String key = tokens.keys[i];
// 如果属性值为 null,抛出异常
if (value == null) {
throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + propertyName,
"Cannot access indexed value of property referenced in indexed " +
"property path '" + propertyName + "': returned null");
}
// 如果对应的属性是数组类型
else if (value.getClass().isArray()) {
int index = Integer.parseInt(key);
value = growArrayIfNecessary(value, index, indexedPropertyName);
value = Array.get(value, index);
}
// 如果对应的属性是List类型
else if (value instanceof List) {
int index = Integer.parseInt(key);
List list = (List) value;
growCollectionIfNecessary(list, index, indexedPropertyName, pd, i + 1);
value = list.get(index);
}
// 如果对应的属性是Set类型
else if (value instanceof Set) {
// Apply index to Iterator in case of a Set.
Set set = (Set) value;
int index = Integer.parseInt(key);
if (index < 0 || index >= set.size()) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Cannot get element with index " + index + " from Set of size " +
set.size() + ", accessed using property path '" + propertyName + "'");
}
Iterator it = set.iterator();
for (int j = 0; it.hasNext(); j++) {
Object elem = it.next();
if (j == index) {
value = elem;
break;
}
}
}
// 如果对应的属性是Map类型
else if (value instanceof Map) {
Map map = (Map) value;
Class mapKeyType = GenericCollectionTypeResolver.getMapKeyReturnType(pd.getReadMethod(), i + 1);
// IMPORTANT: Do not pass full property name in here - property editors
// must not kick in for map keys but rather only for map values.
TypeDescriptor typeDescriptor = mapKeyType != null ? TypeDescriptor.valueOf(mapKeyType) : TypeDescriptor.valueOf(Object.class);
Object convertedMapKey = convertIfNecessary(null, null, key, mapKeyType, typeDescriptor);
value = map.get(convertedMapKey);
}
// 其他情况,不是集合类型,不支持,抛出异常
else {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Property referenced in indexed property path '" + propertyName +
"' is neither an array nor a List nor a Set nor a Map; returned value was [" + value + "]");
}
indexedPropertyName += PROPERTY_KEY_PREFIX + key + PROPERTY_KEY_SUFFIX;
}
}
return value;
}
catch (IndexOutOfBoundsException ex) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Index of out of bounds in property path '" + propertyName + "'", ex);
}
catch (NumberFormatException ex) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Invalid index in property path '" + propertyName + "'", ex);
}
catch (TypeMismatchException ex) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Invalid index in property path '" + propertyName + "'", ex);
}
catch (InvocationTargetException ex) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Getter for property '" + actualName + "' threw exception", ex);
}
catch (Exception ex) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Illegal attempt to get property '" + actualName + "' threw exception", ex);
}
}
通过上面的代码分析,我们了解了 BeanWrapper 是如何获取属性值以及设置属性值的。同时 BeanWrapperImpl 通过定义嵌套路径来解析我们真正的需要的属性,并且智能的来判断我们意图。值得一提的是,对比设置方法和获取方法,我们会发现在设置方法中其并没有对 Set 进行处理,但是我可以通过 Set[index] 来获取对应的值。这是因为 Set 是要去重的,在设置的时候无法判断他的索引位置,但是在获取的时候可以通过遍历来获取对应 index 的值。
2.5 BeanWrapper 中类型转换
上面在分析设置 Bean 属性值时,使用了 convertIfNecessary 方法,这个方法中会判断是否需要类型转换。下面我们来分析这个类型转换。
BeanWrapperImpl 中的类型转换是通过 TypeConverterDelegate 来实现的。构建这个实例发生在我们构建 BeanWrapper 实例的时候。
public BeanWrapperImpl(boolean registerDefaultEditors) {
if (registerDefaultEditors) {
registerDefaultEditors();
}
this.typeConverterDelegate = new TypeConverterDelegate(this);
}
public void setWrappedInstance(Object object, String nestedPath, Object rootObject) {
Assert.notNull(object, "Bean object must not be null");
this.object = object;
this.nestedPath = (nestedPath != null ? nestedPath : "");
this.rootObject = (!"".equals(this.nestedPath) ? rootObject : object);
this.nestedBeanWrappers = null;
this.typeConverterDelegate = new TypeConverterDelegate(this, object);
setIntrospectionClass(object.getClass());
}
TypeConverterDelegate 重要提供的服务是 convertIfNecessary 。这个方法提供了几个重载方法,如下:
/**
* Convert the value to the specified required type.
* @param newValue the proposed new value
* @param requiredType the type we must convert to
* (or null
if not known, for example in case of a collection element)
* @param methodParam the method parameter that is the target of the conversion
* (may be null
)
* @return the new value, possibly the result of type conversion
* @throws IllegalArgumentException if type conversion failed
*/
public T convertIfNecessary(Object newValue, Class requiredType, MethodParameter methodParam)
throws IllegalArgumentException {
return convertIfNecessary(null, null, newValue, requiredType,
(methodParam != null ? new TypeDescriptor(methodParam) : TypeDescriptor.valueOf(requiredType)));
}
/**
* Convert the value to the required type for the specified property.
* @param propertyName name of the property
* @param oldValue the previous value, if available (may be null
)
* @param newValue the proposed new value
* @param requiredType the type we must convert to
* (or null
if not known, for example in case of a collection element)
* @return the new value, possibly the result of type conversion
* @throws IllegalArgumentException if type conversion failed
*/
public T convertIfNecessary(
String propertyName, Object oldValue, Object newValue, Class requiredType)
throws IllegalArgumentException {
return convertIfNecessary(propertyName, oldValue, newValue, requiredType, TypeDescriptor.valueOf(requiredType));
}
/**
* Convert the value to the required type (if necessary from a String),
* for the specified property.
* @param propertyName name of the property
* @param oldValue the previous value, if available (may be null
)
* @param newValue the proposed new value
* @param requiredType the type we must convert to
* (or null
if not known, for example in case of a collection element)
* @param typeDescriptor the descriptor for the target property or field
* @return the new value, possibly the result of type conversion
* @throws IllegalArgumentException if type conversion failed
*/
@SuppressWarnings("unchecked")
public T convertIfNecessary(String propertyName, Object oldValue, Object newValue,
Class requiredType, TypeDescriptor typeDescriptor) throws IllegalArgumentException {
Object convertedValue = newValue;
// Custom editor for this type?
// 查找匹配的 PropertyEditor
PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
ConversionFailedException firstAttemptEx = null;
// No custom editor but custom ConversionService specified?
// 如果没有自定义的 PropertyEditor,但是定义了转换服务(conversionService)
ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
if (editor == null && conversionService != null && convertedValue != null && typeDescriptor != null) {
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
TypeDescriptor targetTypeDesc = typeDescriptor;
if (conversionService.canConvert(sourceTypeDesc, targetTypeDesc)) {
try {
return (T) conversionService.convert(convertedValue, sourceTypeDesc, targetTypeDesc);
}
// 如果无法转换成功,走下面的默认处理逻辑
catch (ConversionFailedException ex) {
// fallback to default conversion logic below
firstAttemptEx = ex;
}
}
}
// Value not of required type?
// 如果找到了 PropertyEditor,或者提供转换的新值不是指定的转换类型
if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
// 如果目标类型是集合类型,并且对应的值是 string 类型
if (requiredType != null && Collection.class.isAssignableFrom(requiredType) && convertedValue instanceof String) {
TypeDescriptor elementType = typeDescriptor.getElementTypeDescriptor();
// 处理枚举类型集合
if (elementType != null && Enum.class.isAssignableFrom(elementType.getType())) {
convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
}
}
// 如果对应自定义的 PropertyEditer,使用默认的 PropertyEditor
if (editor == null) {
editor = findDefaultEditor(requiredType, typeDescriptor);
}
// 执行类型转换,获取转换后的值
convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
}
// 如果定义了需要转换的类型
if (requiredType != null) {
// Try to apply some standard type conversion rules if appropriate.
// 如果标准的的转换规则存在,尝试去适配标准的转换器
// 如果通过上面的转换(有自定义的转换或者需要转换的的值不是目标类型)能够得到转换后的值
if (convertedValue != null) {
// 如果是数组,通过数组转换返回
// 如果对应的值是 spring,指定的转换数组的内部类型是枚举,用逗号分隔值
if (requiredType.isArray()) {
// Array required -> apply appropriate conversion of elements.
if (convertedValue instanceof String && Enum.class.isAssignableFrom(requiredType.getComponentType())) {
convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
}
return (T) convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType());
}
// 如果对应的转换值是集合类型,通过集合转换
else if (convertedValue instanceof Collection) {
// Convert elements to target type, if determined.
convertedValue = convertToTypedCollection(
(Collection) convertedValue, propertyName, requiredType, typeDescriptor);
}
// 如果对应的转换值是Map类型,通过Map转换器转换
else if (convertedValue instanceof Map) {
// Convert keys and values to respective target type, if determined.
convertedValue = convertToTypedMap(
(Map) convertedValue, propertyName, requiredType, typeDescriptor);
}
// 如果对应的转换类型不是数组类型,对应的值是数组类型,取第一个值作为转换值
if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {
convertedValue = Array.get(convertedValue, 0);
}
// 如果对应转换类型是String类型,转化值是标准内部类型,直接转换为string返回
if (String.class.equals(requiredType) && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
// We can stringify any primitive value...
return (T) convertedValue.toString();
}
// 处理枚举类型,值是枚举对应的名称
else if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) {
if (!requiredType.isInterface() && !requiredType.isEnum()) {
try {
Constructor strCtor = requiredType.getConstructor(String.class);
return (T) BeanUtils.instantiateClass(strCtor, convertedValue);
}
catch (NoSuchMethodException ex) {
// proceed with field lookup
if (logger.isTraceEnabled()) {
logger.trace("No String constructor found on type [" + requiredType.getName() + "]", ex);
}
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Construction via String failed for type [" + requiredType.getName() + "]", ex);
}
}
}
String trimmedValue = ((String) convertedValue).trim();
if (requiredType.isEnum() && "".equals(trimmedValue)) {
// It's an empty enum identifier: reset the enum value to null.
return null;
}
convertedValue = attemptToConvertStringToEnum(requiredType, trimmedValue, convertedValue);
}
}
// 如果转换后的值不是指定的转换类型,异常
if (!ClassUtils.isAssignableValue(requiredType, convertedValue)) {
if (firstAttemptEx != null) {
throw firstAttemptEx;
}
// Definitely doesn't match: throw IllegalArgumentException/IllegalStateException
StringBuilder msg = new StringBuilder();
msg.append("Cannot convert value of type [").append(ClassUtils.getDescriptiveType(newValue));
msg.append("] to required type [").append(ClassUtils.getQualifiedName(requiredType)).append("]");
if (propertyName != null) {
msg.append(" for property '").append(propertyName).append("'");
}
if (editor != null) {
msg.append(": PropertyEditor [").append(editor.getClass().getName()).append(
"] returned inappropriate value of type [").append(
ClassUtils.getDescriptiveType(convertedValue)).append("]");
throw new IllegalArgumentException(msg.toString());
}
else {
msg.append(": no matching editors or conversion strategy found");
throw new IllegalStateException(msg.toString());
}
}
}
if (firstAttemptEx != null) {
if (editor == null && convertedValue == newValue) {
throw firstAttemptEx;
}
logger.debug("Original ConversionService attempt failed - ignored since " +
"PropertyEditor based conversion eventually succeeded", firstAttemptEx);
}
return (T) convertedValue;
}
3. 实例化的各个流程
再次回顾一下上次分享中的 Bean 依赖注入的大概过程:
BeanFactory.getBean → AbstarctBeanFactory.doGetBean → AbstractAutowireCapableBeanFactory.createBean
中间会递归查询依赖,从而完成 Bean 的依赖注入过程。这个过程也是 Bean 实例化过程。
3.1 创建Bean实例
创建 Bean 实例当调用到 AbstractAutowireCapableBeanFactory.createBean 方法后,会转发到 AbstractAutowireCapableBeanFactory.doCreateBean 方法。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
// Instantiate the bean.
// 初始化 Bean 实例,创建 Bean 实例的 BeanWrapper。
// 如果是单例,需要从容器实例缓存中获取,并删除缓存
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
Class beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
// Allow post-processors to modify the merged bean definition.
// 给你一次修改 bean 定义的机会,同时你可以来检查bean定义的相关属性
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
mbd.postProcessed = true;
}
}
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
// 可以做代理,改变Bean的引用的地方(SmartInstantiationAwareBeanPostProcessor)
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, new ObjectFactory() {
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
// 组装 Bean,后面会讲到
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
// 二次验证提前暴露引用(SmartInstantiationAwareBeanPostProcessor)的处理是否正确
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set actualDependentBeans = new LinkedHashSet(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
// Register bean as disposable.
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
/**
* 通过指定的实例化策略初始化一个新的的 Bean 实例。初始化策略:工厂方法、自动装载构造方法、默认简单构造方法
* Create a new instance for the specified bean, using an appropriate instantiation strategy:
* factory method, constructor autowiring, or simple instantiation.
* @param beanName the name of the bean
* @param mbd the bean definition for the bean
* @param args arguments to use if creating a prototype using explicit arguments to a
* static factory method. It is invalid to use a non-null args value in any other case.
* @return BeanWrapper for the new instance
* @see #instantiateUsingFactoryMethod
* @see #autowireConstructor
* @see #instantiateBean
*/
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
// Make sure bean class is actually resolved at this point.
Class beanClass = resolveBeanClass(mbd, beanName);
if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
}
// 如果配置了通过工厂方法构造,查找对应的工厂方法来初始化 Bean 实例
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// Shortcut when re-creating the same bean…
// 如果是重新创建同一个 bean,可以根据 BeanDefinition 中的标识来判断
boolean resolved = false;
boolean autowireNecessary = false;
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
// 如果已经通过构造方法或者的构造工厂处理过 bean 定义
if (resolved) {
if (autowireNecessary) {
return autowireConstructor(beanName, mbd, null, null);
}
else {
return instantiateBean(beanName, mbd);
}
}
// 剩下的就是需要去查询使用那个构造方法,如果能找到对应的构造方法,使用构造方法来创建实例,
// 否则就是使用默认构造方法。例如经常使用的默认无参数构造方法
// Need to determine the constructor…
// 如果定义了对应的 BeanPostProcessor,定义使用某个构造方法,使用之
Constructor[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null ||
mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
// No special handling: simply use no-arg constructor.
// 默认使用无参数构造方法
return instantiateBean(beanName, mbd);
}
4. 自动注入实现方式
4.1 组装 Bean
你已经厌倦了上面上面长篇的累述,你还没有了解到 BeanWrapper 和组装 Bean 的用途,那么我可以高兴的告诉你,下面我就分析 Bean 的组装,了解 BeanWrapper 的用途。当了解完这个下面这个方法后,我们这次的分享就基本上结束了。
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean:
/**
* 根据 BeanWrapper,来组装 Bean 实例
* Populate the bean instance in the given BeanWrapper with the property values
* from the bean definition.
* @param beanName the name of the bean
* @param mbd the bean definition for the bean
* @param bw BeanWrapper with bean instance
*/
protected void populateBean(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw) {
// 从 BeanDefinition 中获取所有的的 Bean 属性
PropertyValues pvs = mbd.getPropertyValues();
// 如果没有指定 BeanWrapper,但是有 Bean 属性,异常返回,否则跳过处理
if (bw == null) {
if (!pvs.isEmpty()) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
}
else {
// Skip property population phase for null instance.
return;
}
}
// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
// state of the bean before properties are set. This can be used, for example,
// to support styles of field injection.
boolean continueWithPropertyPopulation = true;
// 给你一次扩展的 BeanPostProcessor 的机会,来在 Bean 实例化后做点儿自己想干的事情
// 回想一下对应的 InstantiationAwareBeanPostProcessor 什么时间会在初始化前调用?
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
continueWithPropertyPopulation = false;
break;
}
}
}
}
if (!continueWithPropertyPopulation) {
return;
}
// 处理根据名称自动转载和根据类型自动装载的过程
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// Add property values based on autowire by name if applicable.
// 根据名称增加属性的装载
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
// Add property values based on autowire by type if applicable.
// 根据类型增加属性的装载
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);
// 在 Bean 发布可以使用前,检查依赖是否完整等
// 如果定义了InstantiationAwareBeanPostProcessor,你可以自定义自己的检查逻辑
// 也可以强制加载自己的依赖,例如注解 Resource 的实现等。
if (hasInstAwareBpps || needsDepCheck) {
PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw);
if (hasInstAwareBpps) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvs == null) {
return;
}
}
}
}
if (needsDepCheck) {
checkDependencies(beanName, mbd, filteredPds, pvs);
}
}
// 对Bean相关的属性开始发布赋值,可能递归 getBean。
applyPropertyValues(beanName, mbd, bw, pvs);
}
/**
* Apply the given property values, resolving any runtime references
* to other beans in this bean factory. Must use deep copy, so we
* don't permanently modify this property.
* @param beanName the bean name passed for better exception information
* @param mbd the merged bean definition
* @param bw the BeanWrapper wrapping the target object
* @param pvs the new property values
*/
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
if (pvs == null || pvs.isEmpty()) {
return;
}
MutablePropertyValues mpvs = null;
List original;
if (System.getSecurityManager()!= null) {
if (bw instanceof BeanWrapperImpl) {
((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());
}
}
// 获取属性列表
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 {
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.
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();
// 通过属性转换处理器来完成对应属性值的赋值
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 {
// 通过 BeanWrapper 来把所有的属性值赋值
bw.setPropertyValues(new MutablePropertyValues(deepCopy));
}
catch (BeansException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Error setting property values", ex);
}
}
4.2 详解各个自动注入代码
属性名称自动装载
protected void autowireByName(
String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
for (String propertyName : propertyNames) {
if (containsBean(propertyName)) {
Object bean = getBean(propertyName);
pvs.add(propertyName, bean);
registerDependentBean(propertyName, beanName);
if (logger.isDebugEnabled()) {
logger.debug("Added autowiring by name from bean name '" + beanName +
"' via property '" + propertyName + "' to bean named '" + propertyName + "'");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName +
"' by name: no matching bean found");
}
}
}
}
属性类型自动装载
protected void autowireByType(
String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
TypeConverter converter = getCustomTypeConverter();
if (converter == null) {
converter = bw;
}
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.
if (!Object.class.equals(pd.getPropertyType())) {
MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
// Do not allow eager init for type matching in case of a prioritized post-processor.
boolean eager = !PriorityOrdered.class.isAssignableFrom(bw.getWrappedClass());
DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
if (autowiredArgument != null) {
pvs.add(propertyName, autowiredArgument);
}
for (String autowiredBeanName : autowiredBeanNames) {
registerDependentBean(autowiredBeanName, beanName);
if (logger.isDebugEnabled()) {
logger.debug("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);
}
}
}
5. 小结
通过上面的分享,我们了解了 Bean 创建过程。一般情况下,我们会使用构造函数或者 setter 方法来进行依赖。 BeanWrapper 能很好的处理 getter 和 setter 方式;构造方法、工厂方法的形式构建 bean , Spring 也做了很好的处理。但是我们常用“ Resouce ”方式,即没有构造方法、也没有 setter 方法,又该如何处理呢?这个留在下一次分享,关于 BeanPostProcessor 。
大小: 38.2 KB
你可能感兴趣的:(spring,ioc,依赖注入,bean实例化,BeanWrapper)
vite+vue+ts+element-plus从零开发管理后台框架(15)-暗黑模式
vue3
设置编辑src/main.ts,引入暗黑模式样式文件。import'element-plus/dist/index.css'import'element-plus/theme-chalk/dark/css-vars.css'编辑src/views/Main.vue,template段header-right下最前面添加如下代码。script段引入并实例化useDarkimport{useFulls
手把手教你将 DeepSeek 集成到 Java 的 Spring Boot 项目中
Java_young
AI java spring boot 开发语言
前面阳仔给大家介绍了如何将deepseek结合进入办公软件中,今天阳仔将详细介绍如何将DeepSeek集成到Java的SpringBoot项目中,让你的项目具备智能交互能力。一、准备工作1.注册DeepSeek账号并获取APIKey访问DeepSeek官网,点击右上角的“开放平台”,注册或登录账号。在左侧菜单中点击“APIKeys”,点击“创建APIKey”,为APIKey命名(如“test”),
当R安装包package ‘XXX is not available (for R version 3.5.3)
momo酱豆是沃
遇到这个情况,说明包源不在或者改了地方,那就换个办法呗比如impute就这样子安装:source(“http://bioconductor.org/biocLite.R”)biocLite(“impute”)library(“impute”)或者可以从官网下载,可以参考我之前的一篇文章https://blog.csdn.net/gao_yan1/article/details/103381304
【从0到1构建高并发后端系统:Spring Boot + Redis + RabbitMQ实战指南】
小怪兽9699
spring boot
一、架构演进路径图图1:从单体架构到微服务集群的演进过程二、核心优化策略与落地实践1.数据库优化方案分库分表实践://ShardingSphere分片策略配置spring:shardingsphere:datasource:names:ds0,ds1rules:sharding:tables:order:actual-data-nodes:ds$->{0..1}.order_$->{0..3}ta
【从0到1构建实时聊天系统:Spring Boot + Vue3 + WebSocket全栈实战】
小怪兽9699
spring boot websocket 后端
一、项目架构技术栈清单:后端:SpringBoot3.0+WebSocket+STOMP前端:Vue3+Pinia+WebSocketClient部署:Nginx+DockerCompose二、核心功能实现1.WebSocket双向通信//后端配置类@Configuration@EnableWebSocketMessageBrokerpublicclassWebSocketConfigimplem
C# 类的结构
weixin_48990772
c#
类现实世界,表示一类事物。比如:动物,人。类需要具象化实例化理论上类中的成员没有编写顺序。约定:字段--》构造函数--》属性--》方法
执行顺序:字段如果有初始化先执行字段初始化进入构造函数,实例构造函数可以执行多次//如果是静态构造函数会在类实例化的时候最先执行,并且只执行一次访问嵌套类和调用命名空间一样打点调用普通类中能包含实例方法、静态方法、虚方法、密封方法,不能包含抽象方法静态类中只能包含
Visual Studio Code 基本使用指南
The god of big data
教程 神器?三叉戟? vscode ide 编辑器
VisualStudioCode(简称VSCode)是一款由微软开发的免费、开源、跨平台的代码编辑器,凭借其轻量级设计、丰富的插件生态和强大的功能,成为全球开发者的首选工具。本文将从安装配置到核心功能,全面解析VSCode的基本使用方法。一、安装与配置下载与安装访问VSCode官网下载对应操作系统的安装包(支持Windows、macOS、Linux)134。安装类型选择:UserInstaller
Spring Boot整合MinIO对象存储教程
嘵奇
提升自己 spring boot 后端 java
精心整理了最新的面试资料和简历模板,有需要的可以自行获取点击前往百度网盘获取点击前往夸克网盘获取以下是一份详细的SpringBoot整合MinIO的教程,包含基础配置和常用文件操作示例:SpringBoot整合MinIO对象存储教程一、MinIO简介MinIO是一款高性能、云原生的分布式对象存储系统,兼容AmazonS3API。适用于存储图片、文档、视频等非结构化数据。二、环境准备安装MinIO服
SpringBoot+Vue前后端分离项目的搭建及简单开发(这次保证看明白~)
m0_67265464
前端 html javascript 开发语言 ecmascript
文章目录概述一、搭建SpringBoot后端1.sql脚本2.新建SpringBoot项目3.MP代码生成4.编写Controller二、搭建Vue前端1.IDEA安装Vue.js插件2.IDEA启动Vue项目3.编写Vue代码4.接收后端数据三、ElementUI使用1.简单的数据展示2.Element-ui更多…参看:https://www.bilibili.com/video/BV13741
必看!计算机毕设答辩高分技巧,让你脱颖而出
源码姑娘
毕业设计
必看!计算机毕设答辩高分技巧,让你脱颖而出一、答辩前的充分准备:技术与内容的“双向打磨”1.吃透项目,技术实现了然于胸计算机毕设的核心在于技术逻辑与系统设计的合理性。答辩前需反复回顾代码实现、架构设计、数据库模型等关键细节,确保能清晰阐述以下问题:技术选型依据:为何选择SpringBoot而非其他框架?所选算法的优势与局限性是什么?系统功能验证:如何通过测试用例或用户反馈验证系统可行性?若涉及机器
SpringCloud——Ribbon实现客户端负载均衡
袁雾头
SpringCloud
SpringCloud——Ribbon实现客户端负载均衡SpringCloudRibbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于NetflixRibbon实现。通过SpringCloud的封装,可以让我们轻松地将面向服务的REST模板请求自动转换成客户端负载均衡的服务调用。客户端负载均衡硬件负载均衡的设备或是软件负载均衡的软件模块都会维护一个可用的服务端清单,通过心跳检测来剔除故障
苍穹外卖(Springboot3实现) day01
十年不明
苍穹外卖学习 spring spring boot java
黑马给的起步代码基础工程版本是springboot2.x我的电脑用的是JDK21springboot3.x所以第一天整了很长时间需要慢慢修改配置环境目录依赖版本更换springboot版本更换mybatis版本更换lombok版本更换数据库依赖更新(很重要)swagger配置问题代码补全依赖版本更换springboot版本更换父工程sky-take-out的pom文件添加spring-boot-s
大模型系列——Spring AI Advisor 指南
猫猫姐
大模型 人工智能 spring java
大模型系列——SpringAIAdvisor指南1、概览AI驱动的应用已成为我们的新现实。我们正在广泛实现各种RAG应用和提示API,并使用LLM创建令人印象深刻的项目。借助SpringAI,我们可以更快、更稳定地完成这些任务。本文将带你了解SpringAIAdvisor这一功能,它可以为我们处理各种常规任务。2、SpringAIAdvisor是什么?Advisors是在AI应用程序中处理请求和响
【SpringBoot】Spring AOP详解
一只爱打拳的程序猿
Spring spring java 后端 spring boot
目录一、什么是SpringAOP二、SpringAOP实现2.1添加SpringAOP依赖2.2定义切面和切点2.3解释AspectJ2.4什么是通知一、什么是SpringAOPAOP(AspectOrientedProgramming)即面向切面编程,是对某一类事件的集中处理。面向切面编程(AOP)是一种编程范式,旨在将横切关注点(如日志记录、事务管理、权限验证等)从业务逻辑代码中分离出来,从而
SpringBoot统一功能处理——统一异常处理
棕豆兔&
spring boot spring java
目录一、异常简单使用二、@ControllerAdvice源码分析一、异常简单使用统一异常处理使用的是@ControllerAdvice+@ExceptionHandler来实现的,@ControllerAdvice表示控制器通知类,@ExceptionHandler是异常处理器,两个结合表示当出现异常的时候执行某个通知,也就是执行某个方法事件。importcom.example.demo.mod
大模型——Spring Boot 整合 Spring AI 实现项目接入ChatGPT
不二人生
大模型 人工智能 大模型
大模型——SpringBoot整合SpringAI实现项目接入ChatGPT随着人工智能技术的快速发展,越来越多的应用程序开始集成人工智能功能,以提供更智能、更个性化的体验。诸如ChatGPT等开放性大型语言模型的出现,使得自然语言处理和对话系统的开发变得更加容易和普及。这些技术已经在社交媒体、客户服务、教育等领域展现出巨大潜力,对于提升用户体验和提高工作效率至关重要。优势在之前,openai已经
Java 拦截器实战:从入门到精通(精选)
我真的不想做程序员
java java spring 开发语言 后端 算法
目录一、拦截器基础概念二、实战案例1.用户认证与授权2.请求日志与性能监控3.国际化与本地化三、注册拦截器四、总结在JavaWeb开发中,拦截器是一种强大的工具,它允许开发者在请求处理的各个环节进行拦截和处理。与过滤器类似,拦截器也能够对请求和响应进行操作,但它工作在SpringMVC框架内部,能够访问Spring管理的Bean,与Spring的依赖注入无缝集成。本文将通过实战案例,帮助您深入理解
spring核心:IOC和AOP详解
sc重新启程
spring java 后端
引言Spring是众多开源java项⽬中的⼀员,基于分层的javaEE应⽤⼀站式轻量级开源框架,目的是简化企业应用程序的开发,主要核⼼是IOC(控制反转/依赖注⼊)与AOP(⾯向切⾯)两⼤技术,实现项⽬在开发过程中的轻松解耦,提⾼项⽬的开发效率。在项⽬中引⼊Spring可以降低组件之间的耦合度,实现软件各层之间的解耦。SpringIOCIOC,InversionofControl,控制反转,指将对
技术解析:格意互联商城系统(多端适配+开源二次开发
西安漫格科技
开源
一、系统概述格意互联商城系统由西安漫格网络科技有限公司独立研发,专注于商城线上销售场景,支持多端适配(APP、小程序、公众号、H5)及二次开发。系统基于JAVA技术栈构建,采用SpringBoot+JPA作为后端框架,前端用户端使用UniApp实现跨平台兼容,管理端基于Vue+ElementUI开发,具备高扩展性与灵活性111。二、技术架构核心技术栈后端服务:SpringBoot+JPA,支持高并
spring笔记
@卡卡-罗特
spring 笔记 java
01spring简介1.Spring是什么?•定义:Spring是Java生态中轻量级、开源的全栈应用开发框架,核心目标是简化企业级Java应用的开发。•核心思想:•IoC(控制反转):将对象的创建和管理交给框架,开发者无需手动new对象。•AOP(面向切面编程):通过代理机制,将日志、事务等横切关注点与业务代码解耦。•模块化设计:自由组合所需功能(如Web、数据访问、安全等)。2.Spring的
Spring MVC笔记
@卡卡-罗特
spring mvc 笔记
01什么是SpringMVCSpringMVC是Spring框架中的一个核心模块,专门用于构建Web应用程序。它基于经典的MVC设计模式(Model-View-Controller),但通过Spring的特性(如依赖注入、注解驱动)大幅简化了开发流程。SpringMVC是什么?本质:一个基于Java的Web框架,帮助开发者快速、结构化地开发动态网站或RESTfulAPI。核心思想:将应用程序拆分为
Springboot中使用@Async注解7大失效场景及解决方案
江-小北
Java面试题 spring boot 数据库 java
前言在SpringBoot中,@Async注解就像一把瑞士军刀,能帮你轻松处理那些耗时的任务,让主线程可以继续忙别的事儿。不过,跟所有强大的工具一样,用不好它也可能出岔子。有时候,你可能因为线程池没配好、异常没处理好,或者Spring代理没生效等原因,导致@Async没按你期望的那样工作。为了避免这些坑,咱们得深入了解下@Async是怎么工作的,还要知道怎么用才能不出问题。接下来,咱们就来聊聊7种
```markdown
伍辰惟
#超越边界:构建优雅的六边形架构(HexagonalArchitecture)hex-arch-kotlin-spring-bootReferenceJVMmultimoduleprojectforareactivemicroserviceandlambdausingahexagonalarchitecture,DDD,Kotlin,SpringBoot,Quarkus,Lambda,Gradle
Java基础系列:深入解析抽象类、接口与Lambda表达式及避坑指南
JouJz
java 开发语言
目录一、抽象类:半成品的艺术1.核心特征解析2.典型应用场景3.三大经典陷阱陷阱1:尝试实例化抽象类陷阱2:未实现全部抽象方法陷阱3:构造方法调用可覆盖方法二、接口:行为契约的进化1.接口的现代形态(Java8+)2.接口与抽象类对比3.五大核心陷阱陷阱1:默认方法冲突陷阱2:常量隐藏陷阱3:静态方法陷阱陷阱4:函数式接口误用陷阱5:接口演化风险三、Lambda表达式:简洁之美与暗礁1.核心语法全
Spring Boot笔记
@卡卡-罗特
spring boot 笔记 后端
01概要SpringBoot是Java领域最流行的快速开发框架,专为简化Spring应用的初始搭建和开发而设计。一、SpringBoot解决了什么问题?传统Spring痛点•繁琐的XML配置•需要手动管理依赖版本•部署依赖外部Web服务器(如Tomcat)SpringBoot的答案•约定优于配置:自动配置80%的默认设置•内嵌服务器:直接打包成可执行JAR•起步依赖:一键集成常用技术栈(如数据库、
探索未来架构的钥匙:Hex-Arch-Kotlin-Spring-Boot
孟振优Harvester
探索未来架构的钥匙:Hex-Arch-Kotlin-Spring-Boothex-arch-kotlin-spring-bootReferenceJVMmultimoduleprojectforareactivemicroserviceandlambdausingahexagonalarchitecture,DDD,Kotlin,SpringBoot,Quarkus,Lambda,Gradle.项
使用websocket,注入依赖service的bean为null
木头没有瓜
websocket 网络协议 网络
问题:依赖注入失败,service获取不到,提示null这是参考代码packagecom.shier.ws;importcn.hutool.core.date.DateUtil;importcn.hutool.json.JSONObject;importcn.hutool.json.JSONUtil;importcom.google.gson.Gson;importcom.shier.config
Java必知必会系列:Web框架与Spring MVC
AI天才研究院
Java实战 编程实践 大数据 人工智能 语言模型 Java Python 架构设计
作者:禅与计算机程序设计艺术1.背景介绍在企业级应用开发中,采用前后端分离模式和RESTfulAPI风格的Web服务架构逐渐成为主流。基于这些技术,前端可以采用JavaScript、HTML、CSS等多种技术进行开发,而后端则可以通过各种Web框架构建出高性能、可扩展性强的应用系统。在本章节中,我将介绍Java最具代表性的Web框架SpringMVC,并结合实际案例,展示如何利用SpringMVC
Gateway网关分布式微服务认证鉴权
NaughtyBo
# Spring security spring cloud gateway
文章目录学习链接微服务认证方案学习链接【OAuth2系列】SpringCloudGateway作为OAuth2Client接入第三方单点登录代码实践实战干货!SpringCloudGateway整合OAuth2.0实现分布式统一认证授权!spring-cloud-gateway-oauth2的github代码地址-已克隆到gitee微服务权限终极解决方案,SpringCloudGateway+Oa
Spring Cloud Gateway微服务网关鉴权
chengqiuming
java java
一网关鉴权1问题当我们在未登录状态下点击“购买课程”按钮时,会显示“未知错误”,查看trade微服务控制台,发现,JWT为空,无法鉴权。2解决方案微服务网关中添加自定义全局过滤器,统一处理需要鉴权的服务。3鉴权逻辑描述当客户端第一次请求服务时,服务端对用户进行信息认证(登录)认证通过,将用户信息进行加密形成token,返回给客户端作为登录凭证以后每次请求,客户端都携带认证的token服务端对tok
windows下源码安装golang
616050468
golang安装 golang环境 windows
系统: 64位win7, 开发环境:sublime text 2, go版本: 1.4.1
1. 安装前准备(gcc, gdb, git)
golang在64位系
redis批量删除带空格的key
bylijinnan
redis
redis批量删除的通常做法:
redis-cli keys "blacklist*" | xargs redis-cli del
上面的命令在key的前后没有空格时是可以的,但有空格就不行了:
$redis-cli keys "blacklist*"
1) "blacklist:12: 361942420@qq.com
oracle正则表达式的用法
0624chenhong
oracle 正则表达式
方括号表达示
方括号表达式
描述
[[:alnum:]]
字母和数字混合的字符
[[:alpha:]]
字母字符
[[:cntrl:]]
控制字符
[[:digit:]]
数字字符
[[:graph:]]
图像字符
[[:lower:]]
小写字母字符
[[:print:]]
打印字符
[[:punct:]]
标点符号字符
[[:space:]]
2048源码(核心算法有,缺少几个anctionbar,以后补上)
不懂事的小屁孩
2048
2048游戏基本上有四部分组成,
1:主activity,包含游戏块的16个方格,上面统计分数的模块
2:底下的gridview,监听上下左右的滑动,进行事件处理,
3:每一个卡片,里面的内容很简单,只有一个text,记录显示的数字
4:Actionbar,是游戏用重新开始,设置等功能(这个在底下可以下载的代码里面还没有实现)
写代码的流程
1:设计游戏的布局,基本是两块,上面是分
jquery内部链式调用机理
换个号韩国红果果
JavaScript jquery
只需要在调用该对象合适(比如下列的setStyles)的方法后让该方法返回该对象(通过this 因为一旦一个函数称为一个对象方法的话那么在这个方法内部this(结合下面的setStyles)指向这个对象)
function create(type){
var element=document.createElement(type);
//this=element;
你订酒店时的每一次点击 背后都是NoSQL和云计算
蓝儿唯美
NoSQL
全球最大的在线旅游公司Expedia旗下的酒店预订公司,它运营着89个网站,跨越68个国家,三年前开始实验公有云,以求让客户在预订网站上查询假期酒店时得到更快的信息获取体验。
云端本身是用于驱动网站的部分小功能的,如搜索框的自动推荐功能,还能保证处理Hotels.com服务的季节性需求高峰整体储能。
Hotels.com的首席技术官Thierry Bedos上个月在伦敦参加“2015 Clou
java笔记1
a-john
java
1,面向对象程序设计(Object-oriented Propramming,OOP):java就是一种面向对象程序设计。
2,对象:我们将问题空间中的元素及其在解空间中的表示称为“对象”。简单来说,对象是某个类型的实例。比如狗是一个类型,哈士奇可以是狗的一个实例,也就是对象。
3,面向对象程序设计方式的特性:
3.1 万物皆为对象。
C语言 sizeof和strlen之间的那些事 C/C++软件开发求职面试题 必备考点(一)
aijuans
C/C++求职面试必备考点
找工作在即,以后决定每天至少写一个知识点,主要是记录,逼迫自己动手、总结加深印象。当然如果能有一言半语让他人收益,后学幸运之至也。如有错误,还希望大家帮忙指出来。感激不尽。
后学保证每个写出来的结果都是自己在电脑上亲自跑过的,咱人笨,以前学的也半吊子。很多时候只能靠运行出来的结果再反过来
程序员写代码时就不要管需求了吗?
asia007
程序员不能一味跟需求走
编程也有2年了,刚开始不懂的什么都跟需求走,需求是怎样就用代码实现就行,也不管这个需求是否合理,是否为较好的用户体验。当然刚开始编程都会这样,但是如果有了2年以上的工作经验的程序员只知道一味写代码,而不在写的过程中思考一下这个需求是否合理,那么,我想这个程序员就只能一辈写敲敲代码了。
我的技术不是很好,但是就不代
Activity的四种启动模式
百合不是茶
android 栈模式启动 Activity的标准模式启动 栈顶模式启动 单例模式启动
android界面的操作就是很多个activity之间的切换,启动模式决定启动的activity的生命周期 ;
启动模式xml中配置
<activity android:name=".MainActivity" android:launchMode="standard&quo
Spring中@Autowired标签与@Resource标签的区别
bijian1013
java spring @Resource @Autowired @Qualifier
Spring不但支持自己定义的@Autowired注解,还支持由JSR-250规范定义的几个注解,如:@Resource、 @PostConstruct及@PreDestroy。
1. @Autowired @Autowired是Spring 提供的,需导入 Package:org.springframewo
Changes Between SOAP 1.1 and SOAP 1.2
sunjing
Changes Enable SOAP 1.1 SOAP 1.2
JAX-WS
SOAP Version 1.2 Part 0: Primer (Second Edition)
SOAP Version 1.2 Part 1: Messaging Framework (Second Edition)
SOAP Version 1.2 Part 2: Adjuncts (Second Edition)
Which style of WSDL
【Hadoop二】Hadoop常用命令
bit1129
hadoop
以Hadoop运行Hadoop自带的wordcount为例,
hadoop脚本位于/home/hadoop/hadoop-2.5.2/bin/hadoop,需要说明的是,这些命令的使用必须在Hadoop已经运行的情况下才能执行
Hadoop HDFS相关命令
hadoop fs -ls
列出HDFS文件系统的第一级文件和第一级
java异常处理(初级)
白糖_
java DAO spring 虚拟机 Ajax
从学习到现在从事java开发一年多了,个人觉得对java只了解皮毛,很多东西都是用到再去慢慢学习,编程真的是一项艺术,要完成一段好的代码,需要懂得很多。
最近项目经理让我负责一个组件开发,框架都由自己搭建,最让我头疼的是异常处理,我看了一些网上的源码,发现他们对异常的处理不是很重视,研究了很久都没有找到很好的解决方案。后来有幸看到一个200W美元的项目部分源码,通过他们对异常处理的解决方案,我终
记录整理-工作问题
braveCS
工作
1)那位同学还是CSV文件默认Excel打开看不到全部结果。以为是没写进去。同学甲说文件应该不分大小。后来log一下原来是有写进去。只是Excel有行数限制。那位同学进步好快啊。
2)今天同学说写文件的时候提示jvm的内存溢出。我马上反应说那就改一下jvm的内存大小。同学说改用分批处理了。果然想问题还是有局限性。改jvm内存大小只能暂时地解决问题,以后要是写更大的文件还是得改内存。想问题要长远啊
org.apache.tools.zip实现文件的压缩和解压,支持中文
bylijinnan
apache
刚开始用java.util.Zip,发现不支持中文(网上有修改的方法,但比较麻烦)
后改用org.apache.tools.zip
org.apache.tools.zip的使用网上有更简单的例子
下面的程序根据实际需求,实现了压缩指定目录下指定文件的方法
import java.io.BufferedReader;
import java.io.BufferedWrit
读书笔记-4
chengxuyuancsdn
读书笔记
1、JSTL 核心标签库标签
2、避免SQL注入
3、字符串逆转方法
4、字符串比较compareTo
5、字符串替换replace
6、分拆字符串
1、JSTL 核心标签库标签共有13个,
学习资料:http://www.cnblogs.com/lihuiyy/archive/2012/02/24/2366806.html
功能上分为4类:
(1)表达式控制标签:out
[物理与电子]半导体教材的一个小问题
comsci
问题
各种模拟电子和数字电子教材中都有这个词汇-空穴
书中对这个词汇的解释是; 当电子脱离共价键的束缚成为自由电子之后,共价键中就留下一个空位,这个空位叫做空穴
我现在回过头翻大学时候的教材,觉得这个
Flashback Database --闪回数据库
daizj
oracle 闪回数据库
Flashback 技术是以Undo segment中的内容为基础的, 因此受限于UNDO_RETENTON参数。要使用flashback 的特性,必须启用自动撤销管理表空间。
在Oracle 10g中, Flash back家族分为以下成员: Flashback Database, Flashback Drop,Flashback Query(分Flashback Query,Flashbac
简单排序:插入排序
dieslrae
插入排序
public void insertSort(int[] array){
int temp;
for(int i=1;i<array.length;i++){
temp = array[i];
for(int k=i-1;k>=0;k--)
C语言学习六指针小示例、一维数组名含义,定义一个函数输出数组的内容
dcj3sjt126com
c
# include <stdio.h>
int main(void)
{
int * p; //等价于 int *p 也等价于 int* p;
int i = 5;
char ch = 'A';
//p = 5; //error
//p = &ch; //error
//p = ch; //error
p = &i; //
centos下php redis扩展的安装配置3种方法
dcj3sjt126com
redis
方法一
1.下载php redis扩展包 代码如下 复制代码
#wget http://redis.googlecode.com/files/redis-2.4.4.tar.gz
2 tar -zxvf 解压压缩包,cd /扩展包 (进入扩展包然后 运行phpize 一下是我环境中phpize的目录,/usr/local/php/bin/phpize (一定要
线程池(Executors)
shuizhaosi888
线程池
在java类库中,任务执行的主要抽象不是Thread,而是Executor,将任务的提交过程和执行过程解耦
public interface Executor {
void execute(Runnable command);
}
public class RunMain implements Executor{
@Override
pub
openstack 快速安装笔记
haoningabc
openstack
前提是要配置好yum源
版本icehouse,操作系统redhat6.5
最简化安装,不要cinder和swift
三个节点
172 control节点keystone glance horizon
173 compute节点nova
173 network节点neutron
control
/etc/sysctl.conf
net.ipv4.ip_forward =
从c面向对象的实现理解c++的对象(二)
jimmee
C++ 面向对象 虚函数
1. 类就可以看作一个struct,类的方法,可以理解为通过函数指针的方式实现的,类对象分配内存时,只分配成员变量的,函数指针并不需要分配额外的内存保存地址。
2. c++中类的构造函数,就是进行内存分配(malloc),调用构造函数
3. c++中类的析构函数,就时回收内存(free)
4. c++是基于栈和全局数据分配内存的,如果是一个方法内创建的对象,就直接在栈上分配内存了。
专门在
如何让那个一个div可以拖动
lingfeng520240
html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml
第10章 高级事件(中)
onestopweb
事件
index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/
计算两个经纬度之间的距离
roadrunners
计算 纬度 LBS 经度 距离
要解决这个问题的时候,到网上查了很多方案,最后计算出来的都与百度计算出来的有出入。下面这个公式计算出来的距离和百度计算出来的距离是一致的。
/**
*
* @param longitudeA
* 经度A点
* @param latitudeA
* 纬度A点
* @param longitudeB
*
最具争议的10个Java话题
tomcat_oracle
java
1、Java8已经到来。什么!? Java8 支持lambda。哇哦,RIP Scala! 随着Java8 的发布,出现很多关于新发布的Java8是否有潜力干掉Scala的争论,最终的结论是远远没有那么简单。Java8可能已经在Scala的lambda的包围中突围,但Java并非是函数式编程王位的真正觊觎者。
2、Java 9 即将到来
Oracle早在8月份就发布
zoj 3826 Hierarchical Notation(模拟)
阿尔萨斯
rar
题目链接:zoj 3826 Hierarchical Notation
题目大意:给定一些结构体,结构体有value值和key值,Q次询问,输出每个key值对应的value值。
解题思路:思路很简单,写个类词法的递归函数,每次将key值映射成一个hash值,用map映射每个key的value起始终止位置,预处理完了查询就很简单了。 这题是最后10分钟出的,因为没有考虑value为{}的情