Java 反射和动态代理简介及相关面试题

Java 反射和动态代理简介及相关面试题

1. 反射

1.1 反射简介
	- Java反射机制是在运行过程中, 对于任何一个类(class文件), 都能够知道这个类的所有方法和属性; 对于任何一个对象, 都能够调用它的任意一个方法和属性
	- 这种动态获取的信息以及动态调用对象的方法的功能称为Java的反射机制
1.2 获取Class对象的三种方式
	1. 知道具体类的情况, 调用 类.class
		Class c1 = Person.class;
	2. 通过对象的 对象.getClass()
		Class c2 = person.getClass();
	3. 知道类的全路径, 是使用 Class.forName("path")
		Class c3 = Class.forName("com.htx4ever");
1.3 通过反射获取类的方法和属性 代码示例
	Class<?> c = Class.forName("com.htx4ever.Person");
	Person person = (Person) c.newInstance();
	person.setName("abc");

	// 获取类中的所有方法
	Method[] declaredMethods = c.getDeclaredMethods();
	System.out.println("methods: ");
	for (Method method : declaredMethods) {
		System.out.println(method.getName());
	}

	System.out.println();
	// 获取类中指定方法, 并调用
	Method say = c.getDeclaredMethod("say", String.class);
	String result = (String) say.invoke(person, "good");
	System.out.println(result);

	System.out.println();
	// 获取类中所有属性
	Field[] declaredFields = c.getDeclaredFields();
	System.out.println("fields: ");
	for (Field field : declaredFields) {
		System.out.println(field.getName());
	}

	System.out.println();
	// 获取类中指定属性, 并修改
	Field field = c.getDeclaredField("name");
	field.setAccessible(true);	// 允许访问private属性
	System.out.println(field.get(person));
	field.set(person, "cba");
	System.out.println(person.getName());

	System.out.println();
	// 调用 private 方法
	Method privateSay = c.getDeclaredMethod("privateSay", String.class);
	privateSay.setAccessible(true);	//允许访问private方法
	privateSay.invoke(person, "wow");

Person.class: 
	class Person {
		public Person() {
		}

		public Person(String name) {
			this.name = name;
		}

		private String name;

		public String getName() {
			return name;
		}

		public void setName(String name) {
			this.name = name;
		}

		public String say(String message) {
			System.out.println("message: " + message);
			return "result: " + message;
		}

		private void privateSay(String message) {
			System.out.println("private message: " + message);
		}
	}
1.4 反射机制的优缺点
	- 优点:
		- 反射提高了Java的灵活性和扩展性, 降低了耦合. 它允许程序创建和控制任何类的对象, 无需提前硬编码目标类 -> 一些开发框架使用
	- 缺点: 
		- 性能差: 反射相当于一系列解释操作, 性能相比直接Java代码弱不少
		- 安全性: 反射可以直接动态修改对象属性, 
1.5 反射的应用场景举例
	1. 使用JDBC连接数据库, 使用Class.forName("com.mysql.jdbc.Driver")反射技术来动态加载数据库驱动程序
	2. Spring框架中对象创建
	3. Spring AOP中使用的JDK动态代理也使用到了反射技术

2. 代理模式

	- 代理模式: 使用代理对象来代替对目标对象的访问, 可以在不修改目标对象的前提下, 扩展目标对象已有的功能
2.1 静态代理示例2
	public class 静态代理Test {
		public static void main(String[] args) {
			/*
				静态代理实现步骤:
					1. 定义接口, 以及一个实现类(被代理对象)
					2. 创建一个代理对象, 并实现同一个接口
					3. 代理对象的方法, 调用被代理对象的方法, 且可以对此方法进行增强

				- 静态代理, 对目标对象的各个方法的增强都是手动完成的, 非常不灵活
				- 当接口新增方法时, 需要同时对目标对象和代理对象进行修改
				- 静态代理在编译时就确定了代理对象和目标对象
			 */
			SmsProxyA smsProxyA = new SmsProxyA();
			smsProxyA.sendMessage("静态代理 test");
		}
	}

	interface SmsServiceA {
		void sendMessage(String message);
	}

	class SmsServiceAImpl implements SmsServiceA {
		@Override
		public void sendMessage(String message) {
			System.out.println("send message: " + message);
		}
	}


	class SmsProxyA implements SmsServiceA {
		@Override
		public void sendMessage(String message) {
			System.out.println("proxy before ...");
			SmsServiceA smsService = new SmsServiceAImpl();
			smsService.sendMessage(message);
			System.out.println("proxy after ...");
		}
	}
