Java的代理模式是应用非常广泛的设计模式之一,也叫作委托模式,其目的就是为其他的对象提供一个代理以控制对某个对象的访问和使用,代理类负责为委托类预处理消息,过滤消息并转发消息,以及对消息执行后续处理。

代理就是充当一个中间人的角色。

按照代理的创建时期,代理类可以分为两种:

静态代理:指由程序员直接创建生成源代码,在对其编译生成.class文件,在程序运行之前就已经存在

动态代理:在程序运行时,使用java的反射机制动态创建而成。其中动态代理又分为JDK代理(需要接口)和cglib代理(不需要接口)

下面将以程序案例的方式演示Java静态代理和动态代理的区别

假设现在需要实现计算机的加减乘除操作,现有如下接口和实现类;

 package Reflection.proxy;

 /**
* Created by : Infaraway
* DATE : 2017/3/3
* Time : 15:12
* Funtion : 计算器的功能
*/
public interface Calculator { int add(int a, int b);
int sub(int a, int b); void mul(int a, int b);
void div(int a, int b);
}
 package Reflection.proxy;

 /**
* Created by : Infaraway
* DATE : 2017/3/3
* Time : 15:14
* Funtion :
*/
public class CalculatorImpl implements Calculator{
@Override
public int add(int a, int b) {
//System.out.println("The method add begin...);
int result = a + b;
//System.out.println("The method add end...);
System.out.println(result);
return result;
} @Override
public int sub(int a, int b) {
int result = a - b;
System.out.println(result);
return result;
} @Override
public void mul(int a, int b) {
int result = a * b;
System.out.println(result);
} @Override
public void div(int a, int b) {
int result = a / b;
System.out.println(result);
}
}

如上部分add方法所示,现在希望在每个方法的实现之前打印方法开始和方法结束的日志信息,那么最容易的方法就是在源代码中的每个方法全部加上,但是这样非常的繁琐(需要编写大量的相同的代码),并且代码的维护性非常的差!

为了解决这个问题,我们需要使用代理的方法来解决。

静态代理:

首先我们使用静态代理的解决方法:

package Reflection.proxy;

/**
* Created by : Infaraway
* DATE : 2017/3/3
* Time : 19:51
* Funtion : java静态代理类实现
*/
public class StaticProxy implements Calculator { private CalculatorImpl calculatorImpl; public StaticProxy(CalculatorImpl calculatorImpl){
this.calculatorImpl = calculatorImpl;
} @Override
public int add(int a, int b) {
System.out.println("the add method begin...");
//调用被代理类的方法
int result = calculatorImpl.add(a, b);
System.out.println("the add method end...");
return result;
} @Override
public int sub(int a, int b) {
System.out.println("the sub method begin...");
//调用被代理类的方法
int result = calculatorImpl.sub(a, b);
System.out.println("the sub method end...");
return result;
} @Override
public void mul(int a, int b) {
System.out.println("the mul method begin...");
//调用被代理类的方法
calculatorImpl.mul(a, b);
System.out.println("the mul method end..."); } @Override
public void div(int a, int b) {
System.out.println("the div method begin...");
//调用被代理类的方法
calculatorImpl.div(a, b);
System.out.println("the div method end...");
}
}

显然,静态代理方法并不能改变原来繁琐的步骤,并且每个代理类只能为一个借口服务,这样的话,程序中必然会存在非常多的代理,并且这样的代理仍然会产生修改代码困难的问题;

因此,解决这个问题的一个非常好的办法出现了,那就是动态代理~

动态代理:

动态代理又分为两种:JDK代理 和 cglib代理

JDK代理:主要针对的是有接口的情况;

其中JDK动态代理包含了一个接口和一个类:

Proxy类: 
Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法: 
public static Object newProxyInstance(

      ClassLoader loader,

      Class<?>[] interfaces,

      InvocationHandler h)  throws IllegalArgumentException

/**
* ClassLoader :由动态代理产生的对象由哪个类加载器来加载 通常情况下和被代理对象使用同样的类加载器;
* Class<?>[] : 由动态代理产生的对象必须要实现的接口的Class数组;
* InvocationHandler : 当具体调用代理对象方法时,将产生的具体行为; 通常使用匿名内部类的方式实现。
*/

