Java程序员面试题集(151-180)

Java面试题集(151-180)html

摘要:这部分包含了Spring、Spring MVC以及Spring和其余框架整合以及测试相关的内容,除此以外还包含了大型网站技术架构相关面试内容。前端

 

151. Spring中的BeanFactory和ApplicationContext有什么联系?java

答:Spring经过配置文件描述Bean以及Bean之间的依赖关系,利用Java的反射机制实现Bean的实例化,并创建Bean之间的依赖关系,在此基础上,Spring的IoC容器还提供了Bean实例缓存、生命周期管理、Bean实例代理、事件发布、资源装载等高级服务。BeanFactory是Spring框架最核心的接口,它提供了IoC容器的配置机制。ApplicationContext创建在BeanFactory之上,提供了更多面向应用的功能,包括对国际化和框架事件体系的支持。一般将BeanFactory称为IoC容器,而ApplicationContext称为应用上下文,前者更倾向于Spring自己,后者更倾向于开发者,所以被使用得更多。程序员

【补充】反射(reflection)又叫自省(introspection),是得到对象或类型元数据的方法,Java反射机制能够在运行时判断对象所属的类,在运行时构造任意一个类的对象,在运行时得到一个类的属性和方法,在运行时调用对象的方法,或者生成动态代理。在Java中,能够经过类的Class对象得到类的构造器、属性、方法等类的元数据,还能够访问这些属性或调用这些方法,和反射相关的类还包括:web

 

  1. Constructor:表明类的构造器的类。经过Class对象的getConstructors方法能够得到类的全部构造器的数组,Java 5之后的版本还能够经过getConstrcutor(Class... parameterTypes)得到拥有特定参数的构造器对象。Constructor对象的一个主要方法是newInstance,经过该方法能够建立一个类的实例。
  2. Method:表明方法的类。经过Class对象的getDeclaredMethods方法能够得到全部方法的数组,Java 5之后的版本还能够经过getDeclaredMethod(String name, Class... parameterTypes)得到特定签名的方法,其中name是方法名,可变参数表明方法的参数列表。Method对象最重要的方法是invoke(Object obj, Object[] args),其中obj是调用该方法的目标对象,args是传给方法的参数,这样就能够调用指定对象的方法。此外,Method对象还包括如下重要方法:
    • Class getReturnType():获取方法的返回类型。
    • Class[] getParameterTypes():获取方法参数类型的数组。
    • Class[] getExceptionTypes():获取方法异常类型数组。
    • Annotation[][] getParameterAnnotations():得到方法参数注解信息的数组。
  3. Field:表明属性的类。经过Class对象的getDeclaredFields()方法能够获取类的属性数组。经过getDeclaredField(String name)则能够获取某个特定名称的属性。Field对象最重要的方法是set(Object obj, Object value),其中obj是目标对象,而value是要赋给属性的值。

 

除此以外,Java还提供了Package类用于包的反射,Java 5之后的版本还提供了AnnotationElement类用于注解的反射。总之,Java的反射机制保证了能够经过编程的方式访问目标类或对象的全部元素,对于被private和protected访问修饰符修饰的成员,只要JVM的安全机制容许,也能够经过反射进行调用。下面是一个反射的例子:面试

Car.java正则表达式

 

[java] 
 
  1. package com.lovo;  
  2.   
  3. public class Car {  
  4.     private String brand;  
  5.     private int currentSpeed;  
  6.     private int maxSpeed;  
  7.   
  8.     public Car(String brand, int maxSpeed) {  
  9.         this.brand = brand;  
  10.         this.maxSpeed = maxSpeed;  
  11.     }  
  12.       
  13.     public void run() {  
  14.         currentSpeed += 10;  
  15.         System.out.println(brand + " is running at " + currentSpeed + " km/h");  
  16.         if(currentSpeed > maxSpeed) {  
  17.             System.out.println("It's dangerous!");  
  18.         }  
  19.     }  
  20.   
  21. }  
