Java反射(三)反射与代理设计模式

代理设计模式的思想,即用一个代理类实现为被代理类添加额外的工作,在实际开发中可能是日志记录、耗时计算等。

1.代理模式必不可少的元素:被代理接口、被代理类、代理类(组合被代理类);

2.一般可以使用两种方式实现代理:(1)静态代理;(2)动态代理。

3.还有一个可以直接对类进行代理,不需要被代理接口,即为CGLib。


 

1.静态代理

静态代理很容易理解,代理类与被代理类都实现同一个接口,代理类将被代理类组合为自己的属性,在调用接口方法时,添加额外的功能。举例如下:

  • 被代理接口:IMessage
  • 被代理类:MessageReal
  • 代理类:MessageProxy,其中包含对代理对象的额外处理。
interface IMessage{
    void send(String msg);
}

/**
 * 被代理类
 */
class MessageReal implements  IMessage{

    public void send(String msg){
        System.out.println("发送消息");
    }
}

/**
 * 代理类
 */
class MessageProxy implements  IMessage{

    private IMessage message;

    public MessageProxy(IMessage message){
        this.message = message;
    }

    /**
     * 代理后的处理方法
     * @param msg
     */
    public void send(String msg){
        if(connect()){
            message.send(msg);
            close();
        }
    }

    private boolean connect(){
        System.out.println("创建连接");
        return  true;
    }

    private void close(){
        System.out.println("关闭连接");
    }
}

 静态代理特点:每个代理类只能代理一个被代理类。如果开发中存在上千个类需要代理,这种方式就不适合了,可以采用动态代理的方式。

2.动态代理

上文中说代理必备的元素:被代理接口、被代理类、代理类,在动态代理中也是必不可少的。关键元素:

  • java.lang.reflect.Proxy,代理类,其通过生成代理字节码实现代理,具体底层实现请关注后文。
  • java.lang.reflect.InvocationHandler ,辅助代理类添加额外的功能,由用户自定义实现,执行具体方法时,进行回调。

(1)Proxy

重要的静态方法是newProxyInstance方法,该方法返回的即为代理类,定义如下:

public static Object newProxyInstance​(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)

 loader - 定义代理类的ClassLoader(the class loader to define the proxy class),实现与被代理类使用相同的类加载器

interfaces - 被代理接口(the list of interfaces for the proxy class to implement)

h - 代理回调处理类(the invocation handler to dispatch method invocations to),自定义的InvocationHandler类,一般会包含被代理类

(2)InvocationHandler 

 重要方法invoke(),处理代理类方法调用并返回结果,该方法在关联的代理类调用方法时会被调用。

Object invoke​(Object proxy,Method method,Object[] args)throws Throwable

proxy - 调用该方法的代理类,一般程序中不使用

method - 被调方法的Method对象

args - 被调方法的参数。

(3)动态代理实例

设计个网络代理NetProxy,实现InvocationHandler接口,添加代理类处理逻辑。

  • 被代理接口:target.getClass().getInterfaces()
  • 被代理类:Object target
  • 代理类:Proxy.newProxyInstance
class NetProxy implements InvocationHandler{

    private Object target;//被代理类,Object可以代理任何类型的类

    public <T> T proxy(Object target, Class<T> clazz){
        this.target = target;
        return (T)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    /**
     * 代理对象方法调用的回调处理方法
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable{
        Object result = null;
        if(this.connect()){
            method.invoke(target, args);
            this.close();
        }
        return result;
    }

    private boolean connect(){
        System.out.println("创建连接");
        return  true;
    }

    private void close(){
        System.out.println("关闭连接");
    }
}

  测试:

 public static void main(String[] args){
        IMessage message = new NetProxy().proxy(new MessageReal(), IMessage.class);
        message.send("hello");
    }

  输出:

创建连接
发送消息
关闭连接

 3.CGLib代理

 上文描述的静态代理和动态代理都基于接口实现,即需要有明确的被代理接口。CGLib代理,直接对类进行代理,不需要接口。其底层是通过生成代理类字节码实现的。

  • 被代理接口:无
  • 被代理类:Object target
  • 代理类:Enhancer.create()
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;


class NetProxy implements MethodInterceptor{

    private Object target;//被代理类,Object可以代理任何类型的类

    public <T> T proxy(Object target, Class<T> clazz){
        this.target = target;
        Enhancer enhancer = new Enhancer();//创建代理的程序类
        enhancer.setSuperclass(target.getClass());//假定一个父类
        enhancer.setCallback(this);//设置代理类回调处理方法
        return (T)enhancer.create();//创建代理类
    }

    /**
     * 拦截器,代理执行会被拦截
     * @param proxy
     * @param method
     * @param objects
     * @param methodProxy
     * @return
     * @throws Throwable
     */
    public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        if(this.connect()){
            method.invoke(target, objects);
            this.close();
        }
        return null;
    }

    private boolean connect(){
        System.out.println("创建连接");
        return  true;
    }

    private void close(){
        System.out.println("关闭连接");
    }
}

4.总结

1.常用的代理模式三要素:被代理接口、被代理类、代理类,对面向接口的编程,该方式比较适合

2.静态代理和动态代理都是基于三要素实现的,区别在于静态代理可实现具体代理类代理某个类,不能扩展;动态代理可以代理任一类型的类

3.动态代理核心是Proxy和InvocationHandler类,底层通过生成代理字节码文件实现代理

4.CGLib代理不需要接口,可直接代理类,在Spring中同时支持Java动态代理和CGlib代理。

posted @ 2020-04-11 20:15  水木竹水  阅读(476)  评论(0编辑  收藏  举报