ApplicationContext之功能扩展

接这上篇文章分析,上篇文章分析到创建好beanFactory,Spring也已经完成对配置文件的解析,ApplicationContext也是在这个基础上进行展开的.

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		// 设置 beanFactory的classLoader为当前context的classLoader
		beanFactory.setBeanClassLoader(getClassLoader());
		// 设置beanFactory的表达式语言处理器,Spring3增加了表达式语言的支持
		// 即增加了对SpEL语言的支持
		beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
		// 为beanFactory增加了一个默认的propertyEditor(属性注册编辑器),这个主要是对bean的属性等设置管理的一个工具
		beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

		// 添加beanPostProcessor
		beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
		// 设置了几个忽略自动装配的接口
		beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
		beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
		beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
		beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
		beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
		beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

		// 设置了几个自动装配的特殊规则
		beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
		beanFactory.registerResolvableDependency(ResourceLoader.class, this);
		beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
		beanFactory.registerResolvableDependency(ApplicationContext.class, this);

		// 添加beanPostProcessor
		beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

		// 增加对AspectJ的支持
		if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
			beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
			// Set a temporary ClassLoader for type matching.
			beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
		}

		// 添加默认的系统环境bean
		if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
			beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
		}
		if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
			beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
		}
		if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
			beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
		}
	}

SpEL语言的支持
SpEL的全称是Spring Expression Language,缩写为SpEL,能在运行构建复杂表达式、存取对象图属性、对象方法调用等,并能与Spring功能完美整合.SpEL是单独模块,只依赖于core模块,不依赖于其他模块,可以单独使用.
SpEL使用#{…}作为定界符,所有在大括号中的字符都被认为是SpEL:
举一个简单的使用方式:

<bean id="pluto" value="com.itpluto.xxx.xxx" />
<bean>
	<property name="planet" value="#{pluto}" />
</bean>

相当于:

<bean id="pluto" value="com.itpluto.xxx.xxx" />
<bean>
	<property name="planet" ref="pluto" />
</bean>

在源码中通过代码beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));注册语言解析器,就可以对SpEL进行解析了.
之前我们分析过Spring在bean进行初始化的时候会有一步进行属性填充,而在这一步中Spring会调用AbstractAutowireCapableBeanFactory类的applyPropertyValues函数来完成功能.
正是在这个方法中,会通过构造BeanDefinitionValueResolver类型实例valueResolver来进行属性值的解析.也就是在这个步骤中通过AbstractBeanFactory中的evaluateBeanDefinitionString方法完成了SpEL的解析.

	protected Object evaluateBeanDefinitionString(@Nullable String value, @Nullable BeanDefinition beanDefinition) {
		// 如果不存在语言解析器,则直接返回
		if (this.beanExpressionResolver == null) {
			return value;
		}

		Scope scope = null;
		if (beanDefinition != null) {
			String scopeName = beanDefinition.getScope();
			if (scopeName != null) {
				scope = getRegisteredScope(scopeName);
			}
		}
		// 进行解析
		return this.beanExpressionResolver.evaluate(value, new BeanExpressionContext(this, scope));
	}

应用语言解析的地方除了属性填充之外,还有依赖注入和bean初始化的时候,大家可以回顾之前的分析看看.

属性注册编辑器
在SpringDI的时候可以把普通属性注入进来,但是像Date类型就无法识别
举个例子:

public class BlackDog implements Dog{
	// 日期类型
    private Date birthday;

    public Date getDate() {
        return birthday;
    }

    public void setDate(Date date) {
        this.birthday = date;
    }

    @Override
    public String toString() {
        return "BlackDog{" +
            "birthday=" + birthday +
            '}';
    }
}

配置文件:

    <bean id="blackDog" class="com.ipluto.demo.BlackDog">
        <property name="birthday">
            <value>2022-12-12</value>
        </property>
    </bean>

测试类:

      ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
      BlackDog blackDog = (BlackDog)ctx.getBean("blackDog");
      System.out.println(blackDog);

这个时候程序会报错,因为类型转化不成功.因为BlackDog中的birthday属性是Date类型的,但是XML文件中配置的却是String类型的,所以会报异常.
解决办法:

  • 使用自定义属性编辑器
    通过继承PropertyEditorSupport,重写setAsText方法,具体的步骤如下:

