前言
继前一篇的mybatis之后,这一篇说一说spring。spring作为框架整合的核心,是很重要的一部分,这里简单的讲一下spring的面试问题,源码部分有兴趣的可以自行深入学习,个人也建议阅读spring源码,对以后的开发会有很大的帮助,虽然我也刚开始看~
1、spring的组成
总体来说spring分为七大模块,每块负责不同的功能,换句话说我们可以只用其中的几个部分,这也是spring的一个好处。由于在开发中未使用过struts框架,所以WEB模块这里就不再说了,其主要功能就是为了和struts这类框架整合使用。
说到spring,我们最先想到的就是IOC(控制反转)、DI(依赖注入)等名词,这也是spring最核心的功能。总觉得这些专业词汇听起来高(gǎo)大(bù)上(dǒng),所谓的控制反转就是将依赖对象的生命周期交给spring去管理,依赖注入是指spring在合适的时机将依赖对象注入给当前对象,来段代码方便理解:
1 @Service(value = "service") 2 public class Service { 3 @Resource 4 private Dao dao; 5 }
在Service类中,使用了Dao类,传统的方式就是通过new一个Dao实例之后使用它,而使用spring框架之后,不在需要我们手动的new对象,依赖对象Dao的实例的生命周期由spring的IOC容器管理,并且会将依赖对象的实例注入到当前类Service中,降低了两个类之间的耦合性,这也就是我们所说和控制反转和依赖注入。
接下来说一说spring的AOP模块,通常我们用到的地方就是日志记录和事务管理两个地方,AOP意思是面向切面编程,是对面向对象编程的一个补充。想象这样一个场景:你在开发一个dubbo接口服务,自然需要记录日志,记录包括异常信息等,那么这个记录日志的代码属于你的接口吗?好像属于又好像不属于,因为日志是为了接口的状态而记录的,并不属于你的业务代码,那么调用你接口的人会为你记录日志吗?他们通常只管用就好,根本不会为你记录日志,好尴尬呀!这时候AOP就可以满足你的需求了,通过spring的AOP配置,在你的业务代码之上加入切面,记录日志,让日志代码和业务代码耦合性降低了很多。
spring的AOP有两种实现方式,一种是使用jdk的动态代理,另一种是使用cglib,两者的区别就是jdk动态代理要求被代理类必须实现接口,而cglib则没有这个要求,jdk动态代理返回的是被代理类实现的接口的实现类,cglib返回的是被代理类的子类,对于final类cglib是无法实现的,对于final方法自然也不行。这里列出两种方式的小例子,面试的时候会问到,记住实现的接口名称。
1 //java动态代理 2 import java.lang.reflect.InvocationHandler; 3 import java.lang.reflect.Method; 4 import java.lang.reflect.Proxy; 5 6 public class ProxyTest1 { 7 8 public static void main(String[] args) { 9 10 ProxyTest1Interface1 pt = new ProxyTest1Class1(); 11 InvocationHandler ih = new MyInvocationHandler(pt); 12 // 两种构造方式均可 13 ProxyTest1Interface1 test = (ProxyTest1Interface1) Proxy.newProxyInstance(pt.getClass().getClassLoader(), pt 14 .getClass().getInterfaces(), ih); 15 // ProxyTest1Interface1 test = (ProxyTest1Interface1) Proxy.newProxyInstance( 16 // ProxyTest1Class1.class.getClassLoader(), ProxyTest1Class1.class.getInterfaces(), ih); 17 test.say("111"); 18 } 19 } 20 21 interface ProxyTest1Interface1 { 22 23 void say(String str); 24 } 25 26 class ProxyTest1Class1 implements ProxyTest1Interface1 { 27 28 @Override 29 public void say(String str) { 30 31 System.out.println(str); 32 } 33 } 34 35 class MyInvocationHandler implements InvocationHandler { 36 37 private Object obj; 38 39 public MyInvocationHandler(Object obj) { 40 this.obj = obj; 41 } 42 43 @Override 44 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 45 // 此处的proxy是Proxy类型的对象,无法转换成被代理对象的实例,所以需要通过构造方法传参(cglib使用代理对象调用父类方法执行) 46 System.out.println("--------------"); 47 method.invoke(obj, args); 48 System.out.println("++++++++++++++"); 49 return null; 50 } 51 52 } 53 54 55 //Cglib 56 import java.lang.reflect.Method; 57 58 import net.sf.cglib.proxy.Enhancer; 59 import net.sf.cglib.proxy.MethodInterceptor; 60 import net.sf.cglib.proxy.MethodProxy; 61 62 public class CglibTest1 { 63 64 public static void main(String[] args) { 65 66 CglibProxy cp = new CglibProxy(); 67 SayHello sh = (SayHello) cp.getproxy(SayHello.class); 68 sh.say(); 69 } 70 } 71 72 class CglibProxy implements MethodInterceptor { 73 74 private Enhancer enhancer = new Enhancer(); 75 76 public Object getproxy(Class<?> clazz) { 77 78 enhancer.setSuperclass(clazz); 79 enhancer.setCallback(this); 80 return enhancer.create(); 81 } 82 83 // intercept()方法拦截所有目标类方法的调用,obj表示目标类的实例,method为目标类方法的反射对象,args为方法的动态入参,proxy为代理类实例 84 @Override 85 public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { 86 87 System.out.println("before...."); 88 // 通过代理类实例调用父类方法 89 proxy.invokeSuper(obj, args); 90 System.out.println("after...."); 91 return null; 92 } 93 94 } 95 96 class SayHello { 97 98 void say() { 99 System.out.println("hello"); 100 } 101 }
再接下来就是spring的MVC模块了,spring MVC的知识点不算多,先来一张图熟悉一下结构和流程
这张图里介绍的非常详细,包括主要的控件DispatcherServlet、HandlerMapping、HandlerAdapter和ViewResolver等,整体的流程就是:
spring MVC采用前端控制器模式,即所有的请求都通过一个唯一的DispatcherServlet来处理,在Web应用系统的前端(Front)设置一个入口控制器(Controller),所有request请求都被发往此控制器统一处理。Front Controller可以用来做一个集中处理,如:页面导航、session管理、国际化等。
2、spring面试题
1)spring用到的设计模式?
a)监听者模式,通过在web.xml中配置ContextLoaderListener监听web服务器启动,这也是spring启动的第一步,继而初始化ContextLoader,然后创建ApplicationContext,调用同步的refresh方法,创建BeanFactory,实例化bean等一系列过程。
b)策略模式,spring可以通过不同的方式加载配置文件,创建ApplicationContext对象。
c)单例模式,spring的bean默认都是单例的。
d)代理模式,在spring的AOP模块中使用。
e)模板方法模式,在spring的DAO模块中使用,通过在代码中创建PreparedStatement对象,设置model和数据库列的对应关系。
f)工厂模式,spring的bean都是通过BeanFactory创建的。
暂时想到的就这么多,可能还有其他的,不过对源码的理解比较少,后续待补!
2)spring的bean的生命周期
a)bean被实例化之后,设置bean的属性
b)如果bean实现了BeanNameAware接口,则执行setBeanName方法
c)如果bean实现了BeanFactoryAware接口,则执行setBeanFactory方法
d)如果bean实现了BeanPostProcessor接口,则执行预初始化方法
e)如果bean实现了InitializingBean接口,则执行afterProtertiesSet方法
f)如果bean有自定义的init-method方法,则执行此方法
g)如果bean实现了BeanPostProcessor接口,则执行后初始化方法
h)使用bean
i)容器销毁
j)如果bean实现了DisposableBean接口,则执行destroy方法
k)如果bean有自定义的destroy-method方法,则执行此方法
这里面没有包含BeanFactory的生命周期,BeanFactory在bean创建之前先创建,整体的流程如下图所示:
3)spring的bean的加载过程
4)spring如何实现事务的?
这个题刚开始我也是懵逼状态,不明白啥意思,后来面试官给了点提示,问我事务在哪开启的,怎么提交或者回滚的,后来想到的是jdbc的Connection对象开启的事务并且提交或回滚,面试官的意思是spring在多线程情况下,如果保证同一线程一直使用一个Connection对象。以JdbcTemplate为例,在execute方法中通过DataSourceUtils.getConnection()获取connection,我们再看一看这个方法,发现getConnection方法中通过TransactionSynchronizationManager.getResource()获取了一个ConnectionHolder对象,再往下看就能看到端倪了,原来spring在这里做了一个小把戏,将Connection对象放入了线程的ThreadLocal中,每次getConnection时都优先从ThreadLocal中获取,这样就能保证一个线程一直使用一个Connection对象了,因为是同一个Connection对象,所以可以实现事务了。
1 public static Object getResource(Object key) { 2 Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key); 3 Object value = doGetResource(actualKey); 4 if (value != null && logger.isTraceEnabled()) { 5 logger.trace("Retrieved value [" + value + "] for key [" + actualKey + "] bound to thread [" + 6 Thread.currentThread().getName() + "]"); 7 } 8 return value; 9 }
这个是TransactionSynchronizationManager.getResource()方法,我们看一下里面的doGetResource(actualKey)方法,代码如下:
1 private static Object doGetResource(Object actualKey) { 2 Map<Object, Object> map = resources.get(); 3 if (map == null) { 4 return null; 5 } 6 Object value = map.get(actualKey); 7 // Transparently remove ResourceHolder that was marked as void... 8 if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) { 9 map.remove(actualKey); 10 // Remove entire ThreadLocal if empty... 11 if (map.isEmpty()) { 12 resources.remove(); 13 } 14 value = null; 15 } 16 return value; 17 }
注意这里的这个resources对象,仔细一看,它其实就是一个ThreadLocal,事情也就一目了然了!
关于spring的介绍大概就这么多,每个单位对spring的要求的是高低不一,期间遇到一家公司要求所有开源框架必须熟读源码,包括dubbo、mq等,自己的能力暂时还达不到那个高度,不过对于一些基础的东西也确实需要了解,spring的总结大概就是这么多,希望对大家有帮助,同时欢迎补充!
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
原文链接:http://www.cnblogs.com/1ning/p/6734189.html