接住喽,送你个装逼技能:JDK 动态代理

用户头像
码农神说
关注
发布于: 2020 年 07 月 05 日
接住喽,送你个装逼技能:JDK动态代理

今天讲一个比较深层的知识点:JDK动态代理,这是个可以让小白在大咖面前装逼的神器,顺便送你一个代理模式的温习机会。

代理模式场景



为了引出动态代理的用法,我们先看看代理设计模式,这能让你了解JDK动态代理的应用场景的同时,让你记忆深刻。代理模式是通过代理对象作为中间人来访问目标对象,这样可以完美的遵循开闭原则,从而避免修改目标对象来满足需求而降低可维护性。



现实生活中比较常见的各种中间商、经纪人都是活生生的代理模式例子。咱们拿明星经纪人来说





上述关系现实中还是非常复杂的,比如什么干爹啦、劈腿啦、潜规则啦,巴拉巴拉的.....剥茧抽丝后,其实流程可简化这样





老板不直接接触明星,而是直接和经纪人商谈,毕竟经纪人无论在经验和精力上都有优势。这里经纪人其实就是代理对象,明星就是目标对象,老板就是调用方。转换为代码流程如下





这里需要认真的强调一点:代理模式侧重于控制访问,代理对象不会改变目标对象的职责和能力,它提供与目标对象相同的接口,但会增加相应的逻辑来控制访问目标对象。



有些网友提出代理模式是为了在目标对象的基础上增强功能,如果较真的话小编并不不认同此种说法,因为增强基类的功能那是装饰模式干的活;代理模式和委托模式也有区别,后者是为了提供统一的接口服务,便于方便切换底层实现。



如果大家对设计模式感兴趣,可以留言给 码农神说 提出“设计模式系列专题”的文章需求。



代理模式实现

代理模式的演示实现如下(为了方便观众观看,我会把代码集中在App.java中,项目中不允许)



public class App
{
public static void main( String[] args )
{
//代理对象(经纪人)
Broker broker = new Broker();
System.out.println("A老板的唱歌演出需求");
broker.doSing();
System.out.println("---------------");
System.out.println("B老板的跳舞演出需求");
broker.doDance();
}
}
/**
* 经纪人
*/
class Broker{
/**
* 唱歌演出
*/
public void doSing(){
//演出前业务处理
System.out.println("1.检查节目是否和明星的调性匹配");
System.out.println("2.出场费是否满足");
//演出
new Star().doSing();
//演出后业务处理
System.out.println("1.出场费尾款");
System.out.println("2.粉丝维护");
}
/**
* 跳舞演出
*/
public void doDance(){
//演出前业务处理
System.out.println("1.检查节目是否和明星的调性匹配");
System.out.println("2.出场费是否满足");
//演出
new Star().doDance();
//演出后业务处理
System.out.println("1.出场费尾款");
System.out.println("2.粉丝维护");
}
}
/**
* 明星
*/
class Star{
/**
* 唱歌技能
*/
public void doSing(){
System.out.println("唱歌");
}
/**
* 跳舞技能
*/
public void doDance(){
System.out.println("跳舞");
}
}



老板不直接接触明星,而是通过经纪人满足业务需求。

细心的同学会发现,经纪人的业务处理中存在大量重复代码,当然你可以把演出前后的业务封装成方法调用如

/**
* 唱歌演出
*/
public void doSing(){
//演出前业务处理
beforeDo();
//演出
new Star().doSing();
//演出后业务处理
afterDo();
}



但依然不美,试想如果能把经纪人的业务技能直接一一匹配到明星的技能(doSing,doDance)就方便了,于是引出了JDK动态代理。



JDK动态代理重构

  1. 为了实现“动态”需要使用面向接口的编程思想,把明星和经纪人抽象出一个共同的接口



/**
* 明星和经纪人的接口
*/
interface IAct {
void doSing();
void doDance();
}
  1. 通过实现InvocationHandler接口来做代理业务

/**
* 经纪人
*/
class Broker implements InvocationHandler {
//目标对象
private Object target;
public Broker(Object target){
this.target=target;
}
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
//演出前业务处理
System.out.println("1.检查节目是否和明星的调性匹配");
System.out.println("2.出场费是否满足");
//明星演出技能
Object object=method.invoke(target,args);
//演出后业务处理
System.out.println("1.出场费尾款");
System.out.println("2.粉丝维护");
return object;
}
}
  1. 动态创建代理对象并处理业务

