java面试知识总结--spring工作原理+动态代理知识

目标

汇总spring中ioc和aop核心思想,jdk和cglib原理及区别

ioc分析

IoC不是一种spring独有的技术,只是一种设计思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。ioc的思想给予依赖导致原则,如典型的先造汽车还是先早轮子的话题:(以下内容引用出处,看参考资料)

image

这样的设计看起来没问题,但是可维护性却很低。假设设计完工之后,上司却突然说根据市场需求的变动,要我们把车子的轮子设计都改大一码。这下我们就蛋疼了:因为我们是根据轮子的尺寸设计的底盘,轮子的尺寸一改,底盘的设计就得修改;同样因为我们是根据底盘设计的车身,那么车身也得改,同理汽车设计也得改——整个设计几乎都得改!

我们现在换一种思路。我们先设计汽车的大概样子,然后根据汽车的样子来设计车身,根据车身来设计底盘,最后根据底盘来设计轮子。这时候,依赖关系就倒置过来了:轮子依赖底盘, 底盘依赖车身, 车身依赖汽车。

image

这时候,上司再说要改动轮子的设计,我们就只需要改动轮子的设计,而不需要动底盘,车身,汽车的设计了。

这就是依赖倒置原则——把原本的高层建筑依赖底层建筑“倒置”过来,变成底层建筑依赖高层建筑。高层建筑决定需要什么,底层去实现这样的需求,但是高层并不用管底层是怎么实现的。这样就不会出现前面的“牵一发动全身”的情况。

aop思想

  1. 静态代理 ,不方便,使用麻烦

    • 针对每个具体类分别编写代理类;
    • 针对一个接口编写一个代理类;
  2. 动态代理

    1. JDK实现动态代理,通过
      Proxy.newProxyInstance(class.tInterfaces,this);
      要求:只能对实现了接口的类生成代理,而不能针对类
    2. cglib实现:针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法, 因为是继承,所以该类或方法最好不要声明成final

jdk动态代理实现

动态代理原理(字节码重组,下面为JDK动态代理,cglib类似区别在于它是通过继承实现的):

                1.拿到被代理对象的引用,并且通过反射获取到它的所有接口

                2.JDK Proxy类重新生成一个新的类,同时新的类要实现被代理类的所有实现方法

                3.动态生产java代码,把新加的业务逻辑方法有一定的逻辑代码取调用

                4.编译新生成的iava代码.class

                5.再重新加载到jvm中运行

                6.以上为字节码重组的全过程

4.JDK动态代理源码分析:

  手写JDK动态代理过程,关键代码:

(1)代理类的newInstance方法

public static Object newInstance(GPClassLoader cl, Class[] interfaces, GPInvocationHandler invokehandler) throws Throwable {

    //0.动态生成java文件

    String s = ganeteFile(interfaces);

    //1.写入磁盘

    String path = GPProxy.class.getResource("").getPath();

    System.out.println(path);

    File f=new File(path+"KYProxy$0.java");

    FileOutputStream fileOutputStream=new FileOutputStream(f);

    fileOutputStream.write(s.getBytes());

    fileOutputStream.close();

    //2.把java文件编译成.class文件

    JavaCompiler compiler= ToolProvider.getSystemJavaCompiler();

    StandardJavaFileManager manager=compiler.getStandardFileManager(null,null,null);

    Iterable javaFileObjects = manager.getJavaFileObjects(f);

  JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, javaFileObjects);

    task.call();

    manager.close();

    //3.将class文件加载到jvm中

    Class aClass = cl.findClass("KYProxy$0");

    //4.返回字节码生成到代理对象

    Constructor[] constructors = aClass.getConstructors();

    return constructors[0].newInstance(invokehandler);

}

(2)classloader加载类方法


public class GPClassLoader extends ClassLoader{

    private File classPathFile;

    public GPClassLoader() {

        String path = GPClassLoader.class.getResource("").getPath();

        this.classPathFile = new File(path);

    }

    @Override

    protected Class findClass(String name) throws ClassNotFoundException {

       String className= GPClassLoader.class.getPackage().getName()+"."+ name;

        try {

            if(classPathFile!=null){

                File file=new File(classPathFile,name.replaceAll(".","/"));

                if(file.exists()){

                    FileInputStream fis=new FileInputStream(file+"/"+name+".class");

                    ByteArrayOutputStream byteArrayInputStream=new ByteArrayOutputStream();

                    byte[] bt=new byte[1024];

                    int lend;

                    while ( (lend = fis.read(bt))!=-1){

                        byteArrayInputStream.write(bt,0,lend);

                    }

                    return defineClass(className,byteArrayInputStream.toByteArray(),0,byteArrayInputStream.size());

                }

            }

        } catch (Exception e) {

            e.printStackTrace();

        }

        return null;

    }

}

(2)拼代理类字节码的方法,此处我们可以通过ProxyGenerator.generateProxyClass生成对应的代理源码然后仿写代理类,如: byte[] bytes = ProxyGenerator.generateProxyClass("proxy.XieMu$$EnhancerByCGLIB$$6e794e95@3d012ddd”, FileOutputStream os=new FileOutputStream("/Users/kai.yang/Desktop/test.class");

public static String ganeteFile(Class[] classes) {

    StringBuilder sb = new StringBuilder();

    append(sb, "package proxy.custom;");

    append(sb, "import java.lang.reflect.*;");

    append(sb, "public class KYProxy$0 implements");

    StringBuilder clName = new StringBuilder();

    for (Class clazz : classes

            ) {

        clName.append(" ").append(clazz.getName()).append(",");

    }

    sb.append(clName.toString().substring(0, clName.length() - 1)).append("{\n");

    sb.append("public KYProxy$0(GPInvocationHandler handler){\n" +

            "this.h=handler;" +

            "\n}\n");

    append(sb, "proxy.custom.GPInvocationHandler h=null;");

    for (Class clazz : classes

            ) {

        Method[] methods = clazz.getMethods();

        for (int i=0;i clazz : classes

            ) {

        Method[] methods = clazz.getMethods();

        for (int i=0;i

cglib动态代理实现:

先说下两种动态代理区别:

  1. JDK动态代理是实现了被代理对象的接口,Cglib是继承了被代理对象。
  2. JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。
  3. JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高。

(1)FastClass机制
Cglib动态代理执行代理方法效率之所以比JDK的高是因为Cglib采用了FastClass机制,它的原理简单来说就是:为代理类和被代理类各生成一个Class,这个Class会为代理类或被代理类的方法分配一个index(int类型)。

这个index当做一个入参,FastClass就可以直接定位要调用的方法直接进行调用,这样省去了反射调用,所以调用效率比JDK动态代理通过反射调用高。

//根据方法签名获取index

 public int getIndex(Signature var1) {
      String var10000 = var1.toString();
      switch(var10000.hashCode()) {
      case -2077043409:
         if(var10000.equals("getPerson(Ljava/lang/String;)Lcom/demo/pojo/Person;")) {
            return 21;
         }
         break;
      case -2055565910:
         if(var10000.equals("CGLIB$SET_THREAD_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) {
            return 12;
         }
         break;
      case -1902447170:
         if(var10000.equals("setPerson()V")) {
            return 7;
         }
         break;
   //省略部分代码.....

 

 //根据index直接定位执行方法
 public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
      eaaaed75 var10000 = (eaaaed75)var2;
      int var10001 = var1;

      try {
         switch(var10001) {
         case 0:
            return new Boolean(var10000.equals(var3[0]));
         case 1:
            return var10000.toString();
         case 2:
            return new Integer(var10000.hashCode());
         case 3:
            return var10000.newInstance((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]);
         case 4:
            return var10000.newInstance((Callback)var3[0]);
         case 5:
            return var10000.newInstance((Callback[])var3[0]);
         case 6:
            var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]);
            return null;
         case 7:
            var10000.setPerson();
            return null;
         case 8:
            var10000.setCallbacks((Callback[])var3[0]);
            return null;
         case 9:
            return var10000.getCallback(((Number)var3[0]).intValue());
         case 10:
            return var10000.getCallbacks();
         case 11:
            eaaaed75.CGLIB$SET_STATIC_CALLBACKS((Callback[])var3[0]);
            return null;
         case 12:
            eaaaed75.CGLIB$SET_THREAD_CALLBACKS((Callback[])var3[0]);
            return null;
         case 13:
            return eaaaed75.CGLIB$findMethodProxy((Signature)var3[0]);
         case 14:
            return var10000.CGLIB$toString$3();
         case 15:
            return new Boolean(var10000.CGLIB$equals$2(var3[0]));
         case 16:
            return var10000.CGLIB$clone$5();
         case 17:
            return new Integer(var10000.CGLIB$hashCode$4());
         case 18:
            var10000.CGLIB$finalize$1();
            return null;
         case 19:
            var10000.CGLIB$setPerson$0();
            return null;
        //省略部分代码....
      } catch (Throwable var4) {
         throw new InvocationTargetException(var4);
      }

      throw new IllegalArgumentException("Cannot find matching method/constructor");
   }

参考资料:
1.ioc理解:https://www.zhihu.com/question/23277575

你可能感兴趣的:(java面试知识总结--spring工作原理+动态代理知识)