代理模式

代理模式是常见设计模式的一种,代理模式的定义是:为其他对象提供一种代理以控制对这个对象的访问

在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

静态代理

理解设计模式是比较枯燥的,所以还是以举例子的方式来进行理解,

例如:公司开年会想找个明星来表演,那么并不会直接联系明星(主要还是联系不上),而是会联系明星的经纪人,明星就是被代理的对象,而经纪人就是代理对象。明星只需要准备来参加年会时应该表演什么节目就可以,其他的出场费之类的事情就交给经纪人来处理就好了。代理对象可以理解为被代理对象的扩展,能做被代理对象不能做的事情,也可以调用代理对象做事情。

那么用代码实现这个场景是什么样子的呢?

执行合作方法的接口

/**
* @Description: 经纪公司接口,代理对象和被代理对象都需要实现的接口
*/
public interface Company {
/** 合作 */
void cooperation();
}

被代理对象

/**
* @Description: 目标对象-明星(被代理对象)
*/
public class Start implements Company { @Override
public void cooperation() {
System.out.println("is show time");
}
}

代理对象

/**
* @Description: 经纪人(代理对象)
*/
public class Agent implements Company { private Company company; public Agent(Company company)
{
this.company = company;
} @Override
public void cooperation()
{
System.out.println("收出场费,化妆等等");
company.cooperation();
System.out.println("收拾行李,打道回府");
}
}

测试类

import org.junit.Test;

/**
* @Description: 测试类
*/
public class ProxyTest { @Test
public void AnnualMeeting()
{
//目标对象
Start start = new Start();
//构建代理对象,生成代理关系
Agent agent = new Agent(start);
//用代理对象执行被代理对象的动作
agent.cooperation(); } }

输出结果:

收出场费,化妆等等
is show time
收拾行李,打道回府

静态代理的特点是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。有时候不方便修改别人的代码或者是引入的一个功能,需要进行功能扩展一下才能适用于自己的业务实现,可以使用代理模式来进行设计。

但是静态代理的实现基础是一个目标对象对应一个代理对象,并且在编译时就已经维护好了代理关系,如果目标对象是多个那么就会需要多个代理对象,这样在更新目标的对象的时候还需要更新代理对象,当代理对象持续增加时维护成本就变得非常困难。

针对于这种情况,动态代理应运而生。

动态代理

JDK代理

动态代理的代理对象不需要和目标对象共同实现接口,而是利用JDK的API,动态的在内存中构建代理对象。

动态生成代理对象需要调用JDK中的java.lang.reflect.Proxy类的newProxyInstance方法,这个方法需要三个参数:

@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,]Class<?>[] interfaces,InvocationHandler h)
ClassLoader loader:类加载器,用来加载目标对象类,因为是在运行时获得目标对象,所以肯定需要用到反射。
Class<?>[] interfaces:目标对象类实现的接口集合,这些接口中定义目标对象可以执行的方法。
InvocationHandler h:这个参数代表的是动态代理对象在调用方法的时候,会将方法转发到哪一个invocationHandler对象身上,InvocationHandler是个接口,
需要自己实现它,然后定义自己的动态代理执行方法。
创建包含动态代理对象具体执行方法的实现类。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; /**
* @Description: 包含动态代理对象具体执行方法的实现类
*/
public class MyInvocationHandler implements InvocationHandler { private Company company; public MyInvocationHandler(Company company)
{
this.company = company;
} @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{ System.out.println("收出厂费,化妆等");
//具体执行方法
Object result = method.invoke(company,args); System.out.println("收拾现场,卸妆,打道回府"); return result;
}
}

测试类

import org.junit.Test;
import java.lang.reflect.Proxy; /**
* @Description: 测试类
*/
public class DynamicProxyTest { @Test
public void AnnualMeeting()
{
//创建目标对象
Company start = new Start();
//创建代理对象需要执行的方法处理对象
MyInvocationHandler myInvocationHandler = new MyInvocationHandler(start);
//获得目标对象的类加载器
ClassLoader classLoader = start.getClass().getClassLoader();
//创建动态代理对象
Company proxy = (Company) Proxy.newProxyInstance(classLoader,start.getClass().getInterfaces(),myInvocationHandler);
//用动态代理对象执行目标对象的方法
proxy.cooperation();
}
}

