1、本人总结了一些 ssh 常见的面试题 例举如下 强调在回答的时候不要光回答概念,要思维扩散性的讲些相关的东西 spring 的优点? 1.降低了组件之间的耦合性 ,实现了软件各层之间的解耦 2.可以使用容易提供的众多服务,如事务管理,消息服务等 3.容器提供单例模式支持 4.容器提供了 AOP 技术,利用它很容易实现如权限拦截,运行期监控等功能 5.容器提供了众多的辅助类,能加快应用的开发 6.spring 对于主流的应用框架提供了集成支持,如 hibernate,JPA,Struts 等 7.spring 属于低侵入式设计,代码的污染极低 8.独立于各种应用服务器 9.spring 的 DI
2、 机制降低了业务对象替换的复杂性 10.Spring 的高度开放性,并不强制应用完全依赖于 Spring,开发者可以自由选择 spring 的部分或全部 什么是 DI 机制? 依赖注入(Dependecy Injection)和控制反转(Inversion of Control)是同一个概念,具体的讲:当某个角色 需要另外一个角色协助的时候,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在 spring 中 创建被调用者的工作不再由调用者来完成,因此称为控制反转。创建被调用者的工作由spring 来完成,然后注入调用者 因此也称为依赖注入。 spring 以动态灵活的方式来管理对
3、象 , 注入的两种方式,设置注入和构造注入。 设置注入的优点:直观,自然 构造注入的优点:可以在构造器中决定依赖关系的顺序。 什么是 AOP? 面向切面编程(AOP)完善 spring 的依赖注入(DI) ,面向切面编程在 spring 中主要表现为两个方面 1.面向切面编程提供声明式事务管理 2.spring 支持用户自定义的切面 面向切面编程(aop)是对面向对象编程(oop)的补充, 面向对象编程将程序分解成各个层次的对象,面向切面编程将程序运行过程分解成各个切面。 AOP 从程序运行角度考虑程序的结构,提取业务处理过程的切面,oop 是静态的抽象,aop是动态的抽象, 是对应用执行过程
4、中的步骤进行抽象, ,从而获得步骤之间的逻辑划分。 aop 框架具有的两个特征: 1.各个步骤之间的良好隔离性 2.源代码无关性依赖注入(DI)依赖注入(DI) ,是 spring 容器实现的基础,在 spring-core 模块中实现的。所谓 DI,就是指对象是被动接受依赖类而不是自己主动去找,换句话说就是指对象不是从容器中查找它依赖的类,而是在容器实例化对象的时候主动将它依赖的类注入给它。下面举一个形象的例子:1. class B 2. private A a; 3. public void setA(A a) 4. this.a=a; 5. 6. 7. 8. class A 9. 显然,
5、此时 B 是依赖与 A。我们不妨将 A 比作牛,将 B 比作人,人吃牛是显然的事实。当用户要利用 B 的时候,必须先要指定他所依赖的 A 的对象。这样代码模块之间就具有很强的耦合。通过依赖注入的方式,能够使 B 对 A 的依赖关系对代码人员变得松散。我们将A 和 B 对象都交给容器来管理,通过配置文件告诉 B 该依赖的 A,这样代码中的依赖关系被移到了配置文件中,通过对配置文件的管理,很容易编写低耦合的代码。对于依赖注入的几种实现参考 Dependency Injection 。但是,目前在学术界争论的焦点在于:DI 究竟能不能给程序带来解耦。众所周知,封装和依赖是面向对象编程思想中两个很重要
6、的概念。编写高内聚低耦合的代码是 OOP 编程的目标。但是,这其中本来就存在着互相矛盾。所谓高内聚,就是通过封装之后,是被封装的各个模块(这里一般是一个类) 内部的功能功能相关性、依赖性、协作性等高。此时,我们要做的就是对模块按照上述标准进行分解,直到满意为止。但是,什么时候才是一个尽头?当我们在不断的对模块进行分解时,被分解的模块之间的耦合就不断的增强。此时,我们就会反过来想,低耦合就是不断降低模块之间的关系,没有关系最好。于是我们要做的就是模块合组,将耦合关系强的模块组合在一起。那怎么样才能做到最好?那不就是用一个统一的模块来表示整个需要描述的系统,废话那不就等于什么都没有做。说了这么多,
7、我要说的就是要在内聚和耦合之间进行权衡,找到一个恰当的平衡点。下面切入正题,所谓耦合,简单的理解就是模块与模块之间的关系,最主要的就是依赖关系。从上面的讨论很容易的发现模块之间的耦合是无法消除的,除非他们之间没有任何关系。那么 DI 为什么声称能够给程序带来解耦呢?我觉耦合并没有消除,甚至没有减弱。DI 只是将耦合进行了转移,通常是转移到配置文件中。B 对 A 的依赖关系原来在这里。因此,在主程序中的不再为 new B 的时候为 A 所扰心。但是,如果我们从另外一个方面来考虑,如果我们把 A 和 B 当作两个由第三方编写代码模块,我们无法修改他的源代码。我们会发现,DI 的机制就变得非常有用了
8、。我们只需要根据需要写配置文件,告诉 B 所依赖的模块 A 到底是那个 A。当 A 公司对 A 进行升级时,直接将组件进行替换;或者当我们改用 C 公司的模块 C 时,要做的只是告诉 B 所依赖的模块 A 是 C 公司的那个模块。这种对耦合转移的方式,在实际软件开发,尤其是基于组件或插件的开发中,意义非凡。其实,回头想想我们怎么能够既要求模块之间协作又要求没有耦合呢。DI 只不过是将各个开发人员之间代码的耦合转移到统一管理的地方。但是,当系统比较大的时候,配置文件会被胀的很大,给配置编写带来了一定的难度,目前能够做的只是根据配置的 bean 的不同类别,将配置文件分块。目前,貌似 spring
9、 中增加了注解的功能。我个人觉得,注解的方式,确实给程序员带来了不少方便,他就相当于一部傻瓜相机,拍照的参数由相机自己来调,但是也会给开发带来一些隐含的问题。注入依赖依赖注入(DI) 的基本原理是对象之间的依赖,通过以下几种方式实现:构造器的参数、工厂方法的参数,或给由构造函数或者工厂方法创建的对象设置属性。容器的工作就是创建 bean 时注入那些依赖关系,这样相对由 bean 自己来完成这些工作从控制上发生了根本性的倒转,因此依赖注入也叫控制反转(IoC)。DI 注入有 Setter 和构造器两种注入方式。构造器注入:通过带参数的构造器实现,每个参数就是一个依赖。也可以通过静态工厂方法来传参
10、数,但两者是类似的,先来看下只使用构造器参数来注入依赖关系的例子:Java 代码 1. public class SimpleMovieLister 2. 3. /依赖于 MovieFinder 4. 5. private MovieFinder movieFinder; 6. 7. /注入到构造器中 8. 9. public SimpleMovieLister(MovieFinder movieFinder) 10. 11. this.movieFinder = movieFinder; 12. 13. 14. 15. 构造器参数解析:根据参数类型来匹配,若参数类型定义明确,则 bean 实
11、例化时根据参数定义的顺序来匹配,见下例:Java 代码 1. package x.y; 2. 3. public class Foo 4. 5. /构造器的参数类型分别是 Bar 和 Baz 6. 7. public Foo(Bar bar, Baz baz) 8. 9. 10. 11. 看下其配置:Xml 代码 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 再看下有简单类型的情况:Java 代码 1. package examples; 2. 3. public class Example
12、Bean 4. 5. /简单类型虽然明确,但在配置时必须指明 type 才能匹配 6. 7. private int years; 8. 9. / 同上 10. 11. private String ultimateAnswer; 12. 13. public ExampleBean(int years, String ultimateAnswer) 14. 15. this.years = years; 16. 17. this.ultimateAnswer = ultimateAnswer; 18. 19. 20. 21. 看下其配置情况:Xml 代码 1. 2. 3. 4. 5. 8.
13、9. 10. 11. 12. 13. 也可以用索引的方式达上与上面同样的效果:Xml 代码 1. 2. 3. 4. 5. 8. 9. 10. 11. 12. 13. Setter 注入 :通过调用无参构造器或无参 static 工厂方法实例化 bean 之后,调用该 bean 的 setter 方法,即可实现基于 setter 的 DI。如:Java 代码 1. public class SimpleMovieLister 2. 3. /依赖 MovieFinder 4. 5. private MovieFinder movieFinder; 6. 7. /用 setter 方法注入 8. 9
14、. public void setMovieFinder(MovieFinder movieFinder) 10. 11. this.movieFinder = movieFinder; 12. 13. 14. 15. 构造器注入与 setter 注入的选择?根据具体情况使用,一般而言,构造器注入不宜依赖参数过多的情况,否则构造器显得比较臃肿,参数类型或名字以及顺序也容易写错。Setter 注入当依赖对象过多的时候可以考虑,缺点就是代码也可能臃肿。处理 Bean 依赖关系的步骤:a) 根据 bean 配置文件创建并初始化 BeanFactory 实例,一般使用 ApplicationConte
15、xt 实现。b) Bean 被创建的时候,依赖的属性、构造器参数或静态工厂方法参数都要提供给此bean。c) 依赖的属性或构造器参数可以是一个值也可以是个引用。d) 指定的属性或构造器参数值必须能转换成特定的格式或构造器参数所需的类型。Spring 容器在创建的时候会对每个配置的 bean 进行验证,检查 bean 指定的属性引用是否有效。在 bean 实际被创建之前,依赖的属性是不会被设置的,对于单例或被设置提前实例化的 bean 将与容器同时创建。其它情况的 bean 会被延迟到使用时才创建。默认情况下,ApplicationContext 实现中的 bean 采用提前实例化的 singl
16、eton 模式,这样做的好处是可以提前进行配置检查。被依赖的 bean 必须在依赖 bean 创建之前配置好。一些例子:Setter 注入的配置:Java 代码 1. public class ExampleBean 2. 3. /依赖的属性 4. 5. private AnotherBean beanOne; 6. 7. private YetAnotherBean beanTwo; 8. 9. private int i; 10. 11. /用 setter 方法注入 12. 13. public void setBeanOne(AnotherBean beanOne) 14. 15. t
17、his.beanOne = beanOne; 16. 17. 18. 19. public void setBeanTwo(YetAnotherBean beanTwo) 20. 21. this.beanTwo = beanTwo; 22. 23. 24. 25. public void setIntegerProperty(int i) 26. 27. this.i = i; 28. 29. 30. 31. Xml 代码 1. public class ExampleBean 2. 3. /依赖的属性 4. 5. private AnotherBean beanOne; 6. 7. pri
18、vate YetAnotherBean beanTwo; 8. 9. private int i; 10. 11. /用 setter 方法注入 12. 13. public void setBeanOne(AnotherBean beanOne) 14. 15. this.beanOne = beanOne; 16. 17. 18. 19. public void setBeanTwo(YetAnotherBean beanTwo) 20. 21. this.beanTwo = beanTwo; 22. 23. 24. 25. public void setIntegerProperty(i
19、nt i) 26. 27. this.i = i; 28. 29. 30. 31. 构造器注入的配置:Java 代码 1. public class ExampleBean 2. 3. /依赖的对象 4. 5. private AnotherBean beanOne; 6. 7. private YetAnotherBean beanTwo; 8. 9. private int i; 10. 11. /注入到构造器中 12. 13. public ExampleBean( 14. 15. AnotherBean anotherBean, YetAnotherBean yetAnotherBea
20、n, int i) 16. 17. this.beanOne = anotherBean; 18. 19. this.beanTwo = yetAnotherBean; 20. 21. this.i = i; 22. 23. 24. 25. Xml 代码 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 静态工厂方法参数注入:Java 代码 1. public class ExampleBean 2. 3. 4. 5. / 私有的构造器 6. 7. private Example
21、Bean(.) 8. 9. . 10. 11. 12. 13. /静态工厂方法来实例化(参数为依赖的对象) 14. 15. public static ExampleBean createInstance ( 16. 17. AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) 18. 19. 20. 21. ExampleBean eb = new ExampleBean (.); 22. 23. return eb; 24. 25. 26. 27. Xml 代码 1. 4. 5. 6. 7. 8. 9. 10. 11.
22、 12. 13. 14. 15. 请注意,传给 static 工厂方法的参数由 constructor-arg 元素提供,这与使用构造器注入时完全一样。而且,重要的是,工厂方法所返回的实例的类型并不一定要与包含 static 工厂方法的类类型一致。尽管在此例子中它的确是这样。非静态的实例工厂方法与此相同(除了使用 factory-bean 属性替代 class 属性外) 。实例工厂方法参数注入:Java 代码 1. public class ExampleBean 2. 3. 4. 5. / 私有的构造器 6. 7. private ExampleBean(.) 8. 9. . 10. 11. 12. 13. /实例工厂方法来实例化(参数为依赖的对象) 14. 15. public ExampleBean createInstance ( 16. 17. AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) 18. 19. 20. 21. ExampleBean eb = new ExampleBean (.); 22. 23. return eb; 24. 25. 26. 27. Xml 代码 1. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17.