博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
设计模式-代理模式
阅读量:4163 次
发布时间:2019-05-26

本文共 11101 字,大约阅读时间需要 37 分钟。

文章目录

定义

为其他对象提供一种代理以控制对这个对象的访问

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

作用

不改变原来的代码,而增强原类对象的功能,主要是做方法的增强!!!可以选择前置、后置、环绕、异常处理增强

应用场景

Spring AOP、Spring事务管理…

静态代理

由程序员创建或由特定工具自动生成代理类源代码,再对其编译。在程序运行之前,就知道需要对哪一个类进行代理,代理类的.class文件就已经存在

类图

在这里插入图片描述
土豪TuHao通过中间经纪人BrokerProxy约会苍老师TeacherCangImpl

Girl接口类:

public interface Girl {
/** * 约会 * @param length 身高 * @return */ boolean dating(float length);}

苍老师实现类:

public class TeacherCangImpl implements Girl {
@Override public boolean dating(float length) {
if (length >= 1.7F) {
System.out.println("身高可以,可以约!"); } System.out.println("身高不可以,不可约!"); return false; }}

代理类:

public class BrokerProxy implements Girl{
/** * 经纪人必须持有女孩子 */ private Girl girl; public Girl getGirl() {
return girl; } public void setGirl(Girl girl) {
this.girl = girl; } @Override public boolean dating(float length) {
System.out.println("静态代理模式:前置增强,老板,这个我试过了,很不错,推荐给你!"); boolean flag = this.getGirl().dating(length); System.out.println("静态代理模式:后置增强,老板,你觉得怎样,欢迎下次再约!"); return flag; }}

土豪类:

public class TuHao {
private float length; public TuHao(float length) {
this.length = length; } public void dating(Girl girl) {
girl.dating(length); }}

测试类:

@Controller@RequestMapping("/proxy")public class ProxyTestController {
/** * @return */ @RequestMapping("/statical.json") @ResponseBody public String statical() {
TuHao th = new TuHao(1.7F); Girl tc = new TeacherCangImpl(); //土豪通过经纪人约会苍老师 BrokerProxy proxy = new BrokerProxy(); proxy.setGirl(tc); th.dating(proxy); return "proxy statical success"; }}

缺点

  • 横向扩展(代理更多的类)、纵向扩展(增强更多的方法)能力差
  • 可维护性差
  • 只能代理已知的类,无法代理运行时的类

动态代理

代理类在程序运行时,运用反射机制动态创建而成。

静态代理事先知道代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道

JDK动态代理

JDK动态代理只可对接口创建代理

核心类介绍

  • Proxy类:提供了用于创建动态代理类和实例对象的方法,它是所创建的动态代理类的父类,它最常用的方法如下

    • public static Class<?> getProxyClass(ClassLoader loader,Class<?>… interfaces):该方法用于返回一个Class类型的代理类,在参数中需要提供类加载器并需要指定代理的接口数组(与真实主题类的接口列表一致)
    • public static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces, InvocationHandler h):该方法用于返回一个动态创建的代理类的实例,方法中第一个参数loader表示代理类的类加载器,第二个参数interfaces表示代理类所实现的接口列表(与真实主题类的接口列表一致),第三个参数h表示所指派的调用处理程序类
  • InvocationHandler接口:代理处理程序类的实现接口,该接口作为代理实例的调用处理者的公共父类,每一个代理类的实例都可以提供一个相关的具体调用处理者(InvocationHandler接口的子类)。在该接口中声明了如下方法

    • public Object invoke(Objectproxy, Method method, Object[] args):该方法用于处理对代理类实例的方法调用并返回相应的结果,当一个代理实例中的业务方法被调用时将自动调用该方法。invoke()方法包含三个参数,其中第一个参数proxy表示代理类的实例,第二个参数method表示需要代理的方法,第三个参数args表示代理方法的参数数组

动态代理类需要在运行时指定所代理真实主题类的接口,客户端在调用动态代理对象的方法时,调用请求会将请求自动转发给InvocationHandler对象的invoke()方法,由invoke()方法来实现对请求的统一处理。


类图

在这里插入图片描述
代码示例
被代理的接口(人):

public interface IPepole {
/** * 是人都要吃饭 */ void eat(); /** * 是人都有职业 */ void career();}

被代理的接口实现类(教师):

public class TeacherService implements IPepole {
@Override public void eat() {
System.out.println("TeacherService eat"); } @Override public void career() {
System.out.println("TeacherService career"); }}

被代理的接口(学生):

public class StudentService implements IPepole {
@Override public void eat() {
System.out.println("StudentService eat"); } @Override public void career() {
System.out.println("StudentService career"); }}

功能增强实现类:

public class JDKInvocationHandler implements InvocationHandler {
private Object object; public JDKInvocationHandler(Object object) {
this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("jdk动态代理调用之前的处理"); method.invoke(object, args); System.out.println("jdk动态代理调用之后的处理"); return null; }}

创建代理对象的类:

public class JDKProxyInstanceFactory {
public static Object proxy(Object target) {
return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new JDKInvocationHandler(target) ); }}

测试类:

@Controller@RequestMapping("/proxy")public class ProxyTestController {
/** * JDK动态代理测试 * JDK动态代理只能为接口创建代理实例 * @return */ @RequestMapping("/jdkDynamicProxy.json") @ResponseBody public String jdkDynamicProxy() {
IPepole teacher = (IPepole) JDKProxyInstanceFactory.proxy(new TeacherService()); teacher.eat(); teacher.career(); IPepole student = (IPepole) JDKProxyInstanceFactory.proxy(new StudentService()); student.eat(); student.career(); return "jdk dynamic proxy success"; }}

测试结果:

jdk动态代理调用之前的处理TeacherService eatjdk动态代理调用之后的处理jdk动态代理调用之前的处理TeacherService careerjdk动态代理调用之后的处理jdk动态代理调用之前的处理StudentService eatjdk动态代理调用之后的处理jdk动态代理调用之前的处理StudentService careerjdk动态代理调用之后的处理

生成代理类

jdk动态代理类是动态生成的.class,会缓存在内存中。除此之外,我们也可以自己手动生成.class文件到本地,方式如下(主要使用JDK自带的ProxyGenerator方法):

byte[] data = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{
IPepole.class});try {
FileOutputStream out = new FileOutputStream("D:\\$Proxy0.class"); out.write(data); out.close();} catch (FileNotFoundException e) {
e.printStackTrace();} catch (IOException e) {
e.printStackTrace();}

生成后的代码反编译结果:

import com.dalomao.spring.aop.Pepole;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.lang.reflect.UndeclaredThrowableException;public final class $Proxy0 extends Proxy  implements Pepole{
private static Method m1; private static Method m4; private static Method m2; private static Method m3; private static Method m0; public $Proxy0(InvocationHandler paramInvocationHandler) throws {
super(paramInvocationHandler); } public final boolean equals(Object paramObject) throws {
try {
return ((Boolean)this.h.invoke(this, m1, new Object[] {
paramObject })).booleanValue(); } catch (RuntimeException localRuntimeException) {
throw localRuntimeException; } catch (Throwable localThrowable) {
} throw new UndeclaredThrowableException(localThrowable); } public final void eat() throws {
try {
this.h.invoke(this, m4, null); return; } catch (RuntimeException localRuntimeException) {
throw localRuntimeException; } catch (Throwable localThrowable) {
} throw new UndeclaredThrowableException(localThrowable); } public final String toString() throws {
try {
return (String)this.h.invoke(this, m2, null); } catch (RuntimeException localRuntimeException) {
throw localRuntimeException; } catch (Throwable localThrowable) {
} throw new UndeclaredThrowableException(localThrowable); } public final void sleep() throws {
try {
this.h.invoke(this, m3, null); return; } catch (RuntimeException localRuntimeException) {
throw localRuntimeException; } catch (Throwable localThrowable) {
} throw new UndeclaredThrowableException(localThrowable); } public final int hashCode() throws {
try {
return ((Integer)this.h.invoke(this, m0, null)).intValue(); } catch (RuntimeException localRuntimeException) {
throw localRuntimeException; } catch (Throwable localThrowable) {
} throw new UndeclaredThrowableException(localThrowable); } static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
Class.forName("java.lang.Object") }); m4 = Class.forName("com.dalomao.spring.aop.Pepole").getMethod("eat", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m3 = Class.forName("com.dalomao.spring.aop.Pepole").getMethod("sleep", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); return; } catch (NoSuchMethodException localNoSuchMethodException) {
throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) {
} throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); }}

CGLIB动态代理

CGLIB动态代理可对接口、具体类创建代理

CGLIB是一个高层次的java字节码生成和转换的api库。常在AOP、ORM框架中用来生成动态代理对象、拦截属性访问

学习网址:

maven中可以引入:

cglib
cglib
3.2.6

类图

在这里插入图片描述
代码实例
功能增强实现类:

public class CglibMethodInterceptor implements MethodInterceptor {
private Object target; public CglibMethodInterceptor(Object target) {
this.target = target; } @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//前置增强 System.out.println("cglib interfrace before"); Object res = null;//返回值 //调用父类的该方法,如果是生成接口的代理时不可调用 //res = methodProxy.invokeSuper(proxy, args); //通过method来调用被代理对象的方法 if (this.target != null) {
res = method.invoke(target, args); } //后置增强 System.out.println("cglib interfrace after"); return null; }}

测试类:

@Controller@RequestMapping("/proxy")public class ProxyTestController {
/** * cglib动态代理测试 * cglib可以为接口,也可以为实现类创建代理实例 * @return */ @RequestMapping("/cglibDynamicProxy.json") @ResponseBody public String cglibDynamicProxy() {
/** * 为接口创建代理 */ Enhancer enhancer1 = new Enhancer(); enhancer1.setCallback(new CglibMethodInterceptor(new StudentService()));//设置增强回调 enhancer1.setInterfaces(new Class[]{
IPepole.class});//对接口生成代理对象 IPepole proxy1 = (IPepole) enhancer1.create(); proxy1.eat(); proxy1.career(); /** * 为实体类创建代理 */ Enhancer enhancer2 = new Enhancer(); enhancer2.setCallback(new CglibMethodInterceptor(new TeacherService())); enhancer2.setSuperclass(TeacherService.class);//对类生成代理对象 enhancer2.setInterfaces(null); //当有多个callback时,需要通过callbackFilter来指定被代理方法使用第几个callback /* enhancer2.setCallbackFilter(new CallbackFilter() { @Override public int accept(Method method) { return 0; } }); */ TeacherService proxy2 = (TeacherService) enhancer2.create(); proxy2.eat(); proxy2.career(); return "cglib dynamic proxy success"; }}

结果:

cglib interfrace beforeStudentService eatcglib interfrace aftercglib interfrace beforeStudentService careercglib interfrace aftercglib interfrace beforeTeacherService eatcglib interfrace aftercglib interfrace beforeTeacherService careercglib interfrace after

总结

  • 通过使用动态代理,我们可以实现对多个真实主题类的统一代理和集中控制。
  • 主要用来做方法的增强,让你可以在不修改源码的情况下,增强一些方法,在方法执行前后做任何你想做的事情(甚至根本不去执行这个方法),因为在InvocationHandler的invoke方法中,你可以直接获取正在调用方法对应的Method对象,具体应用的话,比如可以添加日志,做事务控制等

转载地址:http://cwpxi.baihongyu.com/

你可能感兴趣的文章
Xcode 11 报错,提示libstdc++.6 缺失,解决方案
查看>>
vue项目打包后无法运行报错空白页面
查看>>
Vue 解决部署到服务器后或者build之后Element UI图标不显示问题(404错误)
查看>>
element-ui全局自定义主题
查看>>
facebook库runtime.js
查看>>
js报错显示subString/subStr is not a function
查看>>
高德地图js API实现鼠标悬浮于点标记时弹出信息窗体显示详情,点击点标记放大地图操作
查看>>
vue项目使用安装sass
查看>>
在osg场景中使用GLSL语言——一个例子
查看>>
laravel 修改api返回默认的异常处理
查看>>
laravel事务
查看>>
【JavaScript 教程】浏览器—History 对象
查看>>
这才是学习Vite2的正确姿势!
查看>>
7 个适用于所有前端开发人员的很棒API,你需要了解一下
查看>>
隐藏搜索框:CSS 动画正反向序列
查看>>
【视频教程】Javascript ES6 教程27—ES6 构建一个Promise
查看>>
【5分钟代码练习】01—导航栏鼠标悬停效果的实现
查看>>
127个超级实用的JavaScript 代码片段,你千万要收藏好(中)
查看>>
127个超级实用的JavaScript 代码片段,你千万要收藏好(下)
查看>>
Flex 布局教程:语法篇
查看>>