第 17 章 设计模式(5 >>> 代理模式(Proxy))

一、简介

1、基本介绍
1. 代理模式:为一个对象提供一个替身,以控制对这个对象的访问;
2. 被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象;
3. 代理模式有不同的形式(比如 远程代理,静态代理,动态代理),都是为了控制与管理对象访问。
2、提出需求

糖果机项目,具体要求如下:

1. 某公司需要将销售糖果的糖果机放置到本地(本地监控)和外地(远程监控),进行糖果销售;
2. 给糖果机插入硬币,转动手柄,这样就可以购买糖果;
3. 可以监控糖果机的状态和销售情况。
3、完成监控本地糖果机
对本地糖果机的状态和销售情况进行监控,相对比较简单,代码实现该功能。
1. 代码结构图

在这里插入图片描述

2. 代码实现
=================================trait State=================================
package com.lj.akka.proxymode.locally
trait State {
    def insertCoin()  // 插入硬币
    def returnCoin()  // 退回硬币
    def turnCrank()  // 转动手柄
    def printState()  // 输出状态
    def getStateName(): String  // 返回状态的名字
    def dispense()   // 分配状态,比如:卖出一块糖后,就看看当前糖果机应该进入哪个状态.
}
=================================class CandyMachine=================================
package com.lj.akka.proxymode.locally
class CandyMachine {
    private var soldOutState: State = _   // 售空状态
    private var onReadyState: State = _   // 准备待售状态
    private var hasCoin: State = _  // 投入硬币状态
    private var soldState: State = _  // 正在售出状态
    private var winnerState: State = _  // 奖励状态
    private var location: String = _  // 糖果机位置
    private var state: State = _  // 状态
    private var count: Int = _   // Candy数量
    // 辅助构造器
    def this(location: String, count: Int) {
        this
        this.location = location
        this.count = count
        this.soldOutState = new SoldOutState(this)
        this.onReadyState = new OnReadyState(this)
        this.hasCoin = new HasCoin(this)
        this.soldState = new SoldState(this)
        this.winnerState = new WinnerState(this)
        if (count > 0) {
            state = onReadyState
        } else {
            state = soldOutState
        }
    }

    def insertCoin() = {
        state.insertCoin()
    }
    def returnCoin(): Unit = {
        state.returnCoin()
    }
    def turnCrank(): Unit = {
        state.turnCrank()
        state.dispense()
    }
    def releaseCandy(): Unit = {
        if (count > 0) {
            count -= 1
            println("A Candy Rolling Out!")
        }
    }

    def _soldOutState: State = soldOutState
    def _soldOutState_=(value: State): Unit = {
        soldOutState = value
    }
    def _onReadyState: State = onReadyState
    def _onReadyState_=(value: State): Unit = {
        onReadyState = value
    }
    def _hasCoin: State = hasCoin
    def _hasCoin_=(value: State): Unit = {
        hasCoin = value
    }
    def _soldState: State = soldState
    def _soldState_=(value: State): Unit = {
        soldState = value
    }
    def _winnerState: State = winnerState
    def _winnerState_=(value: State): Unit = {
        winnerState = value
    }
    def _location: String = location
    def _location_=(value: String): Unit = {
        location = value
    }
    def _state: State = state
    def _state_=(value: State): Unit = {
        state = value
    }
    def _count: Int = count
    def _count_=(value: Int): Unit = {
        count = value
    }
}
=============================class SoldOutState extends State=============================
package com.lj.akka.proxymode.locally
class SoldOutState extends State {
    //说明:@transient注解将字段标记为瞬态的,即表示一个域不是该对象串行化的一部分
    @transient private var candyMachine: CandyMachine = _
    def this(candyMachine: CandyMachine) {
        this
        this.candyMachine = candyMachine
    }

    override def insertCoin(): Unit = {
        println("Candy已经售空,禁止投入硬币购买糖果.")
    }
    override def returnCoin(): Unit = {
        println("Candy Machine 还没有投入硬币,没有要返还的硬币.")
    }
    override def turnCrank(): Unit = {
        println("Candy已经售空,转动手柄无任何效果.")
    }
    override def printState(): Unit = {
        println("Candy已经售空.")
    }
    override def getStateName(): String = {
        "Candy Sold Out State."
    }
    override def dispense(): Unit = {
    }
}
=============================class OnReadyState extends State=============================
package com.lj.akka.proxymode.locally
class OnReadyState extends State {
    @transient private var candyMachine: CandyMachine = _
    def this(candyMachine: CandyMachine) {
        this
        this.candyMachine = candyMachine
    }