CarTest.java
[java] 
 
  1. package com.lovo;  
  2.   
  3. import java.lang.reflect.Constructor;  
  4. import java.lang.reflect.Field;  
  5. import java.lang.reflect.Method;  
  6.   
  7. public class CarTest {  
  8.   
  9.     public static void main(String[] args) throws Exception {  
  10.         Constructor<Car> con = Car.class.getConstructor(String.class, int.class);  
  11.         Car myCar = (Car) con.newInstance("Benz", 280);  
  12.         Method m = myCar.getClass().getDeclaredMethod("run");  
  13.         for(int i = 0; i < 10; i++) {  
  14.             m.invoke(myCar);  
  15.         }  
  16.         Field f1 = myCar.getClass().getDeclaredField("maxSpeed");  
  17.         Field f2 = myCar.getClass().getDeclaredField("brand");  
  18.         f1.setAccessible(true);  
  19.         f1.set(myCar, 80);  
  20.         f2.setAccessible(true);  
  21.         f2.set(myCar, "QQ");  
  22.         m.invoke(myCar);  
  23.     }  
  24. }  
运行结果:

 



152. Spring中Bean的做用域有哪些?spring

答:在Spring的早期版本中,仅有两个做用域:singleton和prototype,前者表示Bean以单例的方式存在;后者表示每次从容器中调用Bean时,都会返回一个新的实例,prototype一般翻译为原型,而设计模式中的建立型模式中也有一个原型模式,原型模式也是一个经常使用的模式,例如作一个室内设计软件,全部的素材都在工具箱中,而每次从工具箱中取出的都是素材对象的一个原型,能够经过对象克隆来实现原型模式。Spring 2.x中针对WebApplicationContext新增了3个做用域,分别是:request(每次HTTP请求都会建立一个新的Bean)、session(同一个HttpSession共享同一个Bean,不一样的HttpSession使用不一样的Bean)和globalSession(同一个全局Session共享一个Bean)。数据库

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

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

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

 

  • void set(T value):设置当前线程的线程局部变量的值。
  • T get():得到当前线程所对应的线程局部变量的值。
  • void remove():删除当前线程中线程局部变量的值。
ThreadLocal是如何作到为每个线程维护一份独立的变量副本的呢?在ThreadLocal类中有一个Map,键为线程对象,值是其线程对应的变量的副本,本身要模拟实现一个ThreadLocal类其实并不困难,代码以下所示:

 

 

[java] 
 
  1. package com.lovo;  
  2.   
  3. import java.util.Collections;  
  4. import java.util.HashMap;  
  5. import java.util.Map;  
  6.   
  7. public class MyThreadLocal<T> {  
  8.     private Map<Thread, T> map = Collections.synchronizedMap(new HashMap<Thread, T>());  
  9.       
  10.     public void set(T newValue) {  
  11.         map.put(Thread.currentThread(), newValue);  
  12.     }  
  13.       
  14.     public T get() {  
  15.         return map.get(Thread.currentThread());  
  16.     }  
  17.       
  18.     public void remove() {  
  19.         map.remove(Thread.currentThread());  
  20.     }  
  21. }  

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

 

答:

 

  • 链接点:程序执行的某个特定位置(如:某个方法调用前、调用后,方法抛出异常后)。一个类或一段程序代码拥有一些具备边界性质的特定点,这些代码中的特定点就是链接点。Spring仅支持方法的链接点。
  • 切点:若是链接点至关于数据中的记录,那么切点至关于查询条件,一个切点能够匹配多个链接点。Spring AOP的规则解析引擎负责解析切点所设定的查询条件,找到对应的链接点。
  • 加强:加强是织入到目标类链接点上的一段程序代码。Spring提供的加强接口都是带方位名的,如:BeforeAdvice、AfterReturningAdvice、ThrowsAdvice等。不少资料上将加强译为“通知”,这明显是个词不达意的翻译,让不少程序员困惑了许久。
  • 引介:引介是一种特殊的加强,它为类添加一些属性和方法。这样,即便一个业务类本来没有实现某个接口,经过引介功能,能够动态的未该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。
  • 织入:织入是将加强添加到目标类具体链接点上的过程,AOP有三种织入方式:
    1. 编译期织入:须要特殊的Java编译期(例如AspectJ的ajc)
    2. 类装载期织入:要求使用特殊的类加载器
    3. 动态代理织入:在运行时为目标类生成代理实现加强
Spring采用了动态代理织入,而AspectJ采用了编译期织入和类装载期织入的方式。
  • 切面:切面是由切点和加强(引介)组成的,它包括了对横切关注功能的定义,也包括了对链接点的定义。

 

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

下面用一个找枪手代考的例子演示代理模式的使用:

 

