在介绍过滤器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)拦截器被包裹在过滤器之中。