Java面试全集(下)(框架方面)

点此前往原文


1、什么是ORM?

答:对象关系映射(Object-Relational Mapping)是一种为了解决程序的面向对象模型与数据库的关系模型互不匹配问题的技术;简单的说,ORM是通过使用描述对象和数据库之间映射的元数据(在Java中可以使用XML或者是注解的),将程序中的对象自动持久化到关系数据库中或者将关系数据库表中的行转换成Java对象,其本质上就是将数据从一种形式转换到另外一种形式。

2、持久层设计要考虑的问题有哪些?你用过的持久层框架有哪些?

答:所谓“持久”就是将数据保存到可掉电式存储设备中以便今后使用,简单的说就是将内存中的数据保存到关系数据库、文件系统、消息队列等提供持久化支持的设备中。持久层就是系统中专注于实现数据持久化的相对独立的层面。

持久层设计的目标包括:

-数据存储逻辑的分离,提供抽象化的数据访问接口

-数据访问底层实现的分离,可以在不修改代码的情况下切换底层实现

-资源管理和调度的分离,在数据访问层实现统一的资源调度(如缓存机制)

-数据抽象,提供更面向对象的数据操作

持久层框架有

-MyBatis

-Hibernate

3、MyBatis中使用#$书写占位符的区别?

答:#将传入的数据都当成一个字符串,会对传入的数据自动加上引号;$将传入的数据直接显示生成在SQL中。

注意:$占位符可能导致SQL注入攻击,能用#的地方就不要用$,写order by子句的时候应用$而不是#。

4、解释一下MyBatis中命名空间(namespace)的作用。

答:在大型项目中,可能存在大量的SQL语句,这时候为每个SQL语句起一个唯一的标识(ID)就变得并不容易了。为了解决这个问题,在MyBatis中,可以为每个映射文件起一个唯一的命名空间,这样定义在这个映射文件中的每个SQL语句就成了定义在这个命名空间中的一个ID。只要我们能够保证每个命名空间中这个ID是唯一的,即使在不同的

映射文件中的语句ID相同,也不会再产生冲突了。

5、MyBatis中的动态SQL是什么意思?

答:对于一些复杂的查询,我们可能会指定多个查询条件,但是这些条件可能存在也可能不存在。例如在58同城上找房子,我们可能指定面积,楼层和所在位置来查找房源,也可能指定面积、价格、户型和所在位置来查找房源,此时就需要根据用户指定的条件动态生成SQL语句。如果不使用持久层框架我们可能需要自己拼装SQL语句,还好MyBatis提供了动态SQL的功能来解决这个问题。

MyBatis中用于实现动态SQL的元素主要有:

-if

-choose/when/otherwise

-trim(where,set)

-foreach

下面是映射片段

	<select id="getEmp" resultType="emp">
		select * from tbl_employee where 1=1
		<if test="lastName!=null and lastName !=''">
			and last_name like #{lastName}
		</if>
		<if test="email != nukk and email !=''">
			and email=#{email}
		</if>
		<if test="gender!=null and gender !=''">
			and gender=#{gender}
		</if>
	</select>

当然也可以如下所示

	<select id="getEmp" resultType="emp">
		select * from tbl_employee where 1=1
		<choose>
			<!-- 如果带了名字就用名字查,如果带了邮箱就用邮箱查,只会进入一个 -->
			<when test="lastName!=null and lastName!=''">
				and last_name like #{lastName}
			</when>
			<when test="email!=null and email!=''">
				and email=#{email}
			</when>
			<otherwise>
				and gender=#{gender}
			</otherwise>
		</choose>
	</select>

再看下下面这个例子

	<select id="getEmpIdIn" resultType="emp">
		select * from tbl_employee where id in
		<foreach collection="list" item="item_id" open="(" separator="," close=")">
			#{item_id}
		</foreach>
	</select>

6、什么是IOC和DI?DI是如何实现的?