[java] 
 
  1. package com.lovo;  
  2.   
  3. /** 
  4.  * 参考人员接口 
  5.  * @author 骆昊 
  6.  * 
  7.  */  
  8. public interface Candidate {  
  9.       
  10.     /** 
  11.      * 答题 
  12.      */  
  13.     public void answerTheQuestions();  
  14. }  

[java] 
 
  1. package com.lovo;  
  2.   
  3. /** 
  4.  * 懒学生 
  5.  * @author 骆昊 
  6.  * 
  7.  */  
  8. public class LazyStudent implements Candidate {  
  9.     private String name;        // 姓名  
  10.       
  11.     public LazyStudent(String name) {  
  12.         this.name = name;  
  13.     }  
  14.       
  15.     @Override  
  16.     public void answerTheQuestions() {  
  17.         // 懒学生只能写出本身的名字不会答题  
  18.         System.out.println("姓名: " + name);  
  19.     }  
  20.   
  21. }  

[java] 
 
  1. package com.lovo;  
  2.   
  3. /** 
  4.  * 枪手 
  5.  * @author 骆昊 
  6.  * 
  7.  */  
  8. public class Gunman implements Candidate {  
  9.     private Candidate target;   // 被代理对象  
  10.       
  11.     public Gunman(Candidate target) {  
  12.         this.target = target;  
  13.     }  
  14.       
  15.     @Override  
  16.     public void answerTheQuestions() {  
  17.         // 枪手要写上代考的学生的姓名  
  18.         target.answerTheQuestions();  
  19.         // 枪手要帮助懒学生答题并交卷  
  20.         System.out.println("奋笔疾书正确答案");  
  21.         System.out.println("交卷");  
  22.     }  
  23.   
  24. }  

[java] 
 
  1. package com.lovo;  
  2.   
  3. public class ProxyTest1 {  
  4.   
  5.     public static void main(String[] args) {  
  6.         Candidate c = new Gunman(new LazyStudent("王小二"));  
  7.         c.answerTheQuestions();  
  8.     }  
  9. }  

 

从JDK 1.3开始,Java提供了动态代理技术,容许开发者在运行时建立接口的代理实例,主要包括Proxy类和InvocationHandler接口。下面的例子使用动态代理为ArrayList编写一个代理,在添加和删除元素时,在控制台打印添加或删除的元素以及ArrayList的大小:

 

[java] 
 
  1. package com.lovo;  
  2.   
  3. import java.lang.reflect.InvocationHandler;  
  4. import java.lang.reflect.Method;  
  5. import java.util.List;  
  6.   
  7. public class ListProxy<T> implements InvocationHandler {  
  8.     private List<T> target;  
  9.       
  10.     public ListProxy(List<T> target) {  
  11.         this.target = target;  
  12.     }  
  13.       
  14.     @Override  
  15.     public Object invoke(Object proxy, Method method, Object[] args)  
  16.             throws Throwable {  
  17.         Object retVal = null;  
  18.         System.out.println("[" + method.getName() + ": " + args[0] + "]");  
  19.         retVal = method.invoke(target, args);  
  20.         System.out.println("[size=" + target.size() + "]");  
  21.         return retVal;  
  22.     }  
  23.   
  24. }  

[java] 
 
  1. package com.lovo;  
  2.   
  3. import java.lang.reflect.Proxy;  
  4. import java.util.ArrayList;  
  5. import java.util.List;  
  6.   
  7. public class ProxyTest2 {  
  8.   
  9.     @SuppressWarnings("unchecked")  
  10.     public static void main(String[] args) {  
  11.         List<String> list = new ArrayList<String>();  
  12.         Class<?> clazz = list.getClass();  
  13.         ListProxy<String> myProxy = new ListProxy<String>(list);  
  14.         List<String> newList = (List<String>)   
  15.                 Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), myProxy);  
  16.         newList.add("apple");  
  17.         newList.add("banana");  
  18.         newList.add("orange");  
  19.         newList.remove("banana");  
  20.     }  
  21. }  
程序运行结果:

 

使用Java的动态代理有一个局限性就是代理的类必需要实现接口,虽然面向接口编程是每一个优秀的Java程序都知道的规则,但现实每每不尽如人意,对于没有实现接口的类如何为其生成代理呢?继承!继承是最经典的扩展已有代码能力的手段,虽然继承经常被初学者滥用,但继承也经常被进阶的程序员忽视。CGLib采用很是底层的字节码生成技术,经过为一个类建立子类来生成代理,它弥补了Java动态代理的不足,所以Spring中动态代理和CGLib都是建立代理的重要手段,对于实现了接口的类就用动态代理为其生成代理类,而没有实现接口的类就用CGLib经过继承的方式为其建立代理。

 

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

