BeanPostProcessor接口
BeanPostProcessor接口及回调方法图
1、InstantiationAwareBeanPostProcessor:实例化Bean后置处理器(继承BeanPostProcessor)
postProcessBeforeInstantiation :在实例化目标对象之前执行,可以自定义实例化逻辑,如返回一个代理对象等,(3.1处执行;如果此处返回的Bean不为null将中断后续Spring创建Bean的流程,且只执行postProcessAfterInitialization回调方法,如当AbstractAutoProxyCreator的实现者注册了TargetSourceCreator(创建自定义的TargetSource)将改变执行流程,不注册TargetSourceCreator我们默认使用的是SingletonTargetSource(即AOP代理直接保证目标对象),此处我们还可以使用如ThreadLocalTargetSource(线程绑定的Bean)、CommonsPoolTargetSource(实例池的Bean)等等,大家可以去spring官方文档了解TargetSource详情;
postProcessAfterInitialization : Bean实例化完毕后执行的后处理操作,所有初始化逻辑、装配逻辑之前执行,如果返回false将阻止其他的InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation的执行,(3.2和(9.1处执行;在此处可以执行一些初始化逻辑或依赖装配逻辑;
postProcessPropertyValues :完成其他定制的一些依赖注入和依赖检查等,如AutowiredAnnotationBeanPostProcessor执行@Autowired注解注入,CommonAnnotationBeanPostProcessor执行@Resource等注解的注入,PersistenceAnnotationBeanPostProcessor执行@ PersistenceContext等JPA注解的注入,RequiredAnnotationBeanPostProcessor执行@ Required注解的检查等等,(9.3处执行;
2、MergedBeanDefinitionPostProcessor:合并Bean定义后置处理器 (继承BeanPostProcessor)
postProcessMergedBeanDefinition:执行Bean定义的合并,在(7.1处执行,且在实例化完Bean之后执行;
3、SmartInstantiationAwareBeanPostProcessor:智能实例化Bean后置处理器(继承InstantiationAwareBeanPostProcessor)
predictBeanType:预测Bean的类型,返回第一个预测成功的Class类型,如果不能预测返回null;当你调用BeanFactory.getType(name)时当通过Bean定义无法得到Bean类型信息时就调用该回调方法来决定类型信息;BeanFactory.isTypeMatch(name, targetType)用于检测给定名字的Bean是否匹配目标类型(如在依赖注入时需要使用);
determineCandidateConstructors:检测Bean的构造器,可以检测出多个候选构造器,再有相应的策略决定使用哪一个,如AutowiredAnnotationBeanPostProcessor实现将自动扫描通过@Autowired/@Value注解的构造器从而可以完成构造器注入,请参考【第十二章】零配置 之12.2 注解实现Bean依赖注入 ——跟我学spring3 ,(6.2.2.1处执行;
getEarlyBeanReference:当正在创建A时,A依赖B,此时通过(8将A作为ObjectFactory放入单例工厂中进行early expose,此处B需要引用A,但A正在创建,从单例工厂拿到ObjectFactory(其通过getEarlyBeanReference获取及早暴露Bean),从而允许循环依赖,此时AspectJAwareAdvisorAutoProxyCreator(完成xml风格的AOP配置(<aop:config>)将目标对象(A)包装到AOP代理对象)或AnnotationAwareAspectJAutoProxyCreator(完成@Aspectj注解风格(<aop:aspectj-autoproxy> @Aspect)将目标对象(A)包装到AOP代理对象),其返回值将替代原始的Bean对象,即此时通过early reference能得到正确的代理对象,(8.1处实施;如果此处执行了,(10.3.3处的AspectJAwareAdvisorAutoProxyCreator或AnnotationAwareAspectJAutoProxyCreator的postProcessAfterInitialization将不执行,即这两个回调方法是二选一的;
4、BeanPostProcessor:Bean后置处理器
postProcessBeforeInitialization:实例化、依赖注入完毕,在调用显示的初始化之前完成一些定制的初始化任务,如BeanValidationPostProcessor完成JSR-303 @Valid注解Bean验证,InitDestroyAnnotationBeanPostProcessor完成@PostConstruct注解的初始化方法调用,ApplicationContextAwareProcessor完成一些Aware接口的注入(如EnvironmentAware、ResourceLoaderAware、ApplicationContextAware),其返回值将替代原始的Bean对象;(10.2处执行;
postProcessAfterInitialization:实例化、依赖注入、初始化完毕时执行,如AspectJAwareAdvisorAutoProxyCreator(完成xml风格的AOP配置(<aop:config>)的目标对象包装到AOP代理对象)、AnnotationAwareAspectJAutoProxyCreator(完成@Aspectj注解风格(<aop:aspectj-autoproxy> @Aspect)的AOP配置的目标对象包装到AOP代理对象),其返回值将替代原始的Bean对象;(10.3.3处执行;此处需要参考getEarlyBeanReference;
5、DestructionAwareBeanPostProcessor:销毁Bean后置处理器(继承BeanPostProcessor)
postProcessBeforeDestruction:销毁后处理回调方法,该回调只能应用到单例Bean,如InitDestroyAnnotationBeanPostProcessor完成@PreDestroy注解的销毁方法调用;(12.1.1处执行。
六、内置的一些BeanPostProcessor
部分内置的BeanPostProcessor
1、ApplicationContextAwareProcessor
容器启动时会自动注册。注入那些实现ApplicationContextAware、MessageSourceAware、ResourceLoaderAware、EnvironmentAware、
EmbeddedValueResolverAware、ApplicationEventPublisherAware标识接口的Bean需要的相应实例,在postProcessBeforeInitialization回调方法中进行实施,即(10.2处实施。
2、CommonAnnotationBeanPostProcessor
CommonAnnotationBeanPostProcessor继承InitDestroyAnnotationBeanPostProcessor,当在配置文件有<context:annotation-config>或<context:component-scan>会自动注册。
提供对JSR-250规范注解的支持@javax.annotation.Resource、@javax.annotation.PostConstruct和@javax.annotation.PreDestroy等的支持。
2.1、通过@Resource注解进行依赖注入:
postProcessPropertyValues:通过此回调进行@Resource注解的依赖注入;(9.3处实施;
2.2、用于执行@PostConstruct 和@PreDestroy 注解的初始化和销毁方法的扩展点:
postProcessBeforeInitialization()将会调用bean的@PostConstruct方法;(10.2处实施;
postProcessBeforeDestruction()将会调用单例 Bean的@PreDestroy方法(此回调方法会在容器销毁时调用),(12.1.1处实施。
详见【第十二章】零配置 之 12.2 注解实现Bean依赖注入 ——跟我学spring3,JSR-250注解部分。
3、AutowiredAnnotationBeanPostProcessor
当在配置文件有<context:annotation-config>或<context:component-scan>会自动注册。
提供对JSR-330规范注解的支持和Spring自带注解的支持。
3.1、Spring自带注解的依赖注入支持,@Autowired和@Value:
determineCandidateConstructors :决定候选构造器;详见【12.2中的构造器注入】;(6.2.2.1处实施;
postProcessPropertyValues :进行依赖注入;详见【12.2中的字段注入和方法参数注入】;(9.3处实施;
3.2、对JSR-330规范注解的依赖注入支持,@Inject:
同2.1类似只是查找使用的注解不一样;
详见【第十二章】零配置 之 12.2 注解实现Bean依赖注入 ——跟我学spring3,Spring自带依赖注入注解和 JSR-330注解部分。
4、RequiredAnnotationBeanPostProcessor
当在配置文件有<context:annotation-config>或<context:component-scan>会自动注册。
4.1、提供对@ Required注解的方法进行依赖检查支持:
postProcessPropertyValues:如果检测到没有进行依赖注入时抛出BeanInitializationException异常;(9.3处实施;
详见【第十二章】零配置 之 12.2 注解实现Bean依赖注入 ——跟我学spring3,@Required:依赖检查。
5、PersistenceAnnotationBeanPostProcessor
当在配置文件有<context:annotation-config>或<context:component-scan>会自动注册。
5.1、通过对JPA @ javax.persistence.PersistenceUnit和@ javax.persistence.PersistenceContext注解进行依赖注入的支持;
postProcessPropertyValues : 根据@PersistenceUnit/@PersistenceContext进行EntityManagerFactory和EntityManager的支持;
6、AbstractAutoProxyCreator
AspectJAwareAdvisorAutoProxyCreator和AnnotationAwareAspectJAutoProxyCreator都是继承AbstractAutoProxyCreator,AspectJAwareAdvisorAutoProxyCreator提供对(<aop:config>)声明式AOP的支持,AnnotationAwareAspectJAutoProxyCreator提供对(<aop:aspectj-autoproxy>)注解式(@AspectJ)AOP的支持,因此只需要分析AbstractAutoProxyCreator即可。
当使用<aop:config>配置时自动注册AspectJAwareAdvisorAutoProxyCreator,而使用<aop:aspectj-autoproxy>时会自动注册AnnotationAwareAspectJAutoProxyCreator。
6.1、predictBeanType:预测Bean的类型,如果目标对象被AOP代理对象包装,此处将返回AOP代理对象的类型;
- public Class<?> predictBeanType(Class<?> beanClass, String beanName) {
- Object cacheKey = getCacheKey(beanClass, beanName);
- return this.proxyTypes.get(cacheKey); //获取代理对象类型,可能返回null
- }
6.2、postProcessBeforeInstantiation:
- public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
- //1、得到一个缓存的唯一key(根据beanClass和beanName生成唯一key)
- Object cacheKey = getCacheKey(beanClass, beanName);
- //2、如果当前targetSourcedBeans(通过自定义TargetSourceCreator创建的TargetSource)不包含cacheKey
- if (!this.targetSourcedBeans.contains(cacheKey)) {
- //2.1、advisedBeans(已经被增强的Bean,即AOP代理对象)中包含当前cacheKey或nonAdvisedBeans(不应该被增强的Bean)中包含当前cacheKey 返回null,即走Spring默认流程
- if (this.advisedBeans.contains(cacheKey) || this.nonAdvisedBeans.contains(cacheKey)) {
- return null;
- }
- //2.2、如果是基础设施类(如Advisor、Advice、AopInfrastructureBean的实现)不进行处理
- //2.2、shouldSkip 默认false,可以生成子类覆盖,如AspectJAwareAdvisorAutoProxyCreator覆盖 (if (((AbstractAspectJAdvice) advisor.getAdvice()).getAspectName().equals(beanName)) return true; 即如果是自己就跳过)
- if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
- this.nonAdvisedBeans.add(cacheKey);//在不能增强的Bean列表缓存当前cacheKey
- return null;
- }
- }
- //3、开始创建AOP代理对象
- //3.1、配置自定义的TargetSourceCreator进行TargetSource创建
- TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
- if (targetSource != null) {
- //3.2、如果targetSource不为null 添加到targetSourcedBeans缓存,并创建AOP代理对象
- this.targetSourcedBeans.add(beanName);
- // specificInterceptors即增强(包括前置增强、后置增强等等)
- Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
- //3.3、创建代理对象
- Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
- //3.4、将代理类型放入proxyTypes从而允许后续的predictBeanType()调用获取
- this.proxyTypes.put(cacheKey, proxy.getClass());
- return proxy;
- }
- return null;
- }
从如上代码可以看出,当我们配置TargetSourceCreator进行自定义TargetSource创建时,会创建代理对象并中断默认Spring创建流程。
6.3、getEarlyBeanReference
- //获取early Bean引用(只有单例Bean才能回调该方法)
- public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
- Object cacheKey = getCacheKey(bean.getClass(), beanName);
- //1、将cacheKey添加到earlyProxyReferences缓存,从而避免多次重复创建
- this.earlyProxyReferences.add(cacheKey);
- //2、包装目标对象到AOP代理对象(如果需要)
- return wrapIfNecessary(bean, beanName, cacheKey);
- }
6.4、postProcessAfterInitialization
- public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
- if (bean != null) {
- Object cacheKey = getCacheKey(bean.getClass(), beanName);
- //1、如果之前调用过getEarlyBeanReference获取包装目标对象到AOP代理对象(如果需要),则不再执行
- if (!this.earlyProxyReferences.contains(cacheKey)) {
- //2、包装目标对象到AOP代理对象(如果需要)
- return wrapIfNecessary(bean, beanName, cacheKey);
- }
- }
- return bean;
- }
- protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
- if (this.targetSourcedBeans.contains(beanName)) {//通过TargetSourceCreator进行自定义TargetSource不需要包装
- return bean;
- }
- if (this.nonAdvisedBeans.contains(cacheKey)) {//不应该被增强对象不需要包装
- return bean;
- }
- if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {//基础设施/应该skip的不需要保证
- this.nonAdvisedBeans.add(cacheKey);
- return bean;
- }
- // 如果有增强就执行包装目标对象到代理对象
- Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
- if (specificInterceptors != DO_NOT_PROXY) {
- this.advisedBeans.add(cacheKey);//将cacheKey添加到已经被增强列表,防止多次增强
- Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));//创建代理对象
- this.proxyTypes.put(cacheKey, proxy.getClass());//缓存代理类型
- return proxy;
- }
- this.nonAdvisedBeans.add(cacheKey);
- return bean;
- }
从如上流程可以看出 getEarlyBeanReference和postProcessAfterInitialization是二者选一的,而且单例Bean目标对象只能被增强一次,而原型Bean目标对象可能被包装多次。
7、BeanValidationPostProcessor
默认不自动注册,Spring3.0开始支持。
提供对JSR-303验证规范支持。
根据afterInitialization是false/true决定调用postProcessBeforeInitialization或postProcessAfterInitialization来通过JSR-303规范验证Bean,默认false。
8、MethodValidationPostProcessor
Spring3.1开始支持,且只支持Hibernate Validator 4.2及更高版本,从Spring 3.2起可能将采取自动检测Bean Validation 1.1兼容的提供商且自动注册(Bean Validation 1.1 (JSR-349)正处于草案阶段,它将提供方法级别的验证,提供对方法级别的验证),目前默认不自动注册。
Bean Validation 1.1草案请参考http://jcp.org/en/jsr/detail?id=349 http://beanvalidation.org/。
提供对方法参数/方法返回值的进行验证(即前置条件/后置条件的支持),通过JSR-303注解验证,使用方式如:
public @NotNull Object myValidMethod(@NotNull String arg1, @Max(10) int arg2)
默认只对@org.springframework.validation.annotation.Validated注解的Bean进行验证,我们可以修改validatedAnnotationType为其他注解类型来支持其他注解验证。而且目前只支持Hibernate Validator实现,在未来版本可能支持其他实现。
有了这东西之后我们就不需要在进行如Assert.assertNotNull()这种前置条件/后置条件的判断了。
9、ScheduledAnnotationBeanPostProcessor
当配置文件中有<task:annotation-driven>自动注册或@EnableScheduling自动注册。
提供对注解@Scheduled任务调度的支持。
postProcessAfterInitialization:通过查找Bean对象类上的@Scheduled注解来创建ScheduledMethodRunnable对象并注册任务调度方法(仅返回值为void且方法是无形式参数的才可以)。
可参考Spring官方文档的任务调度章节学习@Scheduled注解任务调度。
10、AsyncAnnotationBeanPostProcessor
当配置文件中有<task:annotation-driven>自动注册或@EnableAsync自动注册。
提供对@ Async和EJB3.1的@javax.ejb.Asynchronous注解的异步调用支持。
postProcessAfterInitialization:通过ProxyFactory创建目标对象的代理对象,默认使用AsyncAnnotationAdvisor(内部使用AsyncExecutionInterceptor 通过AsyncTaskExecutor(继承TaskExecutor)通过submit提交异步任务)。
可参考Spring官方文档的异步调用章节学习@Async注解异步调用。
11、ServletContextAwareProcessor
在使用Web容器时自动注册。
类似于ApplicationContextAwareProcessor,当你的Bean 实现了ServletContextAware/ ServletConfigAware会自动调用回调方法注入ServletContext/ ServletConfig。
===================================================================
===================================================================
七、BeanPostProcessor如何注册
1、如ApplicationContextAwareProcessor会在ApplicationContext容器启动时自动注册,而CommonAnnotationBeanPostProcessor和AutowiredAnnotationBeanPostProcessor会在当你使用<context:annotation-config>或<context:component-scan>配置时自动注册。
2、只要将BeanPostProcessor注册到容器中,Spring会在启动时自动获取并注册。
===================================================================
===================================================================
八、BeanPostProcessor的执行顺序
1、如果使用BeanFactory实现,非ApplicationContext实现,BeanPostProcessor执行顺序就是添加顺序。
2、如果使用的是AbstractApplicationContext(实现了ApplicationContext)的实现,则通过如下规则指定顺序。
2.1、PriorityOrdered(继承了Ordered),实现了该接口的BeanPostProcessor会在第一个顺序注册,标识高优先级顺序,即比实现Ordered的具有更高的优先级;
2.2、Ordered,实现了该接口的BeanPostProcessor会第二个顺序注册;
int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;//最高优先级
int LOWEST_PRECEDENCE = Integer.MAX_VALUE;//最低优先级
即数字越小优先级越高,数字越大优先级越低,如0(高优先级)——1000(低优先级)
2.3、无序的,没有实现Ordered/ PriorityOrdered的会在第三个顺序注册;
2.4、内部Bean后处理器,实现了MergedBeanDefinitionPostProcessor接口的是内部Bean PostProcessor,将在最后且无序注册。
3、接下来我们看看内置的BeanPostProcessor执行顺序
//1注册实现了PriorityOrdered接口的BeanPostProcessor
//2注册实现了Ordered接口的BeanPostProcessor
AbstractAutoProxyCreator 实现了Ordered,order = Ordered.LOWEST_PRECEDENCE
MethodValidationPostProcessor 实现了Ordered,LOWEST_PRECEDENCE
ScheduledAnnotationBeanPostProcessor 实现了Ordered,LOWEST_PRECEDENCE
AsyncAnnotationBeanPostProcessor 实现了Ordered,order = Ordered.LOWEST_PRECEDENCE
//3注册无实现任何接口的BeanPostProcessor
BeanValidationPostProcessor 无序
ApplicationContextAwareProcessor 无序
ServletContextAwareProcessor 无序
//3 注册实现了MergedBeanDefinitionPostProcessor接口的BeanPostProcessor,且按照实现了Ordered的顺序进行注册,没有实现Ordered的默认为Ordered.LOWEST_PRECEDENCE。
PersistenceAnnotationBeanPostProcessor 实现了PriorityOrdered,Ordered.LOWEST_PRECEDENCE - 4
AutowiredAnnotationBeanPostProcessor 实现了PriorityOrdered,order = Ordered.LOWEST_PRECEDENCE - 2
RequiredAnnotationBeanPostProcessor 实现了PriorityOrdered,order = Ordered.LOWEST_PRECEDENCE - 1
CommonAnnotationBeanPostProcessor 实现了PriorityOrdered,Ordered.LOWEST_PRECEDENCE
从上到下顺序执行,如果order相同则我们应该认为同序(谁先执行不确定,其执行顺序根据注册顺序决定)。
===================================================================
===================================================================
九、完成Spring事务处理时自我调用的解决方案及一些实现方式的分析分析
场景请先参考请参考Spring事务处理时自我调用的解决方案及一些实现方式的风险中的3.3、通过BeanPostProcessor 在目标对象中注入代理对象。
分析:
问题出现在5和9处:
5、使用步骤1处注册的SingletonFactory(ObjectFactory.getObject() 使用AnnotationAwareAspectJAutoProxyCreator的getEarlyBeanReference获取循环引用Bean),因此此处将返回A目标对象的代理对象;
9、此处调用AnnotationAwareAspectJAutoProxyCreator的postProcessAfterInitialization,但发现之前调用过AnnotationAwareAspectJAutoProxyCreator的getEarlyBeanReference获取代理对象,此处不再创建代理对象,而是直接返回目标对象,因此使用InjectBeanSelfProcessor不能注入代理对象;但此时的Spring容器中的A已经是代理对象了,因此我使用了从上下文重新获取A代理对象的方式注入(context.getBean(beanName))。
此处的getEarlyBeanReference和postProcessAfterInitialization为什么是二者选一的请参考之前介绍的AbstractAutoProxyCreator。
到此问题我们分析完毕,实际项目中的循环依赖应该尽量避免,这违反了“无环依赖原则”。
BeanPostProcessor接口的更多相关文章
- Spring 的 BeanPostProcessor接口实现
今天学习了一下Spring的BeanPostProcessor接口,该接口作用是:如果我们需要在Spring容器完成Bean的实例化,配置和其他的初始化后添加一些自己的逻辑处理,我们就可以定义一个或者 ...
- Spring - BeanPostProcessor接口(后处理器)讲解
概述: BeanPostProcessor接口是众多Spring提供给开发者的bean生命周期内自定义逻辑拓展接口中的一个,其他还有类似InitializingBean,DisposableBean, ...
- 【spring源码学习】Spring的IOC容器之BeanPostProcessor接口学习
一:含义作用 ==>BeanPostProcessor接口是众多Spring提供给开发者的bean生命周期内自定义逻辑拓展接口中的一个 二:接口定义 package org.springfram ...
- Spring实例化bean之后的处理, 关于BeanPostProcessor接口的使用
业务需求:缓存页面,展示需要缓存的所有对象,每类对象在字典表中有编码对应,点击某个对象可以缓存某类对象,每类对象都有自己的缓存runner(弱弱的说一句,本人看到这里的第一反应就是if-else,捂脸 ...
- spring4.1.8扩展实战之七:控制bean(BeanPostProcessor接口)
本章是<spring4.1.8扩展实战>的第七篇,我们来尝试在容器初始化的时候对bean实例做设置: 原文地址:https://blog.csdn.net/boling_cavalry/a ...
- 谈谈Spring中的BeanPostProcessor接口
一.前言 这几天正在复习Spring的相关内容,在了解bean的生命周期的时候,发现其中涉及到一个特殊的接口--BeanPostProcessor接口.由于网上没有找到比较好的博客,所有最后花了好 ...
- spring源码分析——BeanPostProcessor接口
BeanPostProcessor是处理bean的后置接口,beanDefinitionMaps中的BeanDefinition实例化完成后,完成populateBean,属性设置,完成 初始化后,这 ...
- Java框架spring 学习笔记(四):BeanPostProcessor接口
如果我们需要在Spring容器完成Bean的实例化,配置和其他的初始化前后后添加一些自己的逻辑处理. 编写InitHelloWorld.java package com.example.spring; ...
- spring源码分析系列 (2) spring拓展接口BeanPostProcessor
Spring更多分析--spring源码分析系列 主要分析内容: 一.BeanPostProcessor简述与demo示例 二.BeanPostProcessor源码分析:注册时机和触发点 (源码基于 ...
随机推荐
- 经典的兔子生兔子问题(C#递归解法)
古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? 思路:先求出每个月新增的兔子,再用循环求和即可算出这个月 ...
- Xamarin.Android RelativeLayout
初次接触Xamarin.Android. 由于国内Xamarin的资料少见,我大多参考JAVA原生代码,慢慢摸索过来. 我把摸索出来的结果广而告之,希望后来人能少走一点弯路,也希望你也能做出一份贡献. ...
- .net图表之ECharts随笔08-bar柱状图
之前一直都是跟着修改demo,感觉用得很吃力,现在结合上配置手册就好很多了,其实说到底就是参数的配置,所以配置手册尤为重要. 当然,这其中还是很多坑,希望可以找到对应的解决方案吧!!! 1. tool ...
- 知物由学 | AI网络安全实战:生成对抗网络
本文由 网易云发布. “知物由学”是网易云易盾打造的一个品牌栏目,词语出自汉·王充<论衡·实知>.人,能力有高下之分,学习才知道事物的道理,而后才有智慧,不去求问就不会知道.“知物由学” ...
- 基于GeoServer切片地图服务的发布
接着上一篇文章,如何将JPG格式的图片转化为带地理坐标的TIFF格式里提及的最近的一个项目,数据源是一张高分辨率的2.5维图片,现在已经成功转化成了带有地理坐标的TIFF格式.下面将介绍借助GeoSe ...
- ryu 下发流表配置
资料链接:http://ryu.readthedocs.io/en/latest/app/ofctl_rest.html 运行ryu进程: ryu-manager --ofp-tcp-listen-p ...
- 如何成功导入SlidingMenu库?
SlidingMenu是一个开源的侧滑菜单(https://github.com/jfeinstein10/SlidingMenu). 为大家的安卓程序提供侧滑菜单,这个功能也非常有用. 要想正常使 ...
- C指针 【温故】
概念 1 指针也是一个变量,做为指针变量的值是另一个变量的地址.指针存放的内容是一个地址,该地址指向一块内存空间 其一般形式为: 类型说明符 *变量名: 其中,*表示这是一个指针变量,变量名即为定义的 ...
- 【OCP|052】OCP最新题库解析系列-2
2.Which two are true about Optimizer Statistics? ❑ A) They do not persist across Instance restarts. ...
- 【timeisprecious】【JavaScript 】JavaScript对象
JavaScript 对象 var a=[];console.log(new Boolean(a)); VM1319: Boolean {true} undefined var a=[];consol ...