您的位置:首页 > 编程语言 > Java开发

JAVA基础5-JAVA反射和代理学习笔记

2019-07-04 15:00 447 查看

JAVA反射和代理学习笔记

  • Spring依赖注入
  • 代理模式
  • 反射原理

    结构

    Class:对类的描述
    Method:对方法的描述
    Field:对属性的描述
    Constructor:对构造方法描述
    ReflectionData:用于缓存机制

    在类装载器中,会将类加载到内存中,以上面的结构存储。

    反射获取方法

    getDeclaredMethod的分析。

    1. 通过privateGetDeclaredMethods获取类的方法。先从内存中获取,如果内存没有创建一个。
    2. 缓存是通过ReflectionData实现的。
    3. 再通过searchMethods查找对应的方法(匹配名称和参数)。
    4. 找到对应方法,使用Method的copy方法复制一个方法对象(并将这个对象的root指向源method对象)。

    反射调用方法

    invoke的分析

    1. Method实际调用MethodAccessor的invoke方法。
    2. 内部的MethodAccessor如果MethodAccessor为空,则通过acquireMethodAccessor方法创建一个新的。
    3. acquireMethodAccessor是通过ReflectionFactory的创建一个MethodAccessor接口对象。
    4. 创建的是实现类NativeMethodAccessorImpl或者DelegatingMethodAccessorImpl。
    5. 最终调用的还是NativeMethodAccessorImpl的invoke方法。
    6. NativeMethodAccessorImpl最终判断每15次通过MethodAccessorGenerator创建一次新的MethodAccessorImpl。

    流程图

    Spring的IOC

    Spring的IOC容器

    资源定位

    ApplicationContext开始,子类包括FileSystemXmlApplicationContext,ClassPathXmlApplicationContext等子类。
    调用抽象类AbstractApplicationContext的PathMatchingResourcePatternResolver方法构建资源管理器。
    通过AbstractRefreshableConfigApplicationContext类的setConfigLocation来定位资源。

    资源载入

    通过AbstractRefreshableConfigApplicationContext类的refresh方法来载入bean类。
    通过AbstractXmlApplicationContext类的loadBeanDefinitions方法来载入bean类。
    通过AbstractBeanDefinitionReader类的loadBeanDefinitions方法类载入bean类。
    通过XmlBeanDefinitionReader加载有定义bean的xml。

    资源解析

    通过DefaultBeanDefinitionDocumentReader解析定义bean的xml。
    通过BeanDefinitionParserDelegate解析、和等元素。

    资源注册

    DefaultBeanDefinitionDocumentReade中通过processBeanDefinition方法,调用DefaultListableBeanFactory的registerBeanDefinition方法注册bean,注册的bean放在beanDefinitionMap中。

    Spring依赖注入

    通过ApplicationContext的getBean方法获取
    通过AbstractBeanFactory的getBean方法调用doGetBean方法获取
    doGetBean运行过程是判断是否为单例,如果单例则获取存在的,否则则新建一个。
    通过AbstractAutowireCapableBeanFactory的createBean来创建。
    通过的createBeanInstance来创建BeanWrapper,通过BeanWrapper来创建bean,populateBean来注入bean的属性。
    通过SimpleInstantiationStrategy子类的instantiate方法创建bean。
    如果Bean有方法被覆盖了,则使用JDK的反射机制进行实例化,否则,使用CGLIB进行实例化(CglibSubclassingInstantiationStrategy)。

    代理模式

    代理模式原理

    定义:一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。
    意图:为其他对象提供一种代理以控制对这个对象的访问。
    主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。

    Java的代理模式

    JDK的静态代理

    定义: 静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。
    优点: 可以做到在不修改目标对象的功能前提下,对目标功能扩展.
    缺点: 因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护。

    JDK的动态代理

    特点:
    1.代理对象,不需要实现接口。
    2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)。
    3.动态代理也叫做:JDK代理,接口代理。
    4.不能代理没有实现接口的类
    原理:
    利用Proxy的newProxyInstance创建一个内存的代理类。

    1. 通过方法getProxyClass创建接口对应代理类的Class
    2. 方法中getProxyClass通过反射获取接口Class,对接口权限进行判断
    3. 获取缓存中是否存在对应的代理类的Class,如果有则返回
    4. 获取接口的package,如果接口方法为public,则为生成的代理类Class为顶层包。否则采用接口的package。
    5. Proxy存在一个计数器,生成的代理类Class以Proxy+计数器为名称。同一组接口不会生成重复的代理类。代理类继承Proxy 类,所以决定它无法代理不实现接口的类。
    6. 生成代理类是通过ProxyGenerator的generateProxyClass方法。通过生成对应的class文件,在使用反射生成代理类的Class的byte数据。
    7. 生成的代理类,里面有接口方法,而此方法实际是调用InvocationHandler的invoke方法。
    8. 通过defineClass0的方法把代理类的byte数据加载到内存。
    9. 动态生成的Class代理类,使用的是继承Proxy类,实现被代理接口, 每个方法都会被定义为一个method属性放在代理类中。
    10. 最终利用反射调用InvocationHandler的实际方法。。

    Cglib代理

    定义:
    Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展。
    原理:
    利用ASM直接生成代理类并将修改代理类字节码方式嵌入被代理类方法达到代理效果。在执行速度上比JDK要快,但是在生成类的速度上比JDK慢。
    Cglib生成代理类有2种情况。第一种是继承FastClass,当被代理的是一个接口时。第二种是Enhance里面的FastClass,当被代理的是一个类时。
    最终通过索引直接调用委托类方法。

    结论:
    1.同样情况下,cglib两种实现方式,invokeSuper + setSuperClass 永远比 invoke + setInterfaces慢
    2.cglib invoke + setInterfaces 在方法数量较少的时候,在函数平均调用的情况下 比jdkProxy快,随着函数增多,优势越来越不明显,到达某个数量级一定比jdk动态代理慢
    3.cglib invoke + setInterfaces 在调用特定函数(在switch中靠后的case) 会比jdk动态代理慢

    思考:
    cglib的瓶颈在于:
    调用net.sf.cglib.reflect.FastClass#invoke(int, java.lang.Object, java.lang.Object[])时需要switch的case:
    如果有n个函数,那么就要一个个比较,复杂度O(n)
    这里如果有一个key -> behavior的映射就好了,目前并没有。
    如果可以用asm在这里写成一个二分搜索,cglib就会快多了,变成O(log2 n),时间上就是一个飞跃,只不过这个fastclass就会看起来很丑。(目前最新3.2.5的版本也没有改动这一块)

    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: