01、谈理解

首先你要知道,Spring框架原理基本是Java岗面试必问的问题。偶尔会扩展到Spring mvc框架,不过一般很少。每当面试官向你提问,让你说一下Spring的框架原理,你必须要清楚明白的向面试官阐述你的理解,最好不要照本宣科。这一块,我贴出我面对这种问题的回答,供读者参考。

对于Spring,核心就是IOC容器,这个容器说白了就是把你放在里面的对象(Bean)进行统一管理,你不用考虑对象如何创建如何销毁,从这方面来说,所谓的控制反转就是获取对象的方式被反转了。既然你都把对象交给人家Spring管理了,那你需要的时候不得给人家要呀。这就是依赖注入(DI)!再想下,我们在传入一个参数的时候除了在构造方法中就是在setter方法中,换个好听的名字就是构造注入和设值注入。

至于AOP(面向切面),这玩意我举个例子说下,比如你写了个方法用来做一些事情,但这个事情要求登录用户才能做,你就可以在这个方法执行前验证一下,执行后记录下操作日志,把前后的这些与业务逻辑无关的代码抽取出来放一个类里这个类就是切面(Aspect),这个被环绕的方法就是切点(Pointcut),你所做的执行前执行后的这些方法统一叫做增强处理(Advice)。

02、动态代理方式

当你回答了AOP完之后,面试官紧接着会问你AOP原理,很简单,动态代理。那你回答完动态代理,面试官肯定会让你来讲一下实现动态代理的方式。

我觉得你完全可以从容不迫的先说一下静态代理,一句话,自己手写代理类就是静态代理手写代理类也有两种思路,一是通过继承被代理类的方式实现其子类,重写父类方法;二是与被代理类实现共同的一个接口,尴尬的一点是被代理类未必会有接口。

到了这里,你就可以引出动态代理的概念。简单来说,动态代理就是交给程序去自动生成代理类(在程序运行期间由JVM根据反射等机制动态的生成源码 )。

从静态代理的实现也可以类比出,动态代理的实现也有两种方式,即基于接口的JDK动态代理和基于继承的cglib动态代理。

03、JDK的动态代理

使用JDK的动态代理去生成代理只需要一行代码:

Moveable move = (Moveable) 
Proxy.newProxyInstance(Car.class.getClassLoader(), 
Car.class.getInterfaces(), new LogHandler(new Car()));

传入的参数中其实就俩,一是被代理类的类对象,二是自定义的增强处理代码。下面看下LogHandler源码:

public class LogHandler implements InvocationHandler{ private Object target; 
 public LogHandler(Object object){
 super();
 this.target = object;
 }
 @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //增强处理
 Object o = method.invoke(target,args);
 return o;
 }
}

观察InvocationHandler实现类的源码可以发现,首先是定义了一个含参构造方法,该参数即为要代理的实例对象,观察target对象的使用位置在method.invoke()方法的参数中,不难看出,目的也就是为了执行目标方法。那重写的invoke()方法的三个参数又是什么呢?顾名思义,

  • proxy:动态生成的代理对象
  • method:目标方法的实例
  • args:目标方法的参数

从Proxy.newProxyInstance()的方法参数中包含了被代理类的接口的类对象也不难得知,JDK的动态代理只能代理实现了接口的类, 没有实现接口的类不能实现动态代理。

关于Jdk动态代理的原理,我归纳得出四步

  1. 声明一段源码,源码动态产生动态代理
  2. 源码产生java文件,对java文件进行编译
  3. 得到编译生成后的class文件
  4. 把class文件load到内存之中,产生代理类对象返回即可。

依照这四步流程,实现一个Demo版的Jdk动态代理框架,问题不大。

04、cglib的动态代理

cglib使用动态代理的流程:

public static void main(String[] args) {
 Enhancer enhancer = new Enhancer();
 //设置被代理类
 enhancer.setSuperclass(Car.class);
 //设置回调函数
 enhancer.setCallback(new MethodInterceptor() { @Override
 public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { //增强处理... //增强处理...
 Object o= proxy.invokeSuper(obj, args);//代理类调用父类的方法
 //增强处理...
 return o;
 }
 });
 //创建代理类并使用回调
 Car car = (Car) enhancer.create(); 
 //执行目标方法
 System.out.println(car.move());
}

从上面的代码看到,cglib的使用流程还是很清晰明了,各种参数顾名思义,和jdk的区别不大。生成的代理对象直接被该类引用,与我们认知的基于继承的动态代理没冲突。不过这种基于继承的方式就没有什么缺点吗?最明显的一点就是final修饰的类无法使用。

05、Spring用的啥

Jdk的动态代理和cglib的动态代理你都已经知晓,spring底层究竟是如何选择哪种动态代理方式,这也是面试经常考到的一个知识点。

标准回答如下:Spring会根据具体的Bean是否具有接口去选择动态代理方式,如果有接口,使用的是Jdk的动态代理方式,如果没有接口,使用的是cglib的动态代理方式。

原文:https://www.toutiao.com/a6672288337140646407/?tt_from=mobile_qq&utm_campaign=client_share&timestamp=1553645609&app=news_article&utm_source=mobile_qq&utm_medium=toutiao_android&group_id=6672288337140646407

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