Java 设计模式(八) Proxy(代理)模式及Spring引申

Proxy

基本概念

代理模式(Proxy pattern)是一种使用率非常高的模式:

  • 为其他对象提供一种代理以控制对这个对象的访问
  • 代理模式也叫作委托模式,它是一项基本设计技巧

Proxy中的角色

  • Subject(抽象主题类):既可以是抽象类也可以是抽象的接口
  • RealSubject(具体的主题角色):是被委托角色或者说是被代理角色
  • Proxy(代理主题角色):是委托类或者代理类:
    • 它负责对真实的角色的应用
    • 把Subject定义的方法限制委托给RealSubject实现
    • 在RealSubject处理完毕前后做预处理善后处理工作

对Proxy的理解

一个代理类可以代理多个Subject,因此一个Proxy 具体代理的是哪个RealSubject

Proxy的优点:

  • 职责清晰:
    RealSubject就是实现实际的业务逻辑,不用关心其他非本职责的事务(职责单一原则),通过后期的代理完成一件事务,附带的结果的编程的简洁性
  • 高拓展性:
    因为是面向接口的编程,在RealSubject随时会发生变化的场景下,因为接口是持续保持不变的(接口不变性),我们可以在对代理类不进行任何修改的情况下使用(开闭原则)
  • 智能化:
    动态代理中利用反射机制容易体现出来,无需在实现阶段关心代理关系,而在运行阶段才指定代理关系

例讲Proxy

普通代理(Normal Proxy)

在网络设置中,我们可以通过自己设置代理服务器的IP地址获得代理服务(配置代理),也就是在使用代理服务时我们是知道代理的存在的,这种代理模式一般分为两种:

  • 普通代理:我们要知道代理的存在,才能调用
  • 强制代理:调用者直接使用真实角色,代理的产生是由真实角色决定的。
在当前的部分我们只关注普通代理

它要求就是客户端只能访问代理角色,而不能访问真实的角色

既然是例讲,我们当然会缔造一个场景:
常见的办公室场景就可以描述这个代理模式,比如作为一个工程师,你需要全神贯注额关注你的项目,很多非技术问题完全可以由前台代办,比如记录一下Schedule什么的,那么前台充当的就是一个代理。

  • Subject
/***
 * 主题的抽象接口,用来定义代理和实体的方法
 */
interface Subject2 {
    void answerPhone ( );
    void planSchedule( );
    void remind( );
}
  • RealSubject
/***
 * 工程师实体,用来实现具体的逻辑,但是只能通过代理,不能直接创建
 */
class Engineer implements  Subject2{

    private String name=null;

    public Engineer ( Subject2  subject , String name ) throws Exception {
        if( subject == null ){
            throw new Exception("不能创建真实角色工程师");
        }else{
            this.name = name;
        }
    }

    @Override
    public void answerPhone() {
        System.out.println("Hello, this is llin(lichen)");
    }

    @Override
    public void planSchedule() {
        System.out.println("I may set this business Monday");
    }

    @Override
    public void remind() {
        System.out.println("Oh! I have to do it now!");
    }
}
  • Proxy
/**
 * 创建代理,提供对工程师方法的调用的对象(不能逾越而直接和工程师交流)
 */
class ForegroundProxy implements Subject2{

    private Subject2 subject = null;

    public ForegroundProxy( String name ){
        try{
            subject = new Engineer ( this , name );
        }catch ( Exception e ){
            e.printStackTrace();
        }
    }

    @Override
    public void answerPhone() {
        subject.answerPhone();
    }

    @Override
    public void planSchedule() {
        subject.planSchedule();
    }

    @Override
    public void remind() {
        subject.remind();
    }
}
  • 测试代码
public class ProxyTest {
    public static void main ( String [] args ){
        Subject2 subject = new ForegroundProxy("llin");
        subject.answerPhone();
        subject.planSchedule();
        subject.remind();
    }
}

结果如下:
这里写图片描述

强制代理(Forced-Proxy)

一般的思维都是通过代理找到真实的角色(前台->工程师),但是强制的代理是要突出强制,你必须通过真实角色查找到代理角色,否则不能访问,就像你要找魏总,不能找安总的秘书把,而且往往魏总往往留下的是自己的名片,但是往往你联系的和联系你的也往往是魏总的秘书…就酱,那么就根据(秘书->经理),我们来例讲强制代理
既然说是秘书,就不像是前台那样只是转接电话了,还会做一些其他的工作,我们也藉此机会,阐述一下代理模式的另外一些功能:

Proxy是可以为RealSubject提供预处理消息过滤消息消息转发事后处理消息等功能
import java.util.StringTokenizer;

/**
 * Created by liulin on 16-5-5.
 */

interface Subject3{
    void agreeDecision();
    void planSchedule();
    void answerPhone( String askInfo );
}

class Boss implements Subject3{

    Subject3 proxy = null;

    @Override
    public void agreeDecision() {
        if( this.isProxy()){
            System.out.println("I agree on it!");
        }else{
            prompt();
        }
    }

