Java代理模式详细解析

代理模式是我们比较常用的设计模式之一。其中新思想是为了提供额外的处理或者不同的操作而在实际对象与调用者之间插入一个代理对象。这些额外的操作通常需要与实际对象进行通信,代理模式一般涉及到的角色有:

抽象角色:声明真实对象和代理对象的共同接口;

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

真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。

以下以发送消息为例来说明一个简单的代理模式的基本实现:

首先明确目的:有一条消息,需要把这个消息发送出去,根据这个目的定义对应接口MessageHandler。需要的附加操作:假设需要验证消息的长度不能超过指定长度并且不能为空,并且我们需要统计相关信息发送到次数,超过指定的次数我们需要输出警报。我们通过代理模式来实现这个附加的操作。下面为对应的类关系图及示例代码。

//接口定义
public interface MessageHandler {
public void sendMessage(String msg);
}
//通过Email方式发送消息的实现类
public class EmailMessage implements MessageHandler {
@Override
public void sendMessage(String msg) {
// TODO Auto-generated method stub
System.out.println(msg+" send!!");
}
}
//消息处理的代理类
public class MessageProxy implements MessageHandler {
private static int count;
private MessageHandler emailMsg;
@Override
public void sendMessage(String msg) {
// TODO Auto-generated method stub
if(checkMessage(msg))
{
if(emailMsg==null) emailMsg=new EmailMessage();
count++;
emailMsg.sendMessage(msg);
System.out.println("Message sent:"+count);
}
}
private boolean checkMessage(String msg) {
return msg != null && msg.length() > 10;
}
}
//调用类
public class MainClass {
private static void runProxy(MessageHandler handler)
{
handler.sendMessage("message for test");
}
/**
 * @param args
 */
public static void main(String[] args) {
// TODO Auto-generated method stub
runProxy(new EmailMessage());
System.out.println("++++++++++++++++Pjroxy++++++++++++++++++");
runProxy(new MessageProxy());
}
}
//输出
message for test send!!
++++++++++++++++Pjroxy++++++++++++++++++
message for test send!!
Message sent:1 

在例子中我们可以方便的在消息发送过程中添加各种需要的附加处理方式,也能方便的替换消息的处理方式,如将通过Email发送消息替换为通过短信发送消息,而调用方不会有丝毫察觉!在任何你想要将一些额外操作分离到具体对象之外,特别是希望能够很容易做出修改,或者想在具体对象的方法执行前插入一些额外操作的时候,代理就显得十分有用!

动态代理

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):构造函数,估计用于给内部的h赋值。
Static Class getProxyClass (ClassLoader loader, Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。

Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)。

所谓Dynamic Proxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然啦,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。 下面我们通过动态代理来重新实现上面发送信息的例子!

在上面的例子基础上,我们先添加一个通过短信来发送消息的处理类:

public class SmsMessage implements MessageHandler {
@Override
public void sendMessage(String msg) {
// TODO Auto-generated method stub
System.out.println("SMS Message :" + msg+" sent !");
}
}
//动态代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicMessageProxy implements InvocationHandler {
private static int count;
private MessageHandler msgHandler;
public DynamicMessageProxy(MessageHandler handler) {
msgHandler = handler;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
System.out.println("++++++++=============+++++++++");
System.out.println("proxy:" + proxy.getClass());
System.out.println("method:" + method);
System.out.println("++++++++=============+++++++++");
if (args != null && args.length == 1 && checkMessage((String) args[0])) {
count++;
System.out.println("Message sent:" + count);
return method.invoke(msgHandler, args);
}
return null;
}
private boolean checkMessage(String msg) {
return msg != null && msg.length() > 10;
}
}
//下面是调用
import java.lang.reflect.Proxy;
public class MainClass {
private static void runProxy(MessageHandler handler) {
handler.sendMessage("message for test");
}
/**
 * @param args
 */
public static void main(String[] args) {
// TODO Auto-generated method stub
// runProxy(new EmailMessage());
// System.out.println("++++++++++++++++Proxy++++++++++++++++++");
// runProxy(new MessageProxy());
MessageHandler handler = new EmailMessage();
runProxy(handler);
MessageHandler proxy = (MessageHandler) Proxy.newProxyInstance(
MessageHandler.class.getClassLoader(),
new Class[] { MessageHandler.class }, new DynamicMessageProxy(
handler));
runProxy(proxy);
System.out.println("++++++++++++++++++++++++++++++++++");
// 短信方式
handler = new SmsMessage();
runProxy(handler);
proxy = (MessageHandler) Proxy.newProxyInstance(MessageHandler.class
.getClassLoader(), new Class[] { MessageHandler.class },
new DynamicMessageProxy(handler));
runProxy(proxy);
}
}
//下面为以上方法的输出:
message for test send!!
++++++++=============+++++++++
proxy:class $Proxy0
method:public abstract void MessageHandler.sendMessage(java.lang.String)
++++++++=============+++++++++
Message sent:1
message for test send!!
++++++++++++++++++++++++++++++++++
SMS Message :message for test sent !
++++++++=============+++++++++
proxy:class $Proxy0
method:public abstract void MessageHandler.sendMessage(java.lang.String)
++++++++=============+++++++++
Message sent:2
SMS Message :message for test sent !

