Spring

作者: 麦大大吃不胖 | 来源:发表于2021-02-23 23:03 被阅读0次

    by shihang.mai

    Spring的核心是IOC+AOP

    1. IOC-控制反转

    1. 传统应用程序依赖的对象由自己去创建
    2. 有了IoC容器后,把创建和查找依赖对象的控制权交给了Spring容器,依赖的对象由Spring容器注入

    反转:依赖对象的获取被反转了

    IOC是一个思想,DI是它的实现

    2. AOP

    AOP意思是面向切面编程。在程序运行期间,将某段代码动态切入到指定方法的指定位置进行运行的这种编程方式。

    2.1 AOP的实现

    AOP的实现:动态代理

    动态代理就是说AOP框架不会去被代理对象字节码,而是在内存中临时为方法生成一个代理对象,这个代理对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法.运行时生成代理对象字节码,字节码load进内存,然后反射生成代理对象

    2.1.1 JDK动态代理使用

    接口Calculator

    public interface Calculator {
        public int add(int i,int j);
        public int sub(int i,int j);
        public int div(int i,int j);
        public int mul(int i,int j);
    }
    

    实现类MyCalculator

    public class MyCalculator implements Calculator{
        @Override
        public int add(int i, int j) {
            System.out.println(i+j);
            return I+j;
        }
    
        @Override
        public int sub(int i, int j) {
            return i-j;
        }
    
        @Override
        public int div(int i, int j) {
            return I/j;
        }
    
        @Override
        public int mul(int i, int j) {
            return I*j;
        }
    }
    

    获取代理对象类CalcutorProxy

    public class CalcutorProxy {
    
        public static Calculator getProxy(final Calculator calculator){
            ClassLoader classLoader = calculator.getClass().getClassLoader();
            Class<?>[] interfaces = calculator.getClass().getInterfaces();
            InvocationHandler invocationHandler = (proxy, method, args) -> {
                System.out.println("=====增强前====");
                Object result = method.invoke(calculator, args);
                System.out.println("=====增强后====");
                return result;
            };
            Object o = Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
            return (Calculator) o;
        }
    
    }
    

    测试类

    public class Test {
    
        public static void main(String[] args) {
            Calculator proxy = CalcutorProxy.getProxy(new MyCalculator());
            proxy.add(1,1);
            System.out.println(proxy.getClass());
        }
    }
    
    1. 通过类CalcutorProxy.getProxy(new MyCalculator())获取代理类
    2. 调用代理类的add方法,结果如下
    =====增强前====
    2
    =====增强后====
    class com.sun.proxy.$Proxy0
    

    2.1.2 JDK动态代理原理分析

    1. 总的来说就是获取代理类的Class对象,然后通过反射获取代理对象
    2. 而获取代理类的Class对象的过程,先获取被代理对象的ClassLoader(AppClassLoader)和被代理对象实现的接口.然后通过WeakCache(代理Class的缓存)获取,
    • 缓存有,直接返回
    • 缓存没,由ProxyClassFactory创建
    1. 生成代理类名称为com.sun.proxy.$Proxynum,然后经过下面的核心代码
    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                    proxyName, interfaces, accessFlags);
    return defineClass0(loader, proxyName,
                                        proxyClassFile, 0, proxyClassFile.length);
    

    生成一个byte数组,即字节码。并加载到内存中,到此为止已经获取到Class对象。


    设置

    System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
    

    可以看到保留代理类的class文件,反射看到如下

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    
    package com.sun.proxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    import proxy.jdk.Calculator;
    
    public final class $Proxy0 extends Proxy implements Calculator {
        private static Method m1;
        private static Method m2;
        private static Method m4;
        private static Method m6;
        private static Method m5;
        private static Method m3;
        private static Method m0;
    
        public $Proxy0(InvocationHandler var1) throws  {
            super(var1);
        }
    
        public final boolean equals(Object var1) throws  {
            try {
                return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
            } catch (RuntimeException | Error var3) {
                throw var3;
            } catch (Throwable var4) {
                throw new UndeclaredThrowableException(var4);
            }
        }
    
        public final String toString() throws  {
            try {
                return (String)super.h.invoke(this, m2, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final int mul(int var1, int var2) throws  {
            try {
                return (Integer)super.h.invoke(this, m4, new Object[]{var1, var2});
            } catch (RuntimeException | Error var4) {
                throw var4;
            } catch (Throwable var5) {
                throw new UndeclaredThrowableException(var5);
            }
        }
    
        public final int add(int var1, int var2) throws  {
            try {
                return (Integer)super.h.invoke(this, m6, new Object[]{var1, var2});
            } catch (RuntimeException | Error var4) {
                throw var4;
            } catch (Throwable var5) {
                throw new UndeclaredThrowableException(var5);
            }
        }
    
        public final int sub(int var1, int var2) throws  {
            try {
                return (Integer)super.h.invoke(this, m5, new Object[]{var1, var2});
            } catch (RuntimeException | Error var4) {
                throw var4;
            } catch (Throwable var5) {
                throw new UndeclaredThrowableException(var5);
            }
        }
    
        public final int div(int var1, int var2) throws  {
            try {
                return (Integer)super.h.invoke(this, m3, new Object[]{var1, var2});
            } catch (RuntimeException | Error var4) {
                throw var4;
            } catch (Throwable var5) {
                throw new UndeclaredThrowableException(var5);
            }
        }
    
        public final int hashCode() throws  {
            try {
                return (Integer)super.h.invoke(this, m0, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        static {
            try {
                m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
                m2 = Class.forName("java.lang.Object").getMethod("toString");
                m4 = Class.forName("proxy.jdk.Calculator").getMethod("mul", Integer.TYPE, Integer.TYPE);
                m6 = Class.forName("proxy.jdk.Calculator").getMethod("add", Integer.TYPE, Integer.TYPE);
                m5 = Class.forName("proxy.jdk.Calculator").getMethod("sub", Integer.TYPE, Integer.TYPE);
                m3 = Class.forName("proxy.jdk.Calculator").getMethod("div", Integer.TYPE, Integer.TYPE);
                m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    }
    
    
    1. 从上面的代理对象可以看到,静态代码快会先初始化所有的Method
    2. 当代理对象调用add时,会调用super.h.invoke(this, m6, new Object[]{var1, var2});即调用我们定义的InvocationHandler的invoke方法,传入参数:
    • this-代理对象
    • m6-add
    • new Object[]{var1, var2}-对应的参数
    1. 这样就可以在不需要改原来逻辑的前提下,在InvocationHandler前后加入自己想要东西。

    2.1.3 CGLIB动态代理使用

    cgLib,如果目标类没有实现接口,spring就会选择cgLib,是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的

    CGLIB(Code Generation Library)它可以在运行期扩展Java类与实现Java接口。CGLIB是一个强大的高性能的代码生成包。它广泛的被许多AOP的框架使用,例如Spring AOP为他们提供方法的interception(拦截)。CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。

    类MyCalculator,并不要求实现接口

    public class MyCalculator {
        public int add(int i,int j){
            return I+j;
        }
    
        public int sub(int i,int j){
            return i-j;
        };
    
        public int div(int i,int j){
            return I/j;
        };
    
        public int mul(int i,int j){
            return I*j;
        };
    }
    

    类MyCglib实现MethodInterceptor

    public class MyCglib implements MethodInterceptor {
        /**
         *
         * @param o 表示增加的对象,即实现这个接口类的一个对象
         * @param method 表示要拦截的方法
         * @param args 表示要拦截的方法的参数
         * @param proxy 表示要触发父类方法的对象
         * @return
         * @throws Throwable
         */
        @Override
        public Object intercept(Object o, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("方法执行前");
            Object result = proxy.invokeSuper(o, args);
            System.out.println("方法执行后");
            return result;
        }
    }
    

    测试类

    public class Test {
        public static void main(String[] args) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(MyCalculator.class);
            enhancer.setCallback(new MyCglib());
            MyCalculator myCalculator =(MyCalculator) enhancer.create();
            System.out.println(myCalculator.add(1,1));
            System.out.println(myCalculator.getClass());
    
        }
    }
    
    1. 通过Enhancer获取代理对象
    2. 代理对象调用add,结果如下:
    方法执行前
    2
    方法执行后
    class com.proxy.cglib.MyCalculator$$EnhancerByCGLIB$$c2b8bdc
    

    2.1.4 CGLIB动态代理原理

    设置

    System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "target/cglib");
    

    把字节文件保留


    1. 先预验证,是否是下面的7个CallBack其中一个
            new CallbackInfo(NoOp.class, NoOpGenerator.INSTANCE),
            new CallbackInfo(MethodInterceptor.class, MethodInterceptorGenerator.INSTANCE),
            new CallbackInfo(InvocationHandler.class, InvocationHandlerGenerator.INSTANCE),
            new CallbackInfo(LazyLoader.class, LazyLoaderGenerator.INSTANCE),
            new CallbackInfo(Dispatcher.class, DispatcherGenerator.INSTANCE),
            new CallbackInfo(FixedValue.class, FixedValueGenerator.INSTANCE),
            new CallbackInfo(ProxyRefDispatcher.class, DispatcherGenerator.PROXY_REF_INSTANCE),
    
    1. 最终生成一个代理类 extends 被代理类 implements Factory
    public class MyCalculator$$EnhancerByCGLIB$$c2b8bdc extends MyCalculator implements Factory {
    public final int add(int var1, int var2) {
            MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
            if (var10000 == null) {
                CGLIB$BIND_CALLBACKS(this);
                var10000 = this.CGLIB$CALLBACK_0;
            }
    
            if (var10000 != null) {
                Object var3 = var10000.intercept(this, CGLIB$add$0$Method, new Object[]{new Integer(var1), new Integer(var2)}, CGLIB$add$0$Proxy);
                return var3 == null ? 0 : ((Number)var3).intValue();
            } else {
                return super.add(var1, var2);
            }
        }
    }
    
    1. 调用add时,直接调用代理类的add,再调用MethodInterceptor.intercept,再调用proxy.invokeSuper(o, args);这个会根据一个数字定位到具体的方法执行返回
    public class MyCalculator$$EnhancerByCGLIB$$c2b8bdc$$FastClassByCGLIB$$da1d1b9e extends FastClass {
    
    public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
            c2b8bdc var10000 = (c2b8bdc)var2;
            int var10001 = var1;
    
            try {
                switch(var10001) {
                case 0:
                    var10000.setCallbacks((Callback[])var3[0]);
                    return null;
                case 1:
                    c2b8bdc.CGLIB$SET_STATIC_CALLBACKS((Callback[])var3[0]);
                    return null;
                case 2:
                    c2b8bdc.CGLIB$SET_THREAD_CALLBACKS((Callback[])var3[0]);
                    return null;
                case 3:
                    return var10000.getCallback(((Number)var3[0]).intValue());
                case 4:
                    return var10000.getCallbacks();
                case 5:
                    return c2b8bdc.CGLIB$findMethodProxy((Signature)var3[0]);
                case 6:
                    c2b8bdc.CGLIB$STATICHOOK1();
                    return null;
                case 7:
                    return new Integer(var10000.CGLIB$add$0(((Number)var3[0]).intValue(), ((Number)var3[1]).intValue()));
                case 8:
                    return new Integer(var10000.CGLIB$div$1(((Number)var3[0]).intValue(), ((Number)var3[1]).intValue()));
                case 9:
                    return new Integer(var10000.CGLIB$mul$2(((Number)var3[0]).intValue(), ((Number)var3[1]).intValue()));
                case 10:
                    return new Integer(var10000.CGLIB$sub$3(((Number)var3[0]).intValue(), ((Number)var3[1]).intValue()));
                case 11:
                    return new Boolean(var10000.CGLIB$equals$4(var3[0]));
                case 12:
                    return var10000.CGLIB$toString$5();
                case 13:
                    return new Integer(var10000.CGLIB$hashCode$6());
                case 14:
                    return var10000.CGLIB$clone$7();
                case 15:
                    return new Integer(var10000.add(((Number)var3[0]).intValue(), ((Number)var3[1]).intValue()));
                case 16:
                    return new Boolean(var10000.equals(var3[0]));
                case 17:
                    return var10000.toString();
                case 18:
                    return new Integer(var10000.hashCode());
                case 19:
                    return var10000.clone();
                case 20:
                    return var10000.newInstance((Callback)var3[0]);
                case 21:
                    return var10000.newInstance((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]);
                case 22:
                    return var10000.newInstance((Callback[])var3[0]);
                case 23:
                    var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]);
                    return null;
                case 24:
                    return new Integer(var10000.div(((Number)var3[0]).intValue(), ((Number)var3[1]).intValue()));
                case 25:
                    return new Integer(var10000.mul(((Number)var3[0]).intValue(), ((Number)var3[1]).intValue()));
                case 26:
                    return new Integer(var10000.sub(((Number)var3[0]).intValue(), ((Number)var3[1]).intValue()));
                }
            } catch (Throwable var4) {
                throw new InvocationTargetException(var4);
            }
    
            throw new IllegalArgumentException("Cannot find matching method/constructor");
        }
    }
    

    2.1.5 两种动态代理对比

    2.1.5.1 原理区别

    • jdk动态代理
    1. 是利用反射机制生成一个实现代理接口的代理类
      代理类 extends Proxy implements 被代理类也实现的接口
    2. 在调用代理类方法时,会在方法内调用InvocationHandler的invoke方法。在invoke方法中对被代理类的方法前后进行增加
    • cglib动态代理是利用asm开源包
    1. 代理类的class,并load进内存,反射生成代理类
      代理类 extends 被代理类 implements Factory
    2. 在调用代理类方法时,会调用MethodInterceptor的intercept方法,这个方法对被代理类的方法进行增强。而调用被代理类的方法会调用另外一个生成的类里面的swtich分支的方法进行实际的执行

    2.1.5.2 使用区别

    1. JDK动态代理只能对实现了接口的被代理类生成代理,而不能针对类

    2. CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以该类或者方法不要声明成final

    3. SpringMVC

    springmvc
    1. 用户发出请求,DispatcherServlet拦截请求
    2. DispatcherServlet调用HandlerMapping,HandlerMapping根据请求url查找Handler,返回处理器执行链到DispatcherServlet
    public class HandlerExecutionChain {
      private final Object handler;
      private HandlerInterceptor[] interceptors;
      private List<HandlerInterceptor> interceptorList;
    }
    
    1. DispatcherServlet请求HandlerAdapter执行具体的Handler
    2. 具体的Handler执行完后返回ModelAndView到HandlerAdapter,HandlerAdapter再将ModelAndView返回到DispatcherServlet
    3. DispatcherServlet调用ViewResolver解析,返回View
    4. DispatcherServlet渲染View,然后作为Response返回给用户

    4. SpringBean生命周期

    生命周期

    SpringBean生命周期只有4个,其他的都是扩展点
    实例化 Instantiation->属性赋值 Populate->初始化 Initialization->销毁 Destruction

    影响多个Bean的接口

    1. InstantiationAwareBeanPostProcessor
      在实例化前后调用
    2. BeanPostProcessor
      在初始化前后调用

    调用一次的接口

    • Aware类型的接口,都是在初始化前执行的
      1. 代码直接调用
      • BeanNameAware
      • BeanClassLoaderAware
      • BeanFactoryAware
      1. 通过BeanPostProcessor调用
      • EnvironmentAware
      • EmbeddedValueResolverAware
      • ApplicationContextAware(ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware)这几个接口返回值都是ApplicationContextAware
    • 生命周期接口
      1. InitializingBean,初始化时回调,也可用init-method
      2. DisposableBean,销毁时回调,也可用destory-method

    参考博客

    https://www.jianshu.com/p/1dec08d290c1
    

    5. Spring核心接口

    1. BeanFactory,实例化bean,反射,类加载;
    2. Aware接口,当Spring容器创建的bean对象在进行具体操作的时候,callBack的方法
    3. BeanDefinition,获取bean信息;
    4. BeanFactoryPostProcessor,增强BeanDefinition
    5. BeanPostProcessor,增强bean
    6. Environment,获取环境变量
    7. FactoryBean,用来创建对象的,只需要调用getObject就可以返回具体的对象,整个对象的创建过程是由用户自己来控制的,更加灵活

    6. BeanFactory和FactoryBean


    都是用来创建对象的

    1. 当使用BeanFactory的时候必须要遵循完整的创建过程,这个过程是由spring来管理控制的
    2. 使用FactoryBean只需要调用getObject就可以返回具体的对象,整个对象的创建过程是由用户自己来控制的,更加灵活。

    7. beanPostProcessor和beanFactoryPostProcessor

    1. PostProcessor都是增强器
    2. BeanFacatoryPostProcessor是增强BeanDefinition信息
    3. BeanPostProcessor是增强bean信息

    8. BeanFactory 和ApplicationContext

    1. 首先两者加载Bean的方式都是通过Xml配置文件
    2. BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用

    1. BeanFacotry是延迟加载,举个例子:如果Bean没有完全注入,BeanFacotry加载后,会在你第一次调用GetBean方法才会抛出异常
    2. 而ApplicationContext会在初始化的时候就加载并且检查,这样的好处是可以及时检查依赖是否完全注入
    3. BeanFactory需要手动注册PostProcessor,而ApplicationContext则是自动注册PostProcessor
    4. ApplicationContext接口是由BeanFactory接口派生出来的,所以提供了BeanFactory的所有功能

    9. 循环依赖

    一句话概括:Spring通过将实例化后的对象提前暴露给Spring容器中的singletonFactories,解决了循环依赖的问题

    1. java中的循环依赖分两种,一种是构造器的循环依赖,另一种是属性的循环依赖。
    2. 对于构造器的循环依赖没有什么解决办法,因为JVM虚拟机在对类进行实例化的时候,需先实例化构造器的参数,而由于循环引用这个参数无法提前实例化,故只能抛出错误
    3. Spring解决的循环依赖就是指属性的循环依赖

    9.1 循环依赖问题

    场景:A里面有B,B里面有A

    循环依赖问题

    假设spring先创建A对象,会经历如上图过程,会形成一个闭环

    9.2 解决

    解决方法:保证在第二次获取容器中的A时,能获取到即可

    在spring中,对象的创建分为实例化和初始化,只需将实例化好但未完成初始化的对象提前暴露出去,就可以解环了。而这样的对象存放在缓存中。

    3级缓存yOtlmd.png

    接下来我们结合上面9.1和9.2的3级缓存说说如何解决的

    1. 创建A对象,先经历jvm中的A实例化,初始化,此时将a对应的能够获取初始化状态的A的lamda表达式放入3级缓存,再到赋值A中B对象时,发现B对象并不在容器中,故创建B对象
    2. 创建B对象一样要经历和A对象一样的过程,B实例化,B初始化,也将b对应的能够获取初始化状态的B的lamda表达式放入3级缓存,再到赋值B中的A对象时,从一级缓存,二级缓存,三级缓存的顺序找,找到三级缓存中a的lamda表达式,此时将实例化好的a放入二级缓存
    3. 此时B对象已经得到实例化好的a,虽然不是完整的,但是都可以被引用了,将完整的b对象放到一级缓存,并删除b的三级缓存
    4. 不要忘记,到此为止,我们的目的都是创建A对象,现在B对象已经完成了,返回A对象已经可以引用完整的B对象了,将完整的a对象放入一级缓存,删除3级2级缓存,至此A、B对象已经全部完成,并且解决了循环依赖

    9.3 思考

    1. 用一级缓存呢?
      用1级缓存的话,那么这个缓存里面即同时放未初始化的和初始化的对象,只是后面初始化的会覆盖未初始化的。这样其他获取该对象时,有可能获取到未初始化的对象,会造成空指针。故试试2级缓存
    2. 用2级缓存呢?
      其实用2级缓存,足已解决循环依赖问题。将未初始化和初始化的分开存储。其他对象直接获取初始化好的即可。但是如果引入AOP,那么就会出现,其他对象引用该对象(原始对象)和自身(代理对象)不一致的情况。
    3. 用3级缓存
      用3级缓存,就是spring解决循环依赖的方法。上面例子已经很明白了。

    参考博客

    https://www.cnblogs.com/zzq6032010/p/11406405.html
    https://www.cnblogs.com/grey-wolf/p/13034371.html#_label0
    

    10. Spring整体架构

    1. Spring框架中有一个BeanDefinitionReader接口定义规范,方便扩展,将我们的bean配置解析成BeanDefinition(bean配置可以用xml、注解、properties、yaml)
    2. 还有环境信息封装到一个Environment里
    3. BeanDefinition,可以通过BeanFactoryPostProcessor做增删改加强
    4. 然后有了BeanDefinition,通过Bean生命周期,其中这里还涉及了巧妙的一个解决循环依赖的方案,反射得到具体的实例放到容器中

    相关文章

      网友评论

          本文标题:Spring

          本文链接:https://www.haomeiwen.com/subject/jnyrfltx.html