InvocationHandler接口: 
public interface InvocationHandler { 
  public Object invoke(Object proxy,Method method,Object[] args) throws Throwable; 

参数说明:

/**
* @param proxy 被代理的对象
* @param method: 正在被调用的方法
* @param args :调用方法时传入的参数
* @return 被调用方法的返回值
* @throws Throwable
*/ 因此,当时用JDK代理方式实现上述的需求时,则如下代码所示:
package Reflection.proxy;

import org.junit.Test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; /**
* Created by : Infaraway
* DATE : 2017/3/3
* Time : 20:07
* Funtion :
*/
public class DynamicJDKProxy { @Test
public void dynamicJDKProxy(){
/**
* ClassLoader :由动态代理产生的对象由那个类加载器来加载 通常情况下和被代理对象使用同样的类加载器
* Class<?>[] : 由动态代理产生的对象必须要实现的接口的Class数组
* InvocationHandler : 当具体调用代理对象方法时,将产生什么行为。 通常使用匿名内部类的方式实现
*/
Calculator calculator = new CalculatorImpl(); Calculator calculatorProxy = (Calculator) Proxy.newProxyInstance(
calculator.getClass().getClassLoader(),
new Class[]{Calculator.class},
new InvocationHandler() { /**
* @param proxy 代理
* @param method: 正在被调用的方法
* @param args :调用方法时传入的参数
* @return 被调用方法的返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//System.out.println("invoke..."); System.out.println("The method "+method.getName()+" begin...");
Object obj = method.invoke(calculator, args);
System.out.println("The method "+method.getName()+" end...");
return obj;
}
});

     //测试打印输出结果
calculatorProxy.mul(2, 3);
int result = calculatorProxy.add(1, 5);
System.out.println(result);
}
}

运行结果:

The method mul begins...
6
The method mul ends...
The method add begins...
6
The method add ends...
6

虽然上述方法很好的解决了问题,但是JDK动态代理必须依靠接口实现,如果某些类并没有实现接口,那么就不能使用JDK代理方式;

所以这里又给出一种新的代理方法:cglib动态代理来解决接口的问题。

cglib代理:针对没有接口的情况;主要是针对类来实现,主要思想是对指定的目标类来生成一个子类,然后覆盖子类的方法实现增强。

需要实现MethodInterceptor接口,实现intercept方法。该代理中在add方法前后加入了自定义的切面逻辑,目标类add方法执行语句为proxy.invokeSuper(object, args);

package Reflection.proxy;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method; /**
* Created by : Infaraway
* DATE : 2017/3/3
* Time : 20:46
* Funtion : 使用cglib动态代理
*/
public class DynamicCglibProxy implements MethodInterceptor { //被代理对象
private Object target; /**
* 创建代理对象
* @param target 被代理对象
* @return 创建的代理对象
*/
public Object createProxy(Object target){
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
// 回调方法
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
} /**
* @param obj 被代理对象的
* @param method 正在被调用的方法
* @param objects 调用方法的参数
* @param methodProxy 代理对象
* @return 返回方法的结果
* @throws Throwable
*/
@Override
public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("The method "+method.getName()+" begins...");
Object object = methodProxy.invokeSuper(obj, objects);
System.out.println("The method "+method.getName()+" ends...");
return object;
}
}
package Reflection.proxy;

import org.junit.Test;

/**
* Created by : Infaraway
* DATE : 2017/3/3
* Time : 21:15
* Funtion :
*/
public class TestDynamicCglibProxy {
@Test
public void testProxy(){
DynamicCglibProxy CglibProxy = new DynamicCglibProxy();
CalculatorImpl testCalculator = (CalculatorImpl) CglibProxy.createProxy(new CalculatorImpl());
testCalculator.add(3,5);
}
}

想要更进一步了解Java代理模式,则需要认真学习Java的反射机制

本文的所有代码可获取:https://git.oschina.net/infaraway/basisJava/tree/master/src/proxy

Java代理(静态/动态 JDK,cglib)的更多相关文章

  1. 浅谈Java代理一:JDK动态代理-Proxy.newProxyInstance

    浅谈Java代理一:JDK动态代理-Proxy.newProxyInstance java.lang.reflect.Proxy:该类用于动态生成代理类,只需传入目标接口.目标接口的类加载器以及Inv ...

  2. Java代理和动态代理机制分析和应用

    本博文中项目代码已开源下载地址:GitHub Java代理和动态代理机制分析和应用 概述 代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问.代理类负责为委托类预处理消息 ...

  3. java代理的深入浅出(二)-CGLIB

    java代理的深入浅出(二)-CGLIB 1.基本原理 CGLIB的原理就是生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法.在子类中拦截所有父类方法的调用,拦截下来交给设置的Me ...

  4. java代理与动态代理的学习

    静态代理比较简单,就是代理对象与被代理对象继承相同的接口,代理类负责调用被代理类(委托类)的对象的相关方法去提供具体的服务,一个代理类只能为一个接口服务,要是有很多服务的话需要开很多代理类.而动态代理 ...

  5. Java代理和动态代理

    code from <Thinking in java> 代理模式 interface Interface { void doSomething(); void somethingElse ...

  6. 【Java入门提高篇】Day10 Java代理——静态代理

    今天要介绍的是一个Java中一个很重要的概念--代理. 什么是代理?联系生活想想看,代理似乎并不陌生,最形象的代表便是经纪人,明星一般都有经纪人,经纪人作为中间人,负责代理明星的相关事宜,比如说,有人 ...

  7. Spring专题1: 静态代理和动态代理

    合集目录 Spring专题1: 静态代理和动态代理 为什么需要代理模式? 代理对象处于访问者和被访问者之间,可以隔离这两者之间的直接交互,访问者与代理对象打交道就好像在跟被访者者打交道一样,因为代理者 ...

  8. Java 的静态代理 动态代理(JDK和cglib)

    转载:http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html JAVA的动态代理 代理模式 代理模式是常用的java设计模式,他的特征是 ...

  9. Java代理(jdk静态代理、动态代理和cglib动态代理)

    一.代理是Java常用的设计模式,代理类通过调用被代理类的相关方法,并对相关方法进行增强.加入一些非业务性代码,比如事务.日志.报警发邮件等操作. 二.jdk静态代理 1.业务接口 /** * 业务接 ...

随机推荐

  1. add jars和add external jars有什么区别

    原文add jars和add external jars有什么区别?   add jars和add external jars有什么区别?   add external jars  = 增加工程外部的 ...

  2. 前端MVVM框架:Knockout.JS(一)

    前言 在我们平时开发 Web 应用程序的时候,如果项目不算特别大的话,一般都是拿 jQuery 再配合一些前端 UI 框架就在项目上面应用了.如果页面逻辑稍微复杂的话,那个在写前端 JavaScrip ...

  3. Java Web快速入门——全十讲

    Java Web快速入门——全十讲 这是一次培训的讲义,就是我在给学生讲的过程中记录下来的,非常完整,原来发表在Blog上,我感觉这里的学生可能更需要. 内容比较长,你可以先收藏起来,慢慢看. 第一讲 ...

  4. ZeroMQ 的模式

    在需要并行化处理数据的时候,采用消息队列通讯的方式来协作,比采用共享状态的方式要好的多.Erlang ,Go 都使用这一手段来让并行任务之间协同工作. 最近读完了 ZeroMQ 的 Guide.写的很 ...

  5. 3D游戏开发之在UE4中创建非玩家角色(NPC)

    接着上节我们继续学习,现在我们来创建一些NPC(non-playable characters,非玩家角色).在这个游戏中,当我们靠近NPC时,它们会做出相应的反应. 一 创建C++类 1) 在UE编 ...

  6. 通过Spring Data Neo4J操作您的图形数据库

    在前面的一篇文章<图形数据库Neo4J简介>中,我们已经对其内部所使用的各种机制进行了简单地介绍.而在我们尝试对Neo4J进行大版本升级时,我发现网络上并没有任何成型的样例代码以及简介,而 ...

  7. MATLAB绘制等高线和梯度场

    clear;clc;close all [X,Y] = meshgrid(-:.:); % 产生网格数据X和Y Z = X.*exp(-X.^ - Y.^); % 计算网格点处曲面上的Z值 [DX,D ...

  8. [html] 学习笔记-Canvas使用路径

    想要绘制其他图形,需要使用路径,使用路径包含4个步骤,开始创建路径.创建图形的路径.路径创建完成后关闭路径.设定绘制样式,之后就可以调用绘制方法绘制路径了. 1.绘制圆形 <!DOCTYPE h ...

  9. WebForm 控件(一)、连接数据库

    一.控件 [简单控件] (一)文字显示 1.Label → 在html中相当于span  <asp:Label ID="控件名 runat="server" Tex ...

  10. js中的3种弹出式消息提醒(警告窗口,确认窗口,信息输入窗口)的命令式

    alert("A"); confirm("B");var name = confirm("B");if(name){ alert(" ...