@ -0,0 +1,246 @@
|
|||||||
|
# Spring initApplicationEventMulticaster
|
||||||
|
- Author: [HuiFer](https://github.com/huifer)
|
||||||
|
- 源码阅读仓库: [huifer-spring](https://github.com/huifer/spring-framework-read)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## demo
|
||||||
|
|
||||||
|
```java
|
||||||
|
package com.huifer.source.spring.applicationListener;
|
||||||
|
|
||||||
|
import org.springframework.context.ApplicationEvent;
|
||||||
|
import org.springframework.context.ApplicationListener;
|
||||||
|
|
||||||
|
public class DemoApplicationListener implements ApplicationListener {
|
||||||
|
@Override
|
||||||
|
public void onApplicationEvent(ApplicationEvent event) {
|
||||||
|
System.out.println("com.huifer.source.spring.applicationListener.DemoApplicationListener.onApplicationEvent");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```XML
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://www.springframework.org/schema/beans"
|
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||||
|
|
||||||
|
<bean id="demoApplicationListener" class="com.huifer.source.spring.applicationListener.DemoApplicationListener"/>
|
||||||
|
</beans>
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```JAVA
|
||||||
|
public class ListenerSourceCode {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
ApplicationContext context = new ClassPathXmlApplicationContext("Listener-demo.xml");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 初始化入口
|
||||||
|
- `org.springframework.context.support.AbstractApplicationContext.refresh`中的`initApplicationEventMulticaster`方法
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```java
|
||||||
|
protected void initApplicationEventMulticaster() {
|
||||||
|
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
|
||||||
|
// 判断是否存在名字applicationEventMulticaster的bean
|
||||||
|
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
|
||||||
|
this.applicationEventMulticaster =
|
||||||
|
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
|
||||||
|
if (logger.isTraceEnabled()) {
|
||||||
|
logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// 创建一个
|
||||||
|
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
|
||||||
|
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
|
||||||
|
if (logger.isTraceEnabled()) {
|
||||||
|
logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
|
||||||
|
"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 注册
|
||||||
|
|
||||||
|
```JAVA
|
||||||
|
protected void registerListeners() {
|
||||||
|
// Register statically specified listeners first.
|
||||||
|
// 读取 ApplicationListener
|
||||||
|
for (ApplicationListener<?> listener : getApplicationListeners()) {
|
||||||
|
getApplicationEventMulticaster().addApplicationListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not initialize FactoryBeans here: We need to leave all regular beans
|
||||||
|
// uninitialized to let post-processors apply to them!
|
||||||
|
/**
|
||||||
|
* 寻找类型为{@link ApplicationListener} 的beanName,目标文件为用户配置文件
|
||||||
|
*/
|
||||||
|
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
|
||||||
|
for (String listenerBeanName : listenerBeanNames) {
|
||||||
|
/**
|
||||||
|
* 1. 获取 {@link applicationEventMulticaster}
|
||||||
|
* 2. 添加监听器名称
|
||||||
|
*/
|
||||||
|
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Publish early application events now that we finally have a multicaster...
|
||||||
|
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
|
||||||
|
this.earlyApplicationEvents = null;
|
||||||
|
if (earlyEventsToProcess != null) {
|
||||||
|
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
|
||||||
|
getApplicationEventMulticaster().multicastEvent(earlyEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## finishRefresh 发布
|
||||||
|
|
||||||
|
```java
|
||||||
|
protected void finishRefresh() {
|
||||||
|
// Clear context-level resource caches (such as ASM metadata from scanning).
|
||||||
|
clearResourceCaches();
|
||||||
|
|
||||||
|
// Initialize lifecycle processor for this context.
|
||||||
|
initLifecycleProcessor();
|
||||||
|
|
||||||
|
// Propagate refresh to lifecycle processor first.
|
||||||
|
getLifecycleProcessor().onRefresh();
|
||||||
|
|
||||||
|
// Publish the final event.
|
||||||
|
// 发布事件做处理
|
||||||
|
publishEvent(new ContextRefreshedEvent(this));
|
||||||
|
|
||||||
|
// Participate in LiveBeansView MBean, if active.
|
||||||
|
LiveBeansView.registerApplicationContext(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
- `org.springframework.context.support.AbstractApplicationContext#publishEvent(org.springframework.context.ApplicationEvent)`
|
||||||
|
- `org.springframework.context.support.AbstractApplicationContext#publishEvent(java.lang.Object, org.springframework.core.ResolvableType)`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```java
|
||||||
|
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
|
||||||
|
Assert.notNull(event, "Event must not be null");
|
||||||
|
|
||||||
|
// Decorate event as an ApplicationEvent if necessary
|
||||||
|
ApplicationEvent applicationEvent;
|
||||||
|
if (event instanceof ApplicationEvent) {
|
||||||
|
applicationEvent = (ApplicationEvent) event;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
applicationEvent = new PayloadApplicationEvent<>(this, event);
|
||||||
|
if (eventType == null) {
|
||||||
|
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multicast right now if possible - or lazily once the multicaster is initialized
|
||||||
|
if (this.earlyApplicationEvents != null) {
|
||||||
|
this.earlyApplicationEvents.add(applicationEvent);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/**
|
||||||
|
* 执行监听事件
|
||||||
|
* {@link ApplicationEventMulticaster} ->{@link SimpleApplicationEventMulticaster}
|
||||||
|
*/
|
||||||
|
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Publish event via parent context as well...
|
||||||
|
if (this.parent != null) {
|
||||||
|
if (this.parent instanceof AbstractApplicationContext) {
|
||||||
|
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.parent.publishEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- 执行监听方法
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```java
|
||||||
|
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
|
||||||
|
ErrorHandler errorHandler = getErrorHandler();
|
||||||
|
if (errorHandler != null) {
|
||||||
|
try {
|
||||||
|
doInvokeListener(listener, event);
|
||||||
|
}
|
||||||
|
catch (Throwable err) {
|
||||||
|
errorHandler.handleError(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
doInvokeListener(listener, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```java
|
||||||
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
|
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
|
||||||
|
try {
|
||||||
|
// 最后调用方法
|
||||||
|
listener.onApplicationEvent(event);
|
||||||
|
}
|
||||||
|
catch (ClassCastException ex) {
|
||||||
|
String msg = ex.getMessage();
|
||||||
|
if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
|
||||||
|
// Possibly a lambda-defined listener which we could not resolve the generic event type for
|
||||||
|
// -> let's suppress the exception and just log a debug message.
|
||||||
|
Log logger = LogFactory.getLog(getClass());
|
||||||
|
if (logger.isTraceEnabled()) {
|
||||||
|
logger.trace("Non-matching event type for listener: " + listener, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
@ -0,0 +1,403 @@
|
|||||||
|
# Spring 自定义属性解析器
|
||||||
|
- Author: [HuiFer](https://github.com/huifer)
|
||||||
|
- 源码阅读仓库: [huifer-spring](https://github.com/huifer/spring-framework-read)
|
||||||
|
|
||||||
|
## 用例
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://www.springframework.org/schema/beans"
|
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||||
|
|
||||||
|
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
|
||||||
|
<property name="propertyEditorRegistrars">
|
||||||
|
<list>
|
||||||
|
<bean class="com.huifer.source.spring.bean.DatePropertyRegister"/>
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<property name="customEditors">
|
||||||
|
<map>
|
||||||
|
<entry key="java.util.Date" value="com.huifer.source.spring.bean.DatePropertyEditor">
|
||||||
|
</entry>
|
||||||
|
</map>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
<bean id="apple" class="com.huifer.source.spring.bean.Apple">
|
||||||
|
<property name="date" value="2020-01-01 01:01:01"/>
|
||||||
|
</bean>
|
||||||
|
</beans>
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class DatePropertyRegister implements PropertyEditorRegistrar {
|
||||||
|
@Override
|
||||||
|
public void registerCustomEditors(PropertyEditorRegistry registry) {
|
||||||
|
registry.registerCustomEditor(Date.class, new CustomDateEditor(
|
||||||
|
new SimpleDateFormat("yyyy-MM-dd"), true)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class DatePropertyEditor extends PropertyEditorSupport {
|
||||||
|
private String format = "yyyy-MM-dd";
|
||||||
|
|
||||||
|
public String getFormat() {
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFormat(String format) {
|
||||||
|
this.format = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAsText(String text) throws IllegalArgumentException {
|
||||||
|
System.out.println(text);
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat(format);
|
||||||
|
try {
|
||||||
|
Date date = sdf.parse(text);
|
||||||
|
this.setValue(date);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## PropertyEditorRegistrar解析
|
||||||
|
- 直接在`DatePropertyRegister`打上断点进行查看注册流程
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
直接看调用堆栈获取调用层次
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Override
|
||||||
|
public void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor) {
|
||||||
|
registerCustomEditor(requiredType, null, propertyEditor);
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Override
|
||||||
|
public void registerCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath, PropertyEditor propertyEditor) {
|
||||||
|
if (requiredType == null && propertyPath == null) {
|
||||||
|
throw new IllegalArgumentException("Either requiredType or propertyPath is required");
|
||||||
|
}
|
||||||
|
if (propertyPath != null) {
|
||||||
|
if (this.customEditorsForPath == null) {
|
||||||
|
this.customEditorsForPath = new LinkedHashMap<>(16);
|
||||||
|
}
|
||||||
|
this.customEditorsForPath.put(propertyPath, new CustomEditorHolder(propertyEditor, requiredType));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (this.customEditors == null) {
|
||||||
|
this.customEditors = new LinkedHashMap<>(16);
|
||||||
|
}
|
||||||
|
// 放入 customEditors map对象中
|
||||||
|
this.customEditors.put(requiredType, propertyEditor);
|
||||||
|
this.customEditorCache = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
- `PropertyEditorRegistrySupport`
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
此处对象是通过`DatePropertyRegister`传递的
|
||||||
|
|
||||||
|
- `org.springframework.beans.factory.support.AbstractBeanFactory#registerCustomEditors`
|
||||||
|
|
||||||
|
```java
|
||||||
|
protected void registerCustomEditors(PropertyEditorRegistry registry) {
|
||||||
|
PropertyEditorRegistrySupport registrySupport =
|
||||||
|
(registry instanceof PropertyEditorRegistrySupport ? (PropertyEditorRegistrySupport) registry : null);
|
||||||
|
if (registrySupport != null) {
|
||||||
|
registrySupport.useConfigValueEditors();
|
||||||
|
}
|
||||||
|
if (!this.propertyEditorRegistrars.isEmpty()) {
|
||||||
|
for (PropertyEditorRegistrar registrar : this.propertyEditorRegistrars) {
|
||||||
|
try {
|
||||||
|
/**
|
||||||
|
* {@link ResourceEditorRegistrar#registerCustomEditors(org.springframework.beans.PropertyEditorRegistry)}或者
|
||||||
|
* {@link PropertyEditorRegistrar#registerCustomEditors(org.springframework.beans.PropertyEditorRegistry)}
|
||||||
|
*/
|
||||||
|
registrar.registerCustomEditors(registry);
|
||||||
|
}
|
||||||
|
catch (BeanCreationException ex) {
|
||||||
|
Throwable rootCause = ex.getMostSpecificCause();
|
||||||
|
if (rootCause instanceof BeanCurrentlyInCreationException) {
|
||||||
|
BeanCreationException bce = (BeanCreationException) rootCause;
|
||||||
|
String bceBeanName = bce.getBeanName();
|
||||||
|
if (bceBeanName != null && isCurrentlyInCreation(bceBeanName)) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("PropertyEditorRegistrar [" + registrar.getClass().getName() +
|
||||||
|
"] failed because it tried to obtain currently created bean '" +
|
||||||
|
ex.getBeanName() + "': " + ex.getMessage());
|
||||||
|
}
|
||||||
|
onSuppressedException(ex);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!this.customEditors.isEmpty()) {
|
||||||
|
this.customEditors.forEach((requiredType, editorClass) ->
|
||||||
|
registry.registerCustomEditor(requiredType, BeanUtils.instantiateClass(editorClass)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
- `void registerCustomEditors(PropertyEditorRegistry registry);` 用例中编写的`DatePropertyRegister`正好有这个方法的实现
|
||||||
|
|
||||||
|
·
|
||||||
|
|
||||||
|
- 在`AbstractBeanFactory`中查看变量
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- 为什么最后结果变成`com.huifer.source.spring.bean.DatePropertyEditor`
|
||||||
|
|
||||||
|
看配置文件
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<property name="customEditors">
|
||||||
|
<map>
|
||||||
|
<entry key="java.util.Date" value="com.huifer.source.spring.bean.DatePropertyEditor">
|
||||||
|
</entry>
|
||||||
|
</map>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
- 对应的set方法
|
||||||
|
|
||||||
|
```java
|
||||||
|
public void setCustomEditors(Map<Class<?>, Class<? extends PropertyEditor>> customEditors) {
|
||||||
|
this.customEditors = customEditors;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## applyPropertyValues
|
||||||
|
|
||||||
|
- 应用属性值
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```java
|
||||||
|
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
|
||||||
|
if (pvs.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (System.getSecurityManager() != null && bw instanceof BeanWrapperImpl) {
|
||||||
|
((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
MutablePropertyValues mpvs = null;
|
||||||
|
// 没有解析的属性
|
||||||
|
List<PropertyValue> original;
|
||||||
|
|
||||||
|
if (pvs instanceof MutablePropertyValues) {
|
||||||
|
mpvs = (MutablePropertyValues) pvs;
|
||||||
|
if (mpvs.isConverted()) {
|
||||||
|
//MutablePropertyValues 对象中存在转换后对象直接赋值
|
||||||
|
// 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
|
||||||
|
BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
|
||||||
|
|
||||||
|
// Create a deep copy, resolving any references for values.
|
||||||
|
// 解析后的对象集合
|
||||||
|
List<PropertyValue> 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;
|
||||||
|
/**
|
||||||
|
* 1. isWritableProperty: 属性可写
|
||||||
|
* 2. isNestedOrIndexedProperty: 是否循环嵌套
|
||||||
|
*/
|
||||||
|
boolean convertible = bw.isWritableProperty(propertyName) &&
|
||||||
|
!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
|
||||||
|
if (convertible) {
|
||||||
|
// 转换器解析
|
||||||
|
convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
|
||||||
|
}
|
||||||
|
// Possibly store converted value in merged bean definition,
|
||||||
|
// in order to avoid re-conversion for every created bean instance.
|
||||||
|
if (resolvedValue == originalValue) {
|
||||||
|
if (convertible) {
|
||||||
|
// 设置解析值
|
||||||
|
pv.setConvertedValue(convertedValue);
|
||||||
|
}
|
||||||
|
deepCopy.add(pv);
|
||||||
|
}
|
||||||
|
// 类型解析
|
||||||
|
else if (convertible && originalValue instanceof TypedStringValue &&
|
||||||
|
!((TypedStringValue) originalValue).isDynamic() &&
|
||||||
|
!(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
|
||||||
|
pv.setConvertedValue(convertedValue);
|
||||||
|
deepCopy.add(pv);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
resolveNecessary = true;
|
||||||
|
deepCopy.add(new PropertyValue(pv, convertedValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mpvs != null && !resolveNecessary) {
|
||||||
|
// 转换成功的标记方法
|
||||||
|
mpvs.setConverted();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set our (possibly massaged) deep copy.
|
||||||
|
try {
|
||||||
|
bw.setPropertyValues(new MutablePropertyValues(deepCopy));
|
||||||
|
}
|
||||||
|
catch (BeansException ex) {
|
||||||
|
throw new BeanCreationException(
|
||||||
|
mbd.getResourceDescription(), beanName, "Error setting property values", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- 属性值解析
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
```JAVA
|
||||||
|
@Nullable
|
||||||
|
private Object convertForProperty(
|
||||||
|
@Nullable Object value, String propertyName, BeanWrapper bw, TypeConverter converter) {
|
||||||
|
|
||||||
|
if (converter instanceof BeanWrapperImpl) {
|
||||||
|
return ((BeanWrapperImpl) converter).convertForProperty(value, propertyName);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
|
||||||
|
MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
|
||||||
|
return converter.convertIfNecessary(value, pd.getPropertyType(), methodParam);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```JAVA
|
||||||
|
private Object doConvertTextValue(@Nullable Object oldValue, String newTextValue, PropertyEditor editor) {
|
||||||
|
try {
|
||||||
|
editor.setValue(oldValue);
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("PropertyEditor [" + editor.getClass().getName() + "] does not support setValue call", ex);
|
||||||
|
}
|
||||||
|
// Swallow and proceed.
|
||||||
|
}
|
||||||
|
// 调用子类实现方法
|
||||||
|
editor.setAsText(newTextValue);
|
||||||
|
return editor.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- 调用用例编写的方法
|
||||||
|
|
||||||
|
```JAVA
|
||||||
|
@Override
|
||||||
|
public void setAsText(String text) throws IllegalArgumentException {
|
||||||
|
System.out.println(text);
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat(format);
|
||||||
|
try {
|
||||||
|
Date date = sdf.parse(text);
|
||||||
|
this.setValue(date);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
该值也是这个方法的返回`org.springframework.beans.TypeConverterDelegate#convertIfNecessary(java.lang.String, java.lang.Object, java.lang.Object, java.lang.Class<T>, org.springframework.core.convert.TypeDescriptor)`
|
||||||
|
|
||||||
@ -0,0 +1,210 @@
|
|||||||
|
# Spring MessageSource
|
||||||
|
- Author: [HuiFer](https://github.com/huifer)
|
||||||
|
- 源码阅读仓库: [huifer-spring](https://github.com/huifer/spring-framework-read)
|
||||||
|
|
||||||
|
## 初始化入口
|
||||||
|
- `org.springframework.context.support.AbstractApplicationContext.refresh`方法有`initMessageSource()`方法进行了`MessageSource`初始化
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```java
|
||||||
|
protected void initMessageSource() {
|
||||||
|
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
|
||||||
|
// 判断是否含有 messageSource
|
||||||
|
if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
|
||||||
|
// 读取xml配置文件中 id="messageSource"的数据
|
||||||
|
this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
|
||||||
|
// Make MessageSource aware of parent MessageSource.
|
||||||
|
if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
|
||||||
|
HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
|
||||||
|
if (hms.getParentMessageSource() == null) {
|
||||||
|
// Only set parent context as parent MessageSource if no parent MessageSource
|
||||||
|
// registered already.
|
||||||
|
hms.setParentMessageSource(getInternalParentMessageSource());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (logger.isTraceEnabled()) {
|
||||||
|
logger.trace("Using MessageSource [" + this.messageSource + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Use empty MessageSource to be able to accept getMessage calls.
|
||||||
|
// 没有使用默认的 DelegatingMessageSource
|
||||||
|
DelegatingMessageSource dms = new DelegatingMessageSource();
|
||||||
|
dms.setParentMessageSource(getInternalParentMessageSource());
|
||||||
|
this.messageSource = dms;
|
||||||
|
// 注册单例对象
|
||||||
|
beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
|
||||||
|
if (logger.isTraceEnabled()) {
|
||||||
|
logger.trace("No '" + MESSAGE_SOURCE_BEAN_NAME + "' bean, using [" + this.messageSource + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
读取xml配置文件
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## getMessage
|
||||||
|
|
||||||
|
- `org.springframework.context.support.AbstractApplicationContext#getMessage(java.lang.String, java.lang.Object[], java.util.Locale)`
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Override
|
||||||
|
public String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException {
|
||||||
|
return getMessageSource().getMessage(code, args, locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
- `org.springframework.context.support.AbstractMessageSource#getMessage(java.lang.String, java.lang.Object[], java.util.Locale)`
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Override
|
||||||
|
public final String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException {
|
||||||
|
// 获取对应的信息
|
||||||
|
String msg = getMessageInternal(code, args, locale);
|
||||||
|
if (msg != null) {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
// 默认信息 null
|
||||||
|
String fallback = getDefaultMessage(code);
|
||||||
|
if (fallback != null) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
throw new NoSuchMessageException(code, locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
- 两个方法
|
||||||
|
|
||||||
|
1. `org.springframework.context.support.AbstractMessageSource#getDefaultMessage(java.lang.String)`
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Nullable
|
||||||
|
protected String getDefaultMessage(String code) {
|
||||||
|
// 判断是否使用默认值
|
||||||
|
if (isUseCodeAsDefaultMessage()) {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
- 返回code本身或者`null`
|
||||||
|
|
||||||
|
2. `org.springframework.context.support.AbstractMessageSource#getMessageInternal`
|
||||||
|
|
||||||
|
```JAVA
|
||||||
|
@Nullable
|
||||||
|
protected String getMessageInternal(@Nullable String code, @Nullable Object[] args, @Nullable Locale locale) {
|
||||||
|
if (code == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (locale == null) {
|
||||||
|
// 获取语言默认值
|
||||||
|
locale = Locale.getDefault();
|
||||||
|
}
|
||||||
|
Object[] argsToUse = args;
|
||||||
|
|
||||||
|
if (!isAlwaysUseMessageFormat() && ObjectUtils.isEmpty(args)) {
|
||||||
|
// Optimized resolution: no arguments to apply,
|
||||||
|
// therefore no MessageFormat needs to be involved.
|
||||||
|
// Note that the default implementation still uses MessageFormat;
|
||||||
|
// this can be overridden in specific subclasses.
|
||||||
|
String message = resolveCodeWithoutArguments(code, locale);
|
||||||
|
if (message != null) {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
// Resolve arguments eagerly, for the case where the message
|
||||||
|
// is defined in a parent MessageSource but resolvable arguments
|
||||||
|
// are defined in the child MessageSource.
|
||||||
|
argsToUse = resolveArguments(args, locale);
|
||||||
|
|
||||||
|
MessageFormat messageFormat = resolveCode(code, locale);
|
||||||
|
if (messageFormat != null) {
|
||||||
|
synchronized (messageFormat) {
|
||||||
|
return messageFormat.format(argsToUse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check locale-independent common messages for the given message code.
|
||||||
|
Properties commonMessages = getCommonMessages();
|
||||||
|
if (commonMessages != null) {
|
||||||
|
String commonMessage = commonMessages.getProperty(code);
|
||||||
|
if (commonMessage != null) {
|
||||||
|
return formatMessage(commonMessage, args, locale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not found -> check parent, if any.
|
||||||
|
return getMessageFromParent(code, argsToUse, locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- `org.springframework.context.support.ResourceBundleMessageSource#resolveCodeWithoutArguments`
|
||||||
|
|
||||||
|
```JAVA
|
||||||
|
@Override
|
||||||
|
protected String resolveCodeWithoutArguments(String code, Locale locale) {
|
||||||
|
Set<String> basenames = getBasenameSet();
|
||||||
|
for (String basename : basenames) {
|
||||||
|
// 加载 basename
|
||||||
|
ResourceBundle bundle = getResourceBundle(basename, locale);
|
||||||
|
if (bundle != null) {
|
||||||
|
// 从basename对应的文件中获取对应的值
|
||||||
|
String result = getStringOrNull(bundle, code);
|
||||||
|
if (result != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- 加载后截图
|
||||||
|
|
||||||
|
获取方法`String result = getStringOrNull(bundle, code);`就是map获取
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- 没有配置文件的情况
|
||||||
|
|
||||||
|

|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
# Spring OrderUtils
|
||||||
|
- Author: [HuiFer](https://github.com/huifer)
|
||||||
|
- 源码阅读仓库: [huifer-spring](https://github.com/huifer/spring-framework-read)
|
||||||
|
- `org.springframework.core.annotation.OrderUtils`主要方法如下
|
||||||
|
1. getOrder
|
||||||
|
1. getPriority
|
||||||
|
|
||||||
|
- 测试类`org.springframework.core.annotation.OrderUtilsTests`
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Nullable
|
||||||
|
public static Integer getOrder(Class<?> type) {
|
||||||
|
// 缓存中获取
|
||||||
|
Object cached = orderCache.get(type);
|
||||||
|
if (cached != null) {
|
||||||
|
// 返回 int
|
||||||
|
return (cached instanceof Integer ? (Integer) cached : null);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 注解工具类,寻找{@link Order}注解
|
||||||
|
*/
|
||||||
|
Order order = AnnotationUtils.findAnnotation(type, Order.class);
|
||||||
|
Integer result;
|
||||||
|
if (order != null) {
|
||||||
|
// 返回
|
||||||
|
result = order.value();
|
||||||
|
} else {
|
||||||
|
result = getPriority(type);
|
||||||
|
}
|
||||||
|
// key: 类名,value: intValue
|
||||||
|
orderCache.put(type, (result != null ? result : NOT_ANNOTATED));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Nullable
|
||||||
|
public static Integer getPriority(Class<?> type) {
|
||||||
|
if (priorityAnnotationType == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// 缓存中获取
|
||||||
|
Object cached = priorityCache.get(type);
|
||||||
|
if (cached != null) {
|
||||||
|
// 不为空返回
|
||||||
|
return (cached instanceof Integer ? (Integer) cached : null);
|
||||||
|
}
|
||||||
|
// 注解工具获取注解
|
||||||
|
Annotation priority = AnnotationUtils.findAnnotation(type, priorityAnnotationType);
|
||||||
|
Integer result = null;
|
||||||
|
if (priority != null) {
|
||||||
|
// 获取 value
|
||||||
|
result = (Integer) AnnotationUtils.getValue(priority);
|
||||||
|
}
|
||||||
|
// 向缓存插入数据
|
||||||
|
priorityCache.put(type, (result != null ? result : NOT_ANNOTATED));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 102 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 24 KiB |