Java反射机制、动态代理(过滤器Filter和拦截器Interceptor)

在介绍过滤器Filter和拦截器Interceptor之前,先大篇幅介绍下Java反射机制和动态代理(本来想着只是简单说明下这两个名称,但下笔介绍过程中发现对反射机制和动态代理一知半解,随进行了深入了解和源码剖析),动态代理主要使用了Java凡是机制来实现,而拦截器Interceptor又是通过动态代理实现的。

一、Java反射机制

在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。这种动态获取的信息以及动态调用对象方法的功能成为Java的反射机制。

反射就是把Java类中的各种成分映射成一个个的Java对象。例如一个类中有成员变量、方法、构造方法、包等信息,利用反射机制可以对一个类进行解剖,把类的组成部分映射成一个个对象。反射可以在程序运行过程中动态获取类的相关信息,包括类由哪个类加载器进行加载,类中的成员变量,成员方法,访问修饰符,返回值类型,构造方法等。

举例说明JAVA的反射机制的几个主要功能,(1)在运行时判断任意一个对象所属的类obj.getClass()。(2)在运行时构造任意一个类的对象constructor.newInstance(new Object[]{h})。(3)在运行时判断任意一个类所具有的成员变量和方法getDeclaredFields()、getDeclaredMethods()。

二、动态代理

代理模式为其它对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

动态代理是指客户通过代理类来调用其它对象的方法,主要使用了Java反射机制来实现动态代理。使用Java的反射机制创建动态代理对象,让代理对象在调用目标方法之前和之后分别做一些事情,然后动态代理对象决定是否调用以及何时来调用被代理对象的方法。

Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类。

(1)InvocationHandler:该接口中仅定义了一个方法 public object invoke(Object proxy,Method method, Object[] args),其中第一个参数proxy是代理类的实例,method是被代理的方法,即需要执行的方法,args为该方法的参数数组。这个抽象方法在代理类中动态实现。

(2)Proxy:该类即为动态代理类。

动态代理的步骤

(1)创建一个实现接口InvocationHandler的类,它必须实现invoke方法;

(2)创建被代理的接口以及实现类;

(3)通过Proxy的静态方法

newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建一个代理;

(4)通过代理调用方法。

先通过一段代码看看动态代理是怎么实现的:

public class DynamicProxy {
	public static void main(String[] args) throws Exception {
        // 被代理对象,其中Subject为接口,SubjectImpl为接口实现类
        Subject target = new SubjectImpl();
        // 动态生成的代理对象
        // target.getClass().getInterfaces()和Java的反射机制有关,它能够获得这个对象所实现的接口
        Subject porxy = (Subject)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
        	@Override
        	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        		System.out.println("方法调用前~~");
        		Object temp = method.invoke(target, args);
        		System.out.println("方法调用后~~");
                return temp;
        	}
        });
        // 通过代理对象执行方法say
        porxy.say("wangpf", 20);
    }
}

下面通过简单剖析Proxy源码分析下动态代理是怎么实现的,通过上面代码发现代理对象是Proxy通过静态方法newProxyInstance生成的,那么先看下这个核心方法。它有三个参数:类加载器loader,被代理对象实现的接口interfaces,接口InvocationHandler。接着通过添加注释的方式剖析下这个核心方法。

@CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
	// 检查 h 不为空,否则抛异常
        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.
         */
        // 根据指定接口生成代理类,如果实现了给定接口的代理类已经存在,则生成一个copy直接返回,否则通过ProxyClassFactory生成一个代理类。
        Class<?> cl = getProxyClass0(loader, intfs);

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

            // 通过类对象的getConstructor()方法获得构造器(Constructor)对象
            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;
                    }
                });
            }
            // cons调用其newInstance()方法创建代理对象
            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 = getProxyClass0(loader, intfs),当代理类不存在时需要由ProxyClassFactory生成一个新的代理类,下面看看另一个重要内部类Proxy.ProxyClassFactory是怎样生成代理类的(由于代理较多,这里只截取部分代码说明)。

/**
     * A factory function that generates, defines and returns the proxy class given
     * the ClassLoader and array of interfaces.
     */
    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // prefix for all proxy class names
        private static final String proxyClassNamePrefix = "$Proxy";

        // next number to use for generation of unique proxy class names
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
        	// 此处省略N行代码
            /*
             * Choose a name for the proxy class to generate.
             */
            // proxyPkg 为动态代理类的包名,如果接口类是非public修饰符,采用和接口类相同包名,否则使用包名com.sun.proxy。
            // proxyName 为代理类的完全限定名
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * Generate the specified proxy class.
             */
            // 根据代理类名称生成代理类class文件的byte数组。
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
            	// 加载代理类,返回代理类Class对象
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                throw new IllegalArgumentException(e.toString());
            }
        }
    }

三、过滤器Filter和拦截器Interceptor的区别

关于过滤器Filter和拦截器Interceptor的区别,这里借用网友一张图进行说明。

(1)过滤器是基于函数回调,而拦截器是基于Java反射机制动态代理;

(2)过滤器是servlet规范规定的,只能用于Web程序中,而拦截器是在spring容器中,不依赖servlet容器;

(3)过滤器不能访问action上下文及值栈里的对象,而拦截器都可以;

(4)过滤器只在容器初始化时被调用一次,而拦截器在action生命周期内可以多次调用;

(5)拦截器被包裹在过滤器之中。

赞(52) 打赏
未经允许不得转载:优客志 » JAVA开发
分享到:

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