目录
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();
}
}