    @Override
    public void planSchedule() {
        if( this.isProxy()){
            System.out.println("I plan to feed my cat!");
        }else{
            prompt();
        }
    }

    @Override
    public void answerPhone( String askInfo ) {
        if( this.isProxy()){
            if( !checkMessage(askInfo)){
                System.out.println("You sure call to me ?");
                return;
            }
            System.out.println("Hello, this is boss...!");
        }else{
            prompt();
        }
    }

    public Subject3 getProxy(){
        this.proxy = new Secretary(this);
        return this.proxy;
    }

    //检查当前调用方法的对象是否是代理对象
    private boolean isProxy () {
        if( this.proxy == null ){
            return false;
        }else{
            return true;
        }
    }

    //判断电话是否是真的找魏总的
    private boolean checkMessage ( String message ){
        StringTokenizer st = new StringTokenizer( message );
        boolean yes = false;
        while ( st.hasMoreTokens()){
            String temp = st.nextToken();
            if( temp.equalsIgnoreCase("weizong")){
                yes = true;
            }
        }
        return yes;
    }

    //不直接和外界接触
    private void prompt ( ){
        System.out.println("Please reserve with my secretary!");
    }
}

class Secretary implements Subject3{

    private Subject3 boss=null;

    public Secretary( Subject3 boss ){
        this.boss = boss;
    }

    @Override
    public void agreeDecision() {
        this.boss.agreeDecision();
    }

    @Override
    public void planSchedule() {
        this.boss.planSchedule();
    }

    @Override
    public void answerPhone(String askInfo) {
        this.boss.answerPhone(askInfo);
    }
}

public class ProxyTest1 {
    public static void main ( String [] args ){
        Subject3 subject = new Boss().getProxy();
        subject.answerPhone("I want to chat with Weizong");
        subject.planSchedule();
        subject.agreeDecision();
        subject.answerPhone("I want to chat with hanzong");
    }
}

结果如下:
这里写图片描述

动态代理(Dynamic Proxy)

AOP(Aspect Oriented Programming),面向切面的编程,其核心就是动态代理机制
  • 切面代表一个普遍存在的共有的功能
  • AOP技术是建立在Java的反射动态代理机制
  • 将公共方法接入到接入点
  • 动态代理
    • 是在实现阶段不用关心代理谁,而在运行阶段代理哪一个对象
    • 根据被代理的接口生成所有的方法,也就是说给定一个接口,动态代理会宣称已经实现了所有方法
    • AOP编程对我们的设计、编码有非常大的影响,对于日志事务权限等都可以在系统设计阶段不用考虑,而在设计后通过AOP的方式切过去
  • 我们采取JDK的InvocationHandler实现,CGLIB也是一种实现方式

我们利用上面的秘书和经理的场景来讲解Dynamic Proxy

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * Created by liulin on 16-5-6.
 */

interface People{
    void answerPhone();
}

interface Advice{
    void prompt();
}

class BeforeAdvice implements Advice{
    private String message=null;
    BeforeAdvice ( String message ){
        this.message = message;
    }

    @Override
    public void prompt() {
        System.out.println( message );
    }
}

class MyInvocationHandler implements InvocationHandler {

    //被代理的对象
    private Object target = null;
    //通过构造函数构造一个对象
    public MyInvocationHandler ( Object target ){
        this.target = target;
    }

    //类似责任链模式的Handler处理方式
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke( target , args );
    }
}
//我们可以通过写动态代理类在不修改之前的前提下实现代理逻辑,而且可以不在意具体的接口(反射机制实现)
class DynamicProxy<T>{

    private static List<Advice> advices = new ArrayList();
    //初始话advices建议列表
    static{
        advices.add(new BeforeAdvice("Someone have business to you"));
    }
    public static <T> T newProxyInstance ( ClassLoader classLoader , Class<?>[] interfaces , InvocationHandler h){
        //在代理前通知
        Iterator it = advices.iterator();
        while ( it.hasNext()){
            Advice advice = (Advice)it.next();
            advice.prompt();
        }
        return (T) Proxy.newProxyInstance( classLoader , interfaces , h );
    }
}

class Weizong implements People{

    @Override
    public void answerPhone() {
        System.out.println("This is manager Wei!");
    }
}

public class ProxyTest2 {
    public static void main ( String [] args ){
        People boss = new Weizong();
        //设置对代理的处理者
        InvocationHandler handler = new MyInvocationHandler(boss);
        //设置动态代理
        People secretary = DynamicProxy.newProxyInstance( boss.getClass().getClassLoader(),
                boss.getClass().getInterfaces(),
                handler);
        //如果秘书换职位,接口不再是老板,而是工程师,那么只需要修改参数即可,不需要修改代码
        secretary.answerPhone();
    }
}

Spring引申

Spring的AOP编程依赖注入就是通过动态代理实现的,动态代理就是代理模式的最好的应用

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值