答:

 

  • no:不进行自动装配,手动设置Bean的依赖关系
  • byName:根据Bean的名字进行自动装配
  • byType:根据Bean的类型进行自动装配
  • constructor:相似于byType,不过是应用于构造器的参数,若是正好有一个Bean与构造器的参数类型相同则能够自动装配,不然会致使错误
  • autodetect:若是有默认的构造器,则经过constructor的方式进行自动装配,不然使用byType的方式进行自动装配
【注意】自动装配没有自定义装配方式那么精确,并且不能自动装配简单属性(基本类型、字符串等),在使用时应注意。

 

 

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

答:首先须要在Spring配置文件中增长以下配置:

 

[html] 
 
  1. <context:component-scan base-package="org.example"/>  
而后能够用@Component、@Controller、@Service、@Repository注解来标注须要由Spring IoC容器进行对象托管的类。

 

 

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

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

 

157. 如何在Web项目中配置Spring的IoC容器?

答:若是须要在Web项目中使用Spring的IoC容器,能够在Web项目配置文件web.xml中作出以下配置:

 

[html] 
 
  1. <context-param>  
  2.     <param-name>contextConfigLocation</param-name>  
  3.     <param-value>/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml</param-value>  
  4. </context-param>  
  5.   
  6. <listener>  
  7.     <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
  8. </listener>  

 

 

158. 如何在Web项目中配置Spring MVC?

答:要使用Spring MVC须要在Web项目配置文件中配置其前端控制器DispatcherServlet,以下所示:

 

[html] 
 
  1. <web-app>  
  2.   
  3.     <servlet>  
  4.         <servlet-name>example</servlet-name>  
  5.         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
  6.         <load-on-startup>1</load-on-startup>  
  7.     </servlet>  
  8.   
  9.     <servlet-mapping>  
  10.         <servlet-name>example</servlet-name>  
  11.         <url-pattern>*.html</url-pattern>  
  12.     </servlet-mapping>  
  13.   
  14. </web-app>  

【注意】上面的配置中使用了*.html的后缀映射,这样作一方面不可以经过URL推断采用了何种服务器端的技术,另外一方面能够欺骗搜索引擎,由于搜索引擎不会搜索动态页面,这种作法能够称为伪静态化。

 

159. Spring MVC的工做原理是怎样的?

答:Spring MVC的工做原理以下图所示:

 

  1. 客户端的全部请求都交给前端控制器DispatcherServlet来处理,它会负责调用系统的其余模块来真正处理用户的请求。
  2. DispatcherServlet收到请求后,将根据请求的信息(包括URL、HTTP协议方法、请求头、请求参数、Cookie等)以及HandlerMapping的配置找处处理该请求的Handler(任何一个对象均可以做为请求的Handler)。固然,在这个地方Spring会经过HandlerAdapter对该处理器进行封装,HandlerAdapter是一个适配器,它用统一的接口对各类Handler中的方法进行调用。
  3. Handler完成对用户请求的处理后,会返回一个ModelAndView对象给DispatcherServlet,ModelAndView顾名思义,包含了数据模型以及相应的视图的信息。固然,这里的视图是逻辑视图,DispatcherServlet还要借助ViewResolver完成从逻辑视图到真实视图对象的解析工做。
  4. 当获得真正的视图对象后,Dispatcher会利用视图对象对模型数据进行渲染。
  5. 客户端获得响应,多是一个普通的HTML页面,也能够是XML或JSON字符串,还能够是一张图片或者一个PDF文件。

 

160. 如何在Spring IoC容器中配置数据源?

答:

 

  • DBCP配置:
[html] 
 
  1. <bean id="dataSource"  
  2.         class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">  
  3.     <property name="driverClassName" value="${jdbc.driverClassName}"/>  
  4.     <property name="url" value="${jdbc.url}"/>  
  5.     <property name="username" value="${jdbc.username}"/>  
  6.     <property name="password" value="${jdbc.password}"/>  
  7. </bean>  
  8.   
  9. <context:property-placeholder location="jdbc.properties"/>  

  • C3P0配置:
