首页 文章 精选 留言 我的

精选列表

搜索[禅道],共6117篇文章
优秀的个人博客,低调大师

69Spring面试题和答案

目录 Spring 概述 依赖注入 Spring beans Spring注解 Spring数据访问 Spring面向切面编程(AOP) Spring MVC Spring概述 1.什么是spring? Spring是个java企业级应用的开源开发框架。Spring主要用来开发Java应用,但是有些扩展是针对构建J2EE平台的web应用。Spring框架目标是简化Java企业级应用开发,并通过POJO为基础的编程模型促进良好的编程习惯。 2.使用Spring框架的好处是什么? 轻量:Spring是轻量的,基本的版本大约2MB。 控制反转:Spring通过控制反转实现了松散耦合,对象们给出它们的依赖,而不是创建或查找依赖的对象们。 面向切面的编程(AOP):Spring支持面向切面的编程,并且把应用业务逻辑和系统服务分开。 容器:Spring包含并管理应用中对象的生命周期和配置。 MVC框架:Spring的WEB框架是个精心设计的框架,是Web框架的一个很好的替代品。 事务管理:Spring提供一个持续的事务管理接口,可以扩展到上至本地事务下至全局事务(JTA)。 异常处理:Spring提供方便的API把具体技术相关的异常(比如由JDBC,HibernateorJDO抛出的)转化为一致的unchecked异常。 3.Spring由哪些模块组成? 以下是Spring框架的基本模块: Coremodule Beanmodule Contextmodule ExpressionLanguagemodule JDBCmodule ORMmodule OXMmodule JavaMessagingService(JMS)module Transactionmodule Webmodule Web-Servletmodule Web-Strutsmodule Web-Portletmodule 4. 核心容器(应用上下文)模块。 这是基本的Spring模块,提供spring框架的基础功能,BeanFactory是任何以spring为基础的应用的核心。Spring框架建立在此模块之上,它使Spring成为一个容器。 5.BeanFactory–BeanFactory实现举例。 Bean工厂是工厂模式的一个实现,提供了控制反转功能,用来把应用的配置和依赖从正真的应用代码中分离。 最常用的BeanFactory实现是XmlBeanFactory类。 6.XMLBeanFactory 最常用的就是org.springframework.beans.factory.xml.XmlBeanFactory,它根据XML文件中的定义加载beans。该容器从XML文件读取配置元数据并用它去创建一个完全配置的系统或应用。 7.解释AOP模块 AOP模块用于发给我们的Spring应用做面向切面的开发,很多支持由AOP联盟提供,这样就确保了Spring和其他AOP框架的共通性。这个模块将元数据编程引入Spring。 8.解释JDBC抽象和DAO模块。 通过使用JDBC抽象和DAO模块,保证数据库代码的简洁,并能避免数据库资源错误关闭导致的问题,它在各种不同的数据库的错误信息之上,提供了一个统一的异常访问层。它还利用Spring的AOP模块给Spring应用中的对象提供事务管理服务。 9.解释对象/关系映射集成模块。 Spring通过提供ORM模块,支持我们在直接JDBC之上使用一个对象/关系映射映射(ORM)工具,Spring支持集成主流的ORM框架,如Hiberate,JDO和iBATISSQLMaps。Spring的事务管理同样支持以上所有ORM框架及JDBC。 10.解释WEB模块。 Spring的WEB模块是构建在applicationcontext模块基础之上,提供一个适合web应用的上下文。这个模块也包括支持多种面向web的任务,如透明地处理多个文件上传请求和程序级请求参数的绑定到你的业务对象。它也有对JakartaStruts的支持。 12.Spring配置文件 Spring配置文件是个XML文件,这个文件包含了类信息,描述了如何配置它们,以及如何相互调用。 13.什么是SpringIOC容器? SpringIOC负责创建对象,管理对象(通过依赖注入(DI),装配对象,配置对象,并且管理这些对象的整个生命周期。 14.IOC的优点是什么? IOC或依赖注入把应用的代码量降到最低。它使应用容易测试,单元测试不再需要单例和JNDI查找机制。最小的代价和最小的侵入性使松散耦合得以实现。IOC容器支持加载服务时的饿汉式初始化和懒加载。 15.ApplicationContext通常的实现是什么? FileSystemXmlApplicationContext:此容器从一个XML文件中加载beans的定义,XMLBean配置文件的全路径名必须提供给它的构造函数。 ClassPathXmlApplicationContext:此容器也从一个XML文件中加载beans的定义,这里,你需要正确设置classpath因为这个容器将在classpath里找bean配置。 WebXmlApplicationContext:此容器加载一个XML文件,此文件定义了一个WEB应用的所有bean。 16.Bean工厂和Applicationcontexts有什么区别? Applicationcontexts提供一种方法处理文本消息,一个通常的做法是加载文件资源(比如镜像),它们可以向注册为监听器的bean发布事件。另外,在容器或容器内的对象上执行的那些不得不由bean工厂以程序化方式处理的操作,可以在Applicationcontexts中以声明的方式处理。Applicationcontexts实现了MessageSource接口,该接口的实现以可插拔的方式提供获取本地化消息的方法。 17.一个Spring的应用看起来象什么? 一个定义了一些功能的接口。 这实现包括属性,它的Setter , getter方法和函数等。 SpringAOP。 Spring的XML配置文件。 使用以上功能的客户端程序。 依赖注入 18.什么是Spring的依赖注入? 依赖注入,是IOC的一个方面,是个通常的概念,它有多种解释。这概念是说你不用创建对象,而只需要描述它如何被创建。你不在代码里直接组装你的组件和服务,但是要在配置文件里描述哪些组件需要哪些服务,之后一个容器(IOC容器)负责把他们组装起来。 19.有哪些不同类型的IOC(依赖注入)方式? 构造器依赖注入:构造器依赖注入通过容器触发一个类的构造器来实现的,该类有一系列参数,每个参数代表一个对其他类的依赖。 Setter方法注入:Setter方法注入是容器通过调用无参构造器或无参static工厂方法实例化bean之后,调用该bean的setter方法,即实现了基于setter的依赖注入。 20.哪种依赖注入方式你建议使用,构造器注入,还是Setter方法注入? 你两种依赖方式都可以使用,构造器注入和Setter方法注入。最好的解决方案是用构造器参数实现强制依赖,setter方法实现可选依赖。 SpringBeans 21.什么是Springbeans? Springbeans是那些形成Spring应用的主干的java对象。它们被SpringIOC容器初始化,装配,和管理。这些beans通过容器中配置的元数据创建。比如,以XML文件中的形式定义。 Spring框架定义的beans都是单件beans。在beantag中有个属性”singleton”,如果它被赋为TRUE,bean就是单件,否则就是一个prototypebean。默认是TRUE,所以所有在Spring框架中的beans缺省都是单件。 22.一个SpringBean定义包含什么? 一个SpringBean的定义包含容器必知的所有配置元数据,包括如何创建一个bean,它的生命周期详情及它的依赖。 23.如何给Spring容器提供配置元数据? 这里有三种重要的方法给Spring容器提供配置元数据。 XML配置文件。 基于注解的配置。 基于java的配置。 24.你怎样定义类的作用域? 当定义一个在Spring里,我们还能给这个bean声明一个作用域。它可以通过bean定义中的scope属性来定义。如,当Spring要在需要的时候每次生产一个新的bean实例,bean的scope属性被指定为prototype。另一方面,一个bean每次使用的时候必须返回同一个实例,这个bean的scope属性必须设为singleton。 25.解释Spring支持的几种bean的作用域。 Spring框架支持以下五种bean的作用域: singleton:bean在每个Springioc容器中只有一个实例。 prototype:一个bean的定义可以有多个实例。 request:每次http请求都会创建一个bean,该作用域仅在基于web的SpringApplicationContext情形下有效。 session:在一个HTTPSession中,一个bean定义对应一个实例。该作用域仅在基于web的SpringApplicationContext情形下有效。 global-session:在一个全局的HTTPSession中,一个bean定义对应一个实例。该作用域仅在基于web的SpringApplicationContext情形下有效。 缺省的Springbean的作用域是Singleton. 26.Spring框架中的单例bean是线程安全的吗? 不,Spring框架中的单例bean不是线程安全的。 27.解释Spring框架中bean的生命周期。 Spring容器从XML文件中读取bean的定义,并实例化bean。 Spring根据bean的定义填充所有的属性。 如果bean实现了BeanNameAware接口,Spring传递bean的ID到setBeanName方法。 如果Bean实现了BeanFactoryAware接口,Spring传递beanfactory给setBeanFactory方法。 如果有任何与bean相关联的BeanPostProcessors,Spring会在postProcesserBeforeInitialization()方法内调用它们。 如果bean实现IntializingBean了,调用它的afterPropertySet方法,如果bean声明了初始化方法,调用此初始化方法。 如果有BeanPostProcessors和bean关联,这些bean的postProcessAfterInitialization()方法将被调用。 如果bean实现了DisposableBean,它将调用destroy()方法。 28.哪些是重要的bean生命周期方法?你能重载它们吗? 有两个重要的bean生命周期方法,第一个是setup,它是在容器加载bean的时候被调用。第二个方法是teardown它是在容器卸载类的时候被调用。 Thebean标签有两个重要的属性(init-method和destroy-method)。用它们你可以自己定制初始化和注销方法。它们也有相应的注解(@PostConstruct和@PreDestroy)。 29.什么是Spring的内部bean? 当一个bean仅被用作另一个bean的属性时,它能被声明为一个内部bean,为了定义innerbean,在Spring的基于XML的配置元数据中,可以在或元素内使用元素,内部bean通常是匿名的,它们的Scope一般是prototype。 30.在Spring中如何注入一个java集合? Spring提供以下几种集合的配置元素: 类型用于注入一列值,允许有相同的值。 类型用于注入一组值,不允许有相同的值。 类型用于注入一组键值对,键和值都可以为任意类型。 类型用于注入一组键值对,键和值都只能为String类型。 31.什么是bean装配? 装配,或bean装配是指在Spring容器中把bean组装到一起,前提是容器需要知道bean的依赖关系,如何通过依赖注入来把它们装配到一起。 32.什么是bean的自动装配? Spring容器能够自动装配相互合作的bean,这意味着容器不需要和配置,能通过Bean工厂自动处理bean之间的协作。 33.解释不同方式的自动装配。 有五种自动装配的方式,可以用来指导Spring容器用自动装配方式来进行依赖注入。 no:默认的方式是不进行自动装配,通过显式设置ref属性来进行装配。 byName:通过参数名自动装配,Spring容器在配置文件中发现bean的autowire属性被设置成byname,之后容器试图匹配、装配和该bean的属性具有相同名字的bean。 byType::通过参数类型自动装配,Spring容器在配置文件中发现bean的autowire属性被设置成byType,之后容器试图匹配、装配和该bean的属性具有相同类型的bean。如果有多个bean符合条件,则抛出错误。 constructor:这个方式类似于byType,但是要提供给构造器参数,如果没有确定的带参数的构造器参数类型,将会抛出异常。 autodetect:首先尝试使用constructor来自动装配,如果无法工作,则使用byType方式。 34.自动装配有哪些局限性? 自动装配的局限性是: 重写:你仍需用和配置来定义依赖,意味着总要重写自动装配。 基本数据类型:你不能自动装配简单的属性,如基本数据类型,String字符串,和类。 模糊特性:自动装配不如显式装配精确,如果有可能,建议使用显式装配。 35.你可以在Spring中注入一个null和一个空字符串吗? 可以。 Spring注解 36.什么是基于Java的Spring注解配置?给一些注解的例子. 基于Java的配置,允许你在少量的Java注解的帮助下,进行你的大部分Spring配置而非通过XML文件。 以@Configuration注解为例,它用来标记类可以当做一个bean的定义,被Spring IOC容器使用。另一个例子是@Bean注解,它表示此方法将要返回一个对象,作为一个bean注册进Spring应用上下文。 37.什么是基于注解的容器配置? 相对于XML文件,注解型的配置依赖于通过字节码元数据装配组件,而非尖括号的声明。 开发者通过在相应的类,方法或属性上使用注解的方式,直接组件类中进行配置,而不是使用xml表述bean的装配关系。 38.怎样开启注解装配? 注解装配在默认情况下是不开启的,为了使用注解装配,我们必须在Spring配置文件中配置元素。 39.@Required注解 这个注解表明bean的属性必须在配置的时候设置,通过一个bean定义的显式的属性值或通过自动装配,若@Required注解的bean属性未被设置,容器将抛出BeanInitializationException。 40.@Autowired注解 @Autowired注解提供了更细粒度的控制,包括在何处以及如何完成自动装配。它的用法和@Required一样,修饰setter方法、构造器、属性或者具有任意名称和/或多个参数的PN方法。 41.@Qualifier注解 当有多个相同类型的bean却只有一个需要自动装配时,将@Qualifier注解和@Autowire注解结合使用以消除这种混淆,指定需要装配的确切的bean。 Spring数据访问 42.在Spring框架中如何更有效地使用JDBC? 使用SpringJDBC框架,资源管理和错误处理的代价都会被减轻。所以开发者只需写statements和queries从数据存取数据,JDBC也可以在Spring框架提供的模板类的帮助下更有效地被使用,这个模板叫JdbcTemplate(例子见这里here) 43.JdbcTemplate JdbcTemplate类提供了很多便利的方法解决诸如把数据库数据转变成基本数据类型或对象,执行写好的或可调用的数据库操作语句,提供自定义的数据错误处理。 44.Spring对DAO的支持 Spring对数据访问对象(DAO)的支持旨在简化它和数据访问技术如JDBC,HibernateorJDO结合使用。这使我们可以方便切换持久层。编码时也不用担心会捕获每种技术特有的异常。 45. 使用Spring通过什么方式访问Hibernate? 在Spring中有两种方式访问Hibernate: 控制反转HibernateTemplate和Callback。 继承HibernateDAOSupport提供一个AOP拦截器。 46.Spring支持的ORM Spring支持以下ORM: Hibernate iBatis JPA(JavaPersistenceAPI) TopLink JDO(JavaDataObjects) OJB 47.如何通过HibernateDaoSupport将Spring和Hibernate结合起来? 用Spring的SessionFactory调用LocalSessionFactory。集成过程分三步: 配置theHibernateSessionFactory。 继承HibernateDaoSupport实现一个DAO。 在AOP支持的事务中装配。 48.Spring支持的事务管理类型 Spring支持两种类型的事务管理: 编程式事务管理:这意味你通过编程的方式管理事务,给你带来极大的灵活性,但是难维护。 声明式事务管理:这意味着你可以将业务代码和事务管理分离,你只需用注解和XML配置来管理事务。 49.Spring框架的事务管理有哪些优点? 它为不同的事务API如JTA,JDBC,Hibernate,JPA和JDO,提供一个不变的编程模式。 它为编程式事务管理提供了一套简单的API而不是一些复杂的事务API如 它支持声明式事务管理。 它和Spring各种数据访问抽象层很好得集成。 50.你更倾向用那种事务管理类型? 大多数Spring框架的用户选择声明式事务管理,因为它对应用代码的影响最小,因此更符合一个无侵入的轻量级容器的思想。声明式事务管理要优于编程式事务管理,虽然比编程式事务管理(这种方式允许你通过代码控制事务)少了一点灵活性。 Spring面向切面编程(AOP) 51.解释AOP 面向切面的编程,或AOP,是一种编程技术,允许程序模块化横向切割关注点,或横切典型的责任划分,如日志和事务管理。 52.Aspect切面 AOP核心就是切面,它将多个类的通用行为封装成可重用的模块,该模块含有一组API提供横切功能。比如,一个日志模块可以被称作日志的AOP切面。根据需求的不同,一个应用程序可以有若干切面。在SpringAOP中,切面通过带有@Aspect注解的类实现。 52.在SpringAOP中,关注点和横切关注的区别是什么? 关注点是应用中一个模块的行为,一个关注点可能会被定义成一个我们想实现的一个功能。 横切关注点是一个关注点,此关注点是整个应用都会使用的功能,并影响整个应用,比如日志,安全和数据传输,几乎应用的每个模块都需要的功能。因此这些都属于横切关注点。 54.连接点 连接点代表一个应用程序的某个位置,在这个位置我们可以插入一个AOP切面,它实际上是个应用程序执行SpringAOP的位置。 55.通知 通知是个在方法执行前或执行后要做的动作,实际上是程序执行时要通过SpringAOP框架触发的代码段。 Spring切面可以应用五种类型的通知: before:前置通知,在一个方法执行前被调用。 after:在方法执行之后调用的通知,无论方法执行是否成功。 after-returning:仅当方法成功完成后执行的通知。 after-throwing:在方法抛出异常退出时执行的通知。 around:在方法执行之前和之后调用的通知。 56.切点 切入点是一个或一组连接点,通知将在这些位置执行。可以通过表达式或匹配的方式指明切入点。 57.什么是引入? 引入允许我们在已存在的类中增加新的方法和属性。 58.什么是目标对象? 被一个或者多个切面所通知的对象。它通常是一个代理对象。也指被通知(advised)对象。 59.什么是代理? 代理是通知目标对象后创建的对象。从客户端的角度看,代理对象和目标对象是一样的。 60.有几种不同类型的自动代理? BeanNameAutoProxyCreator DefaultAdvisorAutoProxyCreator Metadataautoproxying 61.什么是织入。什么是织入应用的不同点? 织入是将切面和到其他应用类型或对象连接或创建一个被通知对象的过程。 织入可以在编译时,加载时,或运行时完成。 62.解释基于XMLSchema方式的切面实现。 在这种情况下,切面由常规类以及基于XML的配置实现。 63.解释基于注解的切面实现 在这种情况下(基于@AspectJ的实现),涉及到的切面声明的风格与带有java5标注的普通java类一致。 Spring的MVC 64.什么是Spring的MVC框架? Spring配备构建Web应用的全功能MVC框架。Spring可以很便捷地和其他MVC框架集成,如Struts,Spring的MVC框架用控制反转把业务对象和控制逻辑清晰地隔离。它也允许以声明的方式把请求参数和业务对象绑定。 65.DispatcherServlet Spring的MVC框架是围绕DispatcherServlet来设计的,它用来处理所有的HTTP请求和响应。 66.WebApplicationContext WebApplicationContext继承了ApplicationContext并增加了一些WEB应用必备的特有功能,它不同于一般的ApplicationContext,因为它能处理主题,并找到被关联的servlet。 67.什么是SpringMVC框架的控制器? 控制器提供一个访问应用程序的行为,此行为通常通过服务接口实现。控制器解析用户输入并将其转换为一个由视图呈现给用户的模型。Spring用一个非常抽象的方式实现了一个控制层,允许用户创建多种用途的控制器。 68.@Controller注解 该注解表明该类扮演控制器的角色,Spring不需要你继承任何其他控制器基类或引用ServletAPI。 69.@RequestMapping注解 该注解是用来映射一个URL到一个类或一个特定的方处理法上。 欢迎工作一到五年的Java工程师朋友们加入Java架构开发:744677563 本群提供免费的学习指导 架构资料 以及免费的解答 不懂得问题都可以在本群提出来 之后还会有职业生涯规划以及面试指导

优秀的个人博客,低调大师

SUSE: 大数据,正大行其

ZDNet至顶网服务器频道 07月04日 新闻消息: 作者:SUSE公司全球联盟和营销副总裁MichaelMiller 没错,我们都在谈论大数据。但考虑到与该市场相关的庞大数字,大数据能成为热门话题一点也不奇怪,对吧?IDC市场研究公司预测,到2017年大数据技术和服务消费的复合增长率为27%,达到320亿美元。考虑如此大的市场规模,我立即联想到合作伙伴关系与协作这两个问题。 要想把大量结构化数据和非结构化数据转化为有用的商业洞察,需要优化并综合运用计算能力、托管存储和智能分析。将三者转化为真实世界的解决方案最好借助合作伙伴关系、互操作性和协作。 以下是SUSE与合作伙伴为实现当今真实世界的优化解决方案而进行协作的一些方法。 SAP HANA SAP HANA是内存计算领域的黄金标准之一。 现在的HANA系统能在高度优化的SUSE Linux Enterprise上稳定、灵活而高性能地运行。 CIR food是真实世界的一个典型实例,当前它可在数秒钟内生成商业分析报告,包含关于成千上万名客户的数以百万计的数据点信息。利用开源,整个过程可节省70%。 Hadoop合作伙伴 Hadoop在开源大数据处理领域拥有最高的知名度——其合作伙伴如Cloudera和Hortonworks都是毋庸置疑的行业领导者。 通过与SUSE合作,这些Hadoop发行版可提供优化的性能和企业级的端到端支持。 欲了解有关设置、运行和调试Hadoop工作负载的详细内容,请阅读新版白皮书。 Teradata Teradata十多年来一直是大规模数据分析领域始终如一的领导者。 Teradata只在SUSE Linux Enterprise上可实现其全部的解决方案组合。点击此处了解更多。 世界上的数据密集型公司,如Netflix,依靠Teradata/SUSE合作伙伴关系进行全球数百万客户的复杂分析工作。 大数据出现的意义在于实现海量数据及信息处理的灵活性和性能,这些数据量和信息量在以前几乎是庞大到不可思议的事情。灵活性和性能是企业级Linux和开源技术的特色,这就解释了为什么在实现下一代数据管理、处理和分析的过程中,大数据行业领军企业都使用开源技术并与SUSE等开源公司建立合作伙伴关系。 在不久的将来可能发生的事情,有待我们深入研究。我们期待与开源社区与合作伙伴精诚合作,一起变未来为现实。 原文发布时间为:2014年07月04日 本文来自云栖社区合作伙伴至顶网,了解相关信息可以关注至顶网。

优秀的个人博客,低调大师

洗车题难倒各大 AI 模型

网友们又开始“戏耍”AI了,继「9.11 和 9.9 哪个大」「Strawberry(草莓)里究竟有几个 R」之后,各大 AI 厂商的主力模型,又集体栽进了一个新的逻辑黑洞。 题目如下: 我想洗车,我家距离洗车店只有 50 米,请问你推荐我走路去还是开车去呢? 各大 AI 的回复如下—— ChatGPT回答“走过去”,理由是“别把简单事情复杂化” 千问则表示“既然洗车店距离你家只有50米,强烈建议你走过去”,理由是距离短、节省资源、走路不需要找停车位。 DeepSeek给出了两种答案,建议步行的理由是“节约资源、避免排队、走路环保、避免挪车麻烦”,而建议开车的理由是“洗车后可以直接用车,开车排队人在车上更方便”。 谷歌的Gemini 3大模型给出的回答也包含了两种情况,一方面,Gemini 3建议开车过去,是因为“要洗的就是这辆车”,另一方面,Gemini 3建议走过去,因为“只是去预约或咨询”,或者“车在车库不方便动”。

优秀的个人博客,低调大师

明修"栈"——越过Android启动栈陷阱

作者:vivo 互联网大前端团队- Zhao Kaiping 本文从一例业务中遇到的问题出发,以FLAG_ACTIVITY_NEW_TASK这一flag作为切入点,带大家探究Activity启动前的一项重要的工作——栈校验。 文中列举一系列业务中可能遇到的异常状况,详细描述了使用FLAG_ACTIVITY_NEW_TASK时可能遇到的“坑”,并从源码中探究其根源。只有合理使用flag、launchMode,才能避免因为栈机制的特殊性,导致一系列与预期不符的启动问题。 一、问题及背景 应用间相互联动、相互跳转,是实现系统整体性、体验一致性的重要手段,也是最简单的一种方法。 当我们用最常用的方法去startActivity时,竟也会遇到失败的情况。在真实业务中,就遇到了这样一例异常:用户点击某个按钮时,想要“简简单单”跳转另一个应用,却没有任何反应。 经验丰富的你,脑海中是否涌现出了各种猜想:是不是目标Activity甚至目标App不存在?是不是目标Activty没有对外开放?是不是有权限的限制或者跳转的action/uri错了…… 真实的原因被flag、launchMode、Intent等特性层层藏匿,可能超出你此时的思考。 本文将从源码出发,探究前因后果,展开讲讲在startActivity()真正准备启动一个Activity前,需要经过哪些“磨难”,怎样有据可依地解决由栈问题导致的启动异常。 1.1 业务中遇到的问题 业务中的场景是这样的,存在A、B、C三个应用。 (1)从应用A-Activity1跳转至应用B-Activity2; (2)应用B-Activity2继续跳转到应用C-Activity3; (3)C内某个按钮,会再次跳转B-Activity2,但点击后没有任何反应。如果不经过前面A到B的跳转,C直接跳到B是可以的。 1.2 问题代码 3个Activity的Androidmanifest配置如下,均可通过各自的action拉起,launchMode均为标准模式。 <!--应用A--> <activity android:name=".Activity1" android:exported="true"> <intent-filter> <action android:name="com.zkp.task.ACTION_TO_A_PAGE1" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> <!--应用B--> <activity android:name=".Activity2" android:exported="true"> <intent-filter> <action android:name="com.zkp.task.ACTION_TO_B_PAGE2" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> <!--应用C--> <activity android:name=".Activity3" android:exported="true"> <intent-filter> <action android:name="com.zkp.task.ACTION_TO_C_PAGE3" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> A-1到B-2的代码,指定flag为 FLAG_ACTIVITY_NEW_TASK private void jumpTo_B_Activity2_ByAction_NewTask() { Intent intent = new Intent(); intent.setAction("com.zkp.task.ACTION_TO_B_PAGE2"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent);} B-2到C-3的代码,未指定flag private void jumpTo_C_Activity3_ByAction_NoTask() { Intent intent = new Intent(); intent.setAction("com.zkp.task.ACTION_TO_C_PAGE3"); startActivity(intent);} C-3到B-2的代码,与A-1到B-2的完全一致,指定flag为 FLAG_ACTIVITY_NEW_TASK private void jumpTo_B_Activity2_ByAction_NewTask() { Intent intent = new Intent(); intent.setAction("com.zkp.task.ACTION_TO_B_PAGE2"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent);} 1.3 代码初步分析 仔细查看问题代码,在实现上非常简单,有两个特征: (1)如果直接通过C-3跳B-2,没有任何问题,但A-1已经跳过B-2后,C-3就失败了。 (2)在A-1和C-3跳到B-2时,都设置了flag为FLAG_ACTIVITY_NEW_TASK。 依据经验,我们推测与栈有关,尝试将跳转前栈的状态打印出来,如下图。 由于A-1跳到B-2时设置了FLAG_ACTIVITY_NEW_TASK,B-2跳到C-3时未设置,所以1在独立栈中,2、3在另一个栈中。示意如下图。 C-3跳转B-2一般有3种可能的预期,如下图:预想1,新建一个Task,在新Task中启动一个B-2;预想2,复用已经存在的B-2;预想3,在已有Task中新建一个实例B-2。 但实际上3种预期都没有实现,所有Activity的任何声明周期都没有变化,界面始终停留在C-3。 看一下FLAG_ACTIVITY_NEW_TASK的官方注释和代码注释,如下图: 重点关注这一段: When using this flag, if a task is already running for the activity you are now starting, then a new activity will not be started; instead, the current task will simply be brought to the front of the screen with the state it was last in. 使用此flag时,如果你正在启动的Activity已经在一个Task中运行,那么一个新Activity不会被启动;相反,当前Task将简单地显示在界面的前面,并显示其最后的状态。 ——显然,官方文档与代码注释的表述与我们的异常现象是一致的,目标Activity2已经在Task中存在,则不会被启动;Task直接显示在前面,并展示最后的状态。由于目标Activty3就是来源Activity3,所以页面没有任何变化。 看起来官方还是很靠谱的,但实际效果真的能一直与官方描述一致吗?我们通过几个场景来看一下。 二、场景拓展与验证 2.1 场景拓展 在笔者依据官方描述进行调整、复现的过程中,发现了几个比较有意思的场景。 PS:上面业务的案例中,B-2和C-3在不同应用内,又在相同的Task内,但实际上是否是同一个应用,对结果的影响并不大。为了避免不同应用和不同Task造成阅读混乱,同一个栈的跳转,我们都在本应用内进行,故业务中的场景等价于下面的【场景0】 【场景0】把业务中B-2到C-3的应用间跳转改为B-2到B-3的应用内跳转 // B-2跳转B-3public static void jumpTo_B_3_ByAction_Null(Context context) { Intent intent = new Intent(); intent.setAction("com.zkp.task.ACTION_TO_B_PAGE3"); context.startActivity(intent);} 如下图,A-1设置NEW_TASK跳转B-2,再跳转B-3,最终设置NEW_TASK想跳转B-2。虽然跳C-3改为了跳B-3,但与之前问题的表现一致,没有反应,停留在B-3。 有的读者会指出这样的问题:如果同一个应用内使用NEW_TASK跳转,而不指定目标的taskAffinity属性,实际是无法在新Task中启动的。请大家忽略该问题,可以认为笔者的操作是已经加了taskAffinity的,这对最终结果并没有影响。 【场景1】如果目标Task和来源Task不是同一个,情况是否会如官方文档所说复用已有的Task并展示最近状态?我们改为B-3启动一个新Task的新Activity C-4,再通过C-4跳回B-2 // B-3跳转C-4public static void jumpTo_C_4_ByAction_New(Context context) { Intent intent = new Intent("com.zkp.task.ACTION_TO_C_PAGE4"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent);}// C-4跳转B-2public static void jumpTo_B_2_ByAction_New(Context context) { Intent intent = new Intent(); intent.setAction("com.zkp.task.ACTION_TO_B_PAGE2"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent);} 如下图,A-1设置NEW_TASK跳转B-2,再跳转B-3,再设置NEW_TASK跳转C-4,最终设置NEW_TASK想跳转B-2。 预想的结果是:不会跳到B-2,而是跳到它所在Task的顶层B-3。 实际的结果是:与预期一致,确实是跳到了B-3。 【场景2】把场景1稍做修改:C-4到B-2时,我们不通过action来跳,改为通过setClassName跳转。 // C-4跳转B-2public static void jumpTo_B_2_ByPath_New(Context context) { Intent intent = new Intent(); intent.setClassName("com.zkp.b", "com.zkp.b.Activity2"); // 直接设置classname,不通过action intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent);} 如下图,A-1设置NEW_TASK跳转B-2,再跳转B-3,再设置NEW_TASK跳转C-4,最终设置NEW_TASK想跳转B-2。 预想的结果是:与场景0一致,会跳到B-2所在Task的已有顶层B-3。 实际的结果是:在已有的Task2中,产生了一个新的B-2实例。 仅仅是改变了一下重新跳转B-2的方式,效果就完全不一样了!这与官方文档中提到该flag与"singleTask" launchMode值产生的行为并不一致! 【场景3】把场景1再做修改:这次C-4不跳栈底的B-2,改为跳转B-3,且还是通过action方式。 // C-4跳转B-3public static void jumpTo_B_3_ByAction_New(Context context) { Intent intent = new Intent(); intent.setAction("com.zkp.task.ACTION_TO_B_PAGE3"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent);} 如下图,A-1设置NEW_TASK跳转B-2,再跳转B-3,再设置NEW_TASK跳转C-4,最终设置NEW_TASK想跳转B-3。 预想的结果是:与场景0一致,会跳到B-2所在Task的顶层B-3。 实际的结果是:在已有的Task2中,产生了一个新的B-3实例。 不是说好的,Activity已经存在时,展示其所在Task的最新状态吗?明明Task2中已经有了B-3,并没有直接展示它,而是生成了新的B-3实例。 【场景4】既然Activity没有被复用,那Task一定会被复用吗?把场景3稍做修改,直接给B-3指定一个单独的affinity。 <activity android:name=".Activity3" android:exported="true" android:taskAffinity="b3.task"><!--指定了亲和性标识--> <intent-filter> <action android:name="com.zkp.task.ACTION_TO_B_PAGE3" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter></activity> 如下图,A-1设置NEW_TASK跳转B-2,再跳转B-3,再设置NEW_TASK跳转C-4,最终设置NEW_TASK想跳转B-3。 ——这次,连Task也不会再被复用了……Activity3在一个新的栈中被实例化了。 再回看官方的注释,就会显得非常不准确,甚至会让开发者对该部分的认知产生严重错误!稍微改变过程中的某个毫无关联的属性(如跳转目标、跳转方式……),就会产生很大差异。 在看flag相关注释时,我们要树立一个意识:Task和Activity跳转的实际效果,是launchMode、taskAffinity、跳转方式、Activity在Task中的层级等属性综合作用的结果,不要相信“一面之词”。 回到问题本身,究竟是哪些原因造就了上面的不同效果呢?只有源码最值得信赖了。 三、场景分析与源码探索 本文以Android 12.0源码为基础,进行探究。上述场景在不同Android版本上的表现是一致的。 3.1 源码调试注意事项 源码的调试方法,许多文章已经有了详细的教学,本文不再赘述。此处只简单总结其中需要注意的事项: 下载模拟器时,不要使用Google Play版本,该版本类似user版本,无法选择system_process进程进行断点。 即使是Google官方模拟器和源码,在断点时,也会有行数严重不对应的情况(比如:模拟器实际会运行到方法A,但在源码中打断点时,实际不能定位到方法A的对应行数),该问题并没有很好的处理方法,只能尽量规避,如使模拟器版本与源码版本保持一致、多打一些断点增加关键行数被定位到的几率。 3.2 初步断点,明确启动结果 以【场景0】为例,我们初步确认一下,为什么B-3跳转B-2会无反应,系统是否告知了原因。 3.2.1 明确启动结果及其来源 在Android源码的断点调试中,常见的有两类进程:应用进程和system_process进程。 在应用进程中,我们能获取到应用启动结果的状态码result,这个result用来告诉我们启动是否成功。涉及堆栈如下图(标记1)所示: Activity类::startActivity() → startActivityForResult() →Instrumentation类::execStartActivity(), 返回值result则是ATMS (ActivityTaskManagerService)执行的结果。 如上图(标记2)标注,ATMS类::startActivity()方法,返回了result=3。 在system_process进程中,我们看一下这个result=3是怎样被赋值的。略去详细断点步骤,实际堆栈如下图(标注1)所示: ATMS类::startActivity() →startActivityAsUser() →ActivityStarter类::execute() →executeRequest() →startActivityUnchecked() → startActivityInner() → recycleTask(),在recycleTask()中返回了结果。 如上图(标注2)所示,result在mMovedToFront=false时被赋值,即result=START_DELIVERED_TO_TOP=3,而START_SUCCESS=0才代表创建成功。 看一下源码中对START_DELIVERED_TO_TOP的说明,如下图: Result for IActivityManaqer.startActivity: activity wasn't really started, but the given Intent was given to the existing top activity. (IActivityManaqer.startActivityActivity的结果:Activity并未真正启动,但给定的Intent已提供给现有的顶层Activity。) “Activity并未真正启动”——是的,因为可以复用 “给定的Intent已提供给现有的顶层Activity”——实际没有,顶层Activity3并没有收到任何回调,onNewIntent()未执行,甚至尝试通过Intent::putExtra()传入新的参数,Activity3也没有收到。官方文档又带给了我们一个疑问点?我们把这个问题记录下来,在后面分析。 满足什么条件,才会造成 START_DELIVERED_TO_TOP的结果呢?笔者的思路是,通过与正常启动流程对比,找出差异点。 3.3 过程断点,探索启动流程 一般来说,在定位问题时,我们习惯通过结果反推原因,但反推的过程只能关注到与问题强关联的代码分支,并不能能使我们很好地了解全貌。 所以,本节内容我们通过顺序阅读的方法,正向介绍startActivity过程中与上述【场景01234】强相关的逻辑。再次简述一下: 【场景0】同一个Task内,从顶部B-3跳转B-2——停留在B-3 【场景1】从另一个Task内的C-4,跳转B-2——跳转到B-3 【场景2】把场景1中,C-4跳转B-2的方式改为setClassName()——创建新B-2实例 【场景3】把场景1中,C-4跳转B-2改为跳转B-3——创建新B-3实例 【场景4】给场景3中的B-3,指定taskAffinity——创建新Task和新B-3实例 3.3.1 流程源码概览 源码中,整个启动流程很长,涉及的方法和逻辑也很多,为了便于大家理清方法调用顺序,方便后续内容的阅读,笔者将本文涉及到的关键类及方法调用关系整理如下。 后续阅读中如果不清楚调用关系,可以返回这里查看: // ActivityStarter.java ActivityStarter::execute() { executeRequest(intent) { startActivityUnchecked() { startActivityInner(); } } ActivityStarter::startActivityInner() { setInitialState(); computeLaunchingTaskFlags(); Task targetTask = getReusableTask(){ findTask(); } ActivityRecord targetTaskTop = targetTask.getTopNonFinishingActivity(); if (targetTaskTop != null) { startResult = recycleTask() { setTargetRootTaskIfNeeded(); complyActivityFlags(); if (mAddingToTask) { return START_SUCCESS; //【场景2】【场景3】从recycleTask()返回 } resumeFocusedTasksTopActivities() return mMovedToFront ? START_TASK_TO_FRONT : START_DELIVERED_TO_TOP;//【场景1】【场景0】从recycleTask()返回 } } else { mAddingToTask = true; } if (startResult != START_SUCCESS) { return startResult;//【场景1】【场景0】从startActivityInner()返回 } deliverToCurrentTopIfNeeded(); resumeFocusedTasksTopActivities(); return startResult; } 3.3.2 关键流程分析 (1)初始化 startActivityInner()是最主要的方法,如下列几张图所示,该方法会率先调用setInitialState(),初始化各类全局变量,并调用reset(),重置ActivityStarter中各种状态。 在此过程中,我们记下两个关键变量mMovedToFront和mAddingToTask,它们均在此被重置为false。 其中,mMovedToFront代表当Task可复用时,是否需要将目标Task移动到前台;mAddingToTask代表是否要将Activity加入到Task中。 (2)计算确认启动时的flag 该步骤会通过computeLaunchingTaskFlags()方法,根据launchMode、来源Activity的属性等进行初步计算,确认LaunchFlags。 此处重点处理来源Activity为空的各类场景,与我们上文中的几种场景无关,故不再展开讲解。 (3)获取可以复用的Task 该步骤通过调用getReusableTask()实现,用来查找有没有可以复用的Task。 先说结论:场景0123中,都能获取到可以复用的Task,而场景4中,未获取到可复用的Task。 为什么场景4不可以复用?我们看一下getReusableTask()的关键实现。 上图(标注1)中,putIntoExistingTask代表是否能放入已经存在的Task。当flag含有NEW_TASK且不含MULTIPLE_TASK时,或指定了singleInstance或singleTask的launchMode等条件,且没有指定Task或要求返回结果 时,场景01234均满足了条件。 然后,上图(标注2)通过findTask()查找可以复用的Task,并将过程中找到的栈顶Activity赋值给intentActivity。最终,上图(标注3)将intentActivity对应的Task作为结果。 findTask()是怎样查找哪个Task可以复用呢? 主要是确认两种结果mIdealRecord——“理想的ActivityRecord” 和 mCandidateRecord——"候选的ActivityRecord",作为intentActivity,并取intentActivity对应的Task作为复用Task。 什么ActivityRecord才是理想或候选的ActivityRecord呢? 在mTmpFindTaskResult.process()中确认。 程序会将当前系统中所有的Task进行遍历,在每个Task中,进行如上图所示的工作——将Task的底部Activity realActivity与目标Activity cls进行对比。 场景012中,我们想跳转Activity2,即cls是Activity2,与Task底部的realActivity2相同,则将该Task顶部的Activity3 r作为“理想的Activity”; 场景3中,我们想跳转Activity3,即cls是Activity3,与Task底部的realActivity2不同,则进一步判断task底部Activity2与目标Activity3的栈亲和行,具有相同亲和性,则将Task的顶部Activity3作为“候选Activity”; 场景4中,所有条件都不满足,最终没能找到可复用的Task。在执行完getReusableTask()后将mAddingToTask赋值为true 由此,我们就能解释【场景4】中,新建了Task的现象。 (4)确定是否需要将目标Task移动到前台 如果存在可复用的Task,场景0123会执行recycleTask(),该方法中会相继进行几个操作:setTargetRootTaskIfNeeded()、 complyActivityFlags()。 首先,程序会执行 setTargetRootTaskIfNeeded(),用来确定是否需要将目标Task移动到前台,使用mMovedToFront作为标识。 在【场景123】中,来源Task和目标Task是不同的,differentTopTask为true,再经过一系列Task属性对比,能够得出mMovedToFront为true; 而场景0中,来源Task和目标Task相同,differentTopTask为false,mMovedToFront保持初始的false。 由此,我们就能解释【场景0】中,Task不会发生切换的现象。 (5)通过对比flag、Intent、Component等确认是否要将Activity加入到Task中 还是在【场景0123】中,recycleTask()会继续执行complyActivityFlags(),用来确认是否要将Activity加入到Task中,使用mAddingToTask作为标识。 该方法会对FLAG_ACTIVITY_NEW_TASK、 FLAG_ACTIVITY_CLEAR_TASK、 FLAG_ACTIVITY_CLEAR_TOP等诸多flag、Intent信息进行一系列判断。 上图(标注1)中,会先判断后续是否需要重置Task,resetTask,判断条件则是FLAG_ACTIVITY_RESET_TASK_IF_NEEDED,显然,场景0123的resetTask都为false。继续执行。 接着,会有多种条件判断按顺序执行。 在【场景3】中,目标Component(mActivityComponent)是B-3,目标Task的realActivity则是B-2,两者不相同,进入了resetTask相关的判断(标注2)。 之前resetTask已经是false,故【场景3】的mAddingToTask脱离原始值,被置为true。 在【场景012】中,相对比的两个Activity都是B-2(标注3),可以进入下一级判断——isSameIntentFilter()。 这一步判断的内容就很明显了,目标Activity2的已有Intent 与 新的Intent做对比。很显然,场景2中由于改为了setClassName跳转,Intent自然不一样了。 故【场景2】的mAddingToTask脱离原始值,被置为true。 总结看一下: 【场景123】的mMovedToFront最先被置为true,而【场景0】经历重重考验,保持初始值为false。 ——这意味着当有可复用Task时,【场景0】不需要把Task切换到前列;【场景123】需要切换到目标Task。 【场景234】的mAddingToTask分别在不同阶段被置为true,而【场景01】,始终保持初始值false。 ——这意味着,【场景234】需要将Activity加入到Task中,而【场景01】不再需要。 (6)实际启动Activity或直接返回结果 被启动的各个Activity会通过resumeFocusedTasksTopActivities()等一系列操作,开始真正的启动与生命周期的调用。 我们关于上述各个场景的探索已经得到答案,后续流程便不再关注。 四、问题修复及遗留问题解答 4.1 问题修复 既然前面总结了这么多必要条件,我们只需要破坏其中的某些条件,就可以修复业务中遇到的问题了,简单列举几个的方案。 方案一:修改flag。B-3跳转B-2时,增加FLAG_ACTIVITY_CLEAR_TASK或FLAG_ACTIVITY_CLEAR_TOP,或者直接不设置flag。经验证可行。 方案二:修改intent属性,即【场景2】。A-1通过action方式隐式跳转B-2,则B-3可以通过setClassName方式,或修改action内属性的方式跳转B-2。经验证可行。 方案三:提前移除B-2。B-2跳转B-3时,finish掉B-2。需要注意的是,finish()要在startActivity()之前执行,以避免遗留的ActivityRecord和Intent信息对后续跳转的影响。尤其是当你把B-2作为自己应用的deeplink分发Activity时,更值得警惕。 4.2 遗留问题 还记得我们在文章开端的某个疑惑吗,为什么没有回调onNewIntent()? onNewIntent() 会通过deliverNewIntent()触发,而deliverNewIntent()仅通过以下两个方法调用。 complyActivityFlags()就是上文3.3.1.5中我们着重探讨的方法,可以发现complyActivityFlags()中所有可能调用deliverNewIntent()的条件均被完美避开了。 而deliverToCurrentTopIfNeeded()方法则如下图所示。 mLaunchFlags和mLaunchMode,无法满足条件,导致dontStart为false,无缘 deliverNewIntent()。 至此,onNewIntent()的问题得到解答。 五、结语 通过一系列场景假设,我们发现了许多出乎意料的现象: 文档提到FLAG_ACTIVITY_NEW_TASK等价于singleTask,与事实并不完全如此,只有与其他flag搭配才能达到相似的效果。这一flag的注释非常片面,甚至会引发误解,单一因素无法决定整体表现。 官方文档提到START_DELIVERED_TO_TOP会将新的Intent传递给顶层Activity,但事实上,并不是每一种START_DELIVERED_TO_TOP都会把新的Intent重新分发。 同一个栈底Activity,前后两次都通过action或都通过setClassName跳转到时,第二次跳转竟然会失败,而两次用不同方式跳转时,则会成功。 单纯使用FLAG_ACTIVITY_NEW_TASK时,跳栈底Activity和跳同栈内其他Activity的效果大相径庭。 业务中遇到的问题,归根结底就是对Android栈机制不够了解造成的。 在面对栈相关的编码时,开发者务必要想清楚,承担新开应用栈的Activty在应用全局承担怎样的使命,要对Task历史、flag属性、launchMode属性、Intent内容等全面评估,谨慎参考官方文档,才能避免栈陷阱,达成理想可靠的效果。 END 猜你喜欢 Android系统服务DropBoxManagerService详解与实践应用 vivo官网App模块化开发方案-ModularDevToo 本文分享自微信公众号 - vivo互联网技术(vivoVMIC)。 如有侵权,请联系 support@oschina.cn 删除。 本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

优秀的个人博客,低调大师

11浏览器原理面试题

浏览器与新技术 这是一篇很长的文章,可以算上一本小书了,有精力的非常建议阅读。 常见的浏览器内核有哪些? 浏览器/RunTime 内核(渲染引擎) JavaScript 引擎 Chrome Blink(28~) Webkit(Chrome 27) V8 FireFox Gecko SpiderMonkey Safari Webkit JavaScriptCore Edge EdgeHTML Chakra(for JavaScript) IE Trident Chakra(for JScript) PhantomJS Webkit JavaScriptCore Node.js - V8 浏览器的主要组成部分是什么? 用户界面- 包括地址栏、前进/后退按钮、书签菜单等。除了浏览器主窗口显示的您请求的页面外,其他显示的各个部分都属于用户界面。 浏览器引擎- 在用户界面和呈现引擎之间传送指令。 呈现引擎- 负责显示请求的内容。如果请求的内容是 HTML,它就负责解析 HTML 和 CSS 内容,并将解析后的内容显示在屏幕上。 网络- 用于网络调用,比如 HTTP 请求。其接口与平台无关,并为所有平台提供底层实现。 用户界面后端- 用于绘制基本的窗口小部件,比如组合框和窗口。其公开了与平台无关的通用接口,而在底层使用操作系统的用户界面方法。 JavaScript 解释器。用于解析和执行 JavaScript 代码。 数据存储。这是持久层。浏览器需要在硬盘上保存各种数据,例如 Cookie。新的 HTML 规范 (HTML5) 定义了“网络数据库”,这是一个完整(但是轻便)的浏览器内数据库。 图:浏览器的主要组件。 值得注意的是,和大多数浏览器不同,Chrome 浏览器的每个标签页都分别对应一个呈现引擎实例。每个标签页都是一个独立的进程。 浏览器是如何渲染UI的? 浏览器获取HTML文件,然后对文件进行解析,形成DOM Tree 与此同时,进行CSS解析,生成Style Rules 接着将DOM Tree与Style Rules合成为 Render Tree 接着进入布局(Layout)阶段,也就是为每个节点分配一个应出现在屏幕上的确切坐标 随后调用GPU进行绘制(Paint),遍历Render Tree的节点,并将元素呈现出来 浏览器如何解析css选择器? 浏览器会『从右往左』解析CSS选择器。 我们知道DOM Tree与Style Rules合成为 Render Tree,实际上是需要将Style Rules附着到DOM Tree上,因此需要根据选择器提供的信息对DOM Tree进行遍历,才能将样式附着到对应的DOM元素上。 以下这段css为例 .mod-nav h3 span {font-size: 16px;} 复制代码 我们对应的DOM Tree 如下 若从左向右的匹配,过程是: 从 .mod-nav 开始,遍历子节点 header 和子节点 div 然后各自向子节点遍历。在右侧 div 的分支中 最后遍历到叶子节点 a ,发现不符合规则,需要回溯到 ul 节点,再遍历下一个 li-a,一颗DOM树的节点动不动上千,这种效率很低。 如果从右至左的匹配: 先找到所有的最右节点 span,对于每一个 span,向上寻找节点 h3 由 h3再向上寻找 class=mod-nav 的节点 最后找到根元素 html 则结束这个分支的遍历。 后者匹配性能更好,是因为从右向左的匹配在第一步就筛选掉了大量的不符合条件的最右节点(叶子节点);而从左向右的匹配规则的性能都浪费在了失败的查找上面。 DOM Tree是如何构建的? 转码: 浏览器将接收到的二进制数据按照指定编码格式转化为HTML字符串 生成Tokens: 之后开始parser,浏览器会将HTML字符串解析成Tokens 构建Nodes: 对Node添加特定的属性,通过指针确定 Node 的父、子、兄弟关系和所属 treeScope 生成DOM Tree: 通过node包含的指针确定的关系构建出DOM Tree 浏览器重绘与重排的区别? 重排: 部分渲染树(或者整个渲染树)需要重新分析并且节点尺寸需要重新计算,表现为重新生成布局,重新排列元素 重绘: 由于节点的几何属性发生改变或者由于样式发生改变,例如改变元素背景色时,屏幕上的部分内容需要更新,表现为某些元素的外观被改变 单单改变元素的外观,肯定不会引起网页重新生成布局,但当浏览器完成重排之后,将会重新绘制受到此次重排影响的部分 重排和重绘代价是高昂的,它们会破坏用户体验,并且让UI展示非常迟缓,而相比之下重排的性能影响更大,在两者无法避免的情况下,一般我们宁可选择代价更小的重绘。 『重绘』不一定会出现『重排』,『重排』必然会出现『重绘』。 如何触发重排和重绘? 任何改变用来构建渲染树的信息都会导致一次重排或重绘: 添加、删除、更新DOM节点 通过display: none隐藏一个DOM节点-触发重排和重绘 通过visibility: hidden隐藏一个DOM节点-只触发重绘,因为没有几何变化 移动或者给页面中的DOM节点添加动画 添加一个样式表,调整样式属性 用户行为,例如调整窗口大小,改变字号,或者滚动。 如何避免重绘或者重排? 集中改变样式 我们往往通过改变class的方式来集中改变样式 // 判断是否是黑色系样式 const theme = isDark ? 'dark' : 'light' // 根据判断来设置不同的class ele.setAttribute('className', theme) 复制代码 使用DocumentFragment 我们可以通过createDocumentFragment创建一个游离于DOM树之外的节点,然后在此节点上批量操作,最后插入DOM树中,因此只触发一次重排 var fragment = document.createDocumentFragment(); for (let i = 0;i<10;i++){ let node = document.createElement("p"); node.innerHTML = i; fragment.appendChild(node); } document.body.appendChild(fragment); 复制代码 提升为合成层 将元素提升为合成层有以下优点: 合成层的位图,会交由 GPU 合成,比 CPU 处理要快 当需要 repaint 时,只需要 repaint 本身,不会影响到其他的层 对于 transform 和 opacity 效果,不会触发 layout 和 paint 提升合成层的最好方式是使用 CSS 的 will-change 属性: #target { will-change: transform; } 复制代码 前端如何实现即时通讯? 短轮询 短轮询的原理很简单,每隔一段时间客户端就发出一个请求,去获取服务器最新的数据,一定程度上模拟实现了即时通讯。 优点:兼容性强,实现非常简单 缺点:延迟性高,非常消耗请求资源,影响性能 comet comet有两种主要实现手段,一种是基于 AJAX 的长轮询(long-polling)方式,另一种是基于 Iframe 及 htmlfile 的流(streaming)方式,通常被叫做长连接。 长轮询优缺点: 优点:兼容性好,资源浪费较小 缺点:服务器hold连接会消耗资源,返回数据顺序无保证,难于管理维护 长连接优缺点: 优点:兼容性好,消息即时到达,不发无用请求 缺点:服务器维护长连接消耗资源 SSE SSE(Server-Sent Event,服务端推送事件)是一种允许服务端向客户端推送新数据的HTML5技术。 优点:基于HTTP而生,因此不需要太多改造就能使用,使用方便,而websocket非常复杂,必须借助成熟的库或框架 缺点:基于文本传输效率没有websocket高,不是严格的双向通信,客户端向服务端发送请求无法复用之前的连接,需要重新发出独立的请求 Websocket Websocket是一个全新的、独立的协议,基于TCP协议,与http协议兼容、却不会融入http协议,仅仅作为html5的一部分,其作用就是在服务器和客户端之间建立实时的双向通信。 优点:真正意义上的实时双向通信,性能好,低延迟 缺点:独立与http的协议,因此需要额外的项目改造,使用复杂度高,必须引入成熟的库,无法兼容低版本浏览器 Web Worker 后面性能优化部分会用到,先做了解 Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行 Service workers 后面性能优化部分会用到,先做了解 Service workers 本质上充当Web应用程序与浏览器之间的代理服务器,也可以在网络可用时作为浏览器和网络间的代理,创建有效的离线体验。 什么是浏览器同源策略? 同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。 同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。 下表给出了相对http://store.company.com/dir/page.html同源检测的示例: 浏览器中的大部分内容都是受同源策略限制的,但是以下三个标签可以不受限制: <img src=XXX> <link href=XXX> <script src=XXX> 如何实现跨域? 跨域是个比较古老的命题了,历史上跨域的实现手段有很多,我们现在主要介绍三种比较主流的跨域方案,其余的方案我们就不深入讨论了,因为使用场景很少,也没必要记这么多奇技淫巧。 最经典的跨域方案jsonp jsonp本质上是一个Hack,它利用<script>标签不受同源策略限制的特性进行跨域操作。 jsonp优点: 实现简单 兼容性非常好 jsonp的缺点: 只支持get请求(因为<script>标签只能get) 有安全性问题,容易遭受xss攻击 需要服务端配合jsonp进行一定程度的改造 jsonp的实现: function JSONP({ url, params, callbackKey, callback }) { // 在参数里制定 callback 的名字 params = params || {} params[callbackKey] = 'jsonpCallback' // 预留 callback window.jsonpCallback = callback // 拼接参数字符串 const paramKeys = Object.keys(params) const paramString = paramKeys .map(key => `${key}=${params[key]}`) .join('&') // 插入 DOM 元素 const script = document.createElement('script') script.setAttribute('src', `${url}?${paramString}`) document.body.appendChild(script) } JSONP({ url: 'http://s.weibo.com/ajax/jsonp/suggestion', params: { key: 'test', }, callbackKey: '_cb', callback(result) { console.log(result.data) } }) 复制代码 最流行的跨域方案cors cors是目前主流的跨域解决方案,跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。 如果你用express,可以这样在后端设置 //CORS middleware var allowCrossDomain = function(req, res, next) { res.header('Access-Control-Allow-Origin', 'http://example.com'); res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE'); res.header('Access-Control-Allow-Headers', 'Content-Type'); next(); } //... app.configure(function() { app.use(express.bodyParser()); app.use(express.cookieParser()); app.use(express.session({ secret: 'cool beans' })); app.use(express.methodOverride()); app.use(allowCrossDomain); app.use(app.router); app.use(express.static(__dirname + '/public')); }); 复制代码 在生产环境中建议用成熟的开源中间件解决问题。 最方便的跨域方案Nginx nginx是一款极其强大的web服务器,其优点就是轻量级、启动快、高并发。 现在的新项目中nginx几乎是首选,我们用node或者java开发的服务通常都需要经过nginx的反向代理。 反向代理的原理很简单,即所有客户端的请求都必须先经过nginx的处理,nginx作为代理服务器再讲请求转发给node或者java服务,这样就规避了同源策略。 #进程, 可更具cpu数量调整 worker_processes 1; events { #连接数 worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; #连接超时时间,服务器会在这个时间过后关闭连接。 keepalive_timeout 10; # gizp压缩 gzip on; # 直接请求nginx也是会报跨域错误的这里设置允许跨域 # 如果代理地址已经允许跨域则不需要这些, 否则报错(虽然这样nginx跨域就没意义了) add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Headers X-Requested-With; add_header Access-Control-Allow-Methods GET,POST,OPTIONS; # srever模块配置是http模块中的一个子模块,用来定义一个虚拟访问主机 server { listen 80; server_name localhost; # 根路径指到index.html location / { root html; index index.html index.htm; } # localhost/api 的请求会被转发到192.168.0.103:8080 location /api { rewrite ^/b/(.*)$ /$1 break; # 去除本地接口/api前缀, 否则会出现404 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://192.168.0.103:8080; # 转发地址 } # 重定向错误页面到/50x.html error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } } 复制代码 其它跨域方案 HTML5 XMLHttpRequest 有一个API,postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。 WebSocket 是一种双向通信协议,在建立连接之后,WebSocket 的 server 与 client 都能主动向对方发送或接收数据,连接建立好了之后 client 与 server 之间的双向通信就与 HTTP 无关了,因此可以跨域。 window.name + iframe:window.name属性值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值,我们可以利用这个特点进行跨域。 location.hash + iframe:a.html欲与c.html跨域相互通信,通过中间页b.html来实现。 三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。 document.domain + iframe: 该方式只能用于二级域名相同的情况下,比如 a.test.com 和 b.test.com 适用于该方式,我们只需要给页面添加 document.domain ='test.com' 表示二级域名都相同就可以实现跨域,两个页面都通过js强制设置document.domain为基础主域,就实现了同域。 1.如果觉得这篇文章还不错,来个分享、点赞吧,让更多的人也看到 如果你觉得这篇文章对你有点用的话,麻烦请给我们的开源项目点点star: http://github.crmeb.net/u/defu 不胜感激 ! 来自 “开源世界 ” ,链接: https://ym.baisou.ltd/post/753.html ,如需转载,请注明出处,否则将追究法律责任。

优秀的个人博客,低调大师

Nginx 面试中最常见的 18 题!

公众号关注“杰哥的IT之旅”,选择“星标”,重磅干货,第一时间送达! 作者:CodeMan 来源:https://segmentfault.com/a/1190000010677483 Nginx的并发能力在同类型网页服务器中的表现,相对而言是比较好的,因此受到了很多企业的青睐,我国使用Nginx网站的知名用户包括腾讯、淘宝、百度、京东、新浪、网易等等。Nginx是网页服务器运维人员必备技能之一,下面为大家整理了一些比较常见的Nginx相关面试题,仅供参考: 1、请解释一下什么是Nginx? Nginx是一个web服务器和反向代理服务器,用于HTTP、HTTPS、SMTP、POP3和IMAP协议。 2、请列举Nginx的一些特性。 Nginx服务器的特性包括: 反向代理/L7负载均衡器 嵌入式Perl解释器 动态二进制升级 可用于重新编写URL,具有非常好的PCRE支持 3、请列举Nginx和Apache 之间的不同点 4、请解释Nginx如何处理HTTP请求。 Nginx使用反应器模式。主事件循环等待操作系统发出准备事件的信号,这样数据就可以从套接字读取,在该实例中读取到缓冲区并进行处理。单个线程可以提供数万个并发连接。 5、在Nginx中,如何使用未定义的服务器名称来阻止处理请求? 只需将请求删除的服务器就可以定义为: Server{listen80;server_name““;return444;} 这里,服务器名被保留为一个空字符串,它将在没有“主机”头字段的情况下匹配请求,而一个特殊的Nginx的非标准代码444被返回,从而终止连接。 6、使用“反向代理服务器”的优点是什么? 反向代理服务器可以隐藏源服务器的存在和特征。它充当互联网云和web服务器之间的中间层。这对于安全方面来说是很好的,特别是当您使用web托管服务时。 7、请列举Nginx服务器的最佳用途。 Nginx服务器的最佳用法是在网络上部署动态HTTP内容,使用SCGI、WSGI应用程序服务器、用于脚本的FastCGI处理程序。它还可以作为负载均衡器。 8、请解释Nginx服务器上的Master和Worker进程分别是什么? Master进程:读取及评估配置和维持 Worker进程:处理请求 9、请解释你如何通过不同于80的端口开启Nginx? 为了通过一个不同的端口开启Nginx,你必须进入/etc/Nginx/sites-enabled/,如果这是默认文件,那么你必须打开名为“default”的文件。编辑文件,并放置在你想要的端口: Like server { listen 81; } 10、请解释是否有可能将Nginx的错误替换为502错误、503? 502 =错误网关 503 =服务器超载 有可能,但是您可以确保fastcgi_intercept_errors被设置为ON,并使用错误页面指令。 Location/{fastcgi_pass127.0.01:9001;fastcgi_intercept_errorson;error_page502=503/error_page.html;#…} 11、在Nginx中,解释如何在URL中保留双斜线? 要在URL中保留双斜线,就必须使用merge_slashes_off; 语法:merge_slashes [on/off] 默认值: merge_slashes on 环境: http,server 12、请解释ngx_http_upstream_module的作用是什么? ngx_http_upstream_module用于定义可通过fastcgi传递、proxy传递、uwsgi传递、memcached传递和scgi传递指令来引用的服务器组。 13、请解释什么是C10K问题? C10K问题是指无法同时处理大量客户端(10,000)的网络套接字。 14、请陈述stub_status和sub_filter指令的作用是什么? Stub_status指令:该指令用于了解Nginx当前状态的当前状态,如当前的活动连接,接受和处理当前读/写/等待连接的总数 Sub_filter指令:它用于搜索和替换响应中的内容,并快速修复陈旧的数据 15、解释Nginx是否支持将请求压缩到上游? 您可以使用Nginx模块gunzip将请求压缩到上游。gunzip模块是一个过滤器,它可以对不支持“gzip”编码方法的客户机或服务器使用“内容编码:gzip”来解压缩响应。 16、解释如何在Nginx中获得当前的时间? 要获得Nginx的当前时间,必须使用SSI模块、date_local的变量。 Proxy_set_header THE-TIME $date_gmt; 17、用Nginx服务器解释-s的目的是什么? 用于运行Nginx -s参数的可执行文件。 18、解释如何在Nginx服务器上添加模块? 在编译过程中,必须选择Nginx模块,因为Nginx不支持模块的运行时间选择。 如果您觉得这篇文章对您有点用的话,麻烦您为本文来个四连:转发分享、点赞、点在看、留言,因为这将是我写作与分享更多优质文章的最强动力! 本公众号全部博文已整理成一个目录,请在公众号后台回复「m」获取! 推荐阅读: 1、 GitHub 标星 5.9K+!1250 个运维工程师面试问题,看这个开源项目就够了! 2、 太赞了!一份适合程序员的精选面试题清单。 3、 想要入职腾讯,后端开发工程师必会的面试题。 4、 IT运维面试问题总结-LVS、Keepalived、HAProxy、Kubernetes、OpenShift等 5、 IT运维面试问题总结-数据库、监控、网络管理(NoSQL、MongoDB、MySQL、Prometheus、Zabbix) 6、 IT运维面试问题总结-运维工具、开源应用(Ansible、Ceph、Docker、Apache、Nginx等) 7、 IT运维面试问题总结-基础服务、磁盘管理、虚拟平台和系统管理 8、 IT运维面试问题总结-Linux基础 关注微信公众号「 杰哥的IT之旅」,后台回复「 1024」查看更多内容,回复「 加群」 备注:地区-职业方向-昵称即可加入读者交流群。 点个[在看],是对杰哥最大的支持! 本文分享自微信公众号 - 杰哥的IT之旅(Jake_Internet)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

优秀的个人博客,低调大师

8Python基础面试练习题

1.26个字母大小写成对打印,例如:Aa,Bb...... for i in range(26): print(chr(65+i)+chr(97+i)) 2.一个list包含10个数字,然后生成一个新的list,要求新的list里面的数都比之前的数多1 list=[2,3,6,4,7,5,1,8,9,0] list1=[] for i in list: list1.append(i+1) print(list1) 3.倒序取出每个单词的第一个字母,例如:I am a good boy!方法1 tre='I am a good boy!' t=tre.split() #print(t) t.reverse() list=[] #print(t) for i in t: list.append(i[0]) print(list) 方法2 a = "I AM A BOY" result = [] for i in a.split()[::-1]: result.append(i[0]) print(result) 4.输入一个自己的生日月份,用if和else判断一下当月是不是你的生日月第一种方法,datetime模块获取时间 import datetime date=datetime.datetime.now() #获取当前时间 # print(date.strftime('%Y-%m-%d')) #把当前时间格式化为可读懂的年月日 r=date.strftime('%m') #把当前时间格式化为可读懂的年月日,只取月份 print(r) #Python学习交流QQ群:579817333 t=input('请输入自己的生日月份:') if t==r: print('true') else: print('不是') 第二种方法,time模块获取时间 import time # date=time.time() #获取当前时间 # print(date) # print(time.localtime(time.time()))#按固定格式显示当前时间 # print(time.strftime('%Y-%m-%d')) #把当前时间格式化为可读懂的年月日 # print(time.strftime('%Y-%m-%d',time.localtime(time.time()))) #把时间格式化为可读懂的年月日,后一个参数可省略 # print(time.strftime('%m',time.localtime(time.time()))) #只取月儿份 #t=time.strftime('%m',time.localtime(time.time()))#只取月儿份 t=time.strftime('%m')#只取月儿份 print(t)#Python学习交流QQ群:579817333 r=input('请输入自己的生日月份:') if t==r: print('true') else: print('不是') 5.输入3个字母:e、a、r,如果输入e,那么推出循环,如果输入a,执行continue,如果输入r,那么再读取一次字母,并打印,用死循环实现。 while True: str = input('请输入三个字母:') if str=='r': print(str) if str=='a': continue if str=='e': break else: print('输入有误') 6.输入3个字母:e、a、r,如果输入e,那么退出循环,如果输入a,执行continue,如果输入r,那么再读取一次字母,并打印,只允许输入三次字母,重复输入的字母不算在内。 count = 0 for i in range(3): letter = input("send a letter%d:"%i) if letter == 'e': break elif letter == 'a': continue elif letter == 'r': count += 1 if count == 2: input("send a letter dddd:") 7.把一个字符串"abcdefg"插入到一个list中,每个字母占一个list中的元素位置,例如: ["a","b","c","d","e","f","g"] ls=["a","b","c","d","e","f","g"] s="abcdefg" lt=[] #插入元素到后边 for i in s : ls.extend(i) print(ls) for i in s: ls.append(i) print(ls) #每个元素都插在第一个,或者说倒序插入列表前边 for i in s: ls.insert(0,i) print(ls) 8.['a','b','c','d','e','f','g']操作这个list,拼出一个字符串"adg" lis=['a','b','c','d','e','f','g'] print(len(lis)) t=lis[0]+lis[int(len(lis)/2)]+lis[-1] print(t) 或 print("".join(lis[::3]))

优秀的个人博客,低调大师

企业安全无间之抓内鬼

【大咖・来了 第7期】10月24日晚8点观看《智能导购对话机器人实践》 观察这些年的信息泄漏案件比例,内部威胁在快速上升。内鬼的范围其实很多,传统上安全会抓账号泄漏、横向移动之类。但如果是商业间谍、搞破坏、内部欺诈这些行为,安全上基本上没能力管。很多安全同学吐槽,安全在公司不受重视,得不到资源,在我看来,是安全目前做的这些事相对于公司的大风险来看,太小,在高层那里不受重视理所当然,当安全有能力为公司发现、收敛更大的风险,地位当然会上升。如果你有能力抓内鬼,汇报层级也会直线上升。本文要和大家说的就是:怎么抓内部威胁。 一、内鬼动机及范围 内鬼的动机一般包括:搞破坏、窃取数据、欺诈、商业间谍、无意犯错、偶然间装逼犯。内部人员作案一般是一个持续过程,在这个过程中有逐渐变化,最后到事件一次发生,多次得手。 内部人员的范围并非是“纯粹”内部人员,也包括生态上下游合作伙伴、外包、访客等任何具有内部访问权限或数据的人。给一个清晰定义,就是基于知识、访问、信任的角色。 所谓知识,如果一个人知道系统的位置、防御措施可被绕过,则为掌握了相关知识。常见例如系统的开发人员可能知道产品的几个0DAY,离职员工掌握测试系统账号密码等。 从技术角度看,IT系统验证凭证有效性,允许访问资源。因此任何获得凭证的人都可被视为内部人员,也即访问角色。即使系统有多因素认证,内部人员也可把短信验证码之类的验证要素提供给其他人员,所以从这个角度来说,IT系统很难完全防范。 还有一种是信任角色,最简单可理解为你的合作伙伴、外包等人群,也包括内部人员。这些人群获得公司一定程度的信任,可以获得部分权限资源,并且以公司名义活动。例如公司的用户数据如果泄漏,在监管和舆论来看,这就是你的问题,而不是外包或代理商。 通常内部抓到的坏人处于公司形象的问题不会公开,而由于不会公开,所以实际案例可能比我们看到的多的多。但其实你可以从法院的公开判决文书找到很多案例。对内部坏人的处理逻辑,首先是内部调查,确定性质和行为。再接下来是走司法程序,但事实上很多公司会开除且不声张。 1. 破坏 对IT系统的破坏可能是大家最不重视的环节了,这些人往往都是技术人群,工作中有较高的系统权限,也是相对信任人群。如果这些人准备删库跑路,实施起来很容易,业界此类案例屡见不鲜。 动机主要是报复,不管是什么原因,总之是员工期望没有得到满足,可能是加薪升职,也可能是绩效,也可能是和主管关系不好。案件一般发生在离职前后,有些情况是在系统放入后门,离职后进行操作,例如前阵时间芜湖某网管的案件,就是掌握了远程路由设备的密码,然后更改配置进行了破坏。其结果一般导致可用性、完整性被破坏。 2. 数据窃取 对很多公司来说,数据或核心资料泄漏是最大担忧,这一类案件层出不穷,从世界巨头商业公司到政府部门。大公司数据泄漏还能活下来,很多小公司因为一个配方、工艺的泄漏,可能就结束了。设计师、工程师、程序员和销售最有可能,一般情况下获取的是自己创造的信息。行为上可能发生在离开公司前后60天之内,方式上有很多,邮件、网盘、U盘、拍照、打印等都有可能。 3. 内部欺诈 这是公司里面最大的群体了,跟其他不同的是,其目标是为钱。而这部分里面有相当多是工资比较低的那部分人群,非专业、非技术人员。由于对钱的需求,所以这些行为可能持续较长时间,如果有一个中低层主管参与的团伙,则更容易获得成功。内部欺诈是破坏公司现有流程实现的,例如客服向用户发放红包,就存在内外勾结的可能。除此之外,特权比较多的人员也是其中一个群体。 另外常见的一种情况是贩卖用户个人敏感信息,例如房产公司销售会把用户手机号卖给装修公司。个人敏感信息相对套现比较容易,需求方也明确,获取难度也不大。 4. 商业间谍 不要觉得商业间谍是一个遥远的事情,在当前的商业竞争形势下,各种套取信息、混入内部的案件比比皆是,只不过被公开报道出来的比较少。商业间谍不是在电视上看到的那种高大上间谍场景,又是色诱又是富家子弟什么的。现实中他们既有可能来自竞争对手,也有可能来自黑产,例如一个某宝店铺,雇用了一个员工,这个员工有相当多的某宝运营经验,在获取了用户地址、联系方式等信息后即辞职,去了下一家。而这个员工,就是间谍的一种,专门获取信息获利。 商业间谍在作案时间上和其他不同,他们可能偶然活跃一次,然后沉静下来,直到下一次。 5. 无意威胁 前面的关注都是怀有恶意的内部人员,无意威胁是那些可能没有恶意动机,但行为会给攻击者提供入口,或对安全态势产生负面影响的人。例如乱下载软件,引入病毒木马,被人社工,U盘笔记本丢失等,都在这类范围内。 6. 装逼犯 这类人群的特点是爱炫耀,尤其如果自己在一家知名大公司工作,为了向人证明自己有内部消息,位高权重等。例如某互联网大厂的内部论坛,就曾有人截图八卦。还有一些泄漏公司通告、张贴自己工资表、利用权限查询男女朋友数据的爱好者。 二、内鬼捕获思路 1. 复杂性 内鬼这事不是一个简单的技术、金钱需求问题,和外部环境、诱惑交织在一起。这些外部环境包括: 内外勾结,内部人员可能一开始是个好人,后来开始为竞争对手、黑灰产等工作。 合作伙伴,合作伙伴手上有大量信息,基于某些业务,可能掌握的是核心信息。 组织架构调整,比如公司被收购、裁员重组等,会对员工产生心理预期的不可预测性,尤其在员工利益受损时,变“坏”的可能性变大。 跨国公司的文化差异,不同国家的宗教信仰、政治态度区别很大,典型如Google前阵时间的某项目,就因为某种原因而被泄漏给了媒体,导致项目终止。 黑灰产,员工参与黑灰产也是一个信号,黑产与内部员工的联系程度如何?员工是否羊毛党爱好者,这些都增加了风险。 2. 威胁时间线检测 内鬼具有一些共同特征,这些特征出现在访问日志、流量、文件等地方,和正常活动混杂在一起,导致大量误报,这是需要解决的问题。特征分布在各个系统日志的时间轴上,需要清洗出来做数据融合,串联起来一个人的行为,这个过程是这里的重活,而且需要多次修正让数据可解释,这取决于数据质量、系统架构、正确方法,当然也需要数据人员的认真细致。 特征分为技术指标和非技术指标两类。非技术指标会涉及HR、法务、管理层等参与,但在这一系列的指标里要注意几点,一是不要因为资历老、级别高就忽略,人是变化的。二是关注心理健康,这方面很多大公司都有心理测试和定期心理辅导。三是对员工应有人道关怀的理解和帮助,而不是简单的指责惩罚。千万不要把这事变成官僚主义的形式,这样不但不能帮助,而且会让员工产生逆反心理。例如员工绩效辅导,就不该是办公室里走个过场,而是需要至少1个小时以上的一对一聊天。 每个人对工作、生活看法都千差万别,HR和leader的工作职责中需要了解个人风格,对工作的期望和目标,对周围同事和上级的看法。当技术指标发生变化时,就需要人工干预防止恶化,因此要把两类指标结合起来,起到预防、检测、响应的作用。 搞破坏、泄漏数据和内部欺诈的人,在时间线上是不同的,根据这个特征可以更好地发现异常,在关键节点加强监控。 3. 建设路线 真的要去做这件事,不是安全技术部门负责这么简单,需要有组织保障。信息安全的这些技术,也不足以保障。 抓一个坏人,可能涉及到内控、信息安全、内部监察、内控、廉政、HR等部门,具体落在哪个部门取决于内部博弈,但一般企业内不会是先设立这么一个组织再来开展活动,而是谁能干这件事,责任就落在谁头上。但整体上是一个跨部门工作组才能完成的工作。 另外,这个团队需要高层授权,解决“谁来监视监视者”的问题。这个组的工作是保密的,因此需要管理好信任,确保监视者会受到监视,因为这个组掌握的信息太多太敏感。你可以简单理解为“东厂”角色,但又不能像东厂那样不受约束、大张旗鼓、人人自危。 解决前面的问题之后,接下来的路线就是: 建立风险处理制度,建立识别评估方法。 提升相关人员的能力。 培训演练,提高员工安全意识。 启动调查的程序 有一些具体操作上需要特别列出来的注意事项: (1) 背调 员工入职一般都有背景调查,但这个是静态的,只是在入职时由外包进行调查。一旦本人发生变化,以前的背调就没什么用了。 (2) 纵深防御 信息安全领域的常见做法,但在管理上也需要有纵深防御。 (3) 员工满意度 员工满意度跟公司规模有关,公司越大江湖越深,不满度可能越高,不满度指标会间接产生影响。 (4) 内部特权人员 特权用户掌握了一些敏感关键权限,并且知道如何绕过监控,对抗调查。所以就是刚才这个谁来监视监视者的问题。这需要公司组织架构上有互相制衡的能力。 (5) 安全规则必定被绕过 在商业组织里,安全是一个支撑角色,赚钱才是核心业务。而安全措施叠加,必定会在一定程度上降低效率,由于效率原因,安全规则也一定不被完全遵守。要么是以免打扰的方式实现安全,要么就要让违规受到必要的惩戒,实际工作中是两者结合使用。 (6) 无意行为危害 无意行为危害更为常见,例如DLP抓到的外发,大量都是业务需要的非故意外发行为,真正的坏人可能就隐藏在这里而被淹没。这需要靠安全意识教育、直接触达的警告来强化安全。 三、检测指标 发现内鬼可通过不同维度的指标监测,指标异常引发报警,从而提升某个员工的关注度。 1. 个人情况指标 个人情况指标可能不会直接造成损害,但会是很多事情的诱因。 这里的最大问题是,你可能无法掌握员工的变化情况。这些信息可能会被他周围的同事和HR知道,需要打通这个信息渠道。例如精神类疾病在职场中常见的是抑郁症,会导致无意犯错、破坏发泄,理论上可以在每年的体检报告上获取这个信息,但这属于侵犯个人隐私,在强保密体系下可以关注使用。另一个重点是绩效为差的员工,待离职员工,这些都带有强烈的离职动机,从而导致窃取数据、搞破坏,这些数据是可以通过HR系统检测到的指标。 2. 背景及行为指标 背景侧重于历史记录,很多公司把敏感岗位背调作为招聘必选项。行为则是根据员工工作上的行为方式逐步形成。 参与某些团体指的是例如国泰航空前阵时间的事件,参与了社会事件而带来的对飞行安全的破坏、泄漏用户信息。而在犯罪前科上,要兼顾考虑各类外包人员。背调不只是在入职前进行,在晋升时也需要进行。其他类型不再赘述。 3. 信息安全指标 内部欺诈是利用工作流程,掌握了规则后获利行为,例如风控部门的员工,就可能掌握规则从而绕过获利,检测上很难发现,但可以通过其他维度,例如与情报、钓鱼、黑灰产关联等。而数据窃取则可能有一些对抗绕过,比如对数据加密,使用代理等,可以根据基线、阈值来做关联判断。商业间谍则考虑账号、设备、竞对关联、行为。 4. 终端指标 终端是指用户的终端电脑、手机等,由于员工可以对终端进行操作,所以他可能会篡改数据,破坏监控agent,因此要额外检测agent和日志的运行情况,尤其是当员工有离职等不良倾向的时候需要重点关联检测。 数据窃取可通过打印、复制外发文件,把这个日志和背景行为指标关联,可以监测到数据窃取、商业间谍行为。内部人员如果登陆其他同事账号,目的可能是隐藏自己,提升权限,又或者代其他员工操作。多次登陆失败则说明账号正在被暴力破解。终端多用户登录代表的风险则更大,但要注意有些例如三班倒的工作岗位、测试岗位会存在公用设备现象,但排除这些岗位也行后,其他人员需要重点关注。当然还有其他维度,例如非正常工作时间,只不过在互联网公司这个太常见了,所以没有加入特征。 对终端的检测几乎发现不到什么内部欺诈,欺诈行为一般出现在业务层。 5. 服务端指标 对应的则是用户在服务端的操作行为: 对集中存放的审计日志进行修改是个明确信号,有人在试图抹掉痕迹。同账号多设备则表明账号可能被泄漏,也可能是横向移动攻击。 四、总结 以上所有的指标,单一来看可能只是一个异常,因此需要多指标关联权重,从而提炼出真正的风险。但指标不仅限于此,可以根据自身业务数据形成更广阔的检测维度,例如一个销售,从来不上传新合同,但总是在大量查询历史合同。某个员工的手机号和采购供应商相同,员工与黑产多次出现在同一地址,同一时间维度内同WIFI出现大量注册等等诸如此类的规则,都能够形成某个单项指标。 除了自身数据,也可接入外部数据验证,例如员工是否多头借贷,历史工作单位验证是否一致等。最后,还可以利用情报数据,反向验证内部人员作案。 空间很大,可做的事情有很多,不要自己给信息安全设置了边界。

优秀的个人博客,低调大师

PaaS下半场,任重且

2019年已经到来,PaaS经过懵懂的2015年,觉醒的2016年,试水的2017年,在2018年迎来第一个小高潮。 PaaS(Platform-as-a-Service,平台即服务)是将数据库、开发工具和其他支持应用服务的组件整合为一个平台作为服务提供给开发者,从而使得开发者无需关注应用程序运行和维护时所需要的资源,用户远程使用开发者开发的应用软件。作为云计算模型中的能力层,PaaS可以帮助企业更好地将业务与IT融为一体,从而也成为传统企业数字化转型,进行架构升级的必然选择。 PaaS是当前云计算增长最快的细分市场当前全球云计算三种交付模式中,PaaS份额最小,但增速最快。另一家市场研究公司Gartner则将PaaS细分成几十个领域,每一个细分领域在技术上均走上成熟,开始进入应用和产业化阶段。据前瞻产业研究院发布的《中国云计算产业发展前景与投资战略规划分析报告》统计数据显示,2012年全球PaaS市场规模为12亿美元。2014年全球PaaS市场规模超33亿美元。到了2016年全球PaaS市场规模快速增长至90亿美元。截止到2017年全球PaaS市场规模已经突破100亿美元,达到了114亿美元,同比增长26.7%。 PaaS服务与开发过程中的需求密切相关,细分领域众多。从部署的角度可分为数据库服务、应用开发、应用基础架构、中间件服务,其中APaaS、IPaaS占比较大。 云计算服务交付模式主要有IaaS、PaaS、SaaS三类:IaaS(Infrastructure-as-a-Service,基础设施即服务)是指通过互联网获得计算、存储资源,服务商不参与后续的操作系统、中间件及应用程序开发。 SaaS(Software-as-a-Service,软件即服务)是指通过互联网提供特定的软件产品,服务商介入程度最深。而PaaS是指通过互联网获得软件开发环境及中间件,服务商不参与后续的应用程序开发。 PaaS在云服务产业链中具备承上启下作用。云服务产业链中,位于底部(产业链上游)的一层可以为其上面(产业链下游)的任何一层或者终端客户提供服务。虽然PaaS有众多的细类,不管是哪一个细类,在成熟度上都落后于SaaS和IaaS,其市场发展相对滞后,刚刚到达快速发展期的顶端,但其基础理论基本成熟,即将进入产业化规模的爬坡期阶段。 2018年国内外PaaS市场发展现状目前国内外发展PaaS业务平台企业大多具有行业优势,例如Google、AWS和新浪则凭借其在互联网领的垄断地位将其业务纵向发展到PaaS云计算领域,而Microsoft、Oracle、IBM则凭借其软件行业的领先地位横向发展到PaaS云计算业务中,其他企业发展PaaS业务则较少。 据2017年全球PaaS市场份额比例统计情况来看,AWS、Microsoft、IBM、Salesforce是如今PaaS市场的4个领头羊。分析海外PaaS四大龙头,可以看到:(1)均为平台型PaaS,较强的综合开发能力是其强项所在;(2)上游IaaS、下游SaaS企业跨界具备天然优势。 单看国内PaaS市场,由于我国云计算市场处于起步阶段,PaaS细分市场份额较低。2014年我国PaaS市场规模仅为26亿元,2017年市场规模达到54亿元。 目前,国内平台型PaaS主要以巨头为主,以百度、新浪、阿里、京东为代表。阿里云位居第一且其在PaaS市场的地位难以撼动。其他PaaS服务商份额均很小,市场高度碎片化,目前有电信运营商、互联网巨头、传统软件企业、创业企业四类企业布局。 PaaS未来发展趋势预测1、业务量增长驱动更多PaaS部署:PaaS云计算技术将可提供灵活、可控、高效的业务支持,满足企业不断增长的业务发展需要。 2、IaaS提供商将涵盖PaaS IT:为基础架构提供交付的IaaS技术将逐渐成为一种市场领先的商业模式,为了区别于彼此,IaaS领头羊将会整合一些中间件技术,或者PaaS提供商基于堆栈下层构建绕过它们。 3、公有PaaS将赢得中小企业市场:低廉的订购价格和标准化的中间件服务解决方案将最终推动中小企业市场致力于公共PaaS. 4、私有PaaS将夺得大企业市场:大型企业势必融入云端。但一应俱全的公共PaaS服务存在可靠性问题、安全风险和局限性问题,势必会让这些大企业热衷于私有PaaS.企业云将基于私有PaaS中间件,并最终会寻求一种混合方式来实现。 5、开源PaaS平台将蓬勃发展:随着业务量增加,使得云架构迎来变革,从而出现高度定制化云环境的需求。大企业需要构建于中间件之上的云架构,而企业开发人员和CIO需要可扩展的开源PaaS IT. 6、开源PaaS平台将通过Linux发行版扩展:对开源的PaaS技术而言,Linux解决方案将成为一种更强大的间接发行渠道。 7、PaaS与各大管理工具实现融合:与IaaS一样,PaaS中间件提供商需要灵活高效才能生存下来。PaaS技术(包括开源技术和专有技术)会变得更具有扩展性,并且与Chef、Puppet和Juju等协调和配置管理工具进行融合。 虽然全球PaaS市场起步较晚,但其发展势头强劲。根据Gartner预测,在2020年前PaaS市场的复合增长率将会达到18.24%,增速仅次于IaaS,到2020年规模达到208亿美元。 欢迎转载,转载请注明出处:怡海软件(http://www.frensworkz.com/)

优秀的个人博客,低调大师

护士是患者治愈的一桥梁!

瑞典隆德大学最新研究表明,医院的护士越少,患者死亡率越高,且该死亡率不会因非护士类护工的替代而降低。智慧医疗应用将改善这一现状! 研究团队于1980年启动该项研究,历时37年,对包括瑞典与英国在内的欧洲9个国家的300家急救医院进行了解和调查。在瑞典,74%的护士表示,她们会因为一些原因丢下一些必须照顾的病人不管,而在英国,这一数字达到了86%。 调查中,护士们被要求回答一份调查问卷,统计他们因倒班、调休等因素无法照顾患者的情况。调查结果显示,便携式医疗仪器对于那些医护人员不足的医院来说,每轮班一次,患者死亡的风险就会增加百分之二。调查中的患者平均由三班医护人员护理,与那些由全职医护人员护理的患者相比,他们的死亡风险上升了不止百分之六。护士对患者看护的越少,患者死亡率也会升高;护士照顾水平不足会直接影响患者的生命。 朗锐慧康对此表示:“护士对于挽救患者生命占据着极大的作用。病人在医院得到的医疗照护如何,不仅仅由治疗医师的水平决定,护理人员也起着不可忽视的作用。因此,医护人员在保证护理质量的同时也应该保证数量。” 现在从医人员不足,医院各项设备资源也不够完善,需要政府和医疗行业从业者的共同努力。朗锐慧康始终致力于提供实用的健康终端产品及技术方案,配套高效可靠的健康信息化平台支撑,帮助行业客户快速实现健康物联网应用实施。根据其方案定制出的多功能健康一体机,能够方便医院对病患的日常检查和护理,包括心电图、尿常规、血压、血氧、体温、脉搏、血糖、检眼镜、检耳镜等嵌入式主板应用检查功能,可开展如居民电子健康档案建立、儿童管理、老年人管理、慢性病管理等国家要求的11项公共卫生管理服务,将对医生和护士提供一定的帮助,减轻了医院人员的负担,患者的康复也能得到更大的保障。 本文由朗锐慧康编辑整理(http://www.lrioh.com)(http://www.lrist.com),转载请注明出处。

优秀的个人博客,低调大师

基于容器服务的持续集成与云端交付(一)- 交付之

前言 随着微服务架构与容器虚拟化技术的发展,持续集成与持续交付的概念又重新回到了大家的视野,越来越多的公司开始使用持续集成的系统来解决频繁发布带来的质量问题;使用持续交付的工具来实现代码在不同环境上的自动部署。原本有些学院派乌托邦式的思想正被千千万万次的集成与部署证明着它应有的价值。那么究竟是因为什么让持续集成与持续交付这个已经不再年轻的软件开发与交付的思想重新焕发绽放迷人的光彩呢? 传统软件交付之殇 传统软件的开发与交付的周期都很漫长,一款普通的企业软件通常需要十几个开发人员,几个月的时间来完成,从需求的分析、系统的设计、编写测试用例、系统开发、单元测试、组装测试到交付调试。有条不紊的流程与规范像一辆绿皮火车下的枕木,稳定而可靠的保证整个系统缓慢的推进,每一次交付、升级,都需要提供基础的硬件、软件的环境、软件的代码、软件的文档与手册。

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册