ApplicationContext体系

ApplicationContext是Spring容器针对应用层开放的接口。

相对于BeanFactory体系,仅仅起着容器的作用,需要借助AnnatatedBeanDefinitionReader手动注册类对象,或者ClassPathBeanDefinitionScanner扫描指定路径,才能完成Spring容器的初始化。

ApplicationContext体系直接将AnnatatedBeanDefinitionReaderClassPathBeanDefinitionScanner作为自己的成员变量,集成了读取依赖配置和注册BeanDefinition的功能。此外,还提供了许多增强的功能,比如触发BeanFactoryPostProcessor回调,自动实例化单例bean等。

ApplicationContext体系十分复杂,但是它的核心实现类只有AnnotationConfigApplicationContextAnnotationConfigServletWebServerApplicationContext。我们只需要重点学习这两个实现类,就能深刻理解ApplicationContext的实现原理。

1 AnnotationConfigApplicationContext

AnnotationConfigApplicationContext的核心成员变量:

  • beanFactoryDefaultListableBeanFactory对象,Spring IoC容器。
  • readerAnnotatedBeanDefinitionReader对象,用于将指定类对象注册成bean
  • scannerClassPathBeanDefiniionScanner对象,用于将指定路径下的类对象注册成bean
  • environment:运行时环境。
  • beanFactoryPostProcessorsBeanFactoryPostPr4ocessor缓存。

AnnotationConfigApplicationContext的基本使用:

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();  
context.register(AppConfig.class);  
context.scan("applicationcontext");  
context.refresh();  
AppConfig bean = context.getBean(AppConfig.class);  
System.out.println(bean);

1.1 构造函数

使用默认无参构造函数,会初始化AnnotatedBeanDefinitionReaderClassPathBeanDefinitionScanner对象,用于读取依赖配置:

public AnnotationConfigApplicationContext() {  
   this.reader = new AnnotatedBeanDefinitionReader(this);  
   this.scanner = new ClassPathBeanDefinitionScanner(this);  
}

在父类GenericApplicationContext的默认无参构造函数中,会初始化beanFactory

public GenericApplicationContext() {  
   this.beanFactory = new DefaultListableBeanFactory();  
}

在父类AbstractApplicationContext的默认无参构造函数中,会初始化resourcePatternResolver

public AbstractApplicationContext() {  
   this.resourcePatternResolver = getResourcePatternResolver();  
}

1.2 register和scan

AnnotationConfigApplicationContext#register()会调用内部的AnnotatedBeanDefinitionReader对象的register()方法,读取指定类对象,封装成BeanDefinition,然后注册到BeanFactory中:

public void register(Class<?>... componentClasses) { 
   this.reader.register(componentClasses);  
}

AnnotationConfigApplicationContext#scan()会调用内部的ClassPathBeanDefinitionScanner对象的scan()方法,扫描指定路径下的@Component类,封装成BeanDefinition,然后注册到BeanFactory中:

public void scan(String... basePackages) {
   this.scanner.scan(basePackages);
}

DefaultListableBeanFacoty一样,GenericApplicationContext也实现了BeanDefinitionRegistry接口,它会将BeanDefinition注册到内部持有的beanFactoryDefaultListableBeanFactory对象)中:

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)  
      throws BeanDefinitionStoreException {  
   this.beanFactory.registerBeanDefinition(beanName, beanDefinition);  
}

1.3 refresh

AbstractApplicationContext#refresh()方法会对容器进行一个初始化操作:

  1. 初始化标准上下文的基础配置:BeanFactoryPostProcessor等。
  2. 注册容器特定的BeanFactoryPostProcessor
  3. 触发BeanFactoryPostProcessor的回调。
  4. 注册BeanPostProcessor
  5. 初始化MessageSource
  6. 初始化ApplicationEventMulticaster
  7. 初始化容器特定的bean
  8. 注册listeners
  9. 实例化所有单例bean(non-lazy-init) 。
  10. 清除context-level资源缓存。
  11. 初始化LifecycleProcessor
  12. 触发LifecycleProcessor#onRefresh()方法。
  13. 发布ContextRefreshedEvent事件。
  14. 清除占用的资源。

AbstractApplicationContext#refresh()

