动态代理是大型框架中经常用到的经典的技术之一,博主在理解spring的控制反转(依赖注入)的思想时回头着重复习了一下java的动态代理。

在说动态代理之前我们先简单说一说代理是用来干什么的,用于什么样的业务场景然后在引入静态代理和动态代理。

代理模式一般涉及到的角色有
–抽象角色:声明真实对象和代理对象的共同接口
–代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装
–真实角色:代理角色所代表的真实对象,是我们最终要引用的对象

在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用

代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。

一、静态代理

下面来看具体代码

抽象角色:

public interface Subject {

    public void doSomething();
}

真实角色:

public class RealSubject implements Subject {
@Override
public void doSomething() { System.out.println("do something!");
}
}

代理角色:

public class PoxySubject implements Subject{
private RealSubject subject; @Override
public void doSomething() {
this.doPreThing(); if (null == subject) { subject = new RealSubject();
} subject.doSomething();
this.dopostThing();
} public void doPreThing() {
System.out.println("pre things");
} public void dopostThing() {
System.out.println("post things");
}
}

测试类(main方法)

public class Client {
/**
* 静态代理模式 作用是为其他对象提供一种代理以控制对这个对象的访问。
* 在本例中的作用是我们不仅可以完成doSomething()方法,而且还可以控制在这个方法前后我们再另外做一些事情。
* @param args
*/
public static void main(String[] args) {
Subject subject = new PoxySubject();
subject.doSomething();
}
}

运行main方法:

pre things
do something!
post things

我们发现通过这种方式,我们隐藏了真实角色而是通过代理角色完成了真实角色所做的事情。并且我们可以在代理角色中做一些手脚实现一些我们自己想实现的方法。这就是静态代理的作用。但是很快我们发现其中的局限性。

如果要按照上述的方法使用代理模式,那么真实角色必须是事先已经存在的,并将其作为代理对象的内部属性。但是实际使用时,一个真实角色必须对应一个代理角色,如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道真实角色,该如何使用代理呢?这就是静态代理的局限性,这个问题可以通过Java的动态代理类来解决。

二、动态代理

Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:

(1)Interface InvocationHandler:该接口中仅定义了一个方法
-public object invoke(Object obj,Method method, Object[] args)
在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,args为该方法的参数数组。这个抽象方法在代理类中动态实现。

(2)Proxy:该类即为动态代理类,作用类似于上例中的ProxySubject,其中主要包含以下内容

-protected Proxy(InvocationHandler h):构造函数,用于给内部的h赋值。
-static Class getProxyClass (ClassLoader loader, Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。
-static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)

接下来我们用动态代理实现之前的例子

抽象角色:

public interface Subject {
public void doSomething();
}

真实角色:

public class RealSubject implements Subject {

    public void doSomething() {
System.out.println("from real subject");
}
}

实现代理角色的类:

public class DynamicSubject implements InvocationHandler {

    private Object sub;

    public DynamicSubject(Object obj) {
this.sub = obj;
} public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("调用之前" + method); method.invoke(sub, args); System.out.println("调用之后" + method); return null;
}
}

测试类(main方法):

public class Client {

    public static void main(String[] args) {
RealSubject sub = new RealSubject();
InvocationHandler hanlder = new DynamicSubject(sub); //返回代理类的一个实例
Subject subject =(Subject)Proxy.newProxyInstance(hanlder.getClass().getClassLoader(), sub.getClass().getInterfaces(), hanlder);
subject.doSomething();
}
}

运行结果:

调用之前public abstract void com.wenge.dynamicporxy.Subject.doSomething()
from real subject
调用之后public abstract void com.wenge.dynamicporxy.Subject.doSomething()

我们还是实现了静态代理所实现的功能,而且我们的程序有了更好的可扩展性。我们可以通过这种方式代理不止一个类,而是多个只要这个类实现了上层接口,更为关键的是这一切是动态生成的,显然这是利用了java的反射技术。这样的话动态代理就可以做很多事情了,最经典的就是spring框架中依赖注入,有兴趣的童鞋可以深入去看看源代码。

所谓Dynamic Proxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。

实现动态代理的步骤:

1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法
2.创建被代理的类以及接口
3.通过Proxy的静态方法
newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建一个代理
4.通过代理调用方法

通过这种方式,被代理的对象(RealSubject)可以在运行时动态改变,需要控制的接口(Subject接口)可以在运行时改变,控制的方式(DynamicSubject类)也可以动态改变,从而实现了非常灵活的动态代理关系。

欢迎指正讨论

javase基础回顾(三) 动态代理的更多相关文章

  1. mybatis 基础1(动态代理)

    我目前使用的是mybatis 3.3.0版本. 可使用 1.xml文本, 2.dao类, 3.sqlSession.getMapper(Class<T> type), 生成sql类, 原理 ...

  2. 重学JAVA基础(三):动态代理

    1.接口 public interface Hello { public void sayHello(); } 2.实例类 public class Hello2 { public void sayH ...

  3. 学习Spring必学的Java基础知识(2)----动态代理

    Spring AOP使用动态代理技术在运行期织入增强的代码,为了揭示Spring AOP底层的工作机理,有必要对涉及到的Java知识进行学习.Spring AOP使用了两种代理机制:一种是基于JDK的 ...

  4. Spring AOP系列(三) — 动态代理之JDK动态代理

    JDK动态代理 JDK动态代理核心是两个类:InvocationHandler和Proxy 举个栗子 为便于理解,首先看一个例子: 希望实现这样一个功能:使用UserService时,只需关注自己的核 ...

  5. 《Java基础知识》动态代理(InvocationHandler)详解

    1. 什么是动态代理 对象的执行方法,交给代理来负责.比如user.get() 方法,是User对象亲自去执行.而使用代理则是由proxy去执行get方法. 举例:投资商找明星拍广告,投资商是通过经纪 ...

  6. 【Java 基础】Java动态代理

    Java动态代理InvocationHandler和Proxy java动态代理机制中有两个重要的类和接口InvocationHandler(接口)和Proxy(类),这一个类Proxy和接口Invo ...

  7. 【Java基础】 Java动态代理机制

    在Java的动态代理机制中,有两个重要的类.一个是InvocationHandler,另一个是Proxy. InvocationHandler:每一个动态代理类都必须要实现InvocationHand ...

  8. java 基础之--java动态代理

    1.抽象角色:声明真实对象与代理对象的共同接口: 2.代理角色:相当于中介的作用,bridge,内部包含对真实角色的reference,在执行真实操作对象时,附加其他操作,相当于对真实角色的封装: 3 ...

  9. JavaSE基础(三)--Java基础语法

    Java 基础语法 一个 Java 程序可以认为是一系列对象的集合,而这些对象通过调用彼此的方法来协同工作.下面简要介绍下类.对象.方法和实例变量的概念. 对象:对象是类的一个实例,有状态和行为.例如 ...

随机推荐

  1. opencv2.4.13+python2.7学习笔记--OpenCV中的图像处理--图像轮廓

    阅读对象:无要求. 1.代码 ''' OpenCV中的轮廓 轮廓可以简单认为成将连续的点(连着边界)连在一起的曲线,具有相同的颜色或者灰度.为了更加准确,要使用二值化图像.在寻找轮廓之前,要进行阈值化 ...

  2. jQuery的基本选择器

    <script type="text/javascript"> //演示jQuery的基本选择器 $(function () { //通过ID var obj1 = $ ...

  3. 利用Python+163邮箱授权码发送邮件

    背景 前段时间写了个自动打卡的脚本,但是脚本不够完善,我需要知道,打卡到底成没成功,因此,我想到了用Python执行完代码之后,再执行一段发送邮件的代码.需求开始明确了,就开始分析和写代码实现吧. 分 ...

  4. c++中的const与指针

    const修饰符 使用const修饰变量时,该变量的值不可修改,因此需要初始化. 例如 const int s = 0: 此时s为值不可变的变量. 那么基于此,当const修饰指针时的情况有三种: ( ...

  5. Koa 学习笔记

    开始 就像官网上说的,一切框架都从一个"Hello World"开始,首先我们新建一个 package.json,内容尽量简单: { "name": " ...

  6. 透彻讲解,Java线程的6种状态及切换

    Java中线程的状态分为6种. 1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法.2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running) ...

  7. Open系列相关概念汇总

    最近接触了Android OpenGL ES 和 OpenCL ES,然后就很想知道除了这两个之外到底还有几个Open系列的API集.搜集的结果如下(纯为自己科普): 1. OpenGL(OpenGr ...

  8. 7.侧滑、ViewDragHelper、属性动画

    实现这样的效果: ## 侧滑面板(对ViewGroup的自定义)* 应用场景: 扩展主面板的功能* 功能实现: > 1. ViewDragHelper: Google2013年IO大会提出的, ...

  9. 吴恩达机器学习笔记16-决策边界(decision boundary)

    现在讲下决策边界(decision boundary)的概念.这个概念能更好地帮助我们理解逻辑回归的假设函数在计算什么. 在逻辑回归中,我们预测:当ℎ

  10. 读《Linux Shell脚本攻略》(第2版) 一遍、二遍体会

    前段时间读完了<Linux Shell脚本攻略>(第2版)这本书,给部分想读这本书的人分享下个人感受. 第一遍体会解读:就像黑夜中的灯塔,指明前进的道路. 推荐指数:强烈推荐. 书中讲解的 ...