jdk动态代理(动态生成字节码与反射机制的结合)

2022-11-30 17:01:11 浏览数 (1)

       java jdk动态代理其实是动态生成字节码与反射机制的一个结合,说到反射机制很多人都用到过反射,只要得到对应类的Class对象即可,调用方法,获取成员变量等等,那么jdk的动态代理就是在程序运行的过程中,动态的将我们维护的检查性的代码,放在正常的业务代码之前,那么怎么调用我正常的业务代码呢,因为业务可能有很多种,也就是说可能会有不同的类,但是都要执行相同的检查性代码,如我们要取钱,或者是修改身份证,都必须验证通过才可以,这显然是两个类,一个是Money,一个是ID,所以我们在调用正常业务代码的时候,其实不知道我们调用的是谁的代码,这时候就用到反射,通过反射,动态的识别类型,然后再调用方法,如我们要取钱,那么传入的应该是一个Money的实例,通过这个实例的getDeclaredMethod,或者是getMethod就可以,获取对应实例的方法,然后即可动态调用方法,只要在用反射前,加入我们验证的代码即可,这时反射在动态代理中的应用。这部分代码需要我们自己实现InvocationHandler接口,实现其中的invoke方法,在这个方法中,就是我们上述反射的实现。当然为了调用到对应类的方法,我们实现的invocationHandler类中,需要保存我们要代理类的实例。

      而动态生成字节码是一种技术,就是在编译期不能决定要生成字节码的类型,也就是没有对应的java文件,所以就不能生成class文件,像是静态代理的话,我们会明确的实现一个代理类,所以可以在编译期生成字节码文件,但是动态代理不会明确实现某一个类的代理类,是针对所有业务类的一个公用的类,由于在编译期不能决定生成那个业务类的代理类所以就不能生成字节码,反而是在运行的时候,看我们传入的实例是输入什么类的,生成对应类的代理类,因为这时候要确定生成一个代理类 ,如果没有字节码文件,那么该类就不会加载,更加不会执行,所以动态代理技术,会把字节码文件动态的拼接出来,形成一个class文件,这就是动态生成字节码的文件。这种技术是在java一个类中实现的

Proxy的静态方法,newProxyInstance(),这个方法有三个参数,第一个参数是被代理类的类加载器(classLoader),第二个参数是被代理类的所有接口,第三个参数是我们上面实现类(InvocationHandler)的实例,那么这个方法,返回的就是一个代理类,就是在这个方法中,动态拼接处代理类的字节码。

       动态代理有两个主要应用:第一是如果代码中多处会用到相同的代码,如安全性检查,权限的验证,在很对业务模块都会用到,一般都是放在我们业务代码的头部或者是尾部,那么如果不用动态代理的话,这些相同的代码就会分散在你程序的各个部分,首先代码服用率太差,其次是可维护性不高,如果你想要改变你权限验证的方式的话,那么你需要在程序中每个地方都改,这样的话会很难维护,索性我们将所有检查性的代码,都提取出来,一起维护,这样的代码维护在一起,那么就相当方便的,这就是面向切面的编程也就是AOP,我们把检查性的代码放在一个类里一起维护,那么这个类就叫做切面。AOP的概念在spring中会广泛被用到,如spring中的生命式事务,就是用AOP实现的,动态为数据库操作加上事务,第二个主要应用,是类似拦截器的一种应用,拦截器就是在你业务代码前,加上检查性代码,如果通过则执行你的业务代码,如果不通过则不执行你的业务代码。其实这里不会完全用到动态代理,主要是用带动态代理中反射的实现部分,在实际的应用中一般会使用拦截器栈,也就是一系列的拦截器,就是说要检查多个合法性,一个拦截器验证通过则进入下一个拦截器,当所有拦截器验证都通过,才是真正的业务代码。这种思想在其实就是设计模式中的责任链模式,在springmvc当中会有很大的应用,下面我简单手动实现了一个拦截器(不是拦截器栈),这既是一个拦截器,也是动态代理的一个应用,希望对大家有帮助,下面是我的代码。

代码语言:javascript复制
package com.test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Scanner;

public class Main {
	
	private static boolean checkFlag=true;
	
	public static String username="root";
	
	public static String password="root";

	/**
	 * javajdk动态代理Demo
	 * @param args
	 */
	static class MyInvocationHandler implements InvocationHandler{
        //拦截器
		private Object obj;
		
		public MyInvocationHandler(Object obj){
			this.obj=obj;
		}
		
		@Override
		public Object invoke(Object proxy, Method method, Object[] args)
				throws Throwable {
			//首先看是否开启了拦截模式
			if(checkFlag){
				Scanner sc=new Scanner(System.in);
				System.out.println("输入用户名:");
				String username=sc.next();
				System.out.println("输入密码: ");
				String password=sc.next();
				//如果开启了,检查用户名以及密码是否正确
				if(username.equals(Main.username)&&password.equals(Main.password)){
				    System.out.println("通过校验!!!");	
				    return method.invoke(obj, args);
				}else{
					System.out.println("没有通过校验");
					return null;
				}
			}else{
				 return method.invoke(obj, args);
			}
		}
		
	}
	
	static class ProxyUtils{
		private Object obj;
		
		public ProxyUtils(Object obj){
			this.obj=obj;
		}
		
		public Object getProxy(){
			return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new MyInvocationHandler(obj));
		}
	}
	
	static interface HelloWorld{
		String print();
	}
	
	static class HelloWorldImpl implements HelloWorld{

		@Override
		public String print() {
			System.out.println("正常的业务代码");
			return "yes";
		}
		
	}
	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		System.out.print("是否允许校验(y/n):");
		String tag=sc.next();
		if(tag.equals("y")){
			checkFlag=true;
		}else{
			checkFlag=false;
		}
		HelloWorld h=(HelloWorld)new ProxyUtils(new HelloWorldImpl()).getProxy();
        String flag=h.print();
        System.out.println("返回值:           " flag);
	}

}

0 人点赞