2.2 JDK动态代理示例
	public class Jdk动态代理Test {
		// 通过反射机制来实现JDK动态代理, 通过反射调用委托类
		// 委托类必须实现接口, 代理类和委托类实现相同的接口
		public static void main(String[] args) {
			SmsServiceB target = new SmsServiceBImpl();

			// 方式1
			SmsServiceB smsServiceB = (SmsServiceB) Proxy.newProxyInstance(target.getClass().getClassLoader(),
					target.getClass().getInterfaces(), new MyInvocationHandler1(target));
			smsServiceB.sendMessage("hello world");

			// 方式2
			MyInvocationHandler2 myInvocationHandler2 = new MyInvocationHandler2();
			SmsServiceB proxy = (SmsServiceB) myInvocationHandler2.getInstance(target);
			proxy.sendMessage("world hello");
		}
	}

	/**
	 * 目标对象需要实现的接口
	 */
	interface SmsServiceB {
		void sendMessage(String message);
	}

	/**
	 * 目标对象
	 */
	class SmsServiceBImpl implements SmsServiceB {
		@Override
		public void sendMessage(String message) {
			System.out.println("send message: " + message);
		}
	}

	/**
	 * 实现InvocationHandler接口
	 */
	class MyInvocationHandler1 implements InvocationHandler {
		private Object target;
		public MyInvocationHandler1(Object target) {
			this.target = target;
		}
		@Override
		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
			System.out.println("jdk proxy before, method: " + method.getName());
			Object result = method.invoke(target, args);
			System.out.println("jdk proxy after, method: " + method.getName());
			return result;
		}
	}

	class MyInvocationHandler2 implements InvocationHandler {
		private Object target;

		public Object getInstance(Object target) {
			this.target = target;
			return Proxy.newProxyInstance(this.target.getClass().getClassLoader(),
					this.target.getClass().getInterfaces(), this);
		}

		@Override
		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
			System.out.println("jdk proxy before, method: " + method.getName());
			Object result = method.invoke(target, args);
			System.out.println("jdk proxy after, method: " + method.getName());
			return result;
		}
	}
2.3 Cglib代理示例
	/**	
	 * CGLIB 动态代理
	 * 	- 基于ASM字节码生成库, 允许我们在运行时对字节码进行修改和动态生成
	 * 	- CGLIB通过继承方式实现动态代理, 代理类是委托类的子类(所以不能代理final方法)
	 * 	- 核心: MethodInterceptor接口, Enhancer类
	 * 	- Spring AOP模块对CGLIB的使用:
	 * 		- 若目标对象实现了接口, 则默认使用JDK动态代理
	 * 		- 若目标对象未实现接口, 则使用CGLIB动态代理
	 */
	public class Cglib代理Test {
		public static void main(String[] args) {
			SmsServiceC proxy = (SmsServiceC) CglibProxyFactory.getProxy(SmsServiceC.class);
			proxy.send("hello");
			proxy.show();
		}
	}

	class SmsServiceC {
		public void send(String message) {
			System.out.println("send message: " + message);
		}

		public void show() {
			System.out.println("show ...");
		}
	}

	class DebugMethodInterceptor implements MethodInterceptor {
		@Override
		public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
			System.out.println("cglib proxy before, method: " + method.getName());
			Object object = methodProxy.invokeSuper(o, objects);
			System.out.println("cglib proxy after, method: " + method.getName());
			return object;
		}
	}

	class CglibProxyFactory {
		public static Object getProxy(Class<?> clazz) {
			// 创建动态代理增强类
			Enhancer enhancer = new Enhancer();
			// 设置类加载器
			enhancer.setClassLoader(clazz.getClassLoader());
			// 设置被代理类
			enhancer.setSuperclass(clazz);
			// 设置方法拦截
			enhancer.setCallback(new DebugMethodInterceptor());
			// 创建代理类并返回
			return enhancer.create();
		}
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值