Java * 静态代理实例分析
作者:谭宝贵 发布时间:2023-11-14 18:06:07
代理模式:为其他对象提供一种代理以控制某个对象的访问。用在:在某些情况下,一个客户不想或者不能直接访问另一个对象,而代理对象可以在客户端和目标对象之前起到中介的作用,代理对象还可以完成它附加的操作。
例子:就像房东、租客、中介的关系。中介(代理对象)为房东(真实对象)出租房子,租客(客户)通过中介(代理对象)来找房子租房子,中介完成了租房以后可以收取中介费(附加操作)。
先看看静态代理模式,通过上面对代理模式的理解,可以了解到代理模式:即不直接通过new一个真实对象来调用方法,而是通过代理对象来调用一个方法,所以代理对象包含真实对象的引用。下面看一下代码
接口:Subject包含一个方法
package com.example.designpattern.proxy;
public interface Subject {
void request();
}
RealSubject类,实现了Subject接口,为了简单起见,方法简单的输出一句话:
package com.example.designpattern.proxy;
public class RealSubject implements Subject {
//真是角色实现了
public void request() {
System.out.println("From real subject");
}
}
代理类ProxySubject,也要实现Subject接口,实现Subject里面的方法,但是在这里里面是通过调用真实对象来实现的。
package com.example.designpattern.proxy;
public class ProxySubject implements Subject {
private RealSubject realSubject; //代理角色内部引用了真实角色
//代理角色实现目标动作
public void request() {
this.preRequest(); //在真实角色操作之前所附加的操作
if (realSubject == null){
realSubject = new RealSubject();
}
realSubject.request(); // 真实角色所完成的事情
this.afterRequet(); //在真实角色操作之后附加的操作
}
//代理角色之前完成的动作
private void preRequest(){
System.out.println("pre request");
}
//代理角色之后完成的动作
private void afterRequet(){
System.out.println("after request");
}
}
客户调用者
package com.example.designpattern.proxy;
public class Client {
public static void main(String[] args) {
ProxySubject proxy = new ProxySubject();
//通过代理对象来调用方法
proxy.request();
}
}
静态代理:
可以运行一下这些代码哦, 可以在Client类中看到,是通过代理ProxySubject的对象proxy来调用方法的,在代理类ProxySubject中,有一个真实对象的引用,在代理对象的中request()方法调用了真实对象的方法。这样的模式叫做代理模式。
优点是:
1. 代理模式能将代理对象与真实对象被调用的目标对象分离。
2. 一定程度上降低了系统的耦合度,扩展性好。
代理类中包含了对真实主题的引用,这样做也有缺点:
1. 真实对象与代理类一一对应,增加真实类也要增加代理类,这样做会快速的增加类的数量,使得系统变得复杂。
2. 设计代理以前真实主题必须事先存在,不太灵活。
采用 * 可以解决以上问题, * 是相对于静态代理来说的。
可能你也会说怎么样实现动态创建实例,以前我们创建实例不都是通过new 的方式来实现的吗?
Hello hi = new Hello();
那么动态创建实例是由Java提供的功能,就不需要我们去new 对象,他已经定义好了静态方法Proxy.newProxyInstance(),只要传入参数调用就可以。Java文档里面有哦,如图:
Java标准库提供了一种 * (DynamicProxy)的机制:可以在运行期动态创建某个interface的实例。
参数解释:
Proxy.newProxyInstance(
ClassLoader loader, // 传入ClassLoader
Class<?>[] interfaces, // 传入要调用的接口的方法数组
InvocationHandler h); //传入InvocationHandler 的实例
下面看一下 * 例子代码:
Subject 接口
package design.dynamicproxy;
public interface Subject {
void request(String str);
}
RealSubject类 实现 Subject 接口
package design.dynamicproxy;
public class RealSubject implements Subject {
@Override
public void request(String str) {
System.out.println("From Real Subject!" + " args:" + str );
}
}
* 类DynamicSubject 实现了InvocationHandler,重写invoke()方法
package design.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 该代理类的内部属性时Object类型,实际使用时,使用该类的构造方法传递一个对象
* 此外该类还实现了invoke() 方法,该方法中的method.invoke() 其实就是要调用被代理对象的要执行的方法
* 方法参数是object,表示该方法从属于object对象,通过 * 类,我们可以在执行真是对象的
* 方法前后可以加入一些额外的方法
*/
public class DynamicSubject implements InvocationHandler {
//引入的类型是Object的,可以随便传入任何一个对象
private Object object;
public DynamicSubject(Object object){
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before calling:" + method);
//等价于realSubject的request() 方法,如果这里不调用的话,不会调用Method 对象中的方法
method.invoke(object, args);
System.out.println("after calling:" + method);
return null;
}
}
Client类
package design.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
InvocationHandler handler = new DynamicSubject(realSubject);
Class<?> classType = handler.getClass();
//下面的代码一次性生成代理
// 动态生成了class com.sun.proxy.$Proxy0 的实例,
Subject subject = (Subject) Proxy.newProxyInstance(classType.getClassLoader(), realSubject.getClass().getInterfaces(),handler);
subject.request("eather");
System.out.println(subject.getClass());
//输出class com.sun.proxy.$Proxy0,可以看到Proxy.newProxyInstance() 是系统自动生成的实例
}
}
在Client中可以看到,我们这里调用方法的是 subject.request("eather"); 这个对象subject 不是通过new DynamicSubject()生成的,而是Java内部写好的方法在运行时动态生成对象;可能有人说
InvocationHandler handler = new DynamicSubject(realSubject);
这里不是通过new new DynamicSubject(realSubject); 生成了一个对象吗? 是的,但是它是InvocationHandler 类型的,主要是传递一个InvocationHandler类型参数给Proxy.newProxyInstance(); 即最后一个参数。通过Client类的最后一句输出可以看到它是 class com.sun.proxy.$Proxy0 ,这是Java运行时生成的。
解决了静态代理的难题:1. 真实对象与代理类一一对应,增加真实类也要增加代理类,这样做会快速的增加类的数量,使得系统变得复杂。 为什么这么说呢, 因为代理类引用的类型是Object的,可以随便传入任何一个对象,当真实类增加时,代理类不用增加,new DynamicSubject(object); new的时候把要传入的对象传进去即可。
下面是Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h); 这个方法的源码啦,可以看看,深入了解一下
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
生成一个代理类对象
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
使用指定的调用处理程序调用其构造函数。就是使用InvocationHandler 实例调用【要调用方法的那个类】的构造方法
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
}
);
}
return cons.newInstance(new Object[]{
h
}
);
}
catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
}
catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
}
catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
来源:https://www.cnblogs.com/eathertan/p/12457862.html