[html] 
 
  1. <bean id="dataSource"  
  2.         class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">  
  3.     <property name="driverClass" value="${jdbc.driverClassName}"/>  
  4.     <property name="jdbcUrl" value="${jdbc.url}"/>  
  5.     <property name="user" value="${jdbc.username}"/>  
  6.     <property name="password" value="${jdbc.password}"/>  
  7. </bean>  
  8.   
  9. <context:property-placeholder location="jdbc.properties"/>  


 

161. 如何配置配置事务加强?

答:

[html] 
 
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.      xmlns:aop="http://www.springframework.org/schema/aop"  
  5.      xmlns:tx="http://www.springframework.org/schema/tx"  
  6.      xsi:schemaLocation="  
  7.      http://www.springframework.org/schema/beans  
  8.      http://www.springframework.org/schema/beans/spring-beans.xsd  
  9.      http://www.springframework.org/schema/tx  
  10.      http://www.springframework.org/schema/tx/spring-tx.xsd  
  11.      http://www.springframework.org/schema/aop  
  12.      http://www.springframework.org/schema/aop/spring-aop.xsd">  
  13.   
  14.   <!-- this is the service object that we want to make transactional -->  
  15.   <bean id="fooService" class="x.y.service.DefaultFooService"/>  
  16.   
  17.   <!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean below) -->  
  18.   <tx:advice id="txAdvice" transaction-manager="txManager">  
  19.   <!-- the transactional semantics... -->  
  20.   <tx:attributes>  
  21.     <!-- all methods starting with 'get' are read-only -->  
  22.     <tx:method name="get*" read-only="true"/>  
  23.     <!-- other methods use the default transaction settings (see below) -->  
  24.     <tx:method name="*"/>  
  25.   </tx:attributes>  
  26.   </tx:advice>  
  27.   
  28.   <!-- ensure that the above transactional advice runs for any execution  
  29.     of an operation defined by the FooService interface -->  
  30.   <aop:config>  
  31.   <aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>  
  32.   <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>  
  33.   </aop:config>  
  34.   
  35.   <!-- don't forget the DataSource -->  
  36.   <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">  
  37.   <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>  
  38.   <property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/>  
  39.   <property name="username" value="scott"/>  
  40.   <property name="password" value="tiger"/>  
  41.   </bean>  
  42.   
  43.   <!-- similarly, don't forget the PlatformTransactionManager -->  
  44.   <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
  45.   <property name="dataSource" ref="dataSource"/>  
  46.   </bean>  
  47.   
  48.   <!-- other <bean/> definitions here -->  
  49.   
  50. </beans>  

 

 

162. 选择使用Spring框架的缘由(Spring框架为企业级开发带来的好处)?

答:能够从如下几个方面做答:

 

  1. 非侵入式:支持基于POJO的编程模式,不强制性的要求实现Spring框架中的接口或继承Spring框架中的类。
  2. IoC容器:IoC容器帮助应用程序管理对象以及对象之间的依赖关系,对象之间的依赖关系若是发生了改变只须要修改配置文件而不是修改代码,由于代码的修改可能意味着项目的从新构建和完整的回归测试。有了IoC容器,程序员不再须要本身编写工厂、单例,这一点特别符合Spring的精神“不要重复的发明轮子”。
  3. AOP:面向切面编程,将全部的横切关注功能封装到切面(aspect)中,经过配置的方式将横切关注功能动态添加到目标代码上,进一步实现了业务逻辑和系统服务之间的分离。另外一方面,有了AOP程序员能够省去不少本身写代理类的工做。
  4. MVC:Spring的MVC框架是很是优秀的,从各个方面均可以甩Struts 2几条街,为Web表示层提供了更好的解决方案。
  5. 事务管理:Spring以宽广的胸怀接纳多种持久层技术(我想这一点与Rod Johnson音乐学博士的身份不无关系,这一点特别值得Gavin King学习,上天不可能赋予一我的太多的优势以致于他没有表达谦虚的余地。关于这二位的轶事能够本身百度一下),而且为其提供了声明式的事务管理,在不须要任何一行代码的状况下就可以完成事务管理。
  6. 其余:选择Spring框架的缘由还远不止于此,Spring为Java企业级开发提供了一站式选择,你能够在须要的时候使用它的部分和所有,更重要的是,你甚至能够在感受不到Spring存在的状况下,在你的项目中使用Spring提供的各类优秀的功能。

 