答:IOC叫控制反转,是Inversion of Control 的缩写,DI(Dependency Injection)叫依赖注入,是对IOC更简单的诠释。控制反转是把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。所谓的“控制反转”就是对组件对象控制权的转移,从程序代码本身转移到了外部容器,由容器来创建对象并管理对象之间的依赖关系。依赖注入的基本原则是应用组件不应该负责查找资源或者其他依赖的协作对象。配置对象的工作应该由容器负责,查找资源的逻辑应该从应用组件的代码中提取出来,交给容器来完成。DI是对IOC更准确的描述,即组件之间依赖关系由容器在运行期决定,形象的来说,即由容器动态的将某种依赖关系注入到组件中。

举个例子:一个类A需要使用到接口B中的方法,那么需要为类A和接口B建立关联或依赖关系,最原始的方法是在类A中创建一个接口B的实现类C的实例,但是这种方法需要开发人员自行维护二者的依赖关系,也就是说当依赖关系发生变动的时候需要修改代码并重新构建整个系统。如果通过一个容器来管理这些对象以及对象的依赖关系,则只需要在类A中定义好用于关联接口B的方法(构造器或setter方法),将类A和接口B的实现类C放入容器中,通过对容器的配置实现二者的关联。

依赖注入可以通过setter方法注入(设值注入)、构造器注入和接口注入三种方式来实现,Spring支持setter注入和构造器注入,通常使用构造器注入来注入必须的依赖关系,对于可选的依赖关系,则setter注入是更好的选择,setter注入需要类提供无参构造器或者无参的静态工厂来创建对象。

7.Spring中Bean的作用域有哪些?

答:在Spring早期的版本中,仅有两个作用域:singleton和prototype,前者表示Bean以单例的方式存在;后者表示每次从容器中调用Bean时,都会返回一个新的实例,prototype通常翻译为原型。

补充:设计模式中的创建型模式中也有一个原型模式,原型模式也是一个常用的模式,例如做一个室内设计软件,所有的素材都在工具箱中,而每次从工具箱中取出的都是素材对象的一个原型,可以通过对象克隆来实现原型模式。

Spring2.x中针对WebApplicationCOntext新增了3个作用域,分别是:request(每次HTTP请求都会创建一个新的Bean)、session(同一个HttpSession共享同一个Bean,不同的HttpSession使用不同的Bean)和globalSession(同一个全局Session共享一个Bean)。

说明:单例模式和原型模式都是重要的设计模式。一般情况下,无状态或状态不可变的类适合使用单例模式。在传统开发中,由于DAO持有Connection这个非线程安全对象因而没有使用单例模式;但在Spring环境下,所有DAO类对可以采用单例模式,因为Spring利用AOP和Java API中的ThreadLocal对非线程安全的对象进行了特殊处理。

ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。ThreadLocal,顾名思义是线程的一个本地化对象,当工作于多线程中的对象使用TreadLocal维护变量时,ThreadLocal为每个使用该变量的线程分配一个独立的变量副本,所以每一个线程都可以独立的改变自己的副本,而不影响其他线程所对应的副本。从线程的角度看,这个变量就像是线程的本地变量。

ThreadLocal类非常简单好用,只有四个方法,能用的上的也就是下面三个方法:

-void set(T value):设置当前线程的线程局部变量的值

-T get():获得当前线程所对应的线程局部变量的值

-void remove():删除当前线程中线程局部变量的值

ThreadLocal是如何做到为每一个线程维护一份独立的变量副本的呢?在ThreadLocal类中有一个Map,键为线程对象,值是其线程对应的变量的副本,自己要模拟实现一个ThreadLocal类其实并不困难,代码如下:

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class MyThreadLocal<T> {
    private Map<Thread, T> map = Collections.synchronizedMap(new HashMap<Thread, T>());

    public void set(T newValue) {
        map.put(Thread.currentThread(), newValue);
    }

    public T get() {
        return map.get(Thread.currentThread());
    }

    public void remove() {
        map.remove(Thread.currentThread());
    }
}

