动态代理应用实例-婚恋网 | 学习笔记

简介: 快速学习动态代理应用实例-婚恋网

开发者学堂课程【Scala 核心编程 - 进阶动态代理应用实例-婚恋网学习笔记,与课程紧密连接,让用户快速学习知识。

课程地址https://developer.aliyun.com/learning/course/610/detail/9148


动态代理应用实例-婚恋网


内容介绍:

一、动态代理应用案例说明

二、代码实现

三、运行效果

四、总结


一、动态代理的应用案例说明

1. spark 反射

设计一个案例,这段代码在网上是没有的,完全按照 Java 的反射机制不能成功运行,通过看源代码可以发现 spark 的反射跟 Java 的反射有区别,有的地方如果按照Java 反射来写,会显示异常。

2、案例介绍

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

1)不能自己给自己评分

2)其它用户可以评分,但是不能设置信息,兴趣爱好。3)请使用动态代理实现保护代理的效果。

4)分析这里我们需要写两个代理。一个是自己使用,一个是提供给其它用户使用。

5)示意图画出:

image.png

将 Girlfriend 改成 person 比较合适,自己调的时候走代理对象,其他用户也走代理对象来调用方法。


二、代码实现

代码其实并不难,但是实现起来比较麻烦,一共大概有6个文件。

首先根据刚才的设计图,先找到 subject 接口,PersonBean 相当于就是subject,1.是一个 trait 在 Java 中是一个 interface。

具体代码:

package com.atguigu.chapter17.dyn

//这个就是我们的Subject(是一个trait/ Java中interface)

trait PersonBean {

def getName(): string

def getGender(): string

def getInterests(): string

def getscore(): Int

def setName( name: string)

def setGender(gender: string)

def setInterests(interests: string)

def setscore(score : Int)

}

2.PersonBeanImpl 相当于 RealSubject为被调用的对象,具体代

码为:

package com. atguigu.chapter17.dyn

class PersonBeanImpl extends PersonBean {

var name = ""

var gender = ""

var interests = ""

var score : Int = _//评分值

override def setName(): string ={

return name

}

override def setGender(): string = {

Gender

}

override def setInterests(): string ={

interests

}

override def setscore(): Int ={

Score

}

override def setscore(score: Int): Unit = {

this.score = score

}

}

其中代码:

override def setscore(score: Int): Unit = {

this.score = score

表示自己不可以调用,其他用户可以调用评分;

而像:

override def setName(): string ={

return name

}

override def setGender(): string = {

Gender

}

override def setInterests(): string ={

interests

}

override def setscore(): Int ={

Score

}

override def setscore(score: Int): Unit = {

this.score = score

}

}

表示姓名、性别、兴趣等自己可以调,但是别人不可以调用。

3.最核心的地方在于InvocationHandler,具有一定难度应用到了

反射,OwnerInvocationHanler具体代码为:

package com.atguigu.chapter17.dyn

import java.lang.reflect.{InvocationHandler,Method}

//自己调用的代理