    override def insertCoin(): Unit = {
        println("投入硬币成功,接下来请转动手柄(turn crank)..")
        // 设置Candy Machine为有硬币的状态
        this.candyMachine._state_=(candyMachine._hasCoin)
    }
    override def returnCoin(): Unit = {
        println("你还没有投入硬币,没有硬币需要退回..")
    }
    override def turnCrank(): Unit = {
        println("请投入硬币购买Candy..")
        // 此时就不需要出Candy
        this.candyMachine._state_=(candyMachine._soldState)
    }
    override def printState(): Unit = {
        println("On Ready State")
    }
    override def getStateName(): String = {
        "On Ready State.."
    }
    override def dispense(): Unit = {
        // 出一个糖果
        candyMachine.releaseCandy()
        // 检查Candy数量
        if (candyMachine._count == 0) {
            candyMachine._state_=(candyMachine._soldOutState)
        } else {
            candyMachine._state_=(candyMachine._onReadyState)
        }
    }
}
=============================class HasCoin extends State=============================
package com.lj.akka.proxymode.locally
class HasCoin extends State {
    private val random = new java.util.Random()
    @transient private var candyMachine: CandyMachine = _
    def this(candyMachine: CandyMachine) {
        this
        this.candyMachine = candyMachine
    }

    override def insertCoin(): Unit = {
        println("你已经投入了硬币,无需再次投入硬币......")
    }
    override def returnCoin(): Unit = {
        println("投入的硬币已退回,欢迎下次光临......")
        candyMachine._state_=(candyMachine._onReadyState)
    }
    override def turnCrank(): Unit = {
        println("手柄正在转动,等待出Candy......")
        // 奖励机制
        val num = random.nextInt(10)
        if (num == 0) {
            candyMachine._state_=(candyMachine._winnerState)
        } else {
            candyMachine._state_=(candyMachine._onReadyState)
        }
    }
    override def printState(): Unit = {
        println("Has Coin......")
    }
    override def getStateName(): String  = {
        "Has Coin......"
    }
    override def dispense(): Unit = {}
}
============================class SoldState extends State============================
package com.lj.akka.proxymode.locally
class SoldState extends State {
    @transient private var candyMachine: CandyMachine = _
    def this(candyMachine: CandyMachine) {
        this
        this.candyMachine = candyMachine
    }

    override def insertCoin(): Unit = {
        println("等待投入硬币,Candy Machine正在出Candy....")
    }
    override def returnCoin(): Unit = {
        println("目前正在出Candy,无法退出硬币....")
    }
    override def turnCrank(): Unit = {
        println("Candy Machine正在出Candy,不能转动手柄....")
    }
    override def printState(): Unit = {
        println("Sold State...")
    }
    override def getStateName(): String = {
        "Sold State..."
    }
    // 这里不做任何操作,不出糖果
    override def dispense(): Unit = {}
}
=============================class WinnerState extends State=============================
package com.lj.akka.proxymode.locally
class WinnerState extends State {
    @transient private var candyMachine: CandyMachine = _
    def this(candyMachine: CandyMachine) {
        this
        this.candyMachine = candyMachine
    }
    
