在某些情况下,一个客户不想或不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到了中介作用,这不仅仅使用代理模式,还可以实现适配器模式、装饰模式等。

代理对象内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。

传统式上Java代理对象分为静态代理和动态代理,其实本质上是一样只是两种不同的编码方式,后者更有通用性。

静态代理

先来看看静态代理,可以认为代理只是对真实类的一个包装,就想去办证大厅办证一样,你代办除了在大厅办事流程一样外,还需要做些额外的处理,比如还需要自己的一些复印件之类的。我们来代码示例一下:

package net.oseye;

public class ProxyDemoApp {

	public static void main(String[] args) {
//代理对象的使用
new HelloProxy(new Hello()).sayHello("oseye");;
}
} /**
* 真实类
* ProxyDemoApp.java:TempTest
* Jul 9, 2014
* @author kevin.zhai
*/
class Hello{
public void sayHello(String userName) {
System.out.println("Hello,"+userName);
}
}
/**
* 代理类
* ProxyDemoApp.java:TempTest
* Jul 9, 2014
* @author kevin.zhai
*/
class HelloProxy{
private Hello hello;
public HelloProxy(Hello hello){
this.hello=hello;
} public void sayHello(String userName) {
System.out.println("代理模式开始....");
this.hello.sayHello(userName);
System.out.println("代理模式结束....");
}
}

但通常我们会先定义一个接口,真实类和代理类都实现这个接口:

package net.oseye;

public class ProxyDemoApp {

	public static void main(String[] args) {
//代理对象的使用
new HelloProxy(new Hello()).sayHello("oseye");;
}
}
/**
* 接口
* ProxyDemoApp.java:TempTest
* Jul 9, 2014
* @author kevin.zhai
*/
interface IHello{
public void sayHello(String username);
} /**
* 真实类
* ProxyDemoApp.java:TempTest
* Jul 9, 2014
* @author kevin.zhai
*/
class Hello implements IHello{
public void sayHello(String userName) {
System.out.println("Hello,"+userName);
}
}
/**
* 代理类
* ProxyDemoApp.java:TempTest
* Jul 9, 2014
* @author kevin.zhai
*/
class HelloProxy implements IHello{ private Hello hello;
public HelloProxy(Hello hello){
this.hello=hello;
} public void sayHello(String userName) {
System.out.println("代理模式开始....");
this.hello.sayHello(userName);
System.out.println("代理模式结束....");
}
}

由以上可以看得出这种模式非常适合代理、适配、装饰等模式。然而每一个代理类只能为一个接口服务,如果非常多的代理不仅会让类快速膨胀而且造成很多重复代码,因此Java提供了动态代理接口来解决这个问题。

动态代理

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

  1. Interface InvocationHandler:该接口中仅定义了一个方法

    Object:invoke(Object obj,Method method, Object[] args)

    在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组。这个抽象方法在代理类中动态实现。

  2. Proxy:该类即为动态代理类,作用类似于上例中的ProxySubject,其中主要包含以下内容:
    Protected Proxy(InvocationHandler h):使用其调用处理程序的指定值从子类(通常为动态代理类)构建新的 Proxy 实例;
    Static Class getProxyClass (ClassLoader loader, Class[] interfaces):返回代理类的 java.lang.Class 对象,并向其提供类加载器和接口数组。该代理类将由指定的类加载器定义,并将实现提供的所有接口。如果类加载器已经定义了具有相同排列接口的代理类,那么现有的代理类将被返回;否则,类加载器将动态生成并定义这些接口的代理类;
    Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。此方法相当于:

    Proxy.getProxyClass(loader, interfaces).
    getConstructor(new Class[] { InvocationHandler.class }).
    newInstance(new Object[] { handler });

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

package net.oseye;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; public class ProxyDemoApp { public static void main(String[] args) {
//代理对象的使用
IHello hello=(IHello)new ProxyDemo().bind(new Hello());
hello.sayHello("oseye");
}
}
/**
* 接口
* ProxyDemoApp.java:TempTest
* Jul 9, 2014
* @author kevin.zhai
*/
interface IHello{
public void sayHello(String username);
} /**
* 真实类
* ProxyDemoApp.java:TempTest
* Jul 9, 2014
* @author kevin.zhai
*/
class Hello implements IHello{
public void sayHello(String userName) {
System.out.println("Hello,"+userName);
}
} /**
* 动态代理类
* ProxyDemoApp.java:TempTest
* Jul 9, 2014
* @author kevin.zhai
*/
class ProxyDemo implements InvocationHandler{
private Object target; public Object bind(Object object){
this.target=object;
return Proxy.newProxyInstance(this.target.getClass().getClassLoader(), this.target.getClass().getInterfaces(), this);
} public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result=null;
System.out.println("代理模式开始....");
result=method.invoke(this.target, args);
System.out.println("代理模式结束....");
return result;
}
}

但JDK的代理API仍有一个缺陷:必须针对接口,如果没有接口就没办法了!这是可以考虑使用一个开源的框架cglib。