class OwnerInvocationHandler extends InvocationHandler {

//被调用的对象为PersonBeanImpl

var person: PersonBean =

//构造器

def this(person: PersonBean){

this

this.person = person

//说明:这里的 proxy 就是和 ownerInvocationHandLer 合作的代理,

下面为是核心代码:

@throws (classof[Throwable])

override def invoke(proxy: scala.Any,method: Method,args:

Array [AnyRef]):AnyRef ={

//首先拿到一个代理,会有代理,有方法以及参数的传入

//下面进行判断,如果是get方法就直接调用

if (method. getName(.startswith("get")) {

return method .invoke(person)

//自己不能调用setHotOrNotRating,给自己评分

}else if (method.getName().equals("setscore")){

//返回一个异常,同时invoke throws掉了

return new IllegalAccessException()

//如果是set方法就直接调用

}else if (method.getName().startswith("set")){

return method . invoke(person,args(o).tostring))

}

null

}

}

在写代理的时候要重新写入方法,然后在里面加上一些列内容,代码为:

override def invoke(proxy: scala.Any,method: Method,args: Array[AnyRef]): AnyRef = {

//}

在进行 return method .invoke(person)代码写入时,遇到了一些问题,在Java调用时是这样调用的:method.invoke(person),会把对象person传进来。

在学反射时最基本的就是方法在前对象在后,参数通过args传进来,这个语法理论上没有任何错误,但是这样书写运行时总会出错,因为这是在Java中的书写方式,在spark里通过查看源代码无法将person调过去,因为这里有一个可变参数,通过可变参数追踪到最底层发现它的底层不能直接传入,在运行时会报错,所以说在这里要通过代码:return method .invoke(person)直接传入person,因此大家要注意这一点区别。

  • 给其他用户调用的代理

给其他用户调用的代理,前面和自己调用的方式一样,代码为:

var person: PersonBean =

//构造器

def this(person: PersonBean){

this

this.person = person

唯一改变的就是下面的代码:

//如果是get方法就直接调用

if(method.getName().startswith("get")){

return method.invoke(person)

//其它用户可以调用setHotorNotRating,进行评分

}else if (method.getName().equals("setscore")) {

return method.invoke(person,Integer. valueof(args(0).tostring))

//其它用户不能调用set方法

}else if (method.getName().startswith("set")){

return new IllegalAccessException()

}

null

表示如果是get也可以调用;如果是setScore也调用,调用方法为传入一个person,然后把传进来的参数转成toString的,即真正传进来的值要转成toString,这样才能接受,因此需要使用valueof(args(0).tostring),将设置的分数转换成字符串;如果他是其他的set方法,则不执行。以上充分体现出代理控制调用方法的核心。


三、运行效果

将影响我们理解的内容进行注销,然后对所需代码进行理解

1、自己调用方法的完整代码

具体代码:package com.atguigu.chapter17.dynimport java.lang.reflect.Proxyclass MatchService {

//创建了一个Person

val tom = getPersonInfo "tom","男","爱好编程")

//得到一个给自己调用的代理对象,替代被调用的对象

val OwnerProxy = getownerProxy(tom)

println("Name is " + Ownerproxy.getName())

println("Interests is " +OwnerProxy.getInterests())

OwnerProxy . setInterests("爱好淘宝~")

println("Interests is " +OwnerProxy.getInterests())

//自己给自己设置评分,通过代理控制,不能成功

Ownerproxy.setscore(100)

printLn("score is " +Ownerproxy.getscore())//分值仍然为0

代码解读:

首先使用 getPersonInfo 创建了一个 person,通过得到一个自己的代理对象,替代被调用的对象,getName 后输出 Tom;getInterets 后输出爱好编程,由于用户进行了修改,改完之后的兴趣爱好变为爱好淘宝;setScore 处刷分不成功,因为在代理处进行了控制,不能给自己刷分,获取的分值默认为0。

运行效果

效果显示为Name is tom;Interests is爱好编程;Interests is爱好淘宝~;score is 0,姓名为Tom,爱好编程变成了爱好淘宝,设置分数失败,但是并不会抛出异常,只是返回的结果为0,实现动态的效果。

2、测试另外一个人的用法

具体代码:

val mary = getPersonInfo( "mary",“女”,“爱好购物..")

val nonOwnerProxy =getNonownerProxy (mary)

println("Name is " + nonOwnerProxy.getName())

println("Interests is " +nonOwnerProxy.getInterests())

//其它人不能修改兴趣,通过代理进行控制不能调setInterests

nonOwnerProxy . setInterests(“爱好小猫咪~~")println("Interests is " +nonownerproxy.getInterests())nonownerProxy.setscore(68)

//其它人可以评分

printtn( "score is " +nonownerProxy .getscore())

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 ={

代码解读:

创建用户Marry,Mary爱好购物,返回其他用户调用的代理对象,名字得到Marry,兴趣爱好也可以得到爱好购物,客户也没问题,但是尝试着修改爱好是不合理的,因为其他用户无法修改别人的爱好,在动态代理中进行控制显示失败,再去获取她的爱好,仍然是爱好购物;调用setScore是成功的,可以对他人进行评分。

运行效果

运行后显示:Name is mary;Interests is爱好购物...;Interests is爱好购物...;score is 68,爱好信息没有变化,分值显示为设定值。


四、总结

通过这个案例说明了动态代理的实现机制,在以后面试的时候,如果面试官问到了动态代理的实现机制,大家对关系图理解清楚就可以了,一般没有人会让你写一段代码,代码是很麻烦的,所以面试官通常会问大家对动态代理的理解。

1、动态代理

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

动态代理调用的机制图

image.png

1.Proxy 和 InvocationHandler 组合充当代理的角色;

2.RealSubject 是一个实际对象,它实现接口 Subject;

3.在使用时,我们不希望直接访问 RealSubject 的对象,比如:我们对这个对象的访问是有控制的;

4.我们使用动态代理,在程序中通过动态代理创建 RealSubject,并完成调用.

5.动态代理可以根据需要,创建多种组合;

6.Proxy也会实现Subject接口的方法,因此,使用 Proxy+Invocation可以完成对RealSubject的动态调用;

7.但是通过Proxy调用RealSubject方法是否成功,是由 InvocationHandler来控制的;(这里其实就是保护代理)

8.理解:创建一个代理对象替代被调用的真实对象,使用反射实现控制

2、应用案例说明

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

1)不能自己给自己评分

2)其它用户可以评分,但是不能设置信息,兴趣爱好。3)请使用动态代理实现保护代理的效果。

4)分析这里我们需要写两个代理。一个是自己使用,一个是提供给其它用户

使用。

5)示意图画出:

image.png

3、代码实现

相关文章
|
传感器 监控 物联网
FastBond2阶段2——基于ESP32C3开发的简易IO调试设备
FastBond2阶段2——基于ESP32C3开发的简易IO调试设备
280 0
|
存储 缓存 Linux
free命令详解
`free`命令在Linux中显示内存使用详情,包括总内存(`total`)、已用(`used`,含缓存`buffers/cache`)、空闲(`free`)、共享(`shared`)和可用(`available`)内存。交换空间显示其总量、使用量和剩余量。`-h`选项以易读格式显示,`-m`以MB显示,`-t`显示总和,`-s`定时刷新。例如,`free -ht 5`每5秒更新内存和交换空间的总览。
436 3
|
存储 Java 数据安全/隐私保护
【JVM】Java虚拟机栈(Java Virtual Machine Stacks)
【JVM】Java虚拟机栈(Java Virtual Machine Stacks)
223 0
|
4月前
|
关系型数据库 MySQL
MySQL数据表添加字段(三种方式)
本文解析了数据表的基本概念及字段添加方法。在数据表中,字段是纵向列结构,记录为横向行数据。MySQL通过`ALTER TABLE`指令支持三种字段添加方式:1) 末尾追加字段,直接使用`ADD`语句;2) 首列插入字段,通过`FIRST`关键字实现;3) 指定位置插入字段,利用`AFTER`指定目标字段。文内结合`student`表实例详细演示了每种方法的操作步骤与结构验证,便于理解与实践。
|
前端开发 Shell 容器
前端练习小项目——视觉冲击卡片
前端练习小项目——视觉冲击卡片
|
机器学习/深度学习 数据采集 监控
人脸识别与检测
人脸识别与检测
275 4
|
算法 JavaScript 前端开发
国标非对称加密:RSA算法、非对称特征、js还原、jsencrypt和rsa模块解析
国标非对称加密:RSA算法、非对称特征、js还原、jsencrypt和rsa模块解析
968 1
|
传感器 机器学习/深度学习 人工智能
多模态大模型
多模态大模型
750 1
|
关系型数据库 MySQL 数据库
【已解决】[图文步骤] message from server: “Host ‘172.17.0.1‘ is not allowed to connect to this MySQL server“
【已解决】[图文步骤] message from server: “Host ‘172.17.0.1‘ is not allowed to connect to this MySQL server“
486 0