• 59 动态代理是什么?有哪些应用?


    动态代理是什么?有哪些应用?

    答:

    动态代理:在运行时,创建目标类,可以调用和扩展目标类的方法。

     

    应用场景如:

    • 统计每个 api 的请求耗时

    • 统一的日志输出

    • 校验被调用的 api 是否已经登录和权限鉴定

    • Spring的 AOP 功能模块就是采用动态代理的机制来实现切面编程
       

    原文链接: https://blog.csdn.net/meism5/article/details/90413993 (简单)
    原文链接: https://www.cnblogs.com/aheizi/p/4861422.html (详细)
     

    拓展:

     

    原理

    在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class)。

    InvocationHandler

    每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:

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

     

    参数的含义:

    proxy:   指代我们所代理的那个真实对象

    method:  指代的是我们所要调用真实对象的某个方法的Method对象

    args:   指代的是调用真实对象某个方法时接受的参数
     

    Proxy

    Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法:

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

     

    参数的含义:

    loader:   一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载

    interfaces:  一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了

    h: 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
     

    应用

    1.方法性能监测

    获取执行方法前后的系统时间,算出其执行时间。

    long startTime = System.currentTimeMillis();
    Object obj = method.invoke(proxied, args);
    long endTime = System.currentTimeMillis();
    System.out.println("Method " + method.getName() + " execution time: " + (endTime - startTime) * 1.0 / 1000 + "s");
    

     

    2.日志管理

    获取日志需要的方法名和时间,并利用代理写入。

    public String beforeMethod(Method method) {
        return getFormatedTime() + " Method:" + method.getName() + " start running
    ";
    }
     
    public String afterMethod(Method method) {
        return getFormatedTime() + " Method:" + method.getName() + " end running
    ";
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        write(path, beforeMethod(method));
        Object object = method.invoke(proxied, args);
        write(path, afterMethod(method));
        return object;
    }
     
    public String getFormatedTime() {
        DateFormat formater = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        return formater.format(System.currentTimeMillis());
    }
     
    public void write(String path, String content) {
        FileWriter writer = null;
        try {
            writer = new FileWriter(new File(path), true);
            writer.write(content);
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(null != writer) {
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

     

    3.使用动态代理对注解进行处理

    在实例中如何使用和处理注解。

    假定有一个公司的雇员信息系统,从访问控制的角度出发,对雇员的工资的更新只能由具有特定角色的用户才能完成。考虑到访问控制需求的普遍性,可以定义一个注解来让开发人员方便的在代码中声明访问控制权限。

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface RequiredRoles {
        String[] value();
    }
    

     

    下一步则是如何对注解进行处理,这里使用的Java的反射API并结合动态代理。下面是动态代理中的InvocationHandler接口的实现。

    public class AccessInvocationHandler<T> implements InvocationHandler {
        final T accessObj;
        public AccessInvocationHandler(T accessObj) {
            this.accessObj = accessObj;
        }
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            RequiredRoles annotation = method.getAnnotation(RequiredRoles.class); //通过反射API获取注解
            if (annotation != null) {
                String[] roles = annotation.value();
                String role = AccessControl.getCurrentRole();
                if (!Arrays.asList(roles).contains(role)) {
                    throw new AccessControlException("The user is not allowed to invoke this method.");
                }
            }
            return method.invoke(accessObj, args);
        } 
    } 
    

     

    在具体使用的时候,首先要通过Proxy.newProxyInstance方法创建一个EmployeeGateway的接口的代理类,使用该代理类来完成实际的操作。

    相当于使用一个AOP切面对所有实现了这个接口(也就是追加了注解的类)实现切面控制。
     

    总结(链接二)

    Java动态代理美中不足的是仅支持 interface 代理。因为那些动态生成的代理类都有一个共同的父类叫Proxy。Java 的继承机制注定了这些动态代理类们无法实现对 class 的动态代理,原因是多继承在 Java 中本质上就行不通。

    虽然有缺点,但是Proxy设计的非常优美是毋庸置疑的,有时间一定要去看看源码。
    Java动态代理机制分析及扩展这篇文章写得相当好,里面有些东西还没有完全理解,标记下来,多读几遍。

  • 相关阅读:
    UVA 11987 几乎就是并查集= =
    UVALive 5908 更新一下线段相交模板
    【poor几何】UVALive 5908 更新一下线段相交模板
    【poor几何】UVALive 5908 更新一下线段相交模板
    UVALive 3634 熟悉一下STL
    UVALive 3634 熟悉一下STL
    UVALive 3634 熟悉一下STL
    hdu2665 主席树模板题
    hdu2665 主席树模板题
    迷宫问题 POJ
  • 原文地址:https://www.cnblogs.com/ynzj123/p/12914702.html
Copyright © 2020-2023  润新知