8.解释一下什么叫AOP(面向切面编程)

答:AOP(Aspect-Oriented Programming)指一种程序设计泛型,该泛型以一种称为切面(aspect)的语言构造为基础,切面是一种新的模块化机制,用来描述分散在对象、类或方法中的横切关注点(crosscutting concern)。

9.你是如何理解“横切关注”这个概念的?

答:“横切关注”是会影响到整个应用程序的关注功能,它跟正常的业务逻辑是正交的,没有必然的联系,但是几乎所有的业务逻辑都会涉及到这些关注功能。通常,事务,日志,安全性等关注就是应用中的横切关注功能。

10.你如何理解AOP中的连接点(Joinpoint)、切点(Pointcut)、增强(Advice)、引介(Introduction)、织入(Weaving)、切面(Aspect)这些概念?

a.连接点(Joinpoint):程序执行的某个特定位置(如:某个方法调用前、调用后、方法抛出异常后)。一个类或一段程序代码拥有一些具有边界性质的特定点,这些代码中的特定点就是连接点。Spring仅支持方法连接点。

b.切点(Pointcut):如果连接点相当于数据中的记录,那么切点相当于查询条件,一个切点可以匹配多个连接点。Spring AOP的规则解析引擎负责解析切点所设定的查询条件,找到对应的连接点。

c.增强(Adivice):增强是织入到目标类连接点上的一段程序代码。Spring提供的增强接口都是带方位名字的,如:BeforeAdvice、AfterReturningAdivce、ThrowsAdvice等。

说明: Advice在国内的很多书面资料中都被翻译成"通知",但是很显然这个翻译无法表达其本质,有少量的读物上将这个词翻译为"增强",这个翻译是对Advice较为准确的诠释,我们通过AOP将横切关注功能加到原有的业务逻辑上,这就是对原有业务逻辑的一种增强,这种增强可以是前置增强、后置增强、返回后增强、抛异常时增强和包围型增强。

d.引介(Introduction):引介是一种特殊的增强,它为类添加一些属性和方法。这样,即使一个业务原来没有实现某个接口,通过引介功能,可以动态的为该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。

e.织入(Weaving):织入是将增强添加到目标类具体连接点上的过程,AOP有三种织入方式:1.编译期织入,需要特殊的Java编译期(例如Aspect的ajc)2.装载期织入:要求使用特殊的类装载器,在装载类的时候对类进行增强。3.运行时织入:在运行时为目标类生成代理实现增强。Spring采用了动态代理的方式实现了运行时织入,而AspectJ采用了编译期织入和装载期织入的方式。

f.切面(Aspect):切面是由切点和增强(引介)组成的,它包括了对横切关注功能的定义,也包括了对连接点的定义。

补充:代理模式是GoF提出的23种设计模式中最为经典的模式之一,代理模式是对象的结构模式,它给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。简单的说,代理对象可以完成比原对象更多的职责,当需要为原对象添加横切关注功能时,就可以使用原对象的代理对象。我们在打开Office系列的Word文档时,如果文档中有插图,当文档刚加载时,文档中的插图都只是一个虚框占位符,等用户真正翻到某页要查看该图片时,才会真正加载这张图,这其实就是对代理模式的使用,代替真正图片的虚框就是一个虚拟代理;Hibernate的load方法也是返回一个虚拟代理对象,等用户真正需要访问对象的属性时,才向数据库发出SQL语句获得真实对象。


11.Spring中自动装配的方式有哪些?

答:

-no:不进行自动装配,手动设置Bean的依赖关系

-byName:根据Bean的名字进行自动装配

-byType:根据Bean的类型进行自动装配

-constructor:类似于byType,不过是应用于构造器的参数,如果正好有一个Bean与构造器的参数类型相同则可以自动装配,否则会报错

-autodetect:如果有默认的构造器,则通过constructor的方式进行自动装配,否则使用byType的方式进行自动装配