public static void main( String[] args ){
//目标对象
IAct star=new Star();
//使用newProxyInstance创建动态代理对象
IAct broker=(IAct) Proxy.newProxyInstance(
IAct.class.getClassLoader(),
star.getClass().getInterfaces(),
new Broker(star)
);
//业务处理
System.out.println("A老板的唱歌演出需求");
broker.doSing();
System.out.println("---------------");
System.out.println("B老板的跳舞演出需求");
broker.doDance();
}

经纪人执行doSing,经过业务逻辑处理后直接映射到明星的doSing,这样就减少了很多重复的代码,提高了可维护性。



总结JDK实现代理模式流程如下

1. 抽象出目标对象的接口

2. 实现接口InvocationHandler创建代理业务

3. 使用newProxyInstance创建代理对象

4. 业务处理



全部代码如下

package com.zhaiqianfeng;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class App
{
public static void main( String[] args ){
//目标对象
IAct star=new Star();
//使用newProxyInstance创建动态代理
IAct broker=(IAct) Proxy.newProxyInstance(
IAct.class.getClassLoader(),
star.getClass().getInterfaces(),
new Broker(star)
);
//业务处理
System.out.println("A老板的唱歌演出需求");
broker.doSing();
System.out.println("---------------");
System.out.println("B老板的跳舞演出需求");
broker.doDance();
}
}
/**
* 明星和经纪人的接口
*/
interface IAct {
void doSing();
void doDance();
}
/**
* 经纪人
*/
class Broker implements InvocationHandler {
//目标对象
private Object target;
public Broker(Object target){
this.target=target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//演出前业务处理
System.out.println("1.检查节目是否和明星的调性匹配");
System.out.println("2.出场费是否满足");
//明星演出技能
Object object=method.invoke(target,args);
//演出后业务处理
System.out.println("1.出场费尾款");
System.out.println("2.粉丝维护");
return object;
}
}
/**
* 明星
*/
class Star implements IAct {
/**
* 唱歌技能
*/
public void doSing(){
System.out.println("唱歌");
}
/**
* 跳舞技能
*/
public void doDance(){
System.out.println("跳舞");
}
}

Java8 表达式的实现

如果只是临时业务处理,可以使用匿名类或Java8的lambda表达式可以更优

public static void main(String[] args) {
//目标对象
IAct star = new Star();
//使用newProxyInstance创建动态代理
IAct broker = (IAct) Proxy.newProxyInstance(
IAct.class.getClassLoader(),
star.getClass().getInterfaces(),
(proxy, method, args2) -> {
//演出前业务处理
System.out.println("1.检查节目是否和明星的调性匹配");
System.out.println("2.出场费是否满足");
//明星演出技能
Object object = method.invoke(star, args2);
//演出后业务处理
System.out.println("1.出场费尾款");
System.out.println("2.粉丝维护");
return object;
}
);
//业务处理
System.out.println("A老板的唱歌演出需求");
broker.doSing();
System.out.println("---------------");
System.out.println("B老板的跳舞演出需求");
broker.doDance();
}



写在最后



JDK动态代理实际上是在运行时通过反射的方式来实现的,将代理的方法调用转到到目标对象上,最终将目标对象生成的任何结果返回给调用方。由于这是个链式调用,所以很方便代理在目标对象方法调用前后增加处理逻辑。根据这种思路可以在多种设计模式中使用JDK的动态代理比如代理模式、Facade、Decorator等。



在面向方面编程(AOP)也应用广泛,如事务管理、日志记录、数据校验等,主要是将横切关注点从业务逻辑中分离出来,所以一通百通。



最后补充一点,由于JDK的不断优化,到JDK8的时候JDK的动态代理不比CGLIB效率低,大家可以做些实验。



最新、更多漫画请关注微信公众号:码农神说。转载开白可联系助手,微信号:codeceo-01



相关阅读



发布于: 2020 年 07 月 05 日 阅读数: 134
用户头像

码农神说

关注

欢迎关注公众号【码农神说】 2018.11.09 加入

图解码农技术,大话码农故事,漫画感悟码农人生,助力码农翻身!

评论

发布
暂无评论
接住喽,送你个装逼技能:JDK动态代理