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