1、编写自定义的属性编辑器

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 {
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        try {
            Date d = sdf.parse(text);
            this.setValue(d);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        super.setAsText(text);
    }
}

2、将自定义属性编辑器注册到Spring中

    <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
        <property name="customEditors">
            <map>
                <entry key="java.util.Date">
                    <bean class="com.ipluto.demo.DatePropertyEditor">
                        <property name="format" value="yyyy-MM-dd"/>
                    </bean>
                </entry>
            </map>
        </property>
    </bean>

在配置文件中引入类型为org.springframework.beans.factory.config.CustomEditorConfigurer的bean,并在属性customEditors中加入自定义属性编辑器,其中key为属性编辑器所对应的类型.通过这样的配置,当Spring在注入bean的属性的时一旦遇到了java.util.Date类型的属性就会自动使用自定义的DatePropertyEditor解析器进行解析,并用解析器结果代替了配置属性进行注入.

  • 注册Spring自带的属性编辑器CustomerDateEditor

1、定义属性编辑器

public class DatePropertyEditorRegistrar implements PropertyEditorRegistrar {

    @Override
    public void registerCustomEditors(PropertyEditorRegistry registry) {
        registry.registerCustomEditor(Date.class,new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"),true));
    }
    
}

2、注册到Spring中

    <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
        <property name="propertyEditorRegistrars">
            <list>
                <bean class="com.ipluto.demo.DatePropertyEditorRegistrar"></bean>
            </list>
        </property>
    </bean>

在配置文件中将自定义的DatePropertyEditorRegistrar注册进了CustomEditorConfigurerpropertyEditorRegistrars属性中,可以具有与方法1同样的效果.
注意在最开始的代码里边,我们是使用beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));来进行编辑器的注入的,可以发现这里使用的是ResourceEditorRegistrar,而我们自定义的属性编辑器使用的是PropertyEditorRegistryregisterCustomEditor()方法,这两者之间有什么关联呢?
首先看一下ResourceEditorRegistrarregisterCustomEditors()方法

public void registerCustomEditors(PropertyEditorRegistry registry) {
		ResourceEditor baseEditor = new ResourceEditor(this.resourceLoader, this.propertyResolver);
		doRegisterEditor(registry, Resource.class, baseEditor);
		doRegisterEditor(registry, ContextResource.class, baseEditor);
		doRegisterEditor(registry, InputStream.class, new InputStreamEditor(baseEditor));
		doRegisterEditor(registry, InputSource.class, new InputSourceEditor(baseEditor));
		doRegisterEditor(registry, File.class, new FileEditor(baseEditor));
		doRegisterEditor(registry, Path.class, new PathEditor(baseEditor));
		doRegisterEditor(registry, Reader.class, new ReaderEditor(baseEditor));
		doRegisterEditor(registry, URL.class, new URLEditor(baseEditor));

		ClassLoader classLoader = this.resourceLoader.getClassLoader();
		doRegisterEditor(registry, URI.class, new URIEditor(classLoader));
		doRegisterEditor(registry, Class.class, new ClassEditor(classLoader));
		doRegisterEditor(registry, Class[].class, new ClassArrayEditor(classLoader));

		if (this.resourceLoader instanceof ResourcePatternResolver) {
			doRegisterEditor(registry, Resource[].class,
					new ResourceArrayPropertyEditor((ResourcePatternResolver) this.resourceLoader, this.propertyResolver));
		}
	}
private void doRegisterEditor(PropertyEditorRegistry registry, Class<?> requiredType, PropertyEditor editor) {
		if (registry instanceof PropertyEditorRegistrySupport) {
			((PropertyEditorRegistrySupport) registry).overrideDefaultEditor(requiredType, editor);
		}
		else {
			// 这里
			registry.registerCustomEditor(requiredType, editor);
		}
	}

registerCustomEditors()方法的核心功能不过就是注册了一系列常用类型的属性编辑器,例如doRegisterEditor(registry, Class.class, new ClassEditor(classLoader));实现的功能就是注册Class类对应的属性编辑器.一旦某个实体bean中存在一些Class类型的属性,Spring就会调用ClassEditor将配置中定义的String类型转换为Class类型并进行赋值.