    override def insertCoin(): Unit = {
        println("Candy Machine正在出Candy,你不能投入硬币....")
    }
    override def returnCoin(): Unit = {
        println("Candy Machine正在出Candy,不能退出硬币.....")
    }
    override def turnCrank(): Unit = {
        println("Candy Machine正在出Candy,不能转动手柄.....")
    }
    override def printState(): Unit = {
        println("Winner State.....")
    }
    override def getStateName(): String = {
        "Winner State....."
    }
    override def dispense(): Unit = {
        // 出一个Candy
        this.candyMachine.releaseCandy()
        if (candyMachine._count == 0) {
            candyMachine._state_=(candyMachine._soldOutState)
        } else {
            println("恭喜你,再次获得一个Candy的奖励")
            candyMachine.releaseCandy()
            if (candyMachine._count > 0) {
                candyMachine._state_=(candyMachine._onReadyState)
            } else {
                candyMachine._state_=(candyMachine._soldOutState)
            }
        }
    }
}
=================================class Monitor=================================
package com.lj.akka.proxymode.locally
import scala.collection.mutable.ListBuffer
class Monitor {
    private val candyMachines: ListBuffer[CandyMachine] = ListBuffer()
    // add candy machine
    def addCandyMachine(candyMachine: CandyMachine): Unit = {
        candyMachines.append(candyMachine)
    }
    def report(): Unit = {
        for (ele <- candyMachines) {
            println("----------------------------------------")
            println("Machine local:" + ele._location)
            println("Candy还剩余数量:" + ele._count)
            println("Candy Machine State:" + ele._state.getStateName())
        }
    }
}
=================================object Test=================================
package com.lj.akka.proxymode
import com.lj.akka.proxymode.locally.{CandyMachine, Monitor}
object Test {
    def main(args: Array[String]): Unit = {
    
        val monitor = new Monitor()
        var candyMachine = new CandyMachine("上海-徐家汇001", 30)
        monitor.addCandyMachine(candyMachine)

        candyMachine = new CandyMachine("上海-浦东001", 1)
        candyMachine.insertCoin()
        candyMachine.turnCrank()
        monitor.addCandyMachine(candyMachine)
        monitor.report()
    }
}
=========================================运行结果=========================================
投入硬币成功,接下来请转动手柄(turn crank)..
手柄正在转动,等待出Candy......
A Candy Rolling Out!
----------------------------------------
Machine local:上海-徐家汇001
Candy还剩余数量:30
Candy Machine State:On Ready State..
----------------------------------------
Machine local:上海-浦东001
Candy还剩余数量:8
Candy Machine State:On Ready State..
=========================================运行结果=========================================

二、完成监控远程糖果机

1、对远程糖果机的状态和销售情况进行监控,相对麻烦些,我们先分析一下
1. 方式1:因为远程糖果机不在本地,比如在另外的城市,国家,这时可以使用socket编程来进行网络编程
控制(缺点:麻烦);
2. 方案2:在远程放置web服务器,通过web编程来实现远程监控;
3. 方案3:使用RMI(Remote Method Invocation)远程方法调用来完成对远程糖果机的监控,因为RMI将socket
的底层封装起来,对外提供调用方法接口即可,这样比较简单,这样我们就可以实现远程代理模式开发。
2、远程代理模式监控方案
远程代理:远程对象的本地代表,通过它可以把远程对象当本地对象来调用,远程代理通过网络和真正的远程
		 对象沟通信息。(如下图)

在这里插入图片描述

三、Java RMI实现远程代理

1、简介
	RMI指的是远程方法调用 (Remote Method Invocation)。它是一种机制,能够让在某个Java虚拟机上的对象调用
另一个Java虚拟机中的对象上的方法。可以用此方法调用的任何对象必须实现该远程接口,RMI可以将底层的socket
编程封装,简化操作。(如下图)

在这里插入图片描述

2、介绍
1. RMI远程方法调用是计算机之间通过网络实现对象调用的一种通讯机制;
2. 使用RMI机制,一台计算机上的对象可以调用另外 一台计算机上的对象来获取远程数据;
3. RMI被设计成一种面向对象开发方式,允许程序员使用远程对象来实现通信。
3、Java RMI的开发应用案例-说明

请编写一个JavaRMI的案例,代理端(客户端)可以通过rmi远程调用 远程端注册的一个服务的sayHello的方法,并且返回结果。

4、Java RMI的开发应用案例-开发步骤
1. 制作远程接口:接口文件;
2. 远程接口的实现:Service文件;
3. RMI服务端注册,开启服务;
4. RMI代理端通过RMI查询到服务端,建立联系,通过接口调用远程方法。
5、Java RMI的开发应用案例-程序框架

在这里插入图片描述

6、RMI代码实现

代码结构:
在这里插入图片描述
示例代码:

============================trait MyRemote extends Remote============================
package com.lj.akka.rmi
import java.rmi.{Remote, RemoteException}
// 代理端(客户端)共用的接口文件
trait MyRemote extends Remote {

