原文地址:http://blog.csdn.net/jackfrued/article/details/40058989
Java面试题集(151-180)
摘要:这部分包含了Spring、Spring MVC、MyBatis、Spring和其他框架整合以及测试相关的内容,除此之外还包含了大型网站技术架构相关面试内容。
151. Spring中的BeanFactory和ApplicationContext有什么联系?
答:Spring通过配置文件描述Bean以及Bean之间的依赖关系,利用Java的反射机制实现Bean的实例化,并建立Bean之间的依赖关系,在此基础上,Spring的IoC容器还提供了Bean实例缓存、生命周期管理、Bean实例代理、事件发布、资源装载等高级服务。BeanFactory是Spring框架最核心的接口,它提供了IoC容器的配置机制。ApplicationContext建立在BeanFactory之上,提供了更多面向应用的功能,包括对国际化和框架事件体系的支持。通常将BeanFactory称为IoC容器,而ApplicationContext称为应用上下文,前者更倾向于Spring本身,后者更倾向于开发者,因此被使用得更多。
【补充】反射(reflection)又叫自省(introspection),是获得对象或类型元数据的方法,Java反射机制可以在运行时判断对象所属的类,在运行时构造任意一个类的对象,在运行时获得一个类的属性和方法,在运行时调用对象的方法,或者生成动态代理。在Java中,可以通过类的Class对象获得类的构造器、属性、方法等类的元数据,还可以访问这些属性或调用这些方法,和反射相关的类还包括:
- Constructor:代表类的构造器的类。通过Class对象的getConstructors方法可以获得类的所有构造器的数组,Java 5以后的版本还可以通过getConstrcutor(Class... parameterTypes)获得拥有特定参数的构造器对象。Constructor对象的一个主要方法是newInstance,通过该方法可以创建一个类的实例。
- 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():获得方法参数注解信息的数组。
- 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
- package com.lovo;
- public class Car {
- private String brand;
- private int currentSpeed;
- private int maxSpeed;
- public Car(String brand, int maxSpeed) {
- this.brand = brand;
- this.maxSpeed = maxSpeed;
- }
- public void run() {
- currentSpeed += 10;
- System.out.println(brand + " is running at " + currentSpeed + " km/h");
- if(currentSpeed > maxSpeed) {
- System.out.println("It's dangerous!");
- }
- }
- }
- package com.lovo;
- import java.lang.reflect.Constructor;
- import java.lang.reflect.Field;
- import java.lang.reflect.Method;
- public class CarTest {
- public static void main(String[] args) throws Exception {
- Constructor<Car> con = Car.class.getConstructor(String.class, int.class);
- Car myCar = (Car) con.newInstance("Benz", 280);
- Method m = myCar.getClass().getDeclaredMethod("run");
- for(int i = 0; i < 10; i++) {
- m.invoke(myCar);
- }
- Field f1 = myCar.getClass().getDeclaredField("maxSpeed");
- Field f2 = myCar.getClass().getDeclaredField("brand");
- f1.setAccessible(true);
- f1.set(myCar, 80);
- f2.setAccessible(true);
- f2.set(myCar, "QQ");
- m.invoke(myCar);
- }
- }
152. Spring中Bean的作用域有哪些?
答:在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对非线程安全的对象进行了特殊处理。
【补充】ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。ThreadLocal,顾名思义是线程的一个本地化对象,当工作于多线程中的对象使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程分配一个独立的变量副本,所以每一个线程都可以独立的改变自己的副本,而不影响其他线程所对应的副本。从线程的角度看,这个变量就像是线程的本地变量。
ThreadLocal类非常简单好用,只有四个方法,能用上的也就是下面三个方法:
- void set(T value):设置当前线程的线程局部变量的值。
- T get():获得当前线程所对应的线程局部变量的值。
- void remove():删除当前线程中线程局部变量的值。
- package com.lovo;
- 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());
- }
- }
153. 你如何理解AOP中的连接点(Joinpoint)、切点(Pointcut)、增强(Advice)、引介(Introduction)、织入(Weaving)、切面(Aspect)这些概念?
答:
- 连接点:程序执行的某个特定位置(如:某个方法调用前、调用后,方法抛出异常后)。一个类或一段程序代码拥有一些具有边界性质的特定点,这些代码中的特定点就是连接点。Spring仅支持方法的连接点。
- 切点:如果连接点相当于数据中的记录,那么切点相当于查询条件,一个切点可以匹配多个连接点。Spring AOP的规则解析引擎负责解析切点所设定的查询条件,找到对应的连接点。
- 增强:增强是织入到目标类连接点上的一段程序代码。Spring提供的增强接口都是带方位名的,如:BeforeAdvice、AfterReturningAdvice、ThrowsAdvice等。很多资料上将增强译为“通知”,这明显是个词不达意的翻译,让很多程序员困惑了许久。
- 引介:引介是一种特殊的增强,它为类添加一些属性和方法。这样,即使一个业务类原本没有实现某个接口,通过引介功能,可以动态的未该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。
- 织入:织入是将增强添加到目标类具体连接点上的过程,AOP有三种织入方式:
- 编译期织入:需要特殊的Java编译期(例如AspectJ的ajc)
- 类装载期织入:要求使用特殊的类加载器
- 动态代理织入:在运行时为目标类生成代理实现增强
Spring采用了动态代理织入,而AspectJ采用了编译期织入和类装载期织入的方式。
- 切面:切面是由切点和增强(引介)组成的,它包括了对横切关注功能的定义,也包括了对连接点的定义。
【补充】代理模式是GoF提出的23种设计模式中最为经典的模式之一,代理模式是对象的结构模式,它给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。简单的说,代理对象可以完成比原对象更多的职责,当需要为原对象添加横切关注功能时,就可以使用原对象的代理对象。我们在打开Office系列的Word文档时,如果文档中有插图,当文档刚加载时,文档中的插图都只是一个虚框占位符,等用户真正翻到某页要查看该图片时,才会真正加载这张图,这其实就是对代理模式的使用,代替真正图片的虚框就是一个虚拟代理;Hibernate的load方法也是返回一个虚拟代理对象,等用户真正需要访问对象的属性时,才向数据库发出SQL语句获得真实对象。代理模式的类图如下所示:
下面用一个找枪手代考的例子演示代理模式的使用:
- package com.lovo;
- /**
- * 参考人员接口
- * @author 骆昊
- *
- */
- public interface Candidate {
- /**
- * 答题
- */
- public void answerTheQuestions();
- }
- package com.lovo;
- /**
- * 懒学生
- * @author 骆昊
- *
- */
- public class LazyStudent implements Candidate {
- private String name; // 姓名
- public LazyStudent(String name) {
- this.name = name;
- }
- @Override
- public void answerTheQuestions() {
- // 懒学生只能写出自己的名字不会答题
- System.out.println("姓名: " + name);
- }
- }
- package com.lovo;
- /**
- * 枪手
- * @author 骆昊
- *
- */
- public class Gunman implements Candidate {
- private Candidate target; // 被代理对象
- public Gunman(Candidate target) {
- this.target = target;
- }
- @Override
- public void answerTheQuestions() {
- // 枪手要写生代考的学生的姓名
- target.answerTheQuestions();
- // 枪手要帮助懒学生答题并交卷
- System.out.println("奋笔疾书正确答案");
- System.out.println("交卷");
- }
- }
- package com.lovo;
- public class ProxyTest1 {
- public static void main(String[] args) {
- Candidate c = new Gunman(new LazyStudent("王小二"));
- c.answerTheQuestions();
- }
- }
从JDK 1.3开始,Java提供了动态代理技术,允许开发者在运行时创建接口的代理实例,主要包括Proxy类和InvocationHandler接口。下面的例子使用动态代理为ArrayList编写一个代理,在添加和删除元素时,在控制台打印添加或删除的元素以及ArrayList的大小:
- package com.lovo;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.util.List;
- public class ListProxy<T> implements InvocationHandler {
- private List<T> target;
- public ListProxy(List<T> target) {
- this.target = target;
- }
- @Override
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- Object retVal = null;
- System.out.println("[" + method.getName() + ": " + args[0] + "]");
- retVal = method.invoke(target, args);
- System.out.println("[size=" + target.size() + "]");
- return retVal;
- }
- }
- package com.lovo;
- import java.lang.reflect.Proxy;
- import java.util.ArrayList;
- import java.util.List;
- public class ProxyTest2 {
- @SuppressWarnings("unchecked")
- public static void main(String[] args) {
- List<String> list = new ArrayList<String>();
- Class<?> clazz = list.getClass();
- ListProxy<String> myProxy = new ListProxy<String>(list);
- List<String> newList = (List<String>)
- Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), myProxy);
- newList.add("apple");
- newList.add("banana");
- newList.add("orange");
- newList.remove("banana");
- }
- }
使用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配置文件中增加如下配置:
- <context:component-scan base-package="org.example"/>
156. Spring支持的事务管理类型有哪些?你在项目中使用哪种方式?
答:Spring支持编程式事务管理和声明式事务管理。许多Spring框架的用户选择声明式事务管理,因为这种方式和应用程序的关联较少,因此更加符合轻量级容器的概念。声明式事务管理要优于编程式事务管理,尽管在灵活性方面它弱于编程式事务管理(这种方式允许你通过代码控制业务)。
157. 如何在Web项目中配置Spring的IoC容器?
答:如果需要在Web项目中使用Spring的IoC容器,可以在Web项目配置文件web.xml中做出如下配置:
- <context-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml</param-value>
- </context-param>
- <listener>
- <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
- </listener>
158. 如何在Web项目中配置Spring MVC?
答:要使用Spring MVC需要在Web项目配置文件中配置其前端控制器DispatcherServlet,如下所示:
- <web-app>
- <servlet>
- <servlet-name>example</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <load-on-startup>1</load-on-startup>
- </servlet>
- <servlet-mapping>
- <servlet-name>example</servlet-name>
- <url-pattern>/example/*</url-pattern>
- </servlet-mapping>
- </web-app>
159. Spring MVC的工作原理是怎样的?
答:
160. 如何在Spring IoC容器中配置数据源?
答:
161. 如何配置配置事务增强?
答:
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175. 大型网站在架构上应当考虑哪些问题?
答:
176. 你用过的网站前端优化的技术有哪些?
答:
177. 你使用过的应用服务器优化技术有哪些?
答:
178. 什么是XSS攻击?什么是SQL注入攻击?什么是CSRF攻击?
答:
179. 说说你知道的网站性能测试方法和性能优化策略?
答:
180. 大型网站的高可用性和可伸缩性如何保证?
答: