专业的JAVA编程教程与资源

网站首页 > java教程 正文

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

temp10 2024-11-04 14:07:59 java教程 9 ℃ 0 评论

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

一、Java反射机制

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

在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。这种动态获取的信息以及动态调用对象方法的功能成为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)拦截器被包裹在过滤器之中。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表