    @throws(classOf[RemoteException])
    def sayHello(): String

}
================class MyRemoteImpl extends UnicastRemoteObject with MyRemote================
package com.lj.akka.rmi
import java.rmi.registry.LocateRegistry
import java.rmi.server.UnicastRemoteObject
import java.rmi.{Naming, RemoteException}
class MyRemoteImpl extends UnicastRemoteObject with MyRemote {
    @throws(classOf[RemoteException])
    override def sayHello(): String = {
        "Service sayHello() exec..."
    }
}
object MyRemoteImplService {
    def main(args: Array[String]): Unit = {
        val service: MyRemote = new MyRemoteImpl()
        // 把服务绑定到端口13331上
        // 两种写法,根据系统的支持
        LocateRegistry.createRegistry(13331)
//        Naming.rebind("RemoteHello", service)
        Naming.rebind("rmi://127.0.0.1:13331/RemoteHello", service)
        println("远程服务已开启,在127.0.0.1的13331端口监听,服务名:RemoteHello")
    }
}
============================class MyRemoteClient============================
package com.lj.akka.rmi
import java.rmi.Naming
class MyRemoteClient {

    def go(): Unit = {
        val service = Naming.lookup("rmi://127.0.0.1:13331/RemoteHello").asInstanceOf[MyRemote]
        // 设置代理调用远程的方法
        val service_def = service.sayHello()
        println("res:" + service_def)
    }

}
object MyRemoteClient {
    def main(args: Array[String]): Unit = {
        val client = new MyRemoteClient()
        client.go()
    }
}

四、使用远程代理模式完成远程糖果机监控

1、类结构图

在这里插入图片描述

2、功能代码实现

代码结构图:

说明:我的客户端直接使用了server的类和接口,在实际开发中,需要给客户端/代理端拷贝一份。

在这里插入图片描述
示例代码:

====================trait CandyMachineRemote extends Remote====================
package com.lj.akka.proxymode.remotecandymachine.server
import java.rmi.{Remote, RemoteException}
trait CandyMachineRemote extends Remote {
    @throws(classOf[RemoteException])
    def getLocation(): String   // 获取Candy Machine 的位置
    @throws(classOf[RemoteException])
    def getCount(): Int   // 获取Candy数量
    @throws(classOf[RemoteException])
    def setState(state: State)   // 设置Candy Machine 的状态
    @throws(classOf[RemoteException])
    def getState(): State   // 获取Candy Machine 的状态
}
====================trait State extends Serializable====================
package com.lj.akka.proxymode.remotecandymachine.server
//因为需要在网络传输,因此需要继承Serializable,即序列化
trait State extends Serializable {
    def insertCoin()  // 投入硬币
    def returnCoin()  // 退回硬币
    def turnCrank()  // 转动手柄
    def printState()  // 输出当前状态
    def getStateName(): String  // 返回当前状态
    def dispense()  // 分配糖果
}
===========class CandyMachine extends UnicastRemoteObject with CandyMachineRemote==========
package com.lj.akka.proxymode.remotecandymachine.server
import java.rmi.RemoteException
import java.rmi.server.UnicastRemoteObject
class CandyMachine extends UnicastRemoteObject with CandyMachineRemote {
    private var mSoldOutState: State = _
    private var mOnReadyState: State = _
    private var mHasCoinState: State = _
    private var mSoldState: State = _
    private var mWinnerState: State = _
    private var state: State = _
    private var location: String = _
    private var count: Int = _

    @throws(classOf[RemoteException])
    def this(location: String, count: Int) {
        this
        this.location = location
        this.count = count
        this.mSoldOutState = new SoldOut(this)
        this.mOnReadyState = new OnReady(this)
        this.mHasCoinState = new HasCoin(this)
        this.mSoldState = new Sold(this)
        this.mWinnerState = new WinnerState(this)
        if (count > 0) {
            this.state = mOnReadyState
        } else {
            this.state = mSoldOutState
        }
    }

    override def getLocation(): String = location
    override def getCount(): Int = this.count
    override def setState(state: State) = {
        this.state = state
    }
    override def getState()= {
        this.state
    }

    def insertCoin(): Unit = {  // 投入硬币
        state.insertCoin()
    }
    def returnCoin(): Unit = {  // 退回硬币
        state.returnCoin()
    }
    def turnCrank(): Unit = {  // 转动手柄
        state.turnCrank()
        state.dispense()
    }
    def releaseCandy(): Unit = {
        if (count > 0) {
            count -= 1
            println("A Candy Rolling Out!")
        }
    }

    def _mSoldOutState: State = mSoldOutState
    def _mSoldOutState_=(value: State): Unit = {
        mSoldOutState = value
    }

