laravel 最简单的文件上传

PTA甲级 1063 Set Similarity (C++)

  返回  

JAVA静态/动态代理

2021/8/20 13:42:23 浏览:

JAVA

1、静态代理

1.1、为什么要代理

当我们需要添加与业务无关的操作时,一般不会改动业务代码,而是使用代理。

也就是寻找第三方来帮助我们实现我们需要实现的功能,同时添加新的操作。

比如当我们找工作时,我们会寻找中介。

我本来只需要找一份工作就好了(原本业务),但是我现在想到要在找工作的时候记录找工作的时间并且(新的日志需求)。

我---->公司

我并不会去要求招聘者记录工作时间(改动实现类),同样我也不想记录(改动控制类)。那么我需要一个人(代理)来帮我在找工作的时候自动记录时间。

我---->中介(代理)---->公司

由中介(代理)来记录时间,我一样只需要完成找工作的任务就行了,公司也只需要正常招人(不需要改动原有代码)。

1.2、静态代理

静态代理很简单,我们只需要创建一个新的类,在它的内部生成一个我们需要的类的实例,由他来代替我们操作就好了

角色分析

  • 抽象角色:一般为接口或者抽象类
  • 真实角色:一般为被代理的角色
  • 代理角色:代理真实角色,代理真实角色后做一些附属操作
  • 客户:访问代理对象的人

接下来我们只要一一实现上面的四个类就行了

  • 抽象角色

    • 我们只需要创建一个方法就好了,我们需要实现的方法是找招聘(公司招聘员工)

    • public interface Recruit {
          public void hiring();
      }
      
  • 真实角色

    • 这时候我们要去实现上一个方法,实现的人是公司

    • 公司---实现-->招聘

    • public class Company implements Recruit {
          @Override
          public void hiring() {
              System.out.println("面试中");
          }
      }
      
  • 代理角色

    • 代理角色要代理公司实行面试(当然面试还是由公司进行,但是会做一些附加操作)

    • import java.util.Date;
      
      public class StaticProxy implements Recruit{
      
          private Company company;
      		//设置company既可以用有参构造也可以用set
        	public void setCompany(Company company) {
              this.company = company;
          }
      
          public StaticProxy(Company company) {
              this.company = company;
          }
        
          @Override
          public void hiring() {
      
              Date date = new Date();
              System.out.println("记录时间:" + date.toString());
      
              //还是由公司执行面试
              //原有业务不变
              company.hiring();
          }
      }
      
  • 客户

    • 此时我们只需要正常调用我们的代理角色来进行操作就可以了

    • (一般而言实际开发中是控制层但是因为要方便测试所以直接写在main函数中)

    • public class User {
          public static void main(String[] args) {
      
              Recruit sp = new StaticProxy(new Company());
              sp.hiring();
          }
      }
      
      //执行结果
      //记录时间:Fri Aug 20 12:21:25 CST 2021
      //面试中
      

这样静态代理就完成了,当我们需要添加一些业务无关的操作是,我们可以用静态代理来实现,就不用改动原有代码。

但是有个问题是如果许多业务都需要添加同一个代理,比如都需要记录时间时,就需要写很多个静态代理,一个类中有20个方法就需要将20个方法都添加一个静态代理。

此时,为了减少代码,加入了动态代理。

1.3、动态代理

动态代理有一个标准的类,几乎不需要任何改动,只要在invoke中修改需要执行的操作就可以了

先上代码,再慢慢解释

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyIvocationHandler implements InvocationHandler {

  //创建一个对象,此时无所谓我们的对象是什么,只要是一个类就可以了
    private Object target;  
  
  //设置我们的类
    public void setTarget(Object target) {
        this.target = target;
    }
    
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //在方法运行之前
        System.out.println("before running");


        //运行方法时
        Object result = method.invoke(target,args);

        //在方法运行在之后
        System.out.println("after running");

        if(result!=null)
            System.out.println("result:"+result.toString());
        return null;
    }
}

当创建类时继承InvocationHandler,会要求实现invoke方法,也就是当程序中的方法执行时自动唤醒的方法

将上面的几个方法拆开来看

//创建需要实现的对象,比如我们要在公司招聘的时候添加日志此时对象就是公司
//通过set方法传入一个公司就行了
private Object target;
public void setTarget(Object target);

//获取代理对象,当通过getProxy获取了代理对象之后就可以使用代理来实现原有功能
public Object getProxy();

//invoke方法,在程序方法被唤醒时调用的函数
public Object invoke(Object proxy, Method method, Object[] args);

getProxy()方法

在这里插入图片描述

查看方法所需要的参数,一个loader,一个方法类,一个InvocationHandler

loader和InvocationHandler直接用自身的方法就可以了,具体代码里面有

而中间的方法类就是我们之前传入的类target,上方setTarget();target要传实现类,不然没有具体的实现类仅仅代理方法类也没有办法实现业务。

而此处需要的是一个方法类那么通过target.getClass().getInterfaces()来得到target的方法类。

此方法代码几乎不需要变动

invoke()方法

invoke的参数在你重写方法时会自动填充,不需要管,调用方法的也是java的虚拟机,不需要手动调用。

动态代理的重点就在于当你的代理执行方法时,会调用invoke方法,而原来的方法在invoke中被调用

原方法
我--调用方法-->company.method---系统执行--->company.method

动态代理
我--调用方法-->proxy.method
  --系统执行-->invoke
  invoke其中的操作-->日志																					 									        -->company.method
  
  @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //在方法运行之前
        System.out.println("before running");


        //运行方法时
  			//如果将这一行注释掉当你使用代理执行方法时方法也不会被执行
  			//这一行就是唤醒方法本身
  			//result就是方法的返回值,如果没有返回值就是null
        Object result = method.invoke(target,args);

        //在方法运行在之后
        System.out.println("after running");

        return null;
    }
  

主程序

public class User {
    public static void main(String[] args) {

        //创建一个动态代理类的对象
        ProxyIvocationHandler test = new ProxyIvocationHandler();

        //设置target为Company实现类,接下来直接获得proxy代理就可以使用了
        //同样对于不同的类,只要将setTarget传入所需的类就可以动态生成代理类
        test.setTarget(new Company());
        Recruit hr = (Recruit) test.getProxy();
        hr.hiring();
    }
}

//执行结果
//before running
//面试中
//after running

如果需要记录方法的返回值,在invoke方法中直接打印result就可以了,

但是要注意有些方法返回值是null,所以记得判断当返回值不为空时再打印,否则会报错。

@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //在方法运行之前
        System.out.println("before running");

        //运行方法时
        Object result = method.invoke(target,args);
      
      	//打印返回值
				if(result!=null)
            System.out.println("result:"+result.toString());
         else
            System.out.println("result:null");
      
        //在方法运行在之后
        System.out.println("after running");

        return null;
    }

联系我们

如果您对我们的服务有兴趣,请及时和我们联系!

服务热线:18288888888
座机:18288888888
传真:
邮箱:888888@qq.com
地址:郑州市文化路红专路93号