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 功能扩展的更多相关文章

  1. 硕盟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的显 ...

  2. 硕盟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的显 ...

  3. 硕盟type-c转接头|四合一多功能扩展坞

    硕盟SM-T54是一款 TYPE C转HDMI+VGA+USB3.0+PD3.0四合一多功能扩展坞,支持四口同时使用,您可以将含有USB 3.1协议的电脑主机,通过此产品连接到具有HDMI或VGA的显 ...

  4. 硕盟type-c转接头HDMI+VGA+USB3.0+PD3.0四合一多功能扩展坞

    硕盟SM-T54是一款 TYPE C转HDMI+VGA+USB3.0+PD3.0四合一多功能扩展坞,支持四口同时使用,您可以将含有USB 3.1协议的电脑主机,通过此产品连接到具有HDMI或VGA的显 ...

  5. Spring中ApplicationContext和beanfactory区别---解析二

    一.BeanFactory 和ApplicationContext Bean 工厂(com.springframework.beans.factory.BeanFactory)是Spring 框架最核 ...

  6. spring源码深度解析-2功能扩展

    容器功能的扩展ApplicationContext用于扩展BeanFactory中现有的功能.究竟多出了哪些功能,进一步探索.写法上:BeanFactory bf = new XmlBeanFacto ...

  7. 创建ApplicationContext与BeanFactory时的区别-Spring源码学习之容器的基本实现

    传送门 可以加载XML两种方法 使用 BeanFactory 加载 XML BeanFactory bf = new XmlBeanFactory(new ClassPathResource(&quo ...

  8. Spring源码分析(十九)容器的功能扩展概览

    摘要: 本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 经过前面几章的分析,相信大家已经对 Spring 中的容器功能有了简单 ...

  9. Spring中ApplicationContext和beanfactory区别---解析一

    BeanFacotry是spring中比较原始的Factory.如XMLBeanFactory就是一种典型的BeanFactory.原始的BeanFactory无法支持spring的许多插件,如AOP ...

随机推荐

  1. Gradle 在Eclipse中的使用

    eclipse上gradle插件的安装 1)在Eclipse中选择Help -> Eclipse Marketplace…,输入buildship点击Go,然后选择Install安装Gradle ...

  2. Eclipse git 冲突合并

    Eclipse有一个git的插件叫EGit,用于实现本地代码和远程代码对比.合并以及提交.但是在本地代码和远程代码有冲突的时候,EGit的处理方案还是有点复杂.今天就彻底把这些步骤给理清楚,并公开让一 ...

  3. SQLMAP自动注入(四):枚举

    --privileges 查询权限 -U 指定用户 -CU指定当前用户 --schema 查询所有的数据 --batch 批处理,自动选择默认选项 --exclude-sysdbs 排除系统库的查询 ...

  4. metasploit framework(二):记一次入侵

    msfconsole use 其中一个 exploit前台执行注入 后台执行shell 加-j 通过sessions查看后台执行的shell,可以看到这个会话的id号为2 进入会话,sessions ...

  5. 安装Eclipse Maven插件的几种方法

    文章出处:http://blog.csdn.net/lfsfxy9/article/details/9397937 感谢作者的分享! 昨天直接在机器上配置了Maven环境,今天顺便把Eclipse等I ...

  6. contextlib 上下文管理器

    在Python中,读写文件这样的资源要特别注意,必须在使用完毕后正确关闭它们.正确关闭文件资源的一个方法是使用try...finally: try: f = open('/path/to/file', ...

  7. 每月IT摘录201810

    技术 1.Redis.对于单机实例,我们采用原生主从(Master-Slave)模式实现高可用,常规模式下对外仅暴露 Master 节点.由于使用原生 Redis,所以单机实例支持所有 Redis 指 ...

  8. kafka集群压力测试--基础。

    1.生产者测试 kafka-producer-perf-test.bat --num-records 1000000 --topic test --record-size 200 --throughp ...

  9. 在timer的时候突然改变影片简介,先前的不暂停

    import flash.display.MovieClip; import flash.utils.Timer; import flash.events.TimerEvent; var hinder ...

  10. 服务器使用VMware系软件管理主机集群

    在服务器安装ESXI 6.0系统,此系统300多M,用于管理服务器上的主机. 其他主机安装个vsphere client连接后可ESXI系统可进行简单管理 如果要更强大的功能,需要安装vcenter ...