面试必问的Spring实现原理

序言

Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。Spring是于2003
年兴起的一个轻量级的Java 开发框架,由Rod Johnson创建。简单来说,Spring是一个分层的JavaSE/EE
full-stack(一站式) 轻量级开源框架。

传统的软件开发从Model1模式–>到Model2模式,
再到如今的三层架构模式,软件的架构不断的更新,迭代。其架构图如图所示:
面试必问的Spring实现原理_第1张图片
基于MVC架构 servlet+jsp+jdbc,那么来看以下案例
面试必问的Spring实现原理_第2张图片
在过去我们在操作数据库时候,每一次都会去创建dao层对象,再通过dao对象来执行对数据库进行增删该查的操作,这样每一次操作都会去创建一个对象,不仅增加了GC垃圾回收的压力。也浪费掉了这个对象。这个对象的生命周期往往就从线程方法调用到结束。
那么如何去解决这个问题呢? 我们可能会想到使用单例模式来创建对象,如图所示:
面试必问的Spring实现原理_第3张图片
但是,使用单列的设计模式虽然可以在性能上有所提高,也会增加我们的开发成本。由于单列模式它也无法满足我们在性能上的需求,在多线程,高并发环境下,单列模式依然撑不住,无法解决开发上的痛点。所以我们想到了使用池子的方式来管理这么多的对象。而我们想要去创建的池子,实际上就是Spring容器最初的样子。

SpringIOC

而SpringIoc的目的就是用来管理对象,动态的向某个对象提供他所需要的对象。通过DI依赖注入来实现。我们不需要关心这个bean对象到底是怎么来的。在系统运行之初,就将容器里的对象注入到指定的对象。
其实现原理主要是通过了反射来实现。
这样也很好解释了private权限修饰符中,对象依然会被注入到。Spring IOC在创建之初会将xml或者相关的配置文件中的对象全部放到map集合中去,然后再注入到指定的对象中。其思想可以通过下面简化的demo案例来解释:

面试必问的Spring实现原理_第4张图片
那么在SpringIOC中仅仅只用到了单列设计模式吗?答案当然是否定的。在SpringIOC中也使用到了工厂设计模式,Spring 容器是实例化和管理全部bean 的工厂,Spring 默认将所有的bean 设置成单态模式,无须自己完成单态模式,即对所有相同id 的bean 请求都将返回同一个共享实例。因此,单态模式可大大降低Java 对象在创建和销毁时的系统开销。Spring中就大量使用到了单列设计模式以及工厂设计模式。

SpringAOP

Spring倡导一种思想,面向接口编程思想。在说这个之前先来看一下以前的代码
面试必问的Spring实现原理_第5张图片
在这段代码中出现了很多重复的代码.比如登录验证这里。每一次调用方法,就去验证session中是否保存了对象。不仅在登录这里,就连日志记录也是这样,每一次调用方法,都会写上那么一段重复的代码。其实是完全没有必要的。可以想象,如果未来有一天,不需要进行日志记录,或者登录认证等功能需要修改,那么所需要改动的代码也是相当庞大的。

if (admin2 == null) {

			resp.sendRedirect("/WEB-INF/jsp/adminUser/login.jsp");
			return;
		}

那么如何去解决?
通过JDK动态代理或者CG动态代理的方式,

Spring支持两种方法,那么我们在使用spring进行动态代理时究竟使用的哪一种方法呢?spring优先支持实现接口的方式,如果没有接口则使用cglib方式。下面我们看一看这两种方法有什么区别。 下面我们通过JDK实现动态代理,需求是如下:有一个服务层对象ServiceImpl 如下
面试必问的Spring实现原理_第6张图片

他们有三个方法,增删改。我们知道执行增删改操作时需要开启事务,提交事务。接下来我们通过动态代理的方式为这三个方法织如事务控制。以伪代码的形式。因为JDK动态代理需要使用接口,那我们为他创建一个接口。

面试必问的Spring实现原理_第7张图片

接下来我们使用ServiceImpl的代理工厂生成代理对象,创建代理工厂类ServiceProxyFactory如下
面试必问的Spring实现原理_第8张图片

第一眼看上去可能有点混乱,一步一步来,既然代理工厂生成代理对象,我们首先创建一个getInstance方法返回代理对象。JDK提供了一个Proxy下的一个静态方法newProxyInstance(),帮助我们生成代理对象。该方法需要传入三个参数,

第一个参数为的类加载器,
Java有三种类加载器,这里不做重点描述,提供任意一个类的class对象有一个方法getClassLoader()返回一个classLoader对象,源码中该对象是被final修饰的,防止黑客修改类加载以达到不可告人的目的,sun公司在jvm中有着大量的代码维护反射和类加载器,防止被坏人利用。
第二个参数为Class[]
类型数组,什么意思呢,这里可以传入任意的Class类,那么如何获取Class类数组呢,通过任意的class对象getInterfaces()方法可以获得Class[],但是这里我们必须传入需要被代理对象的class对象。
第三个参数需要传入一个执行器对象,该执行器必须符合InvocationHandler接口规范,就是实现这个接口的invoke方法。该方法同样要求传入三个参数,

  • 参数一:局部变量:被代理对象实例
  • 参数二:Method类型对象,该参数有JDK的Proxy负责传入,method为我们被代理对象的执行方法对象。
  • 参数三:Object[],它是被执行方法参数,在方法执行中参数由Proxy负责传入至Object[]中。

那么第3个参数我们实质只需要传入被代理对象实例即可。那么传入被代理方法对象有两种方法,一个是构造器传入,另一个是set方法传入,也可以使用spring注入。我们使用method对象调用invoke()方法,该方法需要两个参数,第一个参数:全局变量的被代理对象实例,这里与全局变量被代理对象实例做一个区分帮助大家更清晰的理解。看图中,被代理对象实例由set方法传入全局变量si,然后si作为InvocationHandler的实现类下的invoke方法的参数method对象下的invoke方法参数传入。 这个参数也就是invoke的调用对象。第二个参数就是被代理对象实例调用的方法参数,也就是上方的参数三Object[]。该invoke方法返回一个Object对象,因为我使用的自动生成变量所以这个Object对象名也是invoke,最后我们将该对象invoke返回即可。这样就可以创建一个代理对象了,我们使用动态代理的目的是在原编码不变的基础上对方法进行增强,

你可能感兴趣的:(Java,spring)