java安全初学之动态代理

前言:作为安全人员,代理大家用的都很多,那什么是java中的动态代理呢?事实上,java中的“动态”也就意味着使用了反射,因此动态代理是基于反射机制的一种代理模式。

简介:

代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。Java 动态代理机制以巧妙的方式近乎完美地实践了代理模式的设计理念。

  首先以一个简单的代码实例看一下动态代理的应用 先声明一个接口subject,再声明一个realSubject类实现该接口 subject接口
public interface subject {
    void simpleSubject();
}

realSubject实现类

public class realSubject implements subject{
    public void simpleSubject(){
        System.out.println("this is simpleSubject");
    }

}

每一个realSubject对象都拥有这个simpleSubject接口,但如果某个对象希望在运行的时候动态的附加一些hardSubject功能,应该怎么做呢?我们通过实现一个代理对象并在代理对象上附加hardSubject功能(或者附加一些信息)来实现。实现方式是通过Proxy.newProxyInstance生成一个代理对象,在生成代理对象的同时给这个代理对象绑定一个Handler,该handler即调用处理器(InvocationHandler)对象。

首先实现一个proxyHandler在原来的接口方法的前或后面来添加hardSubject功能(这里说功能或许不太准确)

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


public class proxyHandler implements InvocationHandler {
    private realSubject subject;
    public proxyHandler(realSubject subject){
        this.subject = subject;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("I'am the added hardSubject1"); method.invoke(subject); System.out.println("I'am the added hardSubject2"); return null; } }

查看InvocationHandler接口的源码注解可以知道,它实现的功能是:在代理对象原本的接口方法被调用时,会绑定执行InvocationHandler中定义的方法。

java安全初学之动态代理

 

接着我们编写一个测试类来实例一个代理对象

proxyTest 测试类

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

public class proxyTest {
    public static void main(String[] args){
        realSubject rs = new realSubject();
        InvocationHandler h = new proxyHandler(rs);
        Class clazz = rs.getClass();
        subject s =  (subject)Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),h);
        s.simpleSubject();
        System.out.println(s.getClass());
    }
}

Proxy的newProxyInstance方法的三个参数,分别是realSubject的类装载器,接口和我们定义的proxyHandler实例对象

 看一下源码:

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

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

代码中我加粗的部分即是关键部分,第一步利用反射获取到代理类的Class对象cl(这个代理类是如何生成的我们暂且不论,只需要知道用到了被代理类的类加载器,实现的接口以及反射。深入需要再往下跟代码跟到ProxyGenerator.generateProxyClass来生成代理类,这里就只分析这段代码) 再通过代理类的Class对象cl使用getConstructor获取构造方法,最后通过构造方法反射获得一个代理类对象,可以看到在用newInstance生成代理类对象的时候我们传入了proxyHandler对象h

值得一提的是这个代理对象给的引用是subject接口类,这个对象其实是不属于realSubject和proxyHandler的,但因为它实现了subject接口,所以可以给它一个安全的类型转换到subject引用。但事实上我们运行这段代码,s.getClass()的输出是

java安全初学之动态代理

 

 

 

 

 

也就是说它不属于任何我们看到的类,但它所属的类的父类是proxy类

 

最后我们来总结一下动态代理实现的步骤:

  1.通过实现 InvocationHandler 接口创建自己的调用处理器;

  2.通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;

  3.通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;

  4.通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

 

上一篇:关于代理模式的个人理解


下一篇:设计模式 静态代理模式