Java中代理对象的使用小结
在某些情况下,一个客户不想或不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到了中介作用,这不仅仅使用代理模式,还可以实现适配器模式、装饰模式等。
代理对象内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
传统式上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包下,一般主要涉及到以下两个类:
- Interface InvocationHandler:该接口中仅定义了一个方法
Object:invoke(Object obj,Method method, Object[] args)
在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组。这个抽象方法在代理类中动态实现。
- 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,由它接管实际的工作。
- Proxy.getProxyClass(loader, interfaces).
- 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中代理对象的使用小结的更多相关文章
- 浅谈Java中的对象和引用
浅谈Java中的对象和对象引用 在Java中,有一组名词经常一起出现,它们就是“对象和对象引用”,很多朋友在初学Java的时候可能经常会混淆这2个概念,觉得它们是一回事,事实上则不然.今天我们就来一起 ...
- 【译】Java中的对象序列化
前言 好久没翻译simple java了,睡前来一篇. 译文链接: http://www.programcreek.com/2014/01/java-serialization/ 什么是对象序列化 在 ...
- java中直接打印对象
java中直接打印对象,会调用对象.toString()方法.如果没有重写toString()方法会输出"类名+@+hasCode"值,hasCode是一个十六进制数 //没有重写 ...
- 如何使用java中的对象
使用java中的对象,分2步: 1.创建一个对象: 2.使用对象的属性和方法. 见下面的示例代码: package com.imooc; //1.定义一个类 public class Telphone ...
- Java中String对象的不可变性
首先看一个程序 package reverse; public class Reverse { public static void main(String[] args) { String c1=n ...
- JAVA中JavaBean对象之间拷贝的方法
JAVA中JavaBean对象之间的拷贝通常是用get/set方法,但如果你有两个属性相同的JavaBean或有大部分属性相同的JavaBean,有个更简便的方法,他们之间的拷贝可以通过copyPro ...
- Java中的对象池技术
java中的对象池技术,是为了方便快捷地创建某些对象而出现的,当需要一个对象时,就可以从池中取一个出来(如果池中没有则创建一个),则在需要重复重复创建相等变量时节省了很多时间.对象池其实也就是一个内存 ...
- Java中计算对象的大小
一.计算对象大小的方法 Java中如何计算对象的大小呢,找到了4种方法: 1.java.lang.instrument.Instrumentation的getObjectSize方法: 2.BTrac ...
- 【学习笔记】Java中生成对象的5中方法
概述:本文介绍以下java五种创建对象的方式: 1.用new语句创建对象,这是最常用的创建对象的方式. 2.使用Class类的newInstance方法 3.运用反射手段,调用java.lang.re ...
随机推荐
- 基因探针富集分析(GSEA)& GO & pathway
http://blog.sina.com.cn/s/blog_4c1f21000100utyx.html GO是Gene Ontology的简称,是生物学家为了衡量基因的功能而而发起的一个项目,从分子 ...
- delphi的TFileStream 内存流
一.文件 文本文件是以行为单位进行读.写操作的.文本文件只能单独为读或写而打开,在一个打开的文本文件上同时进行读.写操作是不允许的. 二.定义 FileStream: TFileStream; 三.打 ...
- 专注手机端前端界面开发的ui组件和js组合
frozenui一款腾讯开发的简化版Bootstrap,只用于手机端 http://frozenui.github.io/ https://github.com/frozenui/frozenui z ...
- 第6组UI组件:ViewAnimator及其子类
ViewAnimator是一个基类,它继承了FrameLayout,因此它表现出FrameLayout的特征,可以将多个View组件“叠”在一起.ViewAnimator额外增加的功能正如它的名字所暗 ...
- endnote 使用方法
选择需要的期刊格式,复制到收藏夹. 下载安装插件. 鼠标放在需要插入引用的地方. 关键词搜索文献,记得在这之前要把需要的文献保存至endnote online.然后insert就行了.初次安装可能会有 ...
- Ubuntu 修改时区
1. 使用命令行 sudo tzselect 根据提示完成修改 2.修改~/.profile文件 添加: TZ='Asia/Shanghai'; export TZ 注销后重新登陆生效
- [JQuery]Jquery对象和dom对象
jquery对象是jquery包装dom对象后产生的对象,它们都只能使用各自的方法. 1.定义变量时,通过$来区分: var $variable = jquery对象: var variable = ...
- Ceph QoS 初探(下)
作者:吴香伟 发表于 2017/01/24 版权声明:可以任意转载,转载时务必以超链接形式标明文章原始出处和作者信息以及版权声明 存储QoS是个可以做很大也可以做很小的特性.SolidFire认为将Q ...
- block和delegate的区别
代理 可读性高 大部分可以属性 block 写的代码少 一般作为参数通知 占用资源 无论是block还是delegate模式本质上都是回调,使用block,其优点是回调的block代码块直 ...
- Android名片扫描识别系统SDK
Android名片扫描识别系统SDK 一.Android名片扫描识别系统应用背景 这些年,随着移动互联的发展,APP应用成爆发式的增长,在很多APP中都涉及到对名片信息的录入,如移动CRM.移动端OA ...