registerCustomEditors方法被调用的层次结构:
![在这里插入图片描述](https://img-blog.csdnimg.cn/38f7c8f161234bcf989d1dd16b4ef2c4.png
可以看到上面都是在AbstractFactory中被调用,接着往下找:
在这里插入图片描述
可以找到一个我们比较熟悉的方法initBeanWrapper,这个就是在bean实例化的时候,将BeanDefinition转换为BeanWrapper.
在这里插入图片描述
在initBeanWrapper中进行批量的通用属性编辑器注册,注册之后,在填充环节就可以直接让Spring使用这些编辑器进行属性解析了.
Spring中用于封装bean的是BeanWrapper类型,他间接继承了PropertyEditorRegistry,也就是我们看到的方法参数registry,其实大部分情况下都是BeanWrapper,对于BeanWrapper在Spring中的默认实现是BeanWrapperImpl,而BeanWrapperImpl除了实现了BeanWrapper之外还间接继承了PropertyEditorRegistrySupport,在PropertyEditorRegistrySupport中有一个方法:

private void createDefaultEditors() {
		this.defaultEditors = new HashMap<>(64);

		// Simple editors, without parameterization capabilities.
		// The JDK does not contain a default editor for any of these target types.
		this.defaultEditors.put(Charset.class, new CharsetEditor());
		this.defaultEditors.put(Class.class, new ClassEditor());
		this.defaultEditors.put(Class[].class, new ClassArrayEditor());
		this.defaultEditors.put(Currency.class, new CurrencyEditor());
		this.defaultEditors.put(File.class, new FileEditor());
		this.defaultEditors.put(InputStream.class, new InputStreamEditor());
		if (!shouldIgnoreXml) {
			this.defaultEditors.put(InputSource.class, new InputSourceEditor());
		}
		this.defaultEditors.put(Locale.class, new LocaleEditor());
		this.defaultEditors.put(Path.class, new PathEditor());
		this.defaultEditors.put(Pattern.class, new PatternEditor());
		this.defaultEditors.put(Properties.class, new PropertiesEditor());
		this.defaultEditors.put(Reader.class, new ReaderEditor());
		this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor());
		this.defaultEditors.put(TimeZone.class, new TimeZoneEditor());
		this.defaultEditors.put(URI.class, new URIEditor());
		this.defaultEditors.put(URL.class, new URLEditor());
		this.defaultEditors.put(UUID.class, new UUIDEditor());
		this.defaultEditors.put(ZoneId.class, new ZoneIdEditor());

		// Default instances of collection editors.
		// Can be overridden by registering custom instances of those as custom editors.
		this.defaultEditors.put(Collection.class, new CustomCollectionEditor(Collection.class));
		this.defaultEditors.put(Set.class, new CustomCollectionEditor(Set.class));
		this.defaultEditors.put(SortedSet.class, new CustomCollectionEditor(SortedSet.class));
		this.defaultEditors.put(List.class, new CustomCollectionEditor(List.class));
		this.defaultEditors.put(SortedMap.class, new CustomMapEditor(SortedMap.class));

		// Default editors for primitive arrays.
		this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor());
		this.defaultEditors.put(char[].class, new CharArrayPropertyEditor());

		// The JDK does not contain a default editor for char!
		this.defaultEditors.put(char.class, new CharacterEditor(false));
		this.defaultEditors.put(Character.class, new CharacterEditor(true));

		// Spring's CustomBooleanEditor accepts more flag values than the JDK's default editor.
		this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false));
		this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true));

		// The JDK does not contain default editors for number wrapper types!
		// Override JDK primitive number editors with our own CustomNumberEditor.
		this.defaultEditors.put(byte.class, new CustomNumberEditor(Byte.class, false));
		this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true));
		this.defaultEditors.put(short.class, new CustomNumberEditor(Short.class, false));
		this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true));
		this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class, false));
		this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true));
		this.defaultEditors.put(long.class, new CustomNumberEditor(Long.class, false));
		this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true));
		this.defaultEditors.put(float.class, new CustomNumberEditor(Float.class, false));
		this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true));
		this.defaultEditors.put(double.class, new CustomNumberEditor(Double.class, false));
		this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class, true));
		this.defaultEditors.put(BigDecimal.class, new CustomNumberEditor(BigDecimal.class, true));
		this.defaultEditors.put(BigInteger.class, new CustomNumberEditor(BigInteger.class, true));

		// Only register config value editors if explicitly requested.
		if (this.configValueEditorsActive) {
			StringArrayPropertyEditor sae = new StringArrayPropertyEditor();
			this.defaultEditors.put(String[].class, sae);
			this.defaultEditors.put(short[].class, sae);
			this.defaultEditors.put(int[].class, sae);
			this.defaultEditors.put(long[].class, sae);
		}
	}

