反射与动态代理

java中反射与动态代理是java程序员不可忽略的一门功课,关于其概念、基本使用方式,网上一大堆,本文主要讲解本人对于这两者的认识,以及关于开源项目中典型用法。

反射

Java反射机制:在程序运行时获取已知名称的类或已有对象的相关信息的一种机制,包括类的方法、属性、父类等,还包括实例的创建和实例类型的判断等。

是不是很抽象?来看个例子,在Spring中经常有如下的配置:

<bean id="person" class="com.linuer.test.Person"></bean>

(在此不讨论具体Spring容器启动细节,只是讨论与反射相关的知识点。)

可以想象到的是Spring框架会读取上面的配置,获取类的名称。然后根据已知类的名称创建实例类型。是否很熟悉描述,在看看反射机制的描述。

看不懂?没事,下面一个实际的例子

  /**
  * 此字符串的获取你可以想象是从配置文件中,读取XMl,获取相关字符串。
  **/
String className = "com.linuer.test.Person";  
Class cls = Class.forName(className);
Object object = cls.newInstance();  //创建实例

上面的操作,用大白话说就是:根据一字符串 来得到类信息或者类方法,或者创建类实例。

至于这个 字符串 你是从哪里得来,配置文件?网络?都行,只要你想象到的方式。

除了上面操作,在实际使用过程中还有许多使用的方式。

在实际学习中关于反射相关的类主要关注:

Class类、类得属性:Field、类得方法: Method 。

动态代理

说动态代理之前,先说代理这个行为。
代理简单来说就是通过中间人来做。
来个图来说明吧


代理.png

图中可以看出,有个代理之后依旧是送花这个动作,可是送花的动作的含义与原先不相同了哦。
大家都知道,代理分为:静态代理与动态代理,具体有什么区别呢?以下结合代码来说:

public interface Calculator {
   public void add(Integer x, Integer y);
    public void div(Integer x, Integer y);
}
--------------------------------------------------------------------------------------------------
public class CalculatorImpl implements Calculator{
 @Override
    public void add(Integer x, Integer y)
    {
        System.out.println(x+y);
    }

    @Override
    public void div(Integer x, Integer y)
    {
        System.out.println(x-y);
    }
}
--------------------------------------------------------------------------------------------------
//正常的使用方式(对应与图中的上面两个小人的情况)
  Calculator calculator = new CalculatorImpl();
  calculator.add(7,6)
输出:
  13

上面是计算数值,现在我想在计算数值的前后,分别要输出一些信息,那该怎么做呢?直接在CalculatorImpl 类中的方法添加,这么做肯定不够优雅,作为立志成为资深程序员的我们,是绝对不能这么干的。
我们要用代理模式来做。来看看静态代理怎么做的吧

public class CalcProxy implements Calculator {
  private Calculator calc;

    public CalcProxy(){
        calc = new CalculatorImpl();
    }

    @Override
    public void add(Integer x, Integer y) {
        before();
        calc.add(x,y);
        after();
    }


    @Override
    public void div(Integer x, Integer y) {
        before();
        calc.div(x,y);
        after();
    }

    private void before() {
        System.out.println("before");
    }

    private void after() {
        System.out.println("after");
    }
}
---------------------------------------------------------------------
   Calculator calc = new CalcProxy();
   calc.add(7,6);
输出:
before
13
after
8

既然静态代理能够完成工作,为什么还需要改进,变成动态代理呢?原因在于:一个静态代理只能代理一个接口,这样就导致项目中到处都是XxxProxy。看动态代理是如何解决这个方案的。

public class DynamicProxy implements InvocationHandler{
    private Object target;

    public DynamicProxy(Object target){
        this.target = target;
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(target,args);
        after();
        return result;
    }
.......
}
代码中target变量就是我们被代理的目标对象,如果用来做Calculator 代理的话,就是Calculator 对象。
调用:
  Calculator calculator = new CalculatorImpl();
  DynamicProxy dynamicProxy = new DynamicProxy(calculator);
  Calculator calc = (Calculator) Proxy.newProxyInstance(
                calculator.getClass().getClassLoader(),
                calculator.getClass().getInterfaces(),
                dynamicProxy
        );
        calc.add(7,6);
输出:
before
13
after

上面的意思在于用dynamicProxy 去包装CalculatorImpl实例,然后调用JDK给我们提供的Proxy类的newProxyInstance去动态创建一个Calculator 接口的代理类。
对于newProxyInstance方法的参数真是让人“蓝廋”。

  • 参数1:ClassLoader
  • 参数2:该实现类的所有接口
  • 参数3:动态代理对象
    其实这块可以进一步封装下,避免程序中到处调用newProxyInstance
public class DynamicProxy implements InvocationHandler{
   .....
   public <T> T getProxy(){
        return (T) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this
        );
    }
  ......
}
----------------------
调用:
  Calculator calculator = new CalculatorImpl();
  DynamicProxy dynamicProxy = new DynamicProxy(calculator);
  Calculator calc = dynamicProxy.getProxy();
  calc.add(7,6);
这样是不是简单很多了。

不知道你们注意到了没有,在DynamicProxy 中不管其代理的对象,如CalculatorImpl实例中接口增加或者减少,DynamicProxy 都不会改动,这也算是动态代理这个优点。但是如果被代理对象,是没有任何接口的类,那么它是不是就没有用武之地了呢?是的。那有没什么办法解决呢?依旧使用动态代理,但是其被代理的对象不必要是一个接口类。答案是:有的。CGLIB的动态代理。

public class CGLibProxy implements MethodInterceptor {

    public <T> T getProxy(Class<T> cls){
        return (T) Enhancer.create(cls,this);
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        before();
        Object result = methodProxy.invokeSuper(obj,args);
        after();
        return result;
    }
......
}
我们需要实现CGLIB给我们提供的MethodInterceptor 实现类,并编写intercept方法,
此方法的最后一个参数MethodProxy值得注意,这个是一个方法的代理。什么意思?
看下面的调用:
CGlibProxy cgLibProxy = new CGlibProxy();
Calculator calc = cgLibProxy.getProxy(CalculatorImpl.class);
calc.add(7,6);
注意到没,在传入参数的时候,并没有传入接口信息。

关于反射与动态代理讲解远远不止这些,还有许多实际的应用,接下来的两篇文章中会针对动态代理的应用进行论述。
由于本人水平有限,有什么问题可以评论,喜欢的可以关注。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351

推荐阅读更多精彩内容