public void refresh() throws BeansException, IllegalStateException {  
   synchronized (this.startupShutdownMonitor) {
      // Prepare this context for refreshing.  
      prepareRefresh();  
  
      // Tell the subclass to refresh the internal bean factory.  
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();  
  
      /*
      * 初始化标准applicationContext的基础配置:
      * 1、上下文的ClassLoader
      * 2、post-processor:ApplicationContextAwareProcessor、ApplicationListenerDetector等
      * 3、注册单例对象:environment、systemProperties、systemEnvironment、applicationStartup
      */
      prepareBeanFactory(beanFactory);  
  
      try {  
         // 不同子类会添加特定的BeanFactoryPostProcessor
         postProcessBeanFactory(beanFactory);  

         // 触发BeanFactoryPostProcessor的回调方法
         invokeBeanFactoryPostProcessors(beanFactory);  
  
         // 注册BeanPostProcessor
         registerBeanPostProcessors(beanFactory);  
  
         // 初始化MessageSource
         initMessageSource();  
  
         // 初始化ApplicationEventMulticaster
         initApplicationEventMulticaster();  
  
         // 不同子类会初始化特定的bean
         onRefresh();  
  
         // 注册ApplicationListener类型的bean为listeners
         registerListeners();  
  
         // 实例化所有单例bean (non-lazy-init) 
         finishBeanFactoryInitialization(beanFactory);  
  
         /*
         * 1、清除context-level资源缓存
         * 2、初始化LifecycleProcessor
         * 3、触发LifecycleProcessor的onRefresh()方法
         * 4、发布ContextRefreshedEvent事件
         */
         finishRefresh();  
      }  
  
      catch (BeansException ex) {  
         // 销毁已创建的所有单例对象,避免资源浪费
         destroyBeans();  
  
         // Reset 'active' flag.  
         cancelRefresh(ex);  
  
         // Propagate exception to caller.  
         throw ex;  
      }  
  
      finally {  
         // Reset common introspection caches in Spring's core, since we  
         // might not ever need metadata for singleton beans anymore...         
         resetCommonCaches();  
      }  
   }  
}

1.4 getBean

AbstractApplicationContext#getBean()会交给内部的beanFactory去执行:

public Object getBean(String name) throws BeansException {  
   assertBeanFactoryActive();  
   return getBeanFactory().getBean(name);  
}

2 AnnotationConfigServletWebServerApplicationContext

AnnotationConfigServletWebServerApplicationContext的成员变量与AnnotationConfigApplicationContext差不多,只是多了web相关的信息:

  • servletContext
  • webServet
  • servletConfig

并且此时会缓存待注册的类对象和扫描路径,在refresh()方法中才会进行实际注册:

  • annotatedClasses:待注册类对象。
  • basePackages:待扫描路径。

AnnotationConfigServletWebServerApplicationContext的使用主要是在Spring Boot项目中,AnnotationConfigServletWebServerApplicationContext.Factory#create()

public ConfigurableApplicationContext create(WebApplicationType webApplicationType) {  
   return (webApplicationType != WebApplicationType.SERVLET) ? null  
         : new AnnotationConfigServletWebServerApplicationContext();  
}

虽然经过了多层封装,但是接下来还是会依次调用以下方法进行容器初始化:

  1. register()scan():注册BeanDefinition
  2. refresh():初始化容器。

2.1 构造函数

使用默认无参构造函数,会初始化AnnotatedBeanDefinitionReaderClassPathBeanDefinitionScanner对象,用于读取依赖配置:

public AnnotationConfigServletWebServerApplicationContext() {  
   this.reader = new AnnotatedBeanDefinitionReader(this);  
   this.scanner = new ClassPathBeanDefinitionScanner(this);  
}

在父类GenericApplicationContext的默认无参构造函数中,会初始化beanFactory

public GenericApplicationContext() {  
   this.beanFactory = new DefaultListableBeanFactory();  
}

在父类AbstractApplicationContext的默认无参构造函数中,会初始化resourcePatternResolver

public AbstractApplicationContext() {  
   this.resourcePatternResolver = getResourcePatternResolver();  
}

2.2 register和scan

AnnotationConfigServletWebServerApplicationContext#register()方法会缓存待注册的类对象:

public final void register(Class<?>... annotatedClasses) {  
   this.annotatedClasses.addAll(Arrays.asList(annotatedClasses));  
}

AnnotationConfigServletWebServerApplicationContext#scan()方法会缓存待扫描的路径:

public final void scan(String... basePackages) {
   this.basePackages = basePackages;  
}

在后续refresh阶段的AnnotationConfigServletWebServerApplicationContext#postProcessBeanFactory()方法中,才会进行实际注册BeanDefinition

protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {  
   super.postProcessBeanFactory(beanFactory);  
   if (this.basePackages != null && this.basePackages.length > 0) {  
      this.scanner.scan(this.basePackages);  
   }  
   if (!this.annotatedClasses.isEmpty()) {  
      this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));  
   }  
}

2.3 refresh

ServletWebServerApplicationContext#refresh()方法实际上会调用到AbstractApplicationContext#refresh()方法:

public final void refresh() throws BeansException, IllegalStateException {  
   try {  
      super.refresh();  
   }  
   catch (RuntimeException ex) {  
      WebServer webServer = this.webServer;  
      if (webServer != null) {  
         webServer.stop();  
      }  
      throw ex;  
   }  
}

需要注意的是,此时调用postProcessBeanFactory()方法时,会进行实际注册BeanDefinition操作。

2.4 getBean

AbstractApplicationContext#getBean()会交给内部的beanFactory去执行:

public Object getBean(String name) throws BeansException {  
   assertBeanFactoryActive();  
   return getBeanFactory().getBean(name);  
}
本文转载于网络 如有侵权请联系删除

相关文章

  • 「电子鼻」鉴别威士忌准确率高达96%,网友:给茅台也整一个

    Alex发自凹非寺 量子位|公众号QbitAI国内的茅台和国外的一些高端威士忌都售价不菲,但也是被造假的重要目标。在没有鉴酒师的情况下,普通人怎么快速判别出酒的品质和真伪?最近,就有这么一群工程师研发出了一种名为NOS.E的“电子鼻”,专门用来闻酒的那种。它能在不到4分钟的时间内,“闻”出不同的威士忌风格、品牌和产地,为鉴酒打开了新思路。为什么是靠“闻”而不是“品尝”?实际上,威士忌的味道、气味、质地和颜色等特征,都能对其评估提供有效信息。其中,气味是影响酒味道的主要因素,研究者以此为主要突破口展开设计出NOS.E。在2019年澳大利亚CEBIT贸易展上,他们用NOS.E对六种威士忌进行了测试:其中,地区准确率达100%,品牌名称准确率为96.15%,风格准确率为92.31%。虽然它叫“电子鼻”,但并不是真的长得像鼻子哦!今年4月,研究成果论文发表在IEEE旗下的IEEESensors期刊上。看到这个消息,有网友激动地表示:终于可以鉴别假酒了!还有网友调侃,应该给茅台也开发一款。所以,这个精巧实用的鉴酒工具是怎么工作的?它真的靠谱吗?测试前的样品预处理此前在CeBIT的贸易展上,NOS

  • Python 按行读取文本文件 缓存 和 非缓存实现

    需求最近项目中有个读取文件的需求,数据量还挺大,10万行的数量级。java使用缓存读取文件是,会相应的创建一个内部缓冲区数组在java虚拟机内存中,因此每次处理的就是这一整块内存。简单的想:就是如果不用缓存,每次都要硬盘–虚拟机缓存–读取;有了缓存,提前读了一段放在虚拟机缓存里,可以避免频繁将硬盘上的数据读到缓存里。因为对内存的操作肯定是比硬盘的操作要快的。对了,java还有映射内存,可以解决大文件读写的问题。思路大文件读写不能一次全部读入内存,这样会导致耗尽内存。(但是在内存允许的情况下,全部读入内存是不是速度更快??) 对于大文件可以一行一行读取,因为我们处理完这行,就可以把它抛弃。我们也可以一段一段读取大文件,实现一种缓存处理。每次读取一段文件,将这段文件放在缓存里,然后对这段处理。这会比一行一行快些。方法1:一行一行读取我们可以打开一个文件,然后用for循环读取每行,比如:defmethod1(newName): s1=time.clock() oldLine='0' count=0 forlineinopen(newName): newLine=line

  • 项目之热点问题和问答列表(9)

    36.热点问题-持久层先创建封装数据的VO类:@Data publicclassQuestionListItemVO{ privateIntegerid; privateStringtitle; privateIntegerstatus; privateIntegerhits; }复制在持久层接口QuestionMapper中添加抽象方法:@Repository publicinterfaceQuestionMapperextendsBaseMapper<Question>{ /** *查询点击量最多的问题的列表 * *@return点击量最多的问题的列表 */ List<QuestionListItemVO>findMostHits(); }复制并在QuestionMapper.xml中配置映射:<selectid="findMostHits" resultType="cn.tedu.straw.portal.vo.QuestionListItemVO"> SELECT id,title,status

  • .net core学习笔记,组件篇:服务的注册与发现(Consul)初篇

    1、什么是服务注册中心?在学习服务注册与发现时,我们要先搞明白到底什么是服务注册与发现。在这里我举一个生活中非常普遍的例子——网购来简单说明,网购在我们日常生活中已经是非常普遍了,其实网购中的(商家—菜鸟驿站—买家),就组成了一个非常简单的注册发现逻辑。在我们购买商品之后,需要拿到这件商品,如果是普通的点对点服务,商家直接将商品快递给买家,如果买家临时有事不在家,这个时候将会收货失败。当引入注册中心—菜鸟驿站之后,商家发货后只需要将商品发送给菜鸟驿站(服务注册),买家在合适的时间通过快递号或者扫码去菜鸟驿站拿取自己的商品(服务发现),在这个环节中,菜鸟驿站只负责商品的收与发,这样就构成了一个简单的服务发现逻辑。2、为什么要使用服务注册中心?1、解耦:服务消费者和服务提供者之间完全解耦。就如同上面的例子:买家不用去关心卖家到底发什么快递,只要我去快递驿站能拿到商品就OK。2、扩展:服务消费者和服务提供者增加和删除新的服务时,对于双方没有任何影响。比如:买家买了多个不同的商品,这时买家也只需要根据不同的取件凭据去菜鸟驿站拿取对应的商品就OK。3、不同的服务注册中心组件zookeeperZo

  • 百度Apollo源码学习之Bazel编译介绍

    image.png什么是BazelBazel是一个类似于Make的编译工具,是Google为其内部软件开发的特点量身定制的工具,如今Google使用它来构建内部大多数的软件。(怪不得看起来很像Android.bp语法 O(∩_∩)O)Google认为直接用Makefile构建软件速度太慢,结果不可靠,所以构建了一个新的工具叫做Bazel,Bazel的规则层级更高。image.png开始使用Bazel的编译是基于工作区(workspace)的概念。workspaceworkspace存放了所有源代码和Bazel编译输出文件的目录,也就是整个项目的根目录。workspace需要包含的必要文件:WORKSPACE文件,用于指定当前文件夹就是一个Bazel的工作区。所以WORKSPACE文件总是存在于项目的根目录下。BUILD文件,用于告诉Bazel怎么构建项目的不同部分。(如果工作区中的一个目录包含BUILD文件,那么它就是一个package)要指定一个目录为Bazel的工作区,就只要在该目录下创建一个空的WORKSPACE文件即可。但是在百度Apollo源码我们只能看到一个WORKSPAC

  • Tesseract文字训练,以及样本生成

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/haluoluo211/article/details/77776831 前面用做Tesseract做文字识别的时候,一般网上教程称使用jTessBoxEditor训练(最终我试验发现对于中文的图片文字识别而言训练基本没什么卵用)当然使用jTessBoxEditor训练新的文字还是可以的,当时我发现jTessBoxEditor训练基本的配置文件基本是文字以及文字的坐标于是我使用python脚本生成了对应的训练图片以及配置文件。先上个图: yahei_font2.box配置文件box的内容如下: 生成的配置文件如下: 下面贴出python代码,大致原理大家看下代码跑一下一个没什么问题:#-*-coding:utf-8-*- fromPILimportImage fromPILimportImageFont fromPILimportImageDraw importos CREATE_PATH='F:/img_test/create_train_image/'

  • “纽约客”的出行疼!为何大苹果的共享单车比出租还快?

    共享单车在中国,乃至全世界都掀起了一阵新出行热潮。数据侠ToddSchneider结合共享单车数据,揭示了大纽约公共交通的出行困境、现实与影响。▍纽约出租车与共享单车的出行大战在纽约,每天都有数以百万计的的人参加一场盛大的交通竞赛,希望在各种交通工具中选出一种能让他们最快到达目的地的方式。在汽车、地铁、公交车以及轮渡等纽约客们典型的出行方式中,我们并没有真正得出过哪一种交通方式更快的结论。本文作者曾经写过纽约出租车行驶和共享单车使用的文章,这让他想到这些数据包可以帮助人们更好的识别更快的交通方式,至少在纽约出租车和共享单车CitiBike这两者中我们可以得出有效的结论。作者使用的方法及有效发现都将在下文中充分解释。作者以一个居民区为计时点和出发地,并通过地图揭示到达另一个居民区的速度谁更快(黄色出租车vs蓝色共享单车CitiBike)。作者使用了2016年7月1日至2017年6月30日的纽约出租车和豪华轿车委员会(NYCTaxi&LimousineCommission)以及共享单车CitiBike的出行数据。▍高峰时段,50%的出租车出行慢过共享单车根据2016年7月至2017

  • 银行只是开始:区块链技术将改变这20个产业

    银行和支付并不是区块链技术改变的唯一行业,网络安全,音乐和汽车等行业也将被区块链改变。比特币作为一种去中心化的数字货币,其发展的重要基础在于区块链技术,尤其是能够安全,自动验证和记录大规模数字交易的公共分类账技术。现在,创业企业开始相信区块链技术将颠覆更多的产业。现在有越来越多的业务能够被去中心化的平台验证和处理,这种平台不需要中央监管机构的存在。以下产业正在被区块链技术颠覆。1、银行业银行的主要功能承担价值保管功能和价值转移中心,区块链—作为一种数字化,安全,防篡改的分类账—能够承担和银行同样的功能。实际上,瑞士银行UBS和英国银行巴克莱都在试验区块链技术,推进后台功能和结算功能。有些银行业内人士甚至认为区块链技术能够降低200亿的中间成本。银行是金融领域对区块链创业企业投资最多的。R3CEV公司,已经有50家银行参与进来开发定制化的区块链技术解决方案。一个名为ThoughtMachine的组织利用私有区块链技术和加密分类账开发出了VaultOS(上图),可以让各类银行提供安全的端对端的金融服务。2、支付和资金转账在最近的报告中,世界经济论坛认为去中心化的支付技术—比如比特币—能够改

  • Golang语言社区-【基础知识】切片

    Go编程切片是一种抽象了Go编程数组。由于Go编程数组允许您定义的变量,可容纳同类的几个数据项类型,但它不提供任何内置的方法来动态地增加它的大小或得到一个子数组自身。切片覆盖这一限制。它提供了数组所需的多种效用函数,被广泛应用在Go编程。定义切片要定义一个切片,你可以声明它作为一个数组时,不需要指定大小或使用make函数来创建。varnumbers[]int/*asliceofunspecifiedsize*/ /*numbers==[]int{0,0,0,0,0}*/ numbers=make([]int,5,5)/*asliceoflength5andcapacity5*/复制len()和cap()函数由于切片是一种抽象数组。它实际上使用数组作为底层structure.len()函数返回的元素呈现在cap()函数返回切片作为多少元素,它可以容纳的容量的切片。以下为例子来解释片的使用:当上述代码被编译和执行时,它产生了以下结果:len=3cap=5slice=[000]复制Nil切片如果一个切片,没有输入默认声明,它被初始化为为nil。其长度和容量都为零。下面是一个例子:子切片切片允

  • “同形异义字”钓鱼攻击,钉钉中招

    技术交流:allen.lan#hotmail.com(#>@)同形异义字钓鱼攻击号称“几乎无法检测”,是最狡猾的钓鱼攻击!这种攻击产生的原因是国际化域名IDNs(InternationalizedDomainNames)支持多语种域名,而其中一些非拉丁字符语种的字母与拉丁字符非常相似,字面看很难区分。关于同形异义字钓鱼攻击的相关技术,freebuf上之前已有文章介绍,这里就不再过多介绍这个技术,不清楚可以自行搜索.0×01腾讯、京东、支付宝、微博、淘宝已面临同形异义字钓鱼攻击真有这么多网站面临威胁?其实还不止,还有爱奇异、小米……目前发现的威胁都是通过西里尔字母来进行混淆上图是西里尔字母表,我们可以发现有不少字母与拉丁字母相识,这就是为什么用西里尔字母来进行混淆的原因浏览器会通过Punycode来编码非拉丁字符的域名,编码后就可以避免产生混淆,但发现如果域名的一个字段里所有字符都是同一种语言,就不会进行编码(之前freebuf上有篇文章可能是笔误,关于这点刚好说反了)。据说这个问题chrome已经修复了,并且google还给相关发现者2000美金的奖励。但我还是发现chrome有

  • 占位图怎么写[通俗易懂]

    想必大家对占位图都不会陌生吧,非常犀利的一个工具,当然也有非常多优秀的网站为我们提供这样的接口。 唯一遗憾的是国内的站点非常少。当然不是说国外的不行,正好相反,国外的那些占位图非常人性化,非常方便,唯一的缺陷就是有时候非常卡。 在百度搜索下占位图就可以找到N多的信息,当然,我也是参考了小影志博客《10个优秀的占位图片(PlaceholderImage)生成工具》 里面非常详细的介绍了各个占位图的功能和特点,最后还列出一张表格,非常直观的展现给我们。来看下这个来自悠着点的一款占位图工具吧。 其实他还是个短网址生成工具,还提供了各种调用接口,非常方便哦。 来看下占位图调用接口吧,其实和其他工具类似,但是功能没那么强。。http://usr.im/[width]x[height] http://usr.im/[width]x[height]?text=[自定义文字,暂不支持中文]没有自定义颜色比较遗憾,,也许以后会更新吧。文本 运行<imgsrc="http://usr.im/100x100"><imgsrc="http://usr.im/20

  • OAuth 2.1 框架

    OAuth2.1Draft 当前版本:v2-1-05 失效时间:2022/09/08 本文对部分原文翻译,同时加了一些笔记,以便理解。 单词 译意 identifiler 识别码 ResourceOwner 资源拥有者 User-Agent 用户代理 AuthorizationCode 授权码 AccessToken 访问令牌 refreshtoken 刷新令牌 scope 可选 endpoint 端点 AS 授权服务器 许可类型 要获取访问令牌,客户端需要从资源拥有者那里获得授权。本规约定义了以下几种授权许可类型。 授权码(authorizationcode) 客户端证书(clientcredentials) 刷新令牌(refreshtoken) 本规约还提供了扩展机制,以便定义其他许可类型。 授权码许可 授权码许可类型用于获取访问令牌和刷新令牌。 许可类型使用额外的授权端点,实现授权服务器与资源拥有者交互,以便获取资源访问准许。 由于这是一个基于重定向的工作流,客户端必须能够通过资源拥有者(比如某个用户)的用户代理(一般指we

  • C#使用迭代器显示公交车站点

        publicstaticIList<object>items=newList<object>();//定义一个泛型对象,用于存储对象 ///<summary> ///通过迭代器获取泛型中的所有对象值 ///</summary> ///<paramNode="n">泛型对象</param> ///<returns>IEnumerable<object></returns> publicstaticIEnumerable<object>GetValues() { if(items!=null)//如果泛型不为空 { foreach(objectiinitems)//遍历泛型中的对象 yieldreturni; } } privatevoidForm1_Load(objectsender,EventArgse) { //向泛型集合中添加站点数据 items.Add("长新东路"); items.Add("同康路"); items.Add("农

  • 数据库知识整理

    查询语句 1.查询班级男女生人数 selectcount(*)fromclassgroupbysex 2.从数据库中删除多条重复数据 如果是每个字段都重复: selectdistinct*into#TmpfromtableName droptabletableName select*intotableNamefrom#Tmp droptable#Tmp 如果是某几个字段重复: 1.select min(id)id,aafromtestGROUPBYaa 2.selectidfrom( SELECT   min(id)idFROM   testGROUPBY   aa)tempt 3.DELETE FROM test WHERE idNOTIN( SELECT id/,aa,count(aa)/ FROM ( SELECT min(id)id FROM test GROUPBY aa )t )   3.查询表中姓张的同学的信息 SELECT*FROMSTUDWHERE姓名LIKE

  • 检查你要加入到gradle的第三方library是否是最新版本

    开发者从博客、githubreadme 或者官方文档中找到如何在gradle文件中加入dependency 的时候,往往版本已经比较老旧了,想要找到最新版,介绍一个利器 http://gradleplease.appspot.com/ 这个在墙外,但是做Android开发的谁都会点穿墙术吧    可以通过google搜索gradleplease找到这个网站   该网站的源代码:https://github.com/broady/gradleplease     信息来源: AndroidImageGalleryUsingGlideandRecyclerView-YouTubehttps://www.youtube.com/watch?v=OVSe6bAnjgg

  • js 一些有意思的小Demo

  • 【原创】(四)Linux进程调度-组调度及带宽控制

    背景 Readthefuckingsourcecode!--By鲁迅 Apictureisworthathousandwords.--By高尔基 说明: Kernel版本:4.14 ARM64处理器,Contex-A53,双核 使用工具:SourceInsight3.5,Visio 1.概述 组调度(task_group)是使用Linuxcgroup(controlgroup)的cpu子系统来实现的,可以将进程进行分组,按组来分配CPU资源等。 比如,看一个实际的例子: A和B两个用户使用同一台机器,A用户16个进程,B用户2个进程,如果按照进程的个数来分配CPU资源,显然A用户会占据大量的CPU时间,这对于B用户是不公平的。组调度就可以解决这个问题,分别将A、B用户进程划分成组,并将两组的权重设置成占比50%即可。 带宽(bandwidth)控制,是用于控制用户组(task_group)的CPU带宽,通过设置每个用户组的限额值,可以调整CPU的调度分配。在给定周期内,当用户组消耗CPU的时间超过了限额值,该用户组内的任务将会受到限制。 由于组调度和带宽控制紧密联系,因此本文将

  • [算法学习] 同余最短路

    对于一些形如询问$$\sum_{i=1}^na_i\timesp_i=k$$的整数解或者有解的情况时,常常使用同余最短路的解法。 我们令\(dis_i\)记录凑出\(\bmodbase\)余i最小的数是多少 然后可以用\(\text{dijkstra}\)的方法转移 \(f((x+y)\bmodbase)=f(x)+y\) 墨墨的等式 constintN=12+5; constintM=5e5+7; intn,E,a[N],head[M]; lll,r,ans; structEDGE{ intw,to,nxt; }edge[M*12]; inlinevoidaddedge(intu,intv,intw){ edge[++E].to=v; edge[E].w=w; edge[E].nxt=head[u]; head[u]=E; } namespaceDIJKSTRA{ lldis[M]; structNode{ llu,d; Node(intU=0,llD=0){ u=U;d=D; } booloperator<(constNode&rhs

  • HTML5实现图片选择并预览

    <%@PageLanguage="C#"AutoEventWireup="true"CodeFile="Default.aspx.cs"Inherits="_Default"%> <!DOCTYPEhtml> <htmlxmlns="http://www.w3.org/1999/xhtml"> <headrunat="server"> <metahttp-equiv="Content-Type"content="text/html;charset=utf-8"/> <title></title> <scriptsrc="jquery-1.11.2.min.js"></script> <scripttype="text/javascript"> $(function(){ $(function(){ $("#fileupload").change(function(){ varreader=newFileReader(); reader.onload=functi

  • xcode中的.h和.m文件分别是什么意思?各有什么用?

    .h表示头文件,用来声明各种成员变量,方法,属性之类的。在import的时候用头文件。 .m主要用来实现.h里声明的方法。举个例子,如果要写一个方法,你要在.h里先声明:-(void)myMethod; 然后在.m里具体实现: -(void)myMethod{ NSLog(@"Thisismynewmethod"); }复制

  • 前端工作流程转变

    近期前端工作流程发生了一些变化如图 首先从工程的角度将之前的工作流程没有并行,同样的项目大概要用4周的时间,流程变更后,能节省1周的时间如果团队配合的好可以节省2周的时间. 先分析下之前的工作流程 需求方对需求发起 PM制定提供需求解决方案,给出产品原型,交付给UI设计师 UI设计师设计根据原型进行产品视觉设计 前端拿到UI稿之后开始页面制作,交互制作 后端工程师拿到前端的页面,开始套页面模版,与前端工程师沟通ajax接口。写后端功能。 后端与PM沟通验收,PM反馈前端后端问题,调整 交付测试工程师bug反馈调整 在这个过程中,很像传统的流水线。一个环节一个环节走。与流程变更后,看似只是多了一个流程但是实际上在工作过程中沟通浪费的时间还是挺多的,比如交付后端的时候,ui实现有变差,就要与前端工程师沟通,在开发阶段,发挥重要责任的是后端工程师,往往担负着很多沟通的责任。 流程优化: 需求方发起需求 Pm制定需求解决方案,确认产品原型 产品原型确定后,交付给UI设计师,于此同时,前端工程师和后端工程师开始定数据,接口。 UI稿确定后,前端工程师页面制作,套后端模版,调试数据和接口

相关推荐

推荐阅读