ApplicationContext(四)BeanFactory 功能扩展
ApplicationContext(四)BeanFactory 功能扩展
上节我们提到容器刷新的第二步初始化 BeanFactory 工厂并解析配制文件,但此时 BeanFactory 的功能还很简单,需要对其进行扩展。这就涉及到下面第三步:BeanFactory 功能扩展。
那 Spring 究竟进行了那些功能扩展呢?
源代码【AbstractApplicationContext】
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// 1. 设置 beanFactory 的 classLoader 为当前 context 的 classLoader
beanFactory.setBeanClassLoader(getClassLoader());
// 2. 设置 beanFactory 的表达式语言处理器,Spring3 增加了表达式语言的支持
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
// 3. 为 beanFactory 增加一个默认的 propertyEditor,这个主要是对 bean 的属性等设置管理的一个工具
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// 4. 添加 BeanPostProcessor
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
// 5. 设置了几个忽略自动装配的接口
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
// 6. 设置了几个自动装配的特殊规则
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
// 7. 增加对 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()));
}
// 8. 添加默认的系统环境 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());
}
}
上面函数中主要进行了几个方面的扩展。
(1) 增加对 SPEL 语言的支持
(2) 增加对属性编辑器的支持
(3) 增加对一些内置类,比如 Environmentaware、 Messagesourceaware 的信息注入。
(4) 设置了依赖功能可忽略的接口
(5) 注册一些固定依的属性
(6) 增加 Aspect的支持(会在第7章中进行详细的讲解)
(7) 将相关环境变量及属性注册以单例模式注册
可能读者不是很理解每个步骤的具体含义,接下来我们会对各个步骤进行详细地分析。
一、增加对 SPEL 语言的支持
SpEL使用 #{...} 作为定界符,所有在大框号中的字符都将被认为是 SpEL,使用格式如下:
@Value("#{19 - 1}")
private int age3;
在源码中通过代码 beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader())) 注册语言解析器,就可以对 SPEL 进行解析了,那么在注册解析器后 Spring 又是在什么时候调用这个解析器进行解析呢?详见 《SPEL语言执行过程》
二、增加对属性编辑器的支持
2.1 属性编辑器的基本用法
在 Spring DI 注入的时候可以把普通属性注入进来,但是像 Date 类型就无法被识別。例如:
public class UserManager {
private Date data;
// 省略get/set
}
上面代码中,需要对日期型属性进行注入:
<bean id="userManager" class="UserManager">
<property name="data" value="2018-08-01 00:00:00"/>
</bean>
如果直接这样使用,程序则会报异常,类型转换不成功。因为在 Usermanager 中的 data Value 属性是 Date 类型型的,而在 XML中配置的却是 String 类型的,所以当然会报异常。
Spring 针对此问题提供了两种解决办法。
1. 使用用自定义属性编辑器
使用自定义属性编辑器,通过继承 PropertyEditorSupport,重写 setastext 方法,具体步骤如下。
(1) 编写自定义的属性编辑器。
public class DatePropertyEdit extends PropertyEditorSupport implements PropertyEditor {
private String format = "yyyy-MM-dd HH:mm:ss";
@Override
public void setAsText(String text) throws IllegalArgumentException {
SimpleDateFormat sdf = new SimpleDateFormat(format);
try {
Date date = sdf.parse(text);
this.setValue(date);
} catch (ParseException e) {
}
}
}
(2) 将自定义属性编辑器注册到 Spring 中。
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="java.util.Date"
value="com.github.binarylei.spring01.day0728.propertyedit.DataPropertyEdit">
</entry>
</map>
</property>
</bean>
在配置文件中引入类型为 CustomEditorConfigurer 的 bean,并在属性 customeditors 中加入自定义的属性编辑器,其中 key 为属性编辑器所对应的类型。通过这样的配置,当 Spring 在注入 bean 的属性时一旦遇到了 java.uti.Date 类型的属性会自动调用自定义的 DatepropertyEditor 解解析器进行解析,并用解析结果代替配置属性进行注入。
2. 注册 Spring 自带的属性编辑器 CustomDateEditor
通过注册 Spring 自带的属性编辑器 CustomDateEditor,具体步骤如下。
(1) 定义属性编辑器
public class DatePropertyEditorRegistar 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="spring.context.spring_di.DatePropertyEditorRegistar"/>
</list>
</property>
</bean>
通过在配置文件中将自定义的 DatePropertyEditorRegistar 注册进人 org.springframework.beans.factory.config.CustomEditorConfigurer 的 propertyEditorRegistrars 属性中,可以具有与方法 1 同样的效果我们了解了自定义属性编辑器的使用。
但是,似乎这与本节中围绕的核心代码 beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())) 并无联系,因为在注册自定义属性编辑器的时候使用的是 PropertyEditorRegistrar 的 registerCustomEditors 方法,而这里使用的是 ConfigurableListableBeanFactory 的 addPropertyEditorRegistrar 方法。我们不妨深入探索下 ResourceEditorRegistrar 的内部实现,在 ResourceEditorRegistrar 中,我们最关心的方法是 registerCustomEditor 方法。
2.2 属性编辑器的内部原理
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()))
源代码【AbstractBeanFactory】
public void addPropertyEditorRegistrar(PropertyEditorRegistrar registrar) {
Assert.notNull(registrar, "PropertyEditorRegistrar must not be null");
this.propertyEditorRegistrars.add(registrar);
}
源代码【ResourceEditorRegistrar】
// 1. PropertyEditorRegistrar 将常用的资源编辑器注册到 PropertyEditorRegistry 上,相当于一个工具类
// 2. PropertyEditorRegistry 实际持有编辑器
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, 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);
}
}
在 doRegisterEditor 函数中,可以看到在之前提到的自定义属性中使用的关键代码 registry.registerCustomEditor(requiredType, editor),回过头来看 ResourceEditorRegistrar 类的 registerCustomEditors 方法的核心功能,其实无非是注册了一系列的常用类型的属性编辑器,例如,代码 doRegisterEditor(registry, Class.class, new ClassEditor(classLoader)) 实现的功能就是注册 Class 类对应的属性编辑器。那么,注册后,一且某个实体 bean 中存在一些 Class 类型的属性,那么 Spring 会调用 Classeditor 将配置中定义的 String 类型转换为 Class 类型并进行赋值。
分析到这里,我们不禁有个疑问,虽说 ResourceEditorRegistrar 类的 registerCustomEditors 方法实现了批量注册的功能,但是 beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())) 仅仅是注册了 ResourceEditorRegistrar 实例,却并没有调用 ResourceEditorRegistrar 的 registerCustomEditors 方法进行注册,那么到底是在什么时候进行注册的呢?
源代码【Abstractbeanfactory】
// 如此,BeanWrapper 就注册了各种属性编辑器,可以在 populate() 时解析各种 String 类型的字段进行注入
protected void initBeanWrapper(BeanWrapper bw) {
bw.setConversionService(getConversionService());
registerCustomEditors(bw);
}
// 调用 propertyEditorRegistrars 为 registry 注入属性编辑器
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) {
registrar.registerCustomEditors(registry);
}
}
if (!this.customEditors.isEmpty()) {
for (Map.Entry<Class<?>, Class<? extends PropertyEditor>> entry : this.customEditors.entrySet()) {
Class<?> requiredType = entry.getKey();
Class<? extends PropertyEditor> editorClass = entry.getValue();
registry.registerCustomEditor(requiredType, BeanUtils.instantiateClass(editorClass));
}
}
}
既然提到了 BeanWrapper,这里也有必要强调下, Spring 中用于封装 bean 的是 BeanWrapper 类型,而它又间接继承了 PropertyEditorRegistry 类型,也就是我们之前反复看到的方法参数 PropertyEditorRegistry registry,其实大部分情况下都是 BeanWrapper,对于 BeanWrapper 在 Spring 中的默认实现是 BeanWrapperlmpl,而 BeanWrapperlmpl 除了实现 BeanWrapper 接口外还继承了 PropertyEditorRegistrySupport,在 PropertyEditorRegistrySupport 中有这样一个方法
源代码【PropertyEditorRegistrySupport】
public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {
// 在 PropertyEditorRegistrySupport 中注册了一系列默认的属性编辑器
private void createDefaultEditors() {
this.defaultEditors = new HashMap<Class<?>, PropertyEditor>(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());
this.defaultEditors.put(InputSource.class, new InputSourceEditor());
this.defaultEditors.put(Locale.class, new LocaleEditor());
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());
if (zoneIdClass != null) {
this.defaultEditors.put(zoneIdClass, 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);
}
}
}
2.3 添加 ApplicationContextAwareProcessor 处理器
在 Spring 中可以注入一些底层的组件,怎么实现的呢?这就是 beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)) 的作用了。首先回顾一下 ApplicationContextAware 的使用方法:
// 可以使用 ApplicationContext 组件
@Service
public class Bean implements ApplicationContextAware {
private ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
}
很显然 context 是在 bean 实例化完成,属性注入后准备进行 init-method 方法前完成的。我们看一下代码:
源代码【PropertyEditorRegistrySupport】
// 抛开各种异常处理 Spring 作了如下的处理,这里我们重点关注 applyBeanPostProcessorsBeforeInitialization
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
// 1. 注入 Spring 组件
invokeAwareMethods(beanName, bean);
// 2. 执行 init-method 前的回调,ApplicationContextAware 就是在这一步注入属性
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
// 3. 执行 init-method 方法
invokeInitMethods(beanName, wrappedBean, mbd);
// 4. 执行 init-method 后的回调
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
return wrappedBean;
}
// 注入 BeanNameAware、BeanClassLoaderAware、BeanFactoryAware
private void invokeAwareMethods(final String beanName, final Object bean) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware) {
((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
}
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}
// 执行后置处理器,ApplicationContextAware 在这里注入了部分其它的 Aware
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
result = beanProcessor.postProcessBeforeInitialization(result, beanName);
if (result == null) {
return result;
}
}
return result;
}
下面我们看一下 ApplicationContextAwareProcessor 都做了那些事情,postProcessBeforeInitialization 实际上事情委托给了 invokeAwareInterfaces()
class ApplicationContextAwareProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
invokeAwareInterfaces(bean);
return bean;
}
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof Aware) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(
new EmbeddedValueResolver(this.applicationContext.getBeanFactory()));
}
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 ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
}
}
2.4 设置忽略依赖
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
很明显 ApplicationContextAware 不能再注入到 bean 中:
public class Bean {
@Autowired
private ApplicationContextAware applicationContextAware;
}
2.5 注册依赖
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
Spring 还简化了底层组件的注入问题:
public class Bean {
@Autowired
private ApplicationContext context;
}
每天用心记录一点点。内容也许不重要,但习惯很重要!
ApplicationContext(四)BeanFactory 功能扩展的更多相关文章
- 硕盟SM-T54| TYPE C转HDMI+VGA+USB3.0+PD3.0四合一多功能扩展坞
硕盟SM-T54是一款 TYPE C转HDMI+VGA+USB3.0+PD3.0四合一多功能扩展坞,支持四口同时使用,您可以将含有USB 3.1协议的电脑主机,通过此产品连接到具有HDMI或VGA的显 ...
- 硕盟SM-T54|type-c转接头HDMI+VGA+USB3.0+PD3.0四合一多功能扩展坞接口功能说明
硕盟SM-T54是一款 TYPE C转HDMI+VGA+USB3.0+PD3.0四合一多功能扩展坞,支持四口同时使用,您可以将含有USB 3.1协议的电脑主机,通过此产品连接到具有HDMI或VGA的显 ...
- 硕盟type-c转接头|四合一多功能扩展坞
硕盟SM-T54是一款 TYPE C转HDMI+VGA+USB3.0+PD3.0四合一多功能扩展坞,支持四口同时使用,您可以将含有USB 3.1协议的电脑主机,通过此产品连接到具有HDMI或VGA的显 ...
- 硕盟type-c转接头HDMI+VGA+USB3.0+PD3.0四合一多功能扩展坞
硕盟SM-T54是一款 TYPE C转HDMI+VGA+USB3.0+PD3.0四合一多功能扩展坞,支持四口同时使用,您可以将含有USB 3.1协议的电脑主机,通过此产品连接到具有HDMI或VGA的显 ...
- Spring中ApplicationContext和beanfactory区别---解析二
一.BeanFactory 和ApplicationContext Bean 工厂(com.springframework.beans.factory.BeanFactory)是Spring 框架最核 ...
- spring源码深度解析-2功能扩展
容器功能的扩展ApplicationContext用于扩展BeanFactory中现有的功能.究竟多出了哪些功能,进一步探索.写法上:BeanFactory bf = new XmlBeanFacto ...
- 创建ApplicationContext与BeanFactory时的区别-Spring源码学习之容器的基本实现
传送门 可以加载XML两种方法 使用 BeanFactory 加载 XML BeanFactory bf = new XmlBeanFactory(new ClassPathResource(&quo ...
- Spring源码分析(十九)容器的功能扩展概览
摘要: 本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 经过前面几章的分析,相信大家已经对 Spring 中的容器功能有了简单 ...
- Spring中ApplicationContext和beanfactory区别---解析一
BeanFacotry是spring中比较原始的Factory.如XMLBeanFactory就是一种典型的BeanFactory.原始的BeanFactory无法支持spring的许多插件,如AOP ...
随机推荐
- Android Studio SVN配置
一 . 原文链接:忽略文件[转] https://blog.csdn.net/buaaroid/article/details/51546521 1.用Android Studio创建一个项目, ...
- 【Django】关于ORM的使用
添加模型并映射到数据库中: 1. 在 settings.py 中,配置好 DATABASES ,做好数据库相关的配置. 以mysql为例: DATABASES = { 'default': { 'EN ...
- linux 升级python2.7
linux为centos6,系统默认安装了python2.6,需要执行的python脚本内容包含标准库之xml.etree.ElementTree 用到库里的一个iter方法是python2.7的新 ...
- 截图软件FastStone
屏幕截图软件(FastStone Capture) 好用,可以粘贴 / 复制. 可以做页面设计,有屏幕标尺,取色器.
- StringBuffer类和String类(原文地址 : http://www.cnblogs.com/springcsc/archive/2009/12/03/1616330.html)
StringBuffer类和String一样,也用来代表字符串,只是由于StringBuffer的内部实现方式和String不同,所以StringBuffer在进行字符串处理时,不生成新的对象,在内存 ...
- CentOS 下搭建Hudson
1.下载Hudson安装包 wget http://ftp.jaist.ac.jp/pub/eclipse/hudson/war/hudson-3.3.3.war 2.执行 java -jar hud ...
- Python+Selenium学习--分页处理
场景 我们在测试一个web 应用时,经常出现翻页的情况,下面介绍翻页场景 代码 #!/usr/bin/env python # -*- codinfg:utf-8 -*- ''' @author: J ...
- SSRF攻击-运用gopher协议构造POST包--emmmm(http://10.112.68.215:10004/index.php?action=login)
还是天枢的一道CTF题,启程! 分析题目,自己注册账户并登陆后,提示输入一个url,网站会自己运行查找网页信息. emmmmm,很挑衅,网站就当作服务器,我们在url框中输 ...
- ES6之对象的简洁表示法
ES6 允许直接写入变量和函数,作为对象的属性和方法.这样的书写更加简洁. let name = 'Pirates of the Caribbean', index = 5, captain = { ...
- [leetcode]449. Serialize and Deserialize BST序列化反序列化二叉搜索树(尽量紧凑)
Serialization is the process of converting a data structure or object into a sequence of bits so tha ...