    def _mOnReadyState: State = mOnReadyState
    def _mOnReadyState_=(value: State): Unit = {
        mOnReadyState = value
    }

    def _mHasCoinState: State = mHasCoinState
    def _mHasCoinState_=(value: State): Unit = {
        mHasCoinState = value
    }

    def _mSoldState: State = mSoldState
    def _mSoldState_=(value: State): Unit = {
        mSoldState = value
    }

    def _mWinnerState: State = mWinnerState
    def _mWinnerState_=(value: State): Unit = {
        mWinnerState = value
    }
}
====================class OnReady extends State====================
package com.lj.akka.proxymode.remotecandymachine.server
class OnReady extends State {
    @transient private var candyMachine: CandyMachine = _
    def this(candyMachine: CandyMachine) {
        this
        this.candyMachine = candyMachine
    }

    override def insertCoin(): Unit = {
        println("投入硬币成功,接下来请转动手柄(turn crank)..")
        // 设置Candy Machine为有硬币的状态
        this.candyMachine.setState(candyMachine._mHasCoinState)
    }
    override def returnCoin(): Unit = {
        println("你还没有投入硬币,没有硬币需要退回..")
    }
    override def turnCrank(): Unit = {
        println("请投入硬币购买Candy..")
        // 此时就不需要出Candy
        this.candyMachine.setState(candyMachine._mSoldState)
    }
    override def printState(): Unit = {
        println("On Ready State")
    }
    override def getStateName(): String = {
        "On Ready State.."
    }
    override def dispense(): Unit = {
        // 出一个糖果
        candyMachine.releaseCandy()
        // 检查Candy数量
        if (candyMachine.getCount() == 0) {
            candyMachine.setState(candyMachine._mSoldOutState)
        } else {
            candyMachine.setState(candyMachine._mOnReadyState)
        }
    }
}
====================class HasCoin extends State====================
package com.lj.akka.proxymode.remotecandymachine.server
class HasCoin extends State {
    private val random = new java.util.Random
    @transient private var candyMachine: CandyMachine = _
    def this(candyMachine: CandyMachine) {
        this
        this.candyMachine = candyMachine
    }

    override def insertCoin(): Unit = {
        println("你已经投入了硬币,无需再次投入硬币......")
    }
    override def returnCoin(): Unit = {
        println("投入的硬币已退回,欢迎下次光临......")
        candyMachine.setState(candyMachine._mOnReadyState)
    }
    override def turnCrank(): Unit = {
        println("手柄正在转动,等待出Candy......")
        // 奖励机制
        val num = random.nextInt(10)
        if (num == 0) {
            candyMachine.setState(candyMachine._mWinnerState)
        } else {
            candyMachine.setState(candyMachine._mOnReadyState)
        }
    }
    override def printState(): Unit = {
        println("Has Coin......")
    }
    override def getStateName(): String  = {
        "Has Coin......"
    }
    override def dispense(): Unit = {}
}
====================class Sold extends State====================
package com.lj.akka.proxymode.remotecandymachine.server
class Sold extends State {
    @transient private var candyMachine: CandyMachine = _
    def this(candyMachine: CandyMachine) {
        this
        this.candyMachine = candyMachine
    }

    override def insertCoin(): Unit = {
        println("等待投入硬币,Candy Machine正在出Candy....")
    }
    override def returnCoin(): Unit = {
        println("目前正在出Candy,无法退出硬币....")
    }
    override def turnCrank(): Unit = {
        println("Candy Machine正在出Candy,不能转动手柄....")
    }
    override def printState(): Unit = {
        println("Sold State...")
    }
    override def getStateName(): String = {
        "Sold State..."
    }
    // 这里不做任何操作,不出糖果
    override def dispense(): Unit = {}
}
====================class SoldOut extends State====================
package com.lj.akka.proxymode.remotecandymachine.server
class SoldOut extends State {
    @transient private var candyMachine: CandyMachine = _
    def this(candyMachine: CandyMachine) {
        this
        this.candyMachine = candyMachine
    }

