当前位置: 首页 > news >正文

Java基础:代理

这里写目录标题

    • 什么是代理
      • 1.静态代理(委托类、代理类):
        • 使用步骤:
        • 示例
        • 优缺点
      • 2.动态代理(委托类、中介类)
        • 2.1 JDK动态代理
          • 使用:
          • 中介类:
          • 示例1:
          • 示例2:
        • 2.2 CGLib动态代理
          • 使用方法:
          • 目标类(原始类)不能为final
          • 示例1:
          • 示例2:

什么是代理

代理模式是一种设计模式,简单说即是在不改变源码的情况下,实现对目标对象的功能扩展。


比如有个歌手对象叫Singer,这个对象有一个唱歌方法叫sing()

1 public class Singer{
2     public void sing(){
3         System.out.println("唱一首歌");
4     }  
5 }

假如你希望对目标对象Singer的sing方法进行功能扩展,例如在唱歌前后向观众问好和答谢,类似这样:

1 public void sing(){
2     System.out.println("向观众问好");
3     System.out.println("唱一首歌");
4     System.out.println("谢谢大家");
5 }  

但是又不能直接对源代码进行修改,甚至有可能你都不知道要对哪个目标对象进行扩展。这时就需要用到java的代理模式了。

1.静态代理(委托类、代理类):

静态代理要求原始类有实现某个接口。
需要创建一个代理类,实现和原始类相同的接口,并在需要增强的方法里,调用原始类的该方法,调用前后加上我们需要添加的代码。
使用的时候,直接创建一个代理类实例,调用目标方法即可。

使用步骤:

共同接口

public interface Action {public void doSomething();
}

原始类

public class RealObject implements Action{public void doSomething() {System.out.println("do something");}
}

代理类

public class Proxy implements Action {private Action realObject; public Proxy(Action realObject) {this.realObject = realObject;}public void doSomething() {System.out.println("proxy do");realObject.doSomething();}
}

使用

Proxy proxy = new Proxy(new RealObject());
proxy.doSomething();

示例

public interface ISinger {void sing();
}/***  目标对象实现了某一接口*/
public class Singer implements ISinger{public void sing(){System.out.println("唱一首歌");}  
}/***  代理对象和目标对象实现相同的接口*/
public class SingerProxy implements ISinger{// 接收目标对象,以便调用sing方法private ISinger target;public UserDaoProxy(ISinger target){this.target=target;}// 对目标对象的sing方法进行功能扩展public void sing() {System.out.println("向观众问好");target.sing();System.out.println("谢谢大家");}
}

测试类:

/*** 测试类*/
public class Test {public static void main(String[] args) {//目标对象ISinger target = new Singer();//代理对象ISinger proxy = new SingerProxy(target);//执行的是代理的方法proxy.sing();}
}

总结:这里做的事情无非就是,创建一个代理类SingerProxy,继承原始类的ISinger接口,实现其中的方法,并在实现中调用目标对象的方法。这里的关键是“调用目标对象方法”,如果直接重写就不叫代理了。

优缺点

优点:扩展原功能,不侵入原代码。

缺点:
①冗余。由于代理对象要实现与目标对象一致的接口,会产生过多的代理类。
②不易维护。代理对象必须提前写出,一旦接口发生了变化,代理对象的代码也要进行维护。

2.动态代理(委托类、中介类)

代理类在程序运行时运用反射机制创建的代理方式被成为动态代理。
也就是说,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。
相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。

2.1 JDK动态代理

同样要求原始类实现某个接口,但不用手动创建代理类,而是创建中介类。中介类实现InvocationHandler接口。

使用:

调用Proxy类中的newProxyInstance(ClassLoader loader,Class<?>[]
interfaces,InvocationHandler h)方法以创建一个动态代理对象,其中第三个参数为我们创建的实现InvocationHandler接口的类(中介类),前两个参数可通过目标类.getclass().getxxx获取。

中介类:

需实现InvocationHandler接口,包含一个Object类型的对象,并利用其编写中介类的有参构造函数。重写的方法:public Object invoke(Object proxy, Method method, Object[] args) throws Throwable里,proxy表示代理类对象, method标识了我们具体调用的代理类的方法,args为这个方法的参数。

示例1:
public interface ISinger {void sing();
}/***  目标对象实现了某一接口*/
public class Singer implements ISinger{public void sing(){System.out.println("唱一首歌");}  
}-------------------------public class Test{public static void main(String[] args) {Singer target = new Singer();//这行要自己写ISinger proxy  = (ISinger) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("向观众问好");//目标对象方法前后编写需要扩展的代码Object returnValue = method.invoke(target, args);System.out.println("谢谢大家");return returnValue;}});proxy.sing();}
}
示例2:

public static void main(String[] args) throws InterruptedException {EnHello enHello=new EnHello();Hello hello=(Hello)Proxy.newProxyInstance(enHello.getClass().getClassLoader(),enHello.getClass().getInterfaces(), new MyInvocationHandler(enHello));hello.sayHello("Tom");}interface Hello{String sayHello(String username);
}static class EnHello implements Hello{@Overridepublic String sayHello(String username) {System.out.println("Hello, "+username);return "finished";}
}static class MyInvocationHandler implements InvocationHandler{private Object object;public MyInvocationHandler(Object object){this.object=object;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object result=null;System.out.println("before say hello");if("sayHello".equals(method.getName())){result=method.invoke(object,args);}System.out.println("before say hello");return result;}
}

还可以只为指定方法动态代理,在invoke方法加上以下判断:

String methodName = method.getName();
if("eating".equals(methodName))method.invoke(obj,args);

优点一:可以隐藏委托类的实现;
优点二:可以实现客户与委托类间的解耦,在不修改委托类代码的情况下能够做一些额外的处理

2.2 CGLib动态代理

JDK动态代理和cglib动态代理有什么区别?

使用JDK动态代理的对象必须实现一个或多个接口
使用cglib代理的对象则无需实现接口。

cglib可以对任意类生成代理对象,它的原理是对目标对象进行继承代理,所以如果目标对象被final修饰,那么该类无法被cglib代理。

使用方法:

导包-创建MethodInterceptor实现类

使用cglib需要引入cglib的jar包,如果你已经有spring-core的jar包,则无需引入,因为spring中包含了cglib。

目标类(原始类)不能为final

目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法

示例1:
/*** 目标对象,没有实现任何接口*/
public class Singer{public void sing() {System.out.println("唱一首歌");}
}----------------------/*** Cglib子类代理工厂*/
public class ProxyFactory implements MethodInterceptor{// 维护目标对象private Object target;public ProxyFactory(Object target) {this.target = target;}// 给目标对象创建一个代理对象public Object getProxyInstance(){//1.工具类Enhancer en = new Enhancer();//2.设置父类en.setSuperclass(target.getClass());//3.设置回调函数en.setCallback(this);//4.创建子类(代理对象)return en.create();}/*
使用时只有intercept方法中,代码行 method.invoke前后的代码需要修改,其他的代码直接使用
*/@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("向观众问好");//执行目标对象的方法Object returnValue = method.invoke(target, args);System.out.println("谢谢大家");return returnValue;}
}-----------------------/*** 测试类*/
public class Test{public static void main(String[] args){//目标对象Singer target = new Singer();//代理对象Singer proxy = (Singer)new ProxyFactory(target).getProxyInstance();//执行代理对象的方法proxy.sing();}
}
示例2:
public class TestCglib implements MethodInterceptor {    Object target;   //动态生成一个新的类,使用父类的无参构造方法创建一个指定了特定回调的代理实例    public Object getProxyObject(Object object) {      this.target = object;       //增强器,动态代码生成器     Enhancer enhancer=new Enhancer();        //回调方法enhancer.setCallback(this); //设置生成类的父类类型        enhancer.setSuperclass(target.getClass());   //动态生成字节码并返回代理对象      return enhancer.create();   }public Object intercept(Object o, Method method, Object[] objects, MethodProxy 
methodProxy) throws Throwable {System.out.println("----------before");    // 调用方法      Object result = methodProxy.invoke(target, objects);   System.out.println("----------after");       return null;  }
}//使用
public static void main(String[] args) {Boss boss=(Boss) new TestCglib().getProxyObject(new Boss());boss.eating();boss.sleeping();
}
http://www.lryc.cn/news/161282.html

相关文章:

  • 每日一学——防火墙2
  • Web学习笔记-React(组合Components)
  • 【strstr函数的介绍和模拟实现——超详细版】
  • 【Terraform】Terraform自动创建云服务器脚本
  • TCP机制之确认应答及超时重传
  • Openharmony3.2 源码编译(ubuntu 22.04) 过程记录
  • PostgreSQL 数据库使用 psql 导入 SQL
  • 容器编排学习(三)端口映射与Harber镜像仓库介绍
  • Day_13 > 指针进阶(2)
  • 对Transformer中的Attention(注意力机制)的一点点探索
  • 车内信息安全技术-安全技术栈-软件安全
  • Redis常见命令
  • Android Studio实现一笔画完小游戏
  • 【Python 程序设计】数据人员入门【02/8】
  • 学习笔记——树上哈希
  • Opencv快速入门教程,Python计算机视觉基础
  • laravel 报错误信息 Carbon\Exceptions\InvalidFormatException
  • UI自动化之混合框架
  • SQL创建用户-非DM8.2环境(达梦数据库)
  • Thread类中run和start的区别
  • ElementUI浅尝辄止35:Checkbox 多选框
  • 讲讲如何用IDEA开发java项目——本文来自AI创作助手
  • Kafka3.0.0版本——消费者(Range分区分配策略以及再平衡)
  • WeiTools
  • 目标检测数据集:医学图像检测数据集(自己标注)
  • 【系统设计系列】数据库
  • mp4压缩视频不改变画质?跟我这样压缩视频大小
  • AQS同步队列和等待队列的同步机制
  • vue3实现无限循环滚动的方法;el-table内容无限循环滚动的实现
  • Windows 安装 MariaDB 数据库