浅谈代理-动态代理

  created  by  鱼鱼 {{tag}}
创建于 2020年10月13日 18:38:20 最后修改于 2020年10月13日 18:38:20

    我们可以很轻松的实现一个简单的代理。

静态代理

    实现静态代理是个很简单的事情,最基础的代理只需要定义一个接口(虽然不是必要,但这显然才是标准的设计)、一个被代理类和一个代理类,例如:

定义一个接口:

public interface HelloWorldInterface{
    public void hello();
}

一个实现类:

public interface HelloWorld extends HelloWorldInterface{
    public void hello(){
        System.out.println("Do something here!");
    }
}

和一个代理类:

public interface HelloWorldProxy extends HelloWorldInterface{
        HelloWorld helloWorld;

        public HelloWorldProxy (HelloWorld helloWorld) {
            super();
            this.helloWorld= helloWorld;
        }
    public void hello(){
            System.out.println("Before hello!");
            helloWorld.hello();
            System.out.println("After hello!");
    }
}

    实际使用时,我们是去调用HelloWorldProxy的方法,其将作为HelloWorld的代理实现。

    此种方式直接实现的代理太过于死板,因为每一种代理行为都要制定一个代理类,我们熟知的很多基于代理的实现(譬如AOP、事务)显然不可能用静态代理的方式针对每一处类切点都覆写一个代理类,这种时候就需要动态代理。

动态代理

    我们所熟知的相当多的框架均基于动态代理开发,JDK本身基于反射(java.lang.reflect)提供了动态代理,我们只需定义代理的行为,而对于代理类的范围并不是固定值。核心接口是InvocationHandler,我们通过对其的实现来定义实际的代理行为,调用被代理类只需要invoke方法。并且可以选择调用0~n个被代理的方法。依旧以上面的代理为例,我们假设已经定义了相应的接口和实现类(实现类可以没有也可以是多个),对于代理行为的定义时这样的。

定义代理行为:

public class HelloWorldProxy extends InvocationHandler{
    HelloWorld helloWorld;

     public HelloWorldProxy (HelloWorld helloWorld) {
        super();
        this.helloWorld= helloWorld;
    }
    @Override
    public void invoke(Object proxy, Method method, Object[] args){
            System.out.println("Before "+ method.getName());
            method.invoke(helloWorld,args);
            System.out.println("After "+ method.getName());
            return null;    //非void直接按method.invoke的调用结果返回
    }
}

由于InvocationHandler其实是函数式接口,可以用lamdba表达式实现。

当我们想要调用代理时,需要指定其 类加载器,代理的接口数组,和代理行为类的实例:

HelloWorld helloWorld= new HelloWorld ();
HelloWorldProxy helloWorldProxy = new HelloProxy(helloWorld);
Class[] clazzList = {HelloWorldInterface.class};
//没有自定义的  类加载器的话直接使用线程上下文的就好,总之都是一个系统  类加载器
HelloWorldInterface proxyHello = (HelloWorldInterface)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), clazzList , helloWorldProxy);
//调用代理方法,从执行结果看等同于调用了HelloWorldProxy的invoke
proxyHello.hello();

如果你懒于定义InvocationHandler的实现,直接使用lamdba即可,当然这种时候我们限定住了可传的被代理实例:

HelloWorldInterface proxyHello = (HelloWorldInterface)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), clazzList , 
 (Object proxy, Method method, Object[] args)->{
            System.out.println("Before "+ method.getName());
            //这一行可以没有,也可以是继承于clazzList的任一实现,本质上 只要能反射执行,就不会报错
            method.invoke(helloWorld,args);
            System.out.println("After "+ method.getName());
            return null;    //非void直接按method.invoke的调用结果返回
});
proxyHello.hello();

可以预见到基于此,AOP的思路大概是这样的:

Class[] clazza = new Class[0];
try {
    clazza = new Class[]{Class.forName("com.xhl.autosite.common.Hello")};
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}
Xxx bean = (Xxx)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), clazzList , 
 (Object proxy, Method method, Object[] args)->{
            System.out.println("Before "+ method.getName());
            //method.invoke(xxx);  这一行可能是没有的,有的话是从bean中获取的
            System.out.println("After "+ method.getName());
            return null;
});
//实际执行的是动态代理类的方法了
bean.xxx();


评论区
评论
{{comment.creator}}
{{comment.createTime}} {{comment.index}}楼
评论