以上例子中,通过调用Proxy.newProxyInstance方法创建动态代理对象,该方法需要传入一个 类加载器、一组希望代理实现的接口列表、InvocationHandler 接口的一个具体实现。动态代理可以将所有调用重定向到调用处理器,通常我们会向该处理器传递一个时间对象的引用。invoke()方法中传递进来了代理对象,当你需要区分请求来源时这是非常有用的,例如你可以通过判断传入的方法名屏蔽掉某些方法的执行!动态代理机制并不是会很频繁使用的方法,它通常用来解决一些特定情况下的问题,因此不要盲目的为了使用而使用,要根据自己的实际需求来决定!

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • JAVA多线程并发下的单例模式应用

    单例模式应该是设计模式中比较简单的一个,也是非常常见的,但是在多线程并发的环境下使用却是不那么简单了,今天给大家分享一个我在开发过程中遇到的单例模式的应用. 首先我们先来看一下单例模式的定义: 一个类有且仅有一个实例,并且自行实例化向整个系统提供. 单例模式的要素: 1.私有的静态的实例对象 2.私有的构造函数(保证在该类外部,无法通过new的方式来创建对象实例) 3.公有的.静态的.访问该实例对象的方法 单例模式分为懒汉形和饿汉式 懒汉式: 应用刚启动的时候,并不创建实例,当外部调用该类的实例

  • java 迭代器模式实例详解

    java 迭代器模式实例详解 今天来818设计模式中的迭代器模式,也是java中Stack,List,Set等接口以及数组这个数据结构都会使用的一种模式. 首先,为什么使用迭代器模式,目的就是通过一个通用的迭代方法,隐藏stack,list,set以及数组中不同的遍历细节.也就是说,我不想让那些调用我的遍历容器的方法的人知道我到底是怎么一个一个的获取这些元素的(stack的pop,list的get,数组的array[i]),我只想让他知道他能 通过一个迭代器Iterator或者通过一个for e

  • Java责任链模式定义与用法分析

    本文实例讲述了Java责任链模式定义与用法.分享给大家供大家参考,具体如下: 使很多对象都有处理请求的机会,从而避免请求的发送者和接受者之间的耦合关系.将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止 责任链模式主要包括以下几个角色 1.处理者:处理者是一个接口,负责规定具体处理者处理用户请求的方法以及具体处理者设置后继处理对象的方法 2.具体处理者:具体处理者是实现处理接口的类的实例,具体处理者通过调用处理接口规定的方法处理用户的请求,既在接到用户的请求后,处理者将调用接

  • Java中Builder模式的实现详解

    前言 本文主要给大家介绍了关于如何实现Builder模式,大家在构建大对象时,对象的属性比较多,我们可以采用一个构造器或者使用空的构造器构造,然后使用setter方法去设置.在使用者使用这些方法时,会很多冗长的构造器参数列表或者setter方法.我们可以使用Builder模式来简化大对象的构造,提高代码的简洁性,同时提高使用者的编码体验. 下面我们将介绍在Java8之前.使用极简代码利器Lombok.Java8之后的Builder模式. Pre Java8 我们先来看下在Java8之前的Buil

  • 详解java 单例模式及方法总结

    java设计模式--单例模式  单例设计模式 Singleton是一种创建型模式,指某个类采用Singleton模式,则在这个类被创建后,只可能产生一个实例供外部访问,并且提供一个全局的访问点. 核心知识点如下: (1) 将采用单例设计模式的类的构造方法私有化(采用private修饰). (2) 在其内部产生该类的实例化对象,并将其封装成private static类型. (3) 定义一个静态方法返回该类的实例. /** * 方法一 * 单例模式的实现:饿汉式,线程安全 但效率比较低 */ pu

  • java 单例模式和工厂模式实例详解

    单例模式根据实例化对象时机的不同分为两种:一种是饿汉式单例,一种是懒汉式单例. 私有的构造方法 指向自己实例的私有静态引用 以自己实例为返回值的静态的公有的方法 饿汉式单例 public class Singleton { private static Singleton singleton = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return singleton; } } 懒

  • Java多线程下的单例模式参考

    单例有多种的写法,本例是懒汉式单例的一种写法.在高并发环境下需要注意的是: 1.单例在并发访问并调用其相应的getInstance方法的时候也会造成创建多个实例对象,加锁是必要的. 2.使用synchronized是比较好的解决方案,优点是代码简洁,缺点是在抛出异常的时候不能处理维护使系统处于良好状态. 3.显示的lock设定是良好的解决方案. 使用lock的代码如下: package demo; import java.util.concurrent.locks.Lock; import ja

  • Java适配器模式定义与用法示例

    本文实例讲述了Java适配器模式定义与用法.分享给大家供大家参考,具体如下: 将一个类的接口转换成客户想要的另一个接口,适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作 适配器一共包括3种角色 1.目标:目标是一个接口,该接口是客户想使用的接口 2.被适配者:被适配者是一个已存在的接口或抽象类,这个接口或抽象类需要适配 3.适配器:适配器是一个类,该类实现了目标接口并包含有被适配者的引用,即适配者的指着是对被适配者接口与目标进行适配 package org.zhy.adapte

  • java多线程之线程安全的单例模式

    概念: java中单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例.饿汉式单例.登记式单例三种. 单例模式有一下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建自己的唯一实例. 3.单例类必须给所有其他对象提供这一实例. 单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例.在计算机系统中,线程池.缓存.日志对象.对话框.打印机.显卡的驱动程序对象常被设计成单例.这些应用都或多或少具有资源管理器的功能.每台计算机可以有若干个打印机,但只能有一个Printer

  • Java多线程模式之Balking模式详解

    本文实例讲述了Java多线程模式之Balking模式.分享给大家供大家参考,具体如下: 当现在不适合这个操作,或是没有必要进行这个操作时就直接放弃这个操作而回去.这个就是Balking模式 例如王某在餐厅吃饭,当王某需要点餐时喊服务员需要点餐.当服务员A和B都注意到了王某点餐的示意,这时服务员B看到服务员A已经去响应了王某的点餐请求,所以服务员B就不会再过去响应王某的点餐请求. 程序示例: 程序的需求是模拟一个自动保存的功能.自动保存是为了预防计算机忽然断电或则软件突然出错的危险,定期将数据保存

  • Java多线程中的单例模式两种实现方式

    Java多线程中的单例模式 一.在多线程环境下创建单例 方式一: package com.ietree.multithread.sync; public class Singletion { private static class InnerSingletion { private static Singletion single = new Singletion(); } public static Singletion getInstance() { return InnerSinglet

  • Java单利模式与多线程总结归纳

    概念: java中单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例.饿汉式单例.登记式单例三种. 单例模式有一下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建自己的唯一实例. 3.单例类必须给所有其他对象提供这一实例. 单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例.在计算机系统中,线程池.缓存.日志对象.对话框.打印机.显卡的驱动程序对象常被设计成单例.这些应用都或多或少具有资源管理器的功能.每台计算机可以有若干个打印机,但只能有一个Printer

随机推荐