163. 依赖注入的方式以及你在项目中的选择?

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

 

164. 提供Spring IoC容器配置元数据的方式?

答:

 

  1. 基于XML文件进行配置。
  2. 基于注解进行配置。
  3. 基于Java程序进行配置(Spring 3+)
[java] 
 
  1. package com.lovo.bean;  
  2.   
  3. import org.springframework.beans.factory.annotation.Autowired;  
  4. import org.springframework.stereotype.Component;  
  5.   
  6. @Component  
  7. public class Person {  
  8.     private String name;  
  9.     private int age;  
  10.     @Autowired  
  11.     private Car car;  
  12.       
  13.     public Person(String name, int age) {  
  14.         this.name = name;  
  15.         this.age = age;  
  16.     }  
  17.   
  18.     public void setCar(Car car) {  
  19.         this.car = car;  
  20.     }  
  21.   
  22.     @Override  
  23.     public String toString() {  
  24.         return "Person [name=" + name + ", age=" + age + ", car=" + car + "]";  
  25.     }  
  26.   
  27. }  

[java] 
 
  1. package com.lovo.bean;  
  2.   
  3. import org.springframework.stereotype.Component;  
  4.   
  5. @Component  
  6. public class Car {  
  7.     private String brand;  
  8.     private int maxSpeed;  
  9.   
  10.     public Car(String brand, int maxSpeed) {  
  11.         this.brand = brand;  
  12.         this.maxSpeed = maxSpeed;  
  13.     }  
  14.   
  15.     @Override  
  16.     public String toString() {  
  17.         return "Car [brand=" + brand + ", maxSpeed=" + maxSpeed + "]";  
  18.     }  
  19.   
  20. }  

[java] 
 
  1. package com.lovo.config;  
  2.   
  3. import org.springframework.context.annotation.Bean;  
  4. import org.springframework.context.annotation.Configuration;  
  5.   
  6. import com.lovo.bean.Car;  
  7. import com.lovo.bean.Person;  
  8.   
  9. @Configuration  
  10. public class AppConfig {  
  11.   
  12.     @Bean  
  13.     public Car car() {  
  14.         return new Car("Benz", 320);  
  15.     }  
  16.       
  17.     @Bean  
  18.     public Person person() {  
  19.         return new Person("骆昊", 34);  
  20.     }  
  21. }  

[java] 
 
  1. package com.lovo.test;  
  2.   
  3. import org.springframework.context.ConfigurableApplicationContext;  
  4. import org.springframework.context.annotation.AnnotationConfigApplicationContext;  
  5.   
  6. import com.lovo.bean.Person;  
  7. import com.lovo.config.AppConfig;  
  8.   
  9. class Test {  
  10.   
  11.     public static void main(String[] args) {  
  12.         // TWR (Java 7+)  
  13.         try(ConfigurableApplicationContext factory = new AnnotationConfigApplicationContext(AppConfig.class)) {  
  14.             Person person = factory.getBean(Person.class);  
  15.             System.out.println(person);  
  16.         }  
  17.     }  
  18. }  

165. Spring提供了哪些Bean的做用域?

 

答:

 

  • singleton:Bean以单例的方式存在(IoC容器中仅存在该Bean的惟一实例)。
  • prototype:每次从容器中获取Bean的实例时,都会返回一个新的实例(原型模式)。
  • request:每次HTTP请求都会建立新的实例,该做用域仅适用于WebApplicationContext环境。
  • session:同一个HTTP会话共享一个Bean的实例,不一样的HTTP会话使用不一样的Bean实例,该做用域仅适用于WebApplicationContext环境。
  • globalSession:同一个全局会话共享一个Bean的实例,通常应用于Portlet应用中。

 

 

166. 阐述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接口,则调用其afterPropertySet方法。
  7. 若是有和Bean关联的BeanPostProcessors对象,则这些对象的postProcessAfterInitialization方法被调用。
  8. 当销毁Bean实例时,若是Bean实现了DisposableBean接口,则调用其destroy方法。

 

 

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

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

 

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

答:

 

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

 

 

169. 和自动装配相关的注解有哪些?