浅谈代理-动态代理

浅谈代理-动态代理

    我们可以很轻松的实现一个简单的代理。

静态代理

    实现静态代理是个很简单的事情,最基础的代理只需要定义一个接口(虽然不是必要,但这显然才是标准的设计)、一个被代理类和一个代理类,例如:

定义一个接口:

public interface HelloWorldInterface{
    public void hello();
}

一个实现类:

public interface HelloWorld extends HelloWorldInterface{
    public void hello(){
        System.out.println("Do something here!");
    }
}

和一个代理类:

public interface HelloWorldProxy extends HelloWorldInterface{
        HelloWorld helloWorld;

        public HelloWorldProxy (HelloWorld helloWorld) {
            super();
            this.helloWorld= helloWorld;
        }
    public void hello(){
            System.out.println("Before hello!");
            helloWorld.hello();
            System.out.println("After hello!");
    }
}

    实际使用时,我们是去调用HelloWorldProxy的方法,其将作为HelloWorld的代理实现。

    此种方式直接实现的代理太过于死板,因为每一种代理行为都要制定一个代理类,我们熟知的很多基于代理的实现(譬如AOP、事务)显然不可能用静态代理的方式针对每一处类切点都覆写一个代理类,这种时候就需要动态代理。

动态代理

    我们所熟知的相当多的框架均基于动态代理开发,JDK本身基于反射(java.lang.reflect)提供了动态代理,我们只需定义代理的行为,而对于代理类的范围并不是固定值。核心接口是InvocationHandler,我们通过对其的实现来定义实际的代理行为,调用被代理类只需要invoke方法。并且可以选择调用0~n个被代理的方法。依旧以上面的代理为例,我们假设已经定义了相应的接口和实现类(实现类可以没有也可以是多个),对于代理行为的定义时这样的。

定义代理行为:

public class HelloWorldProxy extends InvocationHandler{
    HelloWorld helloWorld;

     public HelloWorldProxy (HelloWorld helloWorld) {
        super();
        this.helloWorld= helloWorld;
    }
    @Override
    public void invoke(Object proxy, Method method, Object[] args){
            System.out.println("Before "+ method.getName());
            method.invoke(helloWorld,args);
            System.out.println("After "+ method.getName());
            return null;    //非void直接按method.invoke的调用结果返回
    }
}

由于InvocationHandler其实是函数式接口,可以用lamdba表达式实现。

当我们想要调用代理时,需要指定其 类加载器,代理的接口数组,和代理行为类的实例:

HelloWorld helloWorld= new HelloWorld ();
HelloWorldProxy helloWorldProxy = new HelloProxy(helloWorld);
Class[] clazzList = {HelloWorldInterface.class};
//没有自定义的  类加载器的话直接使用线程上下文的就好,总之都是一个系统  类加载器
HelloWorldInterface proxyHello = (HelloWorldInterface)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), clazzList , helloWorldProxy);
//调用代理方法,从执行结果看等同于调用了HelloWorldProxy的invoke
proxyHello.hello();

如果你懒于定义InvocationHandler的实现,直接使用lamdba即可,当然这种时候我们限定住了可传的被代理实例:

HelloWorldInterface proxyHello = (HelloWorldInterface)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), clazzList , 
 (Object proxy, Method method, Object[] args)->{
            System.out.println("Before "+ method.getName());
            //这一行可以没有,也可以是继承于clazzList的任一实现,本质上 只要能反射执行,就不会报错
            method.invoke(helloWorld,args);
            System.out.println("After "+ method.getName());
            return null;    //非void直接按method.invoke的调用结果返回
});
proxyHello.hello();

可以预见到基于此,AOP的思路大概是这样的:

Class[] clazza = new Class[0];
try {
    clazza = new Class[]{Class.forName("com.xhl.autosite.common.Hello")};
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}
Xxx bean = (Xxx)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), clazzList , 
 (Object proxy, Method method, Object[] args)->{
            System.out.println("Before "+ method.getName());
            //method.invoke(xxx);  这一行可能是没有的,有的话是从bean中获取的
            System.out.println("After "+ method.getName());
            return null;
});
//实际执行的是动态代理类的方法了
bean.xxx();



浅谈代理-动态代理2020-10-13鱼鱼

{{commentTitle}}

评论   ctrl+Enter 发送评论