通过这个方法就可以知道在Spring中定义了上面一系列常用的属性编辑器我们可以方便的进行配置,只有我们定义的bean中的某个属性的类型不在上面的常用配置中的时候,才需要我们进行个性化属性编辑器的注册.

ApplicationContextAwareProcessor处理器
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
他的主要目的其实只是注册一个beanPostProcessor,而真正的逻辑还在ApplicationContextAwareProcessor中.
之前在讲bean的初始化的时候,在Spring使用配置的init-method的前后,会调用beanPostProcessor的postProcessBeforeInitializationpostProcessAfterInitialization方法,同样对于ApplicationContextAwareProcessor也只关心这两个方法.
在ApplicationContextAwareProcessor中会发现,他根本没有重写postProcessAfterInitialization方法

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (!(bean instanceof EnvironmentAware) && !(bean instanceof EmbeddedValueResolverAware) && !(bean instanceof ResourceLoaderAware) && !(bean instanceof ApplicationEventPublisherAware) && !(bean instanceof MessageSourceAware) && !(bean instanceof ApplicationContextAware) && !(bean instanceof ApplicationStartupAware)) {
            return bean;
        } else {
            AccessControlContext acc = null;
            if (System.getSecurityManager() != null) {
                acc = this.applicationContext.getBeanFactory().getAccessControlContext();
            }

            if (acc != null) {
                AccessController.doPrivileged(() -> {
                    this.invokeAwareInterfaces(bean);
                    return null;
                }, acc);
            } else {
                this.invokeAwareInterfaces(bean);
            }

            return bean;
        }
    }
    
	private void invokeAwareInterfaces(Object bean) {
		// 实现了以下接口的bean,可以在被初始化后得到这些资源
        if (bean instanceof EnvironmentAware) {
            ((EnvironmentAware)bean).setEnvironment(this.applicationContext.getEnvironment());
        }

        if (bean instanceof EmbeddedValueResolverAware) {
            ((EmbeddedValueResolverAware)bean).setEmbeddedValueResolver(this.embeddedValueResolver);
        }

        if (bean instanceof ResourceLoaderAware) {
            ((ResourceLoaderAware)bean).setResourceLoader(this.applicationContext);
        }

        if (bean instanceof ApplicationEventPublisherAware) {
            ((ApplicationEventPublisherAware)bean).setApplicationEventPublisher(this.applicationContext);
        }

        if (bean instanceof MessageSourceAware) {
            ((MessageSourceAware)bean).setMessageSource(this.applicationContext);
        }

        if (bean instanceof ApplicationStartupAware) {
            ((ApplicationStartupAware)bean).setApplicationStartup(this.applicationContext.getApplicationStartup());
        }

        if (bean instanceof ApplicationContextAware) {
            ((ApplicationContextAware)bean).setApplicationContext(this.applicationContext);
        }

    }

从上面的代码可以看到,如果对象实现了以上的接口,那么就可以得到对应资源.

设置忽略

beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
不知道有没有注意到,上面invokeAwareInterfaces方法中的类型恰好是设置忽略的类型,因为这些aware不是普通的bean了,需要在Spring做bean的依赖注入的时候忽略他们,并在初始化的时候时候注入他们.

注册依赖

beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);

对应忽略功能,Spring中也有注册依赖的功能,当注册了依赖解析后,当bean的属性注入的时候,一旦检测到属性是上面的几种类型之后,便会将对应的实例注入进去.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值