答:

 

  • @Required:该依赖关系必须装配(手动或自动装配),不然将抛出BeanInitializationException异常。
  • @Autowired:自动装配,默认按类型进行自动装配。
  • @Qualifier:若是按类型自动装配时有不止一个匹配的类型,那么可使用该注解指定名字来消除歧义。

 

 

170. 如何使用HibernateDaoSupport整合Spring和Hibernate?

答:

 

  1. 在Spring中配置Hibernate的会话工厂(LocalSessionFactoryBean或AnnotationSessionFactoryBean)。
  2. 让DAO的实现类继承HibernateDaoSupport(继承getHibernateTemplate方法来调用模板方法)。
  3. 让Spring来管理Hibernate的事务(推荐使用声明式事务)。

 

 

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

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

 

172. 如何理解Spring AOP中Advice这个概念?

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

 

173. 在Web项目中如何得到Spring的IoC容器?

答:

 

[java] 
 
  1. WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext);  


 

174. Spring MVC如何对RESTful风格提供支持?

答:若是不了解RESTful能够看看百度百科的讲解,关于这个问题,能够看看blogjava上的另外一个帖子。

 

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

答:

 

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

 

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

答:

 

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

 

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

答:

 

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

 

 

178. 什么是XSS攻击?什么是SQL注入攻击?什么是CSRF攻击?

答:

 

  • XSS(Cross Site Script,跨站脚本攻击)是向网页中注入恶意脚本在用户浏览网页时在用户浏览器中执行恶意脚本的攻击方式。跨站脚本攻击分有两种形式:反射型攻击(诱使用户点击一个嵌入恶意脚本的连接以达到攻击的目标,目前有不少攻击者利用论坛、微博发布含有恶意脚本的URL就属于这种方式)和持久型攻击(将恶意脚本提交到被攻击网站的数据库中,用户浏览网页时,恶意脚本从数据库中被加载到页面执行,QQ邮箱的早期版本就曾经被利用做为持久型跨站脚本攻击的平台)。XSS虽然不是什么新鲜玩意,可是攻击的手法却不断翻新,防范XSS主要有两方面:消毒(对危险字符进行转义)和HttpOnly(防范XSS攻击者窃取Cookie数据)。
  • SQL注入攻击是注入攻击最多见的形式(此外还有OS注入攻击(Struts 2的高危漏洞就是经过OGNL实施OS注入攻击致使的)),当服务器使用请求参数构造SQL语句时,恶意的SQL被嵌入到SQL中交给数据库执行。SQL注入攻击须要攻击者对数据库结构有所了解才能进行,攻击者想要得到表结构有多种方式:(1)若是使用开源系统搭建网站,数据库结构也是公开的(目前有不少现成的系统能够直接搭建论坛,电商网站,虽然方便快捷可是风险是必需要认真评估的);(2)错误回显(若是将服务器的错误信息直接显示在页面上,攻击者能够经过非法参数引起页面错误从而经过错误信息了解数据库结构,Web应用应当设置友好的错误页,一方面符合最小惊讶原则,一方面屏蔽掉可能给系统带来危险的错误回显信息);(3)盲注。防范SQL注入攻击也能够采用消毒的方式,经过正则表达式对请求参数进行验证,此外,参数绑定也是很好的手段,这样恶意的SQL会被当作SQL的参数而不是命令被执行,JDBC中的PreparedStatement就是支持参数绑定的语句对象,从性能和安全性上都明显优于Statement。
  • CSRF攻击(Cross Site Request Forgery,跨站请求伪造)是攻击者经过跨站请求,以合法的用户身份进行非法操做(如转帐或发帖等)。CSRF的原理是利用浏览器的Cookie或服务器的Session,盗取用户身份,其原理以下图所示。防范CSRF的主要手段是识别请求者的身份,主要有如下几种方式:(1)在表单中添加令牌(token);(2)验证码;(3)检查请求头中的Referer(前面提到防图片盗连接也是用的这种方式)。令牌和验证都具备一次消费性的特征,所以在原理上一致的,可是验证码是一种糟糕的用户体验,不是必要的状况下不要轻易使用验证码,目前不少网站的作法是若是在短期内屡次提交一个表单未得到成功后才要求提供验证码,这样会得到较好的用户体验。
【补充】防火墙的架设是Web安全的重要保障,ModSecurity是开源的Web防火墙中的佼佼者,有兴趣的能够在 http://www.modsecurity.org/网站得到相关信息。企业级防火墙的架设应当有两级防火墙,Web服务器和部分应用服务器能够架设在两级防火墙之间的DMZ,而数据和资源服务器应当架设在第二级防火墙以后,以下图所示。
 