    override def insertCoin(): Unit = {
        println("Candy已经售空,禁止投入硬币购买糖果.")
    }
    override def returnCoin(): Unit = {
        println("Candy Machine 还没有投入硬币,没有要返还的硬币.")
    }
    override def turnCrank(): Unit = {
        println("Candy已经售空,转动手柄无任何效果.")
    }
    override def printState(): Unit = {
        println("Candy已经售空.")
    }
    override def getStateName(): String = {
        "Candy Sold Out State."
    }
    override def dispense(): Unit = {}
}
====================object RemoteMain====================
package com.lj.akka.proxymode.remotecandymachine.server
import java.rmi.Naming
import java.rmi.registry.LocateRegistry
import com.lj.akka.proxymode.remotecandymachine.client.Monitor

object RemoteMain {
    def main(args: Array[String]): Unit = {
        try {
            val monitor: Monitor = new Monitor
            var service: CandyMachine = new CandyMachine("北京-西郊01", 30)
            monitor.addCandyMachine(service)
            service = new CandyMachine("上海-徐家汇01", 13)
            monitor.addCandyMachine(service)
            service = new CandyMachine("深圳-自贸区01", 8)
            monitor.addCandyMachine(service)
            LocateRegistry.createRegistry(13332)
            Naming.rebind("rmi://127.0.0.1:13332/CandyMachine", service)
            println(s"服务端有${monitor._candyMachines.size}台Candy Machine开始运行,在端口13332监听,信息如下:")
            while (true) {
                monitor.report()
                Thread.sleep(15000)
                println("==================华丽分隔符=====================")
                // 监控客户端信息变化
                val clientCandyMachineRemote = Naming.lookup("rmi://127.0.0.1:13332/ClientCandyMachine")
                    .asInstanceOf[CandyMachineRemote]
                monitor.update(clientCandyMachineRemote.getLocation(), clientCandyMachineRemote.getCount())
            }
        } catch {
            case ex: Exception => ex.printStackTrace()
        }
    }
}
==============================class Monitor===============================
package com.lj.akka.proxymode.remotecandymachine.client
import com.lj.akka.proxymode.remotecandymachine.server.{CandyMachine, CandyMachineRemote}
import scala.collection.mutable.ListBuffer
class Monitor {
    private val candyMachines: ListBuffer[CandyMachineRemote] = ListBuffer()
    def _candyMachines: ListBuffer[CandyMachineRemote] = candyMachines
    // add candy machine
    def addCandyMachine(candyMachine: CandyMachineRemote): Unit = {
        candyMachines.append(candyMachine)
    }
    def report(): Unit = {
        for (ele <- candyMachines) {
            println("----------------------------------------")
            println("Machine local:" + ele.getLocation())
            println("Candy还剩余数量:" + ele.getCount())
            println("Candy Machine State:" + ele.getState().getStateName())
        }
    }
    // 更新
    def update(location: String, count: Int): Unit = {
        for (ele <- candyMachines) {
            if (ele.getLocation() == location) {
                candyMachines -= ele
                candyMachines.append(new CandyMachine(location, count))
            }
        }
    }
}
====================trait CandyMachineRemote extends Remote====================
package com.lj.akka.proxymode.remotecandymachine.client
import java.rmi.Naming
import com.lj.akka.proxymode.remotecandymachine.server.{CandyMachine, CandyMachineRemote}
object RemoteClient {
    def main(args: Array[String]): Unit = {
        val monitor = new Monitor
        try {
            val mCandyMachineRemote = Naming.lookup("rmi://127.0.0.1:13332/CandyMachine").asInstanceOf[CandyMachineRemote]
            monitor.addCandyMachine(mCandyMachineRemote)
            println(s"客户端Candy Machine(位置:${mCandyMachineRemote.getLocation()})在运行,情况如下:")
            monitor.report()
            println("客户对Candy Machine操作......")
            val clientCandyMachine = new CandyMachine(mCandyMachineRemote.getLocation(), mCandyMachineRemote.getCount())
            // LocateRegistry.createRegistry(13332)
            while (true) {
                clientCandyMachine.insertCoin()
                clientCandyMachine.turnCrank()
                // 更新数据到远程监控
                Naming.rebind("rmi://127.0.0.1:13332/ClientCandyMachine", clientCandyMachine)
                Thread.sleep(10000)
            }
        } catch {
            case ex: Exception => ex.printStackTrace()
        }
    }
}

五、动态代理

动态代理:运行时动态的创建代理类(对象),并将方法调用转发到指定类(对象)。
1、类图结构

在这里插入图片描述

