¶Bean相关
- 概念
- BeanDefinition 是将ioc定义如xml,注解扫描的类转为成抽象描述,包含了用户定义的各种bean信息
- BeanWrapper 是从该类抽离出的低级javaBean,包含了属性转换器,以及java 原信息
- PropertyValue 仅仅是封装key-value 表示对象,存在于Bd内部,以及ioc属性转换过程中
- PropertyEditor jdk提供的属性转换,A->B类型的转换,ioc逻辑中使用
¶xml解析
简而言之, 解析xml的类是BeanDefinitionParserDelegate完成的
- 标准bean标签的解析过程
- xml例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20<beans>
<bean id="test" class="com.light.Foo">
<description>
this is a test
</description>
<property name="list">
<list>
<value>123</value>
<value>123</value>
</list>
</property>
<property name="files">
<list>
<value>d:/test/12.txt</value>
<value>d:/test/12.txt</value>
</list>
</property>
<meta key="key1" value="value1"/>
</bean>
</beans>
- xml例子
¶基本元素
- 前言
解析bean的读取器为BeanDefinitionReader
体系,并非是仅仅支持xml. - 外部类
DefaultBeanDefinitionDocumentReader 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94//该类是xml默认的 解析器实现类
protected void doRegisterBeanDefinitions(Element root) {
//委托模式
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// We cannot use Profiles.of(...) since profile expressions are not supported
// in XML config. See SPR-12458 for details.
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
preProcessXml(root); //空,子类实现
parseBeanDefinitions(root, this.delegate);
postProcessXml(root); //空,子类实现
this.delegate = parent;
}
//-------------------------------- 循环处理所有元素---------------------------------
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate); //处理不用引入如<context> 这样的默认标签
}
else {
delegate.parseCustomElement(ele); //处理额外的标签
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
//-----------------------------------处理默认标签-------------------------------------------
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
//------------------------------------处理bean标签-----------------------------------------
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//holder仅仅用来包含BeanDefition,内部的BeanDiftion 就是GenicBeanDeition
//[1] 解析并创建gbdH
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
//[1!]
//[2] 注册bd到beanFacotry(Registry)中
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
//[2!]
//[3] 触发事件
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
//[3!]
}
- 委托类
BeanDefinitionParserDelegate 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296//------------------------------------解析 并创建 gbd--------------------------------------
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
//[1] 处理id和name,别名,保持id唯一性
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
if (logger.isTraceEnabled()) {
logger.trace("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
//[1!]
//[2]解析标签,并创建gbd
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
//[2!]
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
beanName = this.readerContext.generateBeanName(beanDefinition);
// Register an alias for the plain bean class name, if still possible,
// if the generator returned the class name plus a suffix.
// This is expected for Spring 1.2/2.0 backwards compatibility.
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (logger.isTraceEnabled()) {
logger.trace("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
//----------------------------------实际创建gbd的代码---------------------------
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
//class属性
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
//parent属性
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
// 简单创建gbd实例
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 处理<bean> 标签中所有的属性,即attr,设置为abd(AbstractBeanDefinition) 对应的属性
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
// 子类node中寻找 获取desc
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
// 此处代码见下边
parseMetaElements(ele, bd);
// 子类node寻找<lookUp name="" ,bean=""> 元素,添加MethodOverrride对象到bd中
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// 寻找<ReplaceMethod name="" repalcer=""> 元素 添加MEthodOverride对象到bd中
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 处理<constructor-arg index= ...>
// bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
// valueHolder就是解析后创建的要添加到bd中的对象
parseConstructorArgElements(ele, bd);
//处理子类中属性
parsePropertyElements(ele, bd);
//处理qualifier标签
parseQualifierElements(ele, bd);
//bd来源,如xml的路径
bd.setResource(this.readerContext.getResource());
//
bd.setSource(extractSource(ele));
return bd;
}
//...
return null;
}
//------------------------------处理meta元素-----------------------
public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
//从子类寻<meta>标签,获取meat的key以及value属性,作为bd#attribute
NodeList nl = ele.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
Element metaElement = (Element) node;
//这里就能知道BeanMetadataAttributeAccessor的map是什么情况可以设置的
String key = metaElement.getAttribute(KEY_ATTRIBUTE);
String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
//此处体现了BD体系中如何使用key-value
BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
attribute.setSource(extractSource(metaElement));
attributeAccessor.addMetadataAttribute(attribute);
}
}
}
//----------------------------处理property----------------------------
public void parsePropertyElement(Element ele, BeanDefinition bd) {
String propertyName = ele.getAttribute(NAME_ATTRIBUTE); //获取该propery名
if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
this.parseState.push(new PropertyEntry(propertyName));
try {
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
//此处是具体代码
Object val = parsePropertyValue(ele, bd, propertyName);
//返回值创建pv (key-vlaue) 类型
PropertyValue pv = new PropertyValue(propertyName, val);
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
//添加到bd#pvs中,这个pvs在ioc中注值使用,非常关键
bd.getPropertyValues().addPropertyValue(pv);
}
finally {
this.parseState.pop();
}
}
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
String elementName = (propertyName != null ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg> element");
// Should only have one child element: ref, value, list, etc.
//[1]获取当前元素(即Property) 子元素,最多最外层 只能存在一个如上
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
!nodeNameEquals(node, META_ELEMENT)) {
// Child element is what we're looking for.
if (subElement != null) {
error(elementName + " must not contain more than one sub-element", ele);
}
else {
subElement = (Element) node;
}
}
}
//[1!]
//[2] 不允许property元素带有value和ref属性, 或者任意属性并且有子元素
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
if ((hasRefAttribute && hasValueAttribute) ||
((hasRefAttribute || hasValueAttribute) && subElement != null)) {
error(elementName +
" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
}
//[2]
//[3] 分情况讨论
if (hasRefAttribute) {
String refName = ele.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(refName)) {
error(elementName + " contains empty 'ref' attribute", ele);
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName);//创建一个引用
ref.setSource(extractSource(ele));
return ref;
} //ref情况返回
else if (hasValueAttribute) {
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
valueHolder.setSource(extractSource(ele));
return valueHolder;
}//value 类型
else if (subElement != null) {
return parsePropertySubElement(subElement, bd); //复杂情况,即property存在子元素
}
else {
// Neither child element nor "ref" or "value" attribute found.
error(elementName + " must specify a ref or value", ele);
return null;
}
[3!]
}
//-------------------------------处理property子元素------------------------------------
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
//[1]
if (!isDefaultNamespace(ele)) {
return parseNestedCustomElement(ele, bd); //非标准元素,去调用custom逻辑,见下文
}
//[1!]
//[2]
else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
//Bean标签,相当于递归调用
BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
if (nestedBd != null) {
nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
}
return nestedBd;
}
//[2!]
//[3]
else if (nodeNameEquals(ele, REF_ELEMENT)) {
// A generic reference to any name of any bean.
String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
boolean toParent = false;
if (!StringUtils.hasLength(refName)) {
// A reference to the id of another bean in a parent context.
refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
toParent = true;
if (!StringUtils.hasLength(refName)) {
error("'bean' or 'parent' is required for <ref> element", ele);
return null;
}
}
if (!StringUtils.hasText(refName)) {
error("<ref> element contains empty target attribute", ele);
return null;
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
ref.setSource(extractSource(ele));
return ref;// <ref bean=""> 处理
}
//[4]
//[5] <idref bean=""> 此处应该只能填写id
else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
return parseIdRefElement(ele);
}
//[5!]
//[6] <value >处理
else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
return parseValueElement(ele, defaultValueType);
}//[6!]
else if (nodeNameEquals(ele, NULL_ELEMENT)) {
// It's a distinguished null value. Let's wrap it in a TypedStringValue
// object in order to preserve the source location.
TypedStringValue nullHolder = new TypedStringValue(null);
nullHolder.setSource(extractSource(ele));
return nullHolder;
}
// 这几个类型 可能会继续引起子 解析,
else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
return parseArrayElement(ele, bd);
}
else if (nodeNameEquals(ele, LIST_ELEMENT)) {
return parseListElement(ele, bd);
}
else if (nodeNameEquals(ele, SET_ELEMENT)) {
return parseSetElement(ele, bd);
}
else if (nodeNameEquals(ele, MAP_ELEMENT)) {
return parseMapElement(ele, bd);
}
else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
return parsePropsElement(ele);
}
else {
error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
return null;
}
}
¶custom元素
¶NamespaceHandler体系
- 调用点
1 | public BeanDefinition parseCustomElement(Element ele) { |
- 继承树
简单来看实际最多的是直接子类,另一类是
1 | BeanDefinitionParser |
- 抽象逻辑
AbstractBeanDefinitionParser 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41public abstract class AbstractBeanDefinitionParser implements BeanDefinitionParser {
public static final String ID_ATTRIBUTE = "id";
public static final String NAME_ATTRIBUTE = "name";
public final BeanDefinition parse(Element element, ParserContext parserContext) {
AbstractBeanDefinition definition = parseInternal(element, parserContext); //子类实现解析bd
if (definition != null && !parserContext.isNested()) {
try {
//尝试获得id
String id = resolveId(element, definition, parserContext);
if (!StringUtils.hasText(id)) {
parserContext.getReaderContext().error(
"Id is required for element '" + parserContext.getDelegate().getLocalName(element)
+ "' when used as a top-level tag", element);
}
//别名
String[] aliases = null;
if (shouldParseNameAsAliases()) {
String name = element.getAttribute(NAME_ATTRIBUTE);
if (StringUtils.hasLength(name)) {
aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
}
}
//注册
BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
registerBeanDefinition(holder, parserContext.getRegistry());
if (shouldFireEvents()) { //抽象类流出的接口
BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
postProcessComponentDefinition(componentDefinition);
parserContext.registerComponent(componentDefinition);
}
}
catch (BeanDefinitionStoreException ex) {
String msg = ex.getMessage();
parserContext.getReaderContext().error((msg != null ? msg : ex.toString()), element);
return null;
}
}
return definition;
}
}
-
AbstractSingleBeanDefinitionParser
该类表示此标签仅仅创建一个bd,例如
<context:property-placeholder
的结果AbstractSingleBeanDefinitionParser 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
//bdBuidler用来创建bd,这里的getXX都是子类实现的
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
String parentName = getParentName(element);//子类获取
if (parentName != null) {
builder.getRawBeanDefinition().setParentName(parentName);
}
Class<?> beanClass = getBeanClass(element);
if (beanClass != null) {
builder.getRawBeanDefinition().setBeanClass(beanClass);
}
else {
String beanClassName = getBeanClassName(element);
if (beanClassName != null) {
builder.getRawBeanDefinition().setBeanClassName(beanClassName);
}
}
builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
BeanDefinition containingBd = parserContext.getContainingBeanDefinition();
if (containingBd != null) {
// Inner bean definition must receive same scope as containing bean.
builder.setScope(containingBd.getScope());
}
if (parserContext.isDefaultLazyInit()) {
// Default-lazy-init applies to custom bean definitions as well.
builder.setLazyInit(true);
}
doParse(element, parserContext, builder); //再添加一些必要的属性
return builder.getBeanDefinition();//构建bd
}
-
¶xml 和 注解实现的一些区别
- 单纯使用xml的话,gbd创建会包含所有的信息,如pvs,各种ioc属性
- 注解的话很多都要进行后处理器来处理
- name一个String类型
1
2
3<property name="name">
zhangsan
<property> - 注解实现
1
2"张三") (
private String name; - 前者会在为gbd#pvs,后者必须经过AutowiredAnnotationBeanPostProcessor(@AutoWire,@Value,@Inject)处理
- name一个String类型
¶BeanDefinition
spring常见的接口设计模式:
- RootBeanDetinition实质是一个AttributeAccessor和BeanDefinition
- 属于AttributeAccessor的部分由父类BeanMetadataAttributeAccessor实现
- AttributeAccessor:该接口定义如何访问源信息中的属性,在Bd体系中,通过 ‘BeanMetadataAttribut’,作为meta 原信息体现key-value
- AttributeAccessor
AttributeAccessor 1
2
3
4
5
6void setAttribute(String name, @Nullable Object value);
Object getAttribute(String name);
Object removeAttribute(String name);
boolean hasAttribute(String name);
String[] attributeNames();
- AttributeAccessorSupport
AttributeAccessorSupport 1
2
3
4
5
6
7
8
9
10
11
12
13//该map就是属性储存的位置,在该体系中,此map表示的是bean的一些附加信息,跟属性无关
private final Map<String, Object> attributes = new LinkedHashMap<>();
public void setAttribute(String name, @Nullable Object value) {
Assert.notNull(name, "Name must not be null");
if (value != null) {
this.attributes.put(name, value);
}:
else {
removeAttribute(name);
}
}
- BeanMetadataAttributeAccessor
此时的source用来说明bd的来源,该类型会在注册到
BeanMetadataAttributeAccessor 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68//包含了要进行配置的对象
public interface BeanMetadataElement {
/**
* Return the configuration source {@code Object} for this metadata element
* (may be {@code null}).
*/
Object getSource();
}
* Holder for a key-value style attribute that is part of a bean definition.
* Keeps track of the definition source in addition to the key-value pair.
*
* Juergen Hoeller
* 2.5
*/
//包含了source中一个属性的键值对,可以理解为pair
public class BeanMetadataAttribute implements BeanMetadataElement {
private final String name;
private final Object value;
private Object source;
}
//
/**
* Extension of {@link org.springframework.core.AttributeAccessorSupport},
* holding attributes as {@link BeanMetadataAttribute} objects in order
* to keep track of the definition source.
* 该类用来访问source中属性,属性由BeanMetaDataAttribute包装
* @author Juergen Hoeller
* @since 2.5
*/
"serial") (
public class BeanMetadataAttributeAccessor extends AttributeAccessorSupport implements BeanMetadataElement {
private Object source;
/**
* Set the configuration source {@code Object} for this metadata element.
* <p>The exact type of the object will depend on the configuration mechanism used.
*/
public void setSource(@Nullable Object source) {
this.source = source;
}
public Object getSource() {
return this.source;
}
/**
* Add the given BeanMetadataAttribute to this accessor's set of attributes.
* @param attribute the BeanMetadataAttribute object to register
*/
public void addMetadataAttribute(BeanMetadataAttribute attribute) {
super.setAttribute(attribute.getName(), attribute);
}
}
BeanFactory
时被转换为RootBeanDefintion
类型
图中propertyValues,是xml处理时添加的字面属性,在ioc过程中pvs要经过类型转换,以及各种处理器.
- AttributeAccessor
- BeanDefinition:该接口实质是描述了springBean配置
- 常见子类ScannedGenericBeanDefinition和GenericBeanDefinition
BeanDefinition 1
2
3
4
5void setScope(@Nullable String scope);
void setLazyInit(boolean lazyInit);
void setDependsOn(@Nullable String... dependsOn);
//...
¶BeanWrapper
- PropertyEditorRegistry 和 TypeConverter组成属性转换器
¶属性类型转换
-
PropertyEditor是jdk提供的接口,用来进行属性类型转换的
PropertyEditor 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38public interface PropertyEditor {
void setValue(Object value);
//将转递的stirng类型进行转换
void setAsText(String text) throws java.lang.IllegalArgumentException;
}
//一般继承该接口
public class PropertyEditorSupport implements PropertyEditor {
private Object value;
private Object source;
private java.util.Vector<PropertyChangeListener> listeners;
public PropertyEditorSupport() {
setSource(this);
}
public PropertyEditorSupport(Object source) {
if (source == null) {
throw new NullPointerException();
}
setSource(source);
}
}
//spirng中的一个实现
public class ByteArrayPropertyEditor extends PropertyEditorSupport {
public void setAsText(@Nullable String text) {
setValue(text != null ? text.getBytes() : null);
}
public String getAsText() {
byte[] value = (byte[]) getValue();
return (value != null ? new String(value) : "");
}
}
-
PropertyEditor使用
1
2
3
4//1.当类型String->Type转换
//调用setText()
//2.当其他类型->Type
//调用setValue()
-
-
PropertyEditorRegistrySupport使用Map存储PropertyEditor
PropertyEditorRegistrySupport 1
2
3
4
5
6
7
8
9
10
11
12
13
14public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {
//spring 实现的类型转换器,先略过
private ConversionService conversionService;
//默认的类型转换器
private Map<Class<?>, PropertyEditor> defaultEditors;
private void createDefaultEditors() {
this.defaultEditors.put(Charset.class, new CharsetEditor());
this.defaultEditors.put(Class.class, new ClassEditor());
this.defaultEditors.put(Class[].class, new ClassArrayEditor());
//....添加了大量的类型转换器
}
-
TypeConverterDelegate作为委托模式用来真正处理类型转换
TypeConverterSupport 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68public abstract class TypeConverterSupport extends PropertyEditorRegistrySupport implements TypeConverter {
TypeConverterDelegate typeConverterDelegate;
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
@Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException {
return this.typeConverterDelegate.convertIfNecessary(null, null, value, requiredType,
}
}
//委托逻辑
class TypeConverterDelegate {
/**
* propertyName 表示属性名
* oldValue 表示旧值,若该函数是pvs设值时调用一般是null
* newValue 表示此次要设置的值,也是要处理进行类型 转换的值
*/
public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue,
@Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {
// Custom editor for this type?
//获取自定义的属性编辑器
PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
ConversionFailedException conversionAttemptEx = null;
// No custom editor but custom ConversionService specified?
// 尝试使用ConversionService进行转换
ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
try {
return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
}
catch (ConversionFailedException ex) {
// fallback to default conversion logic below
conversionAttemptEx = ex;
}
}
}
Object convertedValue = newValue;
// Value not of required type?
if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) { //没有自定义编辑器或者 不能直接分配(即newValue需要进行向requiredType的转换)
if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) &&
convertedValue instanceof String) {
//此处代码逻辑 是 当属性为Collection<Class>|Collection<Eunm> 时,可以将该字符串当作CSV转化
TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor();
if (elementTypeDesc != null) {
Class<?> elementType = elementTypeDesc.getType();
if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) {
convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
}
}
}
if (editor == null) {
// 根据requiredType从map中获取对应的propertyEditor
editor = findDefaultEditor(requiredType);
}
//进行转换
convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
}
boolean standardConversion = false;
}
//....下边的逻辑是对于不能直接适配的情况,进一步的标准处理
1 | //实际转换逻辑 |
- 例子
1 | public class Foo { |
这里描述一下非基础类型数组|集合转换逻辑
convertIfNecessary->判断能否进行CVS处理,如果不行则执行CustomCollectionEditor转换,结果是一个
单个 元素list,后续逻辑中 进行遍历,再调用convertIfNecessary,那么结果一定是错的.
- 添加自定义编辑器
通过CustomEditorConfigurer实现,这是一个BF_post
¶spring_post处理器
- BeanPostProcessor
1 | public interface BeanPostProcessor { |
- MergedBeanDefinitionPostProcessor
1 | public interface MergedBeanDefinitionPostProcessor extends BeanPostProcessor { |
- InstantiationAwareBeanPostProcessor
1 | public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor { |
- Instantiation表示实例化,即ioc反射创建BeanWrap;Initialization表示初始化,指ioc对于bean的init逻辑,如init-method
- SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference发生在早期引用获取过程
- 具体执行顺序参考生命周期
¶LTW
载入时织入,使用方式参考LTW,
- 由
LoadTimeWeaverBeanDefinitionParser
创建AspectJWeavingEnabler
以及DefaultContextLoadTimeWeaver
¶MessageSource
该接口是spring提供的和i18n相关的基础类,使用起来并不复杂
-
ResourceBulde:jdk实现的国际化类,也是messageSource的内部原理
- 准备多种语言properties文件
error.propertieserror_zh.Properties
1
20000=error
0001=info1
20000=错误
test=this is a {0} - 使用
1
2
3
4//根据Local获取不同的ResourceBulde
ResourceBundle resourceBundle = ResourceBundle.getBundle("error",Locale.getDefault());
String zh_test = resourceBundle.getString("test")
//this is a {0}
- 准备多种语言properties文件
error.properties
-
MessageFormat:jdk提供用来替换占位符的工具类
- 接上文
1
2
3
4
5MessageFormat messageFormat = new MessageFormat(zh_test);
messageFormat.format(new String[]{"测试"})
//静态调用
MessageFormat.format("this is a {0}","测试")
//结果都是 this is a 测试
- 接上文
-
MessageSource
- 定义
1
2
3
4
5
6
7
8
9
10/**
* code 表示要转换的key,如上文的0000,test
* args 表示可以替换的占位符,如MessageFormat.format("this is a {0}",new String[]{"测试"})第二个参数
* defaultMessage 表示未能匹配时返回的默认值
*/
String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);
//无默认,不匹配则抛出异常
String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;
//MessageSourceResolvable仅仅是封装了第一个函数的前三个参数
String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException; - 使用
1
2
3
4
5
6
7
8
9
10
11//ResourceBundleMessageSource是spring提供的实现之一
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
//设置properties
messageSource.setBasename("error");
messageSource.setDefaultEncoding("UTF-8");
String message = messageSource.getMessage("0001", null, Locale.ENGLISH);
System.out.println(message);
//error
String str1 = messageSource.getMessage("test",new String[]{"测试"},Locale.CHINESE);
System.out.println(str1);
//this is a 测试
- 定义
¶spring框架中提供的特殊调用点
¶spring web关于web容器的初始化
- web相关:参考dispatcherServlet
- ContextLoaderListener:初始化MVC中ioc容器
- SpringServletContainerInitializer:ServletContainerInitializer框架
- WebApplicationInitializer:web初始化实际调用接口
- MVC相关
- WebMvcConfigurer:MVC内部的设置
- spring context
- ApplicationContextInitializer:该接口被context#refresh的调用|或context创建者调用
,就是整体项目的入口,如SpringApplication | FrameworkServlet
,就是整体项目的入口,如SpringApplication | FrameworkServlet
- ApplicationContextInitializer:该接口被context#refresh的调用|或context创建者调用
¶springIo
¶resource接口
1 | public interface Resource extends InputStreamSource { |
¶ResolvingResource
这种资源表示从url中获取,支持http协议,file,jboss的vfs协议
1 | public boolean exists() { |
- ClassPathResource
1 | public InputStream getInputStream() throws IOException { |
- PathResource和FileSystemResource实现,都是依托了nio实现
¶HttpResource
这里Resource使用在ResourceHttpRequestHandler
类中处理web请求静态资源的时候用来附件响应头
¶ContextResouce
表示从封闭的context
中获取资源,典型的就是ServletContext
1 | public interface ContextResource extends Resource { |
¶ServletCOntextResouce
直接从ServletContext
中获取资源
1 | public InputStream getInputStream() throws IOException { |
¶ResouceLoader
用来加载Resource资源的support
1 | Resource getResource(String location); |
- 支持全路径:
file:C:/test.dat
- 支持类路径:
classpath:test.dat
- 支持相对路径:
WEB-INF/test.dat
实现中把context排除,那是包装类实现
¶DefaultResourceLoader
1 | public Resource getResource(String location) { |
- 总结
- 若使用url则返回
UrlResource
- 若使用"classpath:"则返回
ClassPathResource
- 若使用"/"开头则调用
getResourceByPath
,由具体实现决定 - 该体系内部都是实现了一个
ContextResouce
的Resource
- 若使用url则返回
¶ClassRelativeResourceLoader
- 代码逻辑
1 | private final Class<?> clazz; |
- 总结:开头为"/"返回classpath下文件
¶FileSystemResourceLoader
- 代码逻辑
1 | public class FileSystemResourceLoader extends DefaultResourceLoader { |
- 总结:开头为"/"返回文件系统
¶ResourcePatternResolver
支持ant风格进行获取资源
1 | public interface ResourcePatternResolver extends ResourceLoader { |
¶PathMatchingResourcePatternResolver
- 概述
- 支持 占位符,即:‘classpath:’,或者ant风格,’*’,
**
,?
- 支持协议"file",“jar”
- “classpath*:”,表示从所有类路径下寻找
- 支持 占位符,即:‘classpath:’,或者ant风格,’*’,
- 代码逻辑
1 | public class PathMatchingResourcePatternResolver implements ResourcePatternResolver { |
- 总结
1 | public void antResource() { |
输出如下
1 | file:/F:/java/example/app/springTest/target/test-classes/aop1.xml |
- 实际上
xx:
,这部分可以是协议 - 如果实际是从类路径中获取数据,那么ide中运行,可能会出现
file
(target文件夹),‘jar’(maven仓库),jrt
(jdk包)