输出结果:

收出厂费,化妆等
is show time
收拾现场,卸妆,打道回府

JDK动态代理的特点:代理对象不需要实现接口,但是目标对象必须实现接口。

那么如果在实际的业务中目标对象确实没有实现接口,怎么办呢?

遇到这种情况的时候就需要时cglib动态代理了。

Cglib代理

Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.

  • JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现。
  • Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)。
  • Cglib包的底层是通过使用一个小块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

在实现cglib代理时需要引入cglib的jar包,但是spring核心功能已经包含了cglib的功能,所以引入spring-core的jar包就可以了。

需要注意的是:代理的类不能为final,否则报错,目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法。

没有实现接口的目标对象类

/**
* @Description: 没有经纪公司的明星,就行像最近以个人练习生出道的蔡徐坤
*/
public class AloneStart {
/** 合作 */
public void cooperation() {
System.out.println("is show time");
} }

生成Cglib代理对象的类

import org.mockito.cglib.proxy.Enhancer;
import org.mockito.cglib.proxy.MethodInterceptor;
import org.mockito.cglib.proxy.MethodProxy;
import java.lang.reflect.Method; /**
* @Description: 生成代理对象的类
*/
public class CglibProxy implements MethodInterceptor { private AloneStart aloneStart; public CglibProxy(AloneStart aloneStart)
{
this.aloneStart = aloneStart;
} /**
* 创建代理对象
* @return
*/
public Object getProxyInstance()
{
//动态代理工具类
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(aloneStart.getClass());
//设置回调函数调用对象
enhancer.setCallback(this);
//返回代理对象
return enhancer.create(); } @Override
public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable
{
System.out.println("收出厂费,化妆等");
//执行代理方法
methodProxy.invokeSuper(obj,objects);
System.out.println("卸妆,回家");
return null;
}
}

测试类

import org.junit.Test;

/**
* @Description: 测试类
*/
public class CglibProxyTest { @Test
public void cglibTest()
{
//创建目标对象
AloneStart aloneStart = new AloneStart();
//创建代理对象
AloneStart startProxy = (AloneStart) new CglibProxy(aloneStart).getProxyInstance();
//用代理对象执行目标对象的方法
startProxy.cooperation();
}
}

输出结果:

收出厂费,化妆等
is show time
卸妆,回家

jdk采用反射机制调用委托类的方法,而cglib采用类似索引的方式直接调用委托类方法;

还有需要注意的是:

在Spring的AOP中

如果加入容器的目标对象有实现接口,用JDK代理
如果目标对象没有实现接口,用Cglib代理

参考:

Java的三种代理模式: https://www.cnblogs.com/cenyu/p/6289209.html

说说代理模式:http://www.importnew.com/26116.html

Java设计模式学习记录-代理模式的更多相关文章

  1. Java设计模式学习记录-模板方法模式

    前言 模板方法模式,定义一个操作中算法的骨架,而将一些步骤延迟到子类中.使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤. 模板方法模式 概念介绍 模板方法模式,其实是很好理解的,具体 ...

  2. Java设计模式学习记录-状态模式

    前言 状态模式是一种行为模式,用于解决系统中复杂的对象状态转换以及各个状态下的封装等问题.状态模式是将一个对象的状态从该对象中分离出来,封装到专门的状态类中,使得对象的状态可以灵活多变.这样在客户端使 ...

  3. Java设计模式学习记录-备忘录模式

    前言 这次要介绍的是备忘录模式,也是行为模式的一种 .现在人们的智能手机上都会有备忘录这样一个功能,大家也都会用,就是为了记住某件事情,防止以后自己忘记了.那么备忘录模式又是什么样子的呢?是不是和手机 ...

  4. Java设计模式学习记录-迭代器模式

    前言 这次要介绍的是迭代器模式,也是一种行为模式.我现在觉得写博客有点应付了,前阵子一天一篇,感觉这样其实有点没理解透彻就写下来了,而且写完后自己也没有多看几遍,上次在面试的时候被问到java中的I/ ...

  5. Java设计模式学习记录-解释器模式

    前言 这次介绍另一个行为模式,解释器模式,都说解释器模式用的少,其实只是我们在日常的开发中用的少,但是一些开源框架中还是能见到它的影子,例如:spring的spEL表达式在解析时就用到了解释器模式,以 ...

  6. Java设计模式学习记录-命令模式

    前言 这次要介绍的是命令模式,这也是一种行为型模式.最近反正没有面试机会我就写博客呗,该投的简历都投了.然后就继续看书,其实看书也会给自己带来成就感,原来以前不明白的东西,书上已经给彻底的介绍清楚了, ...

  7. Java设计模式学习记录-外观模式

    前言 这次要介绍的是外观模式(也称为门面模式),外观模式也属于结构型模式,其实外观模式还是非常好理解的,简单的来讲就是将多个复杂的业务封装成一个方法,在调用此方法时可以不必关系具体执行了哪些业务,而只 ...

  8. Java设计模式学习记录-桥接模式

    前言 这次介绍结构型设计模式中的第二种模式,桥接模式. 使用桥接模式的目的就是为了解耦,松散的耦合更利于扩展,但是会增加相应的代码量和设计难度. 桥接模式 桥接模式是为了将抽象化与实现化解耦,让二者可 ...

  9. Java设计模式学习记录-建造者模式

    前言 今天周末,有小雨,正好也不用出门了,那就在家学习吧,经过了两周的面试,拿到了几个offer,但是都不是自己很想去的那种,要么就是几个人的初创小公司,要么就是开发企业内部系统的这种传统开发,感觉这 ...