179. 什么是领域模型(domain model)?贫血模型(anaemic domain model) 

和充血模型(rich domain model)有什么区别?

答:领域模型是领域内的概念类或现实世界中对象的可视化表示,又称为概念模型或分析对象模型,它专一于分析问题领域自己,发掘重要的业务领域概念,并创建业务领域概念之间的关系。贫血模型是指使用的领域对象中只有setter和getter方法(POJO),全部的业务逻辑都不包含在领域对象中而是放在业务逻辑层。有人将咱们这里说的贫血模型进一步划分红失血模型(领域对象彻底没有业务逻辑)和贫血模型(领域对象有少许的业务逻辑),咱们这里就不对此加以区分了。充血模型将大多数业务逻辑和持久化放在领域对象中,业务逻辑(业务门面)只是完成对业务逻辑的封装、事务和权限等的处理。下面两张图分别展现了贫血模型和充血模型的分层架构。
贫血模型
 
充血模型
 
贫血模型下组织领域逻辑一般使用事务脚本模式,让每一个过程对应用户可能要作的一个动做,每一个动做由一个过程来驱动。也就是说在设计业务逻辑接口的时候,每一个方法对应着用户的一个操做,这种模式有如下几个有点:
  1. 它是一个大多数开发者都可以理解的简单过程模型(适合国内的绝大多数开发者)。
  2. 它可以与一个使用行数据入口或表数据入口的简单数据访问层很好的协做。
  3. 事务边界的显而易见,一个事务开始于脚本的开始,终止于脚本的结束,很容易经过代理(或切面)实现声明式事务。
而后,事务脚本模式的缺点也是不少的,随着领域逻辑复杂性的增长,系统的复杂性将迅速增长,程序结构将变得极度混乱。开源中国社区上有一篇很好的译文 《贫血领域模型是如何致使糟糕的软件产生》对这个问题作了比较细致的阐述。
 
180. 谈一谈测试驱动开发(TDD)的好处以及你的理解。
答:TDD是指在编写真正的功能实现代码以前先写测试代码,而后根据须要重构实现代码。在JUnit的做者Kent Beck的大做《测试驱动开发:实战与模式解析》(Test-Driven Development: by Example)一书中有这么一段内容:“消除恐惧和不肯定性是编写测试驱动代码的重要缘由”。由于编写代码时的恐惧会让你当心试探,让你回避沟通,让你羞于获得反馈,让你变得焦躁不安,而TDD是消除恐惧、让Java开发者更加自信更加乐于沟通的重要手段。TDD会带来的好处可能不会立刻呈现,可是你在某个时候必定会发现,这些好处包括:
  1. 更清晰的代码 --- 只写须要的代码
  2. 更好的设计
  3. 更出色的灵活性 --- 鼓励程序员面向接口编程
  4. 更快速的反馈 --- 不会到系统上线时才知道bug的存在
【补充:】 敏捷软件开发的概念已经有不少年了,并且也部分的改变了软件开发这个行业,TDD也是敏捷开发所倡导的。

TDD能够在多个层级上应用,包括单元测试(测试一个类中的代码)、集成测试(测试类之间的交互)、系统测试(测试运行的系统)和系统集成测试(测试运行的系统包括使用的第三方组件)。TDD的实施步骤是:红(失败测试) --- 绿(经过测试) --- 重构。关于实施TDD的详细步骤请参考另外一篇文章《测试驱动开发之初窥门径》。

在使用TDD开发时,常常会遇到须要被测对象须要依赖其余子系统的状况,可是你但愿将测试代码跟依赖项隔离,以保证测试代码仅仅针对当前被测对象或方法展开,这时候你须要的是测试替身。测试替身能够分为四类:

 

  1. 虚设替身:只传递可是不会使用到的对象,通常用于填充方法的参数列表
  2. 存根替身:老是返回相同的预设响应,其中可能包括一些虚设状态
  3. 假装替身:能够取代真实版本的可用版本(比真实版本仍是会差不少)
  4. 模拟替身:能够表示一系列指望值的对象,而且能够提供预设响应
Java世界中实现模拟替身的第三方工具很是多,包括 EasyMock、Mockito、 jMock等。
相关文章
相关标签/搜索