Java中代理对象的使用小结的更多相关文章

  1. 浅谈Java中的对象和引用

    浅谈Java中的对象和对象引用 在Java中,有一组名词经常一起出现,它们就是“对象和对象引用”,很多朋友在初学Java的时候可能经常会混淆这2个概念,觉得它们是一回事,事实上则不然.今天我们就来一起 ...

  2. 【译】Java中的对象序列化

    前言 好久没翻译simple java了,睡前来一篇. 译文链接: http://www.programcreek.com/2014/01/java-serialization/ 什么是对象序列化 在 ...

  3. java中直接打印对象

    java中直接打印对象,会调用对象.toString()方法.如果没有重写toString()方法会输出"类名+@+hasCode"值,hasCode是一个十六进制数 //没有重写 ...

  4. 如何使用java中的对象

    使用java中的对象,分2步: 1.创建一个对象: 2.使用对象的属性和方法. 见下面的示例代码: package com.imooc; //1.定义一个类 public class Telphone ...

  5. Java中String对象的不可变性

    首先看一个程序 package reverse; public class Reverse { public static void main(String[] args) { String c1=n ...

  6. JAVA中JavaBean对象之间拷贝的方法

    JAVA中JavaBean对象之间的拷贝通常是用get/set方法,但如果你有两个属性相同的JavaBean或有大部分属性相同的JavaBean,有个更简便的方法,他们之间的拷贝可以通过copyPro ...

  7. Java中的对象池技术

    java中的对象池技术,是为了方便快捷地创建某些对象而出现的,当需要一个对象时,就可以从池中取一个出来(如果池中没有则创建一个),则在需要重复重复创建相等变量时节省了很多时间.对象池其实也就是一个内存 ...

  8. Java中计算对象的大小

    一.计算对象大小的方法 Java中如何计算对象的大小呢,找到了4种方法: 1.java.lang.instrument.Instrumentation的getObjectSize方法: 2.BTrac ...

  9. 【学习笔记】Java中生成对象的5中方法

    概述:本文介绍以下java五种创建对象的方式: 1.用new语句创建对象,这是最常用的创建对象的方式. 2.使用Class类的newInstance方法 3.运用反射手段,调用java.lang.re ...

随机推荐

  1. 2.1. 托管对象模型是什么(Core Data 应用程序实践指南)

    托管对象模型是一种数据结构.在这里,数据结构.纲要.对象图.数据模型.托管对象模型这些术语是一个意思.它们是对同一个东西不同场景的描述.比如,对Core Data 而言是托管对象模型,对设计器来说是对 ...

  2. PHP cookie禁用时session 方案

    在PHP中使用过SESSION的朋友可能会碰到这么一个问题,SESSION变量不能跨页传递.这令我苦恼了好些日子,最终通过查资料思考并解决了这个问题.我认为,出现这个问题的原因有以下几点: 1.客户端 ...

  3. MySQL主从同步校验与重新同步

    主从复制环境中,可能有种种原因导致主.从库数据不一致的情况,主从一致性也一直是DBA需要关注的问题,校验MySQL的主从一致性一般有多种工具,诸如MySQL自带的checksum.mysqldiff. ...

  4. XHTML 是以 XML 格式编写的 HTML

    什么是 XHTML? XHTML 指的是可扩展超文本标记语言 XHTML 与 HTML 4.01 几乎是相同的 XHTML 是更严格更纯净的 HTML 版本 XHTML 是以 XML 应用的方式定义的 ...

  5. Bootstrap入门(九)组件3:按钮组

    Bootstrap入门(九)组件3:按钮组   先引入本地的CSS文件和JS文件(注:1.bootstrap是需要jQuery支持的.2.需要在<body>当中添加) <link h ...

  6. EFCore扩展:IQueryable(linq)或sql执行的查询缓存与清理

    前言 上一篇讲述了执行sql和配置的一些功能,这篇说明IQueryable(linq)或执行sql的查询缓存与清理,包括扩展到将缓存存储到Redis中. 扩展类库源码: github:https:// ...

  7. UWP必备知识:App File Explorer

    由来 应用在手机端出问题时如果查看LocalState文件夹的数据库文件与日志文件 如何查看应用在手机端占用带宽与占用CPU内存情况 介绍 [UWP开发之Mvvmlight实践七:如何查找设备(Mob ...

  8. 在GEM5模拟器运行时,对Kill命令的使用

    在Linux下开发执行GEM5程序时,需要先启动GEM5,然后使用telnet对GEM5进行连接,才能看到串口信息.因为操作步骤多,所以写了脚本用来运行GEM5和Telnet程序,并且对两个程序进行监 ...

  9. SQL Server-聚焦SNAPSHOT基于行版本隔离级别详解(三十)

    前言 上一篇SQL Server详细讲解了隔离级别,但是对基于行版本中的SNAPSHOT隔离级别仍未完全理解,本节再详细讲解下,若有疑义或不同见解请在评论中提出,一起探讨. SNAPSHOT行版本隔离 ...

  10. LCD驱动移植在在mini2440(linux2.6.29)和FS4412(linux3.14.78)上实现对比(deep dive)

    1.Linux帧缓冲子系统 帧缓冲(FrameBuffer)是Linux为显示设备提供的一个接口,用户可以将帧缓冲看成是显示内存的一种映像,将其映射到进程地址空间之后,就可以直接进行读写操作,而写操作 ...