随机推荐

  1. hdu 4920

    http://acm.hdu.edu.cn/showproblem.php?pid=4920 给定两个n阶矩阵,求矩阵相乘后模3. 直接搞肯定会超时 特殊处理1和2的情况 实际上是水过的..... 貌 ...

  2. 64位进程调用32位dll的解决方法

    64位进程调用32位dll的解决方法   最近做在Windows XP X64,VS2005环境下做32位程序编译为64位程序的工作,遇到了一些64位编程中可能遇到的问题:如内联汇编(解决方法改为C/ ...

  3. 分形之二叉树(Binary Tree)

    上一篇文章讲的是分形之树(Tree),这一篇中将其简化一下,来展示二叉分形树的生长过程. 核心代码: static void FractalBinaryTree(const Vector3& ...

  4. Python——面向对象(初级篇)

    1.如何创建类 class 类名: pass 2.如何创建方法 构造方法: __init__ obj = 类名('a1') 普通方法: obj = 类名('xxx') obj.普通方法名() 3.图解 ...

  5. Python 学习第三部分函数——第一章函数基础

    函数是python 为了代码最大程度的重用和最小代码冗余而提供的最基本的程序结构.使用它我们可以将复杂的系统分解为可管理的部件. 函数相关语句 def...          创建一个对象并将其赋值给 ...

  6. .NET MVC 学习笔记(四)— 基于Bootstarp自定义弹出框

    .NET MVC 学习笔记(四)—— 基于Bootstarp自定义弹出框 转载自:https://www.cnblogs.com/nele/p/5327380.html (function ($) { ...

  7. delphi datetimepicker 修改时间无效问题

    今天用delphi写个程序,使用datetimepicker获得想要的时间.蛋疼的问题是无论怎么调整明明看着控件里面的日期变了,但是show出来的datetimepicker.datetime日期都不 ...

  8. 阻止事件冒泡,阻止默认事件,event.stopPropagation()和event.preventDefault(),return false的区别

    1.event.stopPropagation()方法 这是阻止事件的冒泡方法,不让事件向documen上蔓延,但是默认事件任然会执行,当你掉用这个方法的时候,如果点击一个连接,这个连接仍然会被打开, ...

  9. [学习笔记]CDQ分治和整体二分

    序言 \(CDQ\) 分治和整体二分都是基于分治的思想,把复杂的问题拆分成许多可以简单求的解子问题.但是这两种算法必须离线处理,不能解决一些强制在线的题目.不过如果题目允许离线的话,这两种算法能把在线 ...

  10. Storm入门示例

    开发Storm的第一步就是设计Topology,为了方便开发者入门,首先我们设计一个简答的例子,该例子的主要的功能就是把每个单词的后面加上Hello,World后缀,然后再打印输出,整个例子的Topo ...