12.Spring中如何使用注解来配置Bean?有哪些相关的注解?

答:首先需要在Spring配置文件中增加如下配置:

<context:componet-scan base-package="org.example"/>
然后可以用@Component、@Controller、@Service、@Repository注解来标注需要由Spring IoC容器进行对象托管的类。

-@Controller通常用于控制器

-@Service通常用于业务逻辑类

-@Repository通常用于仓储类(例如我们的DAO实现类)

-@Component普通类

13.Spring支持的事务管理类型有哪些?你在项目中使用哪种方式?

答:Spring支持编程式事务管理和声明式事务管理。许多Spring框架的用户选择声明式管理,因为这样和应用程序的关联较少,因此更加符合轻量级容器的概念。声明式事务管理要优于编程式事务管理,尽管在灵活性方面它弱于编程式事务管理,因为编程式事务允许你通过代码控制业务。

事务的四个关键属性(ACID):

-原子性(atomicity):事务是一个院子操作,由一系列动作组成,事务的原子性确保动作要么全部完成要么完全不起作用。

-一致性(consistency):一旦所有的事务动作完成,事务就被提交,数据和资源就处于一种满足业务规则的一致性状态中。

-隔离性(isolation):可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。

-持久性(durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,通常情况下,事务的结果被写道持久化存储器中。


Spring提供了如下所示的事务管理器



事务的传播属性:当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。


Spring支持的事务传播行为



事务的隔离级别

当同一个应用程序或者不同应用程序中的多个事务在同一个数据集上并发执行时,可能会出现许多意外的问题。

-脏读:对于两个事务T1,T2。T1读取了已经被T2更新但还没有被提交的字段,之后,若T2回滚,T1读取的内容就是临时且无效的

-不可重复读:对于两个事务T1,T2。T1读取了一个字段,然后T2更新该字段,之后T1再次读取同一个字段,值就不同了

-幻读:对于两个事务T1,T2。T1从一个表中读取了一个字段,然后T2在该表中插入了一些新的行,之后如果T1再次读取同一个表,就会多出几行。

Spring支持的隔离级别


注:Oracle支持两种事务隔离级别:READ_COMMITED,SERIALIZABLE

基于注解的声明式事务:

	<!-- Spring事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	<!-- 启用事务管理器 -->
	<tx:annotation-driven transaction-manager="transactionManager"/>



基于XML的声明式编程

	<!-- =====================事务控制的配置=================== -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 开启基于注解的事务,使用xml配置形式的事务(重要的基本都是使用配置式) -->
	<aop:config>
		<!-- 切入点表达式 -->
		<aop:pointcut expression="execution(* com.zy.crud.service..*(..) )" 
			id="txPoint"/>
		<!-- 配置事务增强 -->
		<aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"/>
	</aop:config>
	<!-- 配置事务增强,事务如何切入 -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<!-- 所有方法都是事务方法 -->
			<tx:method name="*"/>
			<!-- 以get开始的所有方法,可以进行调优 -->
			<tx:method name="get*" read-only="true"/>
		</tx:attributes>
	</tx:advice>
14.如何在Web项目中配置Spring的IoC容器?

在web.xml配置文件中做出如下配置:

	<!-- 1.启动Spring容器 -->
	<!-- needed for ContextLoaderListener -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:applicationContext.xml</param-value>
	</context-param>

	<!-- Bootstraps the root web application context before servlet initialization -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
15.如何在Web项目中配置Spring MVC?

在web.xml做以下配置

<!--2.Spring MVC的前端控制器,拦截所有请求  -->
	<!-- The front controller of this Spring Web application, responsible for handling all application requests -->
	<servlet>
		<servlet-name>dispatcherServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<!-- Map all requests to the DispatcherServlet for handling -->
	<servlet-mapping>
		<servlet-name>dispatcherServlet</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
	
	<!-- 3.字符编码过滤器 ,一定要放在所有过滤器之前-->
	<filter>
		<filter-name>CharacterEncodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>utf-8</param-value>
		</init-param>
		<init-param>
			<param-name>forceRequestEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
		<init-param>
			<param-name>forceResponseEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>CharacterEncodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	
	<!-- 4.使用Rest风格的URI,将页面普通的post请求转为指定的delete或者PUT请求 -->
	<filter>
		<filter-name>HiddenHttpMethodFilter</filter-name>
		<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter
		</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>HiddenHttpMethodFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	
	<!-- 5.能直接使用PUT的过滤器 -->
	<filter>
		<filter-name>HttpPutFormContentFilter</filter-name>
		<filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>HttpPutFormContentFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
16.Spring MVC的工作原理是怎么样的?

如下图所示


1.客户端的所有请求都交给前端控制器DispatcherServlet来处理,它会负责调用系统的其他模块来真正处理用户的请求。

2.DispatcherServlet收到请求后,将根据请求的信息(包括URL,HTTP协议方法、请求头、请求参数、Cookie等)以及HandlerMapping的配置找到处理该请求的Handler(任何一个对象都可以作为请求的Handler)。

3.在这个地方Spring会通过HandlerAdapter对该处理器进行封装。

4.HandlerAdapter是一个适配器,它用统一的接口对各种Handler中的方法进行调用。

5.Handler完成对用户请求的处理后,会返回一个ModelAndView对象给DispatcherServlet,ModelAndView顾名思义,包含了数据模型以及相应的视图的信息。

6.ModelAndView的视图是逻辑视图,DispatcherServlet还要借助ViewResolver完成从逻辑视图到真实视图对象的解析工作。

7.当得到真正的视图对象后,DispatcherServlet会利用视图对象模型数据进行渲染。

8.客户端得到响应,可能是一个普通的HTML页面,也可能是XML或JSON字符串,还可以是一张图片或者一个PDF文件。

17.如何在Spring Ioc容器中配置数据源?

答:C3P0

<context:property-placeholder location="classpath:dbconfig.properties"/>
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
		<property name="driverClass" value="${jdbc.driverClass}"></property>
		<property name="user" value="${jdbc.user}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean>
dbconfig.properties

jdbc.jdbcUrl=jdbc:mysql://localhost:3306/ssm_crud
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.user=root
jdbc.password=123456

18.选择使用Spring框架的原因?(Spring框架为企业级开发带来的好处有哪些?)

答:

-非侵入式:支持基于POJO的编程模式,不强制性的要求实现Spring框架中的接口或继承Spring框架中的类。

-IoC容器:IoC容器帮助应用程序管理对象以及对象之间的依赖关系,对象之间的依赖关系如果发送了改变,只需要修改配置文件而不是代码,因为代码的修改可能意味着项目的重新构建和完整的回归测试。有了IoC容器,程序员再也不需要自己编写工厂、单例、这一点特别符合Spring的精神“不要重复的发明轮子”。

-AOP(面向切面编程):将所有的横切关注功能封装到切面中(aspect)中,通过配置的方式将横切关注功能动态的添加到目标代码上,进一步实现了业务逻辑和系统服务之间的分离。另一方面,有了AOP程序员可以省去很多自己写代理类的工作。

-MVC:SpringMVC是非常优秀的

-事务管理:Spring以宽广的胸怀接纳多种持久层技术,并且为其提供了声明式的事务管理。

19.在Web项目中如何获得Spring的IoC容器?

WebApplicationContext ctx = 
WebApplicationContextUtils.getWebApplicationContext(servletContext)
20.阐述Spring框架中Bean的生命周期?

答:

1.Spring IoC容器找到关于Bean的定义并实例化该Bean

2.Spring IoC容器对Bean进行依赖注入

3.如果Bean实现了BeanNameAware接口,则将该Bean的id传给setBeanName方法。

4.如果Bean实现了BeanFactoryAware接口,则将BeanFactory对象传给setBeanFactory方法。

5.如果Bean实现了BeanPostProcessor接口,则调用其postProcessBeforeInitialization方法。

6.如果Bean实现了InitializingBean接口,则调用其afterProperty方法。

7.如果有和Bean关联的BeanPostProcessors对象,则这些对象的postProcessAfterInitialization方法被调用

8.当销毁Bean实例时,如果Bean实现了DisposableBean接口,则调用其destory方法


21.依赖注入时如何注入集合属性?

答:可以在定义Bean属性时,通过<list>  <set>  <map>  <props>分别为其注入列表,集合,映射和键值都是字符串的映射属性

22.Spring中的自动装配有哪些限制?

答:

- 如果使用了构造器注入或者setter注入,那么将覆盖自动装配的依赖关系。
- 基本数据类型的值、字符串字面量、类字面量无法使用自动装配来注入。
- 优先考虑使用显式的装配来进行更精确的依赖注入而不是使用自动装配。


23.大型网站在架构上应当考虑哪些问题?

答:
- 分层:分层是处理任何复杂系统最常见的手段之一,将系统横向切分成若干个层面,每个层面只承担单一的职责,然后通过下层为上层提供的基础设施和服务以及上层对下层的调用来形成一个完整的复杂的系统。计算机网络的开放系统互联参考模型(OSI/RM)和Internet的TCP/IP模型都是分层结构,大型网站的软件系统也可以使用分层的理念将其分为持久层(提供数据存储和访问服务)、业务层(处理业务逻辑,系统中最核心的部分)和表示层(系统交互、视图展示)。需要指出的是:(1)分层是逻辑上的划分,在物理上可以位于同一设备上也可以在不同的设备上部署不同的功能模块,这样可以使用更多的计算资源来应对用户的并发访问;(2)层与层之间应当有清晰的边界,这样分层才有意义,才更利于软件的开发和维护。
- 分割:分割是对软件的纵向切分。我们可以将大型网站的不同功能和服务分割开,形成高内聚低耦合的功能模块(单元)。在设计初期可以做一个粗粒度的分割,将网站分割为若干个功能模块,后期还可以进一步对每个模块进行细粒度的分割,这样一方面有助于软件的开发和维护,另一方面有助于分布式的部署,提供网站的并发处理能力和功能的扩展。
- 分布式:除了上面提到的内容,网站的静态资源(JavaScript、CSS、图片等)也可以采用独立分布式部署并采用独立的域名,这样可以减轻应用服务器的负载压力,也使得浏览器对资源的加载更快。数据的存取也应该是分布式的,传统的商业级关系型数据库产品基本上都支持分布式部署,而新生的NoSQL产品几乎都是分布式的。当然,网站后台的业务处理也要使用分布式技术,例如查询索引的构建、数据分析等,这些业务计算规模庞大,可以使用Hadoop以及MapReduce分布式计算框架来处理。
- 集群:集群使得有更多的服务器提供相同的服务,可以更好的提供对并发的支持。
- 缓存:所谓缓存就是用空间换取时间的技术,将数据尽可能放在距离计算最近的位置。使用缓存是网站优化的第一定律。我们通常说的CDN、反向代理、热点数据都是对缓存技术的使用。
- 异步:异步是实现软件实体之间解耦合的又一重要手段。异步架构是典型的生产者消费者模式,二者之间没有直接的调用关系,只要保持数据结构不变,彼此功能实现可以随意变化而不互相影响,这对网站的扩展非常有利。使用异步处理还可以提高系统可用性,加快网站的响应速度(用Ajax加载数据就是一种异步技术),同时还可以起到削峰作用(应对瞬时高并发)。&quot;能推迟处理的都要推迟处理"是网站优化的第二定律,而异步是践行网站优化第二定律的重要手段。
- 冗余:各种服务器都要提供相应的冗余服务器以便在某台或某些服务器宕机时还能保证网站可以正常工作,同时也提供了灾难恢复的可能性。冗余是网站高可用性的重要保证。


24.你用过的网站前端优化技术有哪些?

答:
① 浏览器访问优化:
- 减少HTTP请求数量:合并CSS、合并JavaScript、合并图片(CSS Sprite)
- 使用浏览器缓存:通过设置HTTP响应头中的Cache-Control和Expires属性,将CSS、JavaScript、图片等在浏览器中缓存,当这些静态资源需要更新时,可以更新HTML文件中的引用来让浏览器重新请求新的资源
- 启用压缩
- CSS前置,JavaScript后置
- 减少Cookie传输
② CDN加速:CDN(Content Distribute Network)的本质仍然是缓存,将数据缓存在离用户最近的地方,CDN通常部署在网络运营商的机房,不仅可以提升响应速度,还可以减少应用服务器的压力。当然,CDN缓存的通常都是静态资源。
③ 反向代理:反向代理相当于应用服务器的一个门面,可以保护网站的安全性,也可以实现负载均衡的功能,当然最重要的是它缓存了用户访问的热点资源,可以直接从反向代理将某些内容返回给用户浏览器。


25.你用过的应用服务器优化技术有哪些?

答:
① 分布式缓存:缓存的本质就是内存中的哈希表,如果设计一个优质的哈希函数,那么理论上哈希表读写的渐近时间复杂度为O(1)。缓存主要用来存放那些读写比很高、变化很少的数据,这样应用程序读取数据时先到缓存中读取,如果没有或者数据已经失效再去访问数据库或文件系统,并根据拟定的规则将数据写入缓存。对网站数据的访问也符合二八定律(Pareto分布,幂律分布),即80%的访问都集中在20%的数据上,如果能够将这20%的数据缓存起来,那么系统的性能将得到显著的改善。当然,使用缓存需要解决以下几个问题:
- 频繁修改的数据;
- 数据不一致与脏读;
- 缓存雪崩(可以采用分布式缓存服务器集群加以解决,memcached是广泛采用的解决方案);
- 缓存预热;
- 缓存穿透(恶意持续请求不存在的数据)。
② 异步操作:可以使用消息队列将调用异步化,通过异步处理将短时间高并发产生的事件消息存储在消息队列中,从而起到削峰作用。电商网站在进行促销活动时,可以将用户的订单请求存入消息队列,这样可以抵御大量的并发订单请求对系统和数据库的冲击。目前,绝大多数的电商网站即便不进行促销活动,订单系统都采用了消息队列来处理。
③ 使用集群。
④ 代码优化:
- 多线程:基于Java的Web开发基本上都通过多线程的方式响应用户的并发请求,使用多线程技术在编程上要解决线程安全问题,主要可以考虑以下几个方面:A. 将对象设计为无状态对象(这和面向对象的编程观点是矛盾的,在面向对象的世界中被视为不良设计),这样就不会存在并发访问时对象状态不一致的问题。B. 在方法内部创建对象,这样对象由进入方法的线程创建,不会出现多个线程访问同一对象的问题。使用ThreadLocal将对象与线程绑定也是很好的做法,这一点在前面已经探讨过了。C. 对资源进行并发访问时应当使用合理的锁机制。
- 非阻塞I/O: 使用单线程和非阻塞I/O是目前公认的比多线程的方式更能充分发挥服务器性能的应用模式,基于Node.js构建的服务器就采用了这样的方式。Java在JDK 1.4中就引入了NIO(Non-blocking I/O),在Servlet 3规范中又引入了异步Servlet的概念,这些都为在服务器端采用非阻塞I/O提供了必要的基础。
- 资源复用:资源复用主要有两种方式,一是单例,二是对象池,我们使用的数据库连接池、线程池都是对象池化技术,这是典型的用空间换取时间的策略,另一方面也实现对资源的复用,从而避免了不必要的创建和释放资源所带来的开销。
















  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值