猜你喜欢
- 一、泛型简介1.引入泛型的目的了解引入泛型的动机,就先从语法糖开始了解。语法糖语法糖(Syntactic Sugar),也称糖衣语法,是由英
- springboot配置templates直接访问springboot下的templates目录的资源默认是受保护的,类似于javaweb项
- 本文实例讲述了Java基于解释器模式实现定义一种简单的语言功能。分享给大家供大家参考,具体如下:一 模式定 * 释器模式:就是给定一个语言的文
- 公钥与私钥公钥与私钥是成对的,一般的,我们认为的是公钥加密、私钥解密、私钥签名、公钥验证,有人说成私钥加密,公钥解密时不对的。公钥与私钥的生
- 如下所示:using System;using System.Collections.Generic;using System.Diagno
- 什么是TaskScheduler?SynchronizationContext是对“调度程序(scheduler)&am
- 使用注解开发在Spring4之后,要使用注解开发,必须要保证aop的包导入了使用注解需要导入context的约束,增加注解的支持<?x
- 一个系统上线,肯定会或多或少的存在异常情况。为了更快更好的排雷,记录请求参数和响应结果是非常必要的。所以,Nginx 和 Tomcat 之类
- 1. 只有public的property能显示出来,可以通过BrowsableAttribute来控制是否显示,通过CategoryAttr
- 系统: lc android4.4 在做前后摄像头录像的时候,发现会出现花屏的时候,但不是必现,可能会在某一次重启之后会出现,而且出现之后会
- 本文实例为大家分享了Android刮刮卡效果控件,供大家参考,具体内容如下刮刮卡类: package com.reyo.view;impor
- Filter学习Filter功能拦截jsp、静态图片文件、静态html资源文件实现URL级别的权限访问控制过滤敏感词汇压缩相应信息Filte
- 前言 SpringCloud 是微服务中的翘楚,最佳的落地方案。 在微服务架构中多层服务之间会相互调用,如果其中有一
- 在Android中,Activity主要负责前台页面的展示,Service主要负责需要长期运行的任务,所以在我们实际开发中,就会常常遇到Ac
- 本文实例为大家分享了Android实现ListView下拉刷新上拉加载更多的具体代码,供大家参考,具体内容如下其实谷歌官方目前已经推出Lis
- 用java实现的数组创建二叉树以及递归先序遍历,递归中序遍历,递归后序遍历,非递归前序遍历,非递归中序遍历,非递归后序遍历,深度优先遍历,广
- 本文实例为大家分享了Unity Shader实现动态过场切换图片的具体代码,供大家参考,具体内容如下一、简单介绍Shader Languag
- 本文实例演示了visual C#下一个类的定义及实现方法,虽然是一个较为基础的C#代码实例,对于新手来说仍然有很好的参考价值。具体的实例代码
- 一、线程的优先级别线程优先级别的使用范例:package cn.galc.test;public class TestThread6 { p
- 一、reservedcodecachesize参数介绍该参数是JvM虚拟机调优中调整内存大小的一个设置参数,值得大小设置直接影响到Code