2、类图结构说明
1. Proxy和InvocationHandler组合充当代理的角色;
2. RealSubject是一个实际对象,它实现接口Subject;
3. 在使用时,我们不希望直接访问RealSubject的对象,比如:我们对这个对象的访问是有控制的;
4. 使用动态代理,在程序中通过动态代理创建RealSubject,并完成调用;
5. 动态代理可以根据需要,创建多种组合;
6. Proxy也会实现Subject接口的方法,因此,使用Proxy+Invocation可以完成对RealSubject的动态调用;
7. 但是通过Proxy调用RealSubject方法是否成功,是由InvocationHandler来控制的。(这里其实就是保护代理);
8. 理解:创建一个代理对象替代被调用的真实对象,使用反射实现控制。
3、保护代理
	通过前面的分析:大家可以看出动态代理其实就体现出保护代理,即代理时,对被代理的对象(类)的
哪些方法可以调用,哪些方法不能调用在InvocationHandler可以控制。因此动态代理就体现(实现)了保护
代理的效果。
4、动态代理的应用案例

有一个婚恋网项目,女友/男友有个人信息、兴趣爱好和总体评分,要求:

1. 不能自己给自己评分;
2. 其它用户可以评分,但是不能设置信息,兴趣爱好;
3. 请使用动态代理实现保护代理的效果;
4. 分析这里我们需要写两个代理。一个是自己使用,一个是提供给其它用户使用。
5、应用案例示意图

在这里插入图片描述

6、应用案例代码实现

代码结构图:
在这里插入图片描述
示例代码:

================================trait PersonBean================================
package com.lj.akka.dynproxymode
// 这个就是Subject(是一个trait/Java中interface)
trait PersonBean {
    def getName(): String
    def getGender(): String
    def getInterests(): String
    def getScore(): Int
    def setName(name: String)
    def setInterests(interests: String)
    def setScore(score: Int)
    def setGender(gender: String)
}
========================class PersonBeanImpl extends PersonBean========================
package com.lj.akka.dynproxymode
// 这个是要被调用的对象,相当于RealSubject
class PersonBeanImpl extends PersonBean {
    var name: String = _
    var gender: String = _
    var interests: String = _
    var score: Int = _

    override def getName(): String = {
        this.name
    }
    override def getGender(): String = {
        this.gender
    }
    override def getInterests(): String = {
        this.interests
    }
    override def getScore(): Int = {
        this.score
    }
    override def setName(name: String): Unit = {
        this.name = name
    }
    override def setInterests(interests: String): Unit = {
        this.interests = interests
    }
    override def setScore(score: Int): Unit = {
        this.score = score
    }
    override def setGender(gender: String): Unit = {
        this.gender = gender
    }
}
==================class OwnerInvocationHandler extends InvocationHandler==================
package com.lj.akka.dynproxymode
import java.lang.reflect.{InvocationHandler, Method}
// 自己调用的代理,继承java.lang.reflect.InvocationHandler
class OwnerInvocationHandler extends InvocationHandler {
    var person: PersonBean = _
    def this(person: PersonBean) {
        this
        this.person = person
    }
    //说明
    //1.这里的proxy就是和OwnerInvocationHandler合作的代理
    @throws(classOf[Throwable])
    override def invoke(proxy: scala.Any, method: Method, objects: Array[AnyRef]): AnyRef = {
        if (method.getName.startsWith("get")) {
            /**
              * 说明:method.invoke(person, objects)中objects为什么不能传入进去
              * 原因:底层传的是一个可变参数,写上会导致运行不起来,实际在底层它自己就会获取这个objects
              */
            method.invoke(person)
            //自己不能调用setHotOrNotRating,给自己评分
        } else if (method.getName.equals("setScore")) {
            //返回一个异常,同时invoke throws掉了
            new IllegalAccessException("无权限修改自己的分数...")
        } else if (method.getName.equals("set")) {
            method.invoke(person, objects(0).toString)
        } else {
            null
        }
    }
}
==================class NonOwnerInvocationHandler extends InvocationHandler==================
package com.lj.akka.dynproxymode
import java.lang.reflect.{InvocationHandler, Method}
//其它用户调用的代理
class NonOwnerInvocationHandler extends InvocationHandler {
    var person: PersonBean = _
    def this(person: PersonBean) {
        this
        this.person = person
    }
    //说明
    //1.这里的proxy就是和OwnerInvocationHandler合作的代理
    @throws(classOf[Throwable])
    override def invoke(proxy: scala.Any, method: Method, objects: Array[AnyRef]): AnyRef = {
        if (method.getName.startsWith("get")) {
            method.invoke(person)
        } else if (method.getName.equals("setScore")) {
            method.invoke(person, Integer.valueOf(objects(0).toString))
        } else if (method.getName.equals("set")) {
            //其它用户不能调用set方法
            new IllegalAccessException("其他用户无权限修改他/她人的信息...")
        } else {
            null
        }
    }
}
================================class MatchService================================
package com.lj.akka.dynproxymode
import java.lang.reflect.Proxy
class MatchService {
    // 设置第一个人的信息
    val tom = getPersonInfo("tom", "男", "爱好编程")
    // 获取自己的代理
    val ownerProxy = getOwnerProxy(tom)
    // 给自己设置爱好
    ownerProxy.setInterests("爱好淘宝~")
    // 自己给自己设置评分,通过代理控制,不能成功,分数仍然为0
    ownerProxy.setScore(100)
    println(s"第一个人的信息:Name:${ownerProxy.getName()}"  +
        s",Interests:${ownerProxy.getInterests()}" +
        s",setScore:${ownerProxy.getInterests()}")
    println("=====================测试NonOwnerInvocationHandler=====================")
    // 设置第二个人的信息
    val marry = getPersonInfo("marry", "女", "爱好购物...")
    // 获取别人的代理
    val nonOwnerProxy = getNonOwnerProxy(marry)
    // 其它人不能修改兴趣,通过代理进行控制不能调用setInterests
    nonOwnerProxy.setInterests("爱好小猫咪~~")
    nonOwnerProxy.setScore(68)   //其它人可以评分
    println(s"第二个人的信息:Name:${ownerProxy.getName()}"  +
        s",Interests:${ownerProxy.getInterests()}" +
        s",setScore:${ownerProxy.getInterests()}")
    // 设置个人信息
    def getPersonInfo(name: String, gender: String, interests: String): PersonBean = {
        val person = new PersonBeanImpl()
        person.setName(name)
        person.setGender(gender)
        person.setInterests(interests)
        person
    }
    // 获取自己的代理
    def getOwnerProxy(person: PersonBean): PersonBean = {
        Proxy.newProxyInstance(person.getClass().getClassLoader()
            , person.getClass().getInterfaces()
            , new OwnerInvocationHandler(person)).asInstanceOf[PersonBean]
    }
    def getNonOwnerProxy(person: PersonBean): PersonBean = {
        Proxy.newProxyInstance(person.getClass()
            .getClassLoader(), person.getClass().getInterfaces(),
            new NonOwnerInvocationHandler(person)).asInstanceOf[PersonBean]
    }
}
==============================object MainTest============================
package com.lj.akka.dynproxymode
object MainTest {
    def main(args: Array[String]): Unit = {
        new MatchService()
    }
}
=================================运行结果==================================
第一个人的信息:Name:tom,Interests:爱好编程,setScore:爱好编程
=====================测试NonOwnerInvocationHandler=====================
第二个人的信息:Name:tom,Interests:爱好编程,setScore:爱好编程
=================================运行结果==================================
7、几种常见的代理模式介绍— 几种变体
1. 防火墙代理:内网通过代理穿透防火墙,实现对公网的访问;
2. 缓存代理:比如,当请求图片文件等资源时,先到缓存代理取,如果取到资源则ok,如果取不到资源,再到公网
或者数据库取,然后缓存;
3. 静态代理:静态代理通常用于对原有业务逻辑的扩充。比如:
	持有第二方包的某个类,并调用了其中的某些方法;
	记录日志、打印工作等。可以创建一个代理类实现和第二方方法相同的方法,通过让代理类持有真实对象,
	调用代理类方法,来达到增加业务逻辑的目的;
4. Cglib代理:使用cglib[Code Generation Library]实现动态代理,并不要求委托类必须实现接口,底层采用
asm字节码生成框架生成代理类的字节码;
5. 同步代理:主要使用在多线程编程中,完成多线程间同步工作

对以前的知识回顾,加深基础知识!
学习来自:北京尚硅谷韩顺平老师—尚硅谷大数据技术之Scala
每天进步一点点,也许某一天你也会变得那么渺小!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值