聊一聊Java中的反射机制
# 什么是反射?反射可以做什么用?
反射被称为框架的灵魂,主要是因为它赋予了我们在运行时分析类以及执行类中方法的能力。通过反射你可以获取任意一个类的所有属性和方法,你还可以调用这些方法和属性。Spring
等框架需要获取注解上的bean
名称等都会用到反射这个技术。
# 获取Class对象的四种方式
每一个类都只会有一个Class对象,无论何种方式获取Class
对象进行比较,得到的结果都是true,JVM
在加载每个类时都会为每个类通过类加载器中的defineClass
去创建一个Class
对象,而且只创建一次,所以后续我们无论通过何种方式获取到的某个类的Class
对象都是一样的。
/**
* 获取Class对象的三种方式
* 1 Object ——> getClass();
* 2 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
* 3 通过Class类的静态方法:forName(String className)(常用)
* 4 通过类加载器xxxClassLoader.loadClass()传入类路径获取
*/
public class FansheConstruct {
public static void main(String[] args) {
//第一种方式获取Class对象
StudentConstruct stu1 = new StudentConstruct();//这一new 产生一个Student对象,一个Class对象。
Class stuClass = stu1.getClass();//获取Class对象
System.out.println(stuClass.getName());
//第二种方式获取Class对象
Class stuClass2 = StudentConstruct.class;
System.out.println(stuClass == stuClass2);//判断第一种方式获取的Class对象和第二种方式获取的是否是同一个
//第三种方式获取Class对象
try {
Class stuClass3 =Class.forName("com.blazemaple.refect.StudentConstruct");//注意此字符串必须是真实路 径,就是带包名的类路径,包名.类名
System.out.println(stuClass3 == stuClass2);//判断三种方式是否获取的是同一个Class对象
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//第四种方式(通过类加载器获取 Class不会进行初始化,意味着不进行包括初始化等步骤,静态代码块和静态对象不会得到执行)
ClassLoader.getSystemClassLoader().loadClass("com.blazemaple.refect.StudentConstruct");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 反射的一些基本操作
# 通过反射获取构造方法
public class Student {
//(默认的构造方法)
Student(String str) {
System.out.println("(默认)的构造方法 s = " + str);
}
//无参构造方法
public Student() {
System.out.println("调用了公有、无参构造方法执行了。。。");
}
//有一个参数的构造方法
public Student(char name) {
System.out.println("姓名:" + name);
}
//有多个参数的构造方法
public Student(String name, int age) {
System.out.println("姓名:" + name + "年龄:" + age);
}
//受保护的构造方法
protected Student(boolean n) {
System.out.println("受保护的构造方法 n = " + n);
}
//私有构造方法
private Student(int age) {
System.out.println("私有的构造方法 年龄:" + age);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
几个获取构造方法的方式介绍
public Constructor[] getConstructors()
:所有"公有的"构造方法public Constructor[] getDeclaredConstructors()
:获取所有的构造方法(包括私有、受保护、默认、公有)public Constructor getConstructor(Class... parameterTypes)
:获取单个的"公有的"构造方法:public Constructor getDeclaredConstructor(Class... parameterTypes)
:获取"某个构造方法"可以是私有的,或受保护、默认、公有;
import java.lang.reflect.Constructor;
public class Constructors {
public static void main(String[] args) throws Exception {
//1.加载Class对象
Class clazz = Class.forName("com.blazemaple.reflect.Student");
//2.获取所有公有构造方法
System.out.println("**********************所有公有构造方法*********************************");
Constructor[] conArray = clazz.getConstructors();
for(Constructor c : conArray){
System.out.println(c);
}
System.out.println("************所有的构造方法(包括:私有、受保护、默认、公有)***************");
conArray = clazz.getDeclaredConstructors();
for(Constructor c : conArray){
System.out.println(c);
}
System.out.println("*****************获取公有、无参的构造方法*******************************");
Constructor con = clazz.getConstructor(null);
//1>、因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的类型,切记是类型
//2>、返回的是描述这个无参构造函数的类对象。
System.out.println("con = " + con);
//调用构造方法
Object obj = con.newInstance();
// System.out.println("obj = " + obj);
// com.blazemaple.reflect.Student stu = (com.blazemaple.reflect.Student)obj;
System.out.println("******************获取私有构造方法,并调用*******************************");
con = clazz.getDeclaredConstructor(char.class);
System.out.println(con);
//调用构造方法
con.setAccessible(true);//暴力访问(忽略掉访问修饰符)
obj = con.newInstance('男');
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
输出结果:
**********************所有公有构造方法*********************************
public com.blazemaple.reflect.Student(java.lang.String,int)
public com.blazemaple.reflect.Student(char)
public com.blazemaple.reflect.Student()
************所有的构造方法(包括:私有、受保护、默认、公有)***************
private com.blazemaple.reflect.Student(int)
protected com.blazemaple.reflect.Student(boolean)
public com.blazemaple.reflect.Student(java.lang.String,int)
public com.blazemaple.reflect.Student(char)
public com.blazemaple.reflect.Student()
com.blazemaple.reflect.Student(java.lang.String)
*****************获取公有、无参的构造方法*******************************
con = public com.blazemaple.reflect.Student()
调用了公有、无参构造方法执行了。。。
******************获取私有构造方法,并调用*******************************
public com.blazemaple.reflect.Student(char)
姓名:男
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 通过反射获取成员变量
public class StudentField {
public StudentField() {
}
//**********字段*************//
public String name;
protected int age;
char sex;
private String phoneNum;
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", sex=" + sex
+ ", phoneNum=" + phoneNum + "]";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
用Class
对象的getFields
方法获取不同修饰符的成员变量
import java.lang.reflect.Field;
public class GetFields {
public static void main(String[] args) throws Exception {
//1.获取Class对象
Class stuClass = Class.forName("com.blazemaple.reflect.StudentField");
//2.获取字段
System.out.println("************获取所有公有的字段********************");
Field[] fieldArray = stuClass.getFields();
for (Field f : fieldArray) {
System.out.println(f);
}
System.out.println("************获取所有的字段(包括私有、受保护、默认的)********************");
fieldArray = stuClass.getDeclaredFields();
for (Field f : fieldArray) {
System.out.println(f);
}
System.out.println("*************获取公有字段**并调用***********************************");
Field f = stuClass.getField("name");
System.out.println(f);
//获取一个对象
Object obj = stuClass.getConstructor().newInstance();//产生Student对象--》Student stu = new Student();
//为字段设置值
f.set(obj, "刘德华");//为Student对象中的name属性赋值--》stu.name = "刘德华"
//验证
StudentField stu = (StudentField) obj;
System.out.println("验证姓名:" + stu.name);
System.out.println("**************获取私有字段****并调用********************************");
f = stuClass.getDeclaredField("phoneNum");
System.out.println(f);
f.setAccessible(true);//暴力反射,解除私有限定
f.set(obj, "18888889999");
System.out.println("验证电话:" + stu);
f = stuClass.getDeclaredField("age");
System.out.println(f);
f.setAccessible(true);//暴力反射,解除私有限定
f.set(obj, 18);
System.out.println("验证保护变量年龄:" + stu);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
输出结果:
************获取所有公有的字段********************
public java.lang.String com.blazemaple.reflect.StudentField.name
************获取所有的字段(包括私有、受保护、默认的)********************
public java.lang.String com.blazemaple.reflect.StudentField.name
protected int com.blazemaple.reflect.StudentField.age
char com.blazemaple.reflect.StudentField.sex
private java.lang.String com.blazemaple.reflect.StudentField.phoneNum
*************获取公有字段**并调用***********************************
public java.lang.String com.blazemaple.reflect.StudentField.name
验证姓名:刘德华
**************获取私有字段****并调用********************************
private java.lang.String com.blazemaple.reflect.StudentField.phoneNum
验证电话:Student [name=刘德华, age=0, sex= , phoneNum=18888889999]
protected int com.blazemaple.reflect.StudentField.age
验证保护变量年龄:Student [name=刘德华, age=18, sex= , phoneNum=18888889999]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 通过反射获取方法
public class StudentMethod {
//**************成员方法***************//
public void show1(String s) {
System.out.println("调用了:公有的,String参数的show1(): s = " + s);
}
protected void show2() {
System.out.println("调用了:受保护的,无参的show2()");
}
void show3() {
System.out.println("调用了:默认的,无参的show3()");
}
private String show4(int age) {
System.out.println("调用了,私有的,并且有返回值的,int参数的show4(): age = " + age);
return "abcd";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
通过Class
对象的getMethod
获取到对应方法
import java.lang.reflect.Method;
public class GetMethod {
public static void main(String[] args) throws Exception {
//1.获取Class对象
Class stuClass = Class.forName("com.blazemaple.reflect.StudentMethod");
//2.获取所有公有方法
System.out.println("***************获取所有的”公有“方法*******************");
Method[] methodArray = stuClass.getMethods();
for(Method m : methodArray){
System.out.println(m);
}
System.out.println("***************获取所有的方法,包括私有的*******************");
methodArray = stuClass.getDeclaredMethods();
for(Method m : methodArray){
System.out.println(m);
}
System.out.println("***************获取公有的show1()方法*******************");
Method m = stuClass.getMethod("show1", String.class);
System.out.println(m);
//实例化一个Student对象
Object obj = stuClass.getConstructor().newInstance();
m.invoke(obj, "刘德华");
System.out.println("***************获取私有的show4()方法******************");
m = stuClass.getDeclaredMethod("show4", int.class);
System.out.println(m);
m.setAccessible(true);//解除私有限定
Object result = m.invoke(obj, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
System.out.println("返回值:" + result);
System.out.println("***************获取私有的show3()方法******************");
m = stuClass.getDeclaredMethod("show3");
System.out.println(m);
m.setAccessible(true);//解除私有限定
Object result2 = m.invoke(obj);
System.out.println("返回值:" + result2);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
输出结果:
***************获取所有的”公有“方法*******************
public void com.blazemaple.reflect.StudentMethod.show1(java.lang.String)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
***************获取所有的方法,包括私有的*******************
public void com.blazemaple.reflect.StudentMethod.show1(java.lang.String)
protected void com.blazemaple.reflect.StudentMethod.show2()
private java.lang.String com.blazemaple.reflect.StudentMethod.show4(int)
void com.blazemaple.reflect.StudentMethod.show3()
***************获取公有的show1()方法*******************
public void com.blazemaple.reflect.StudentMethod.show1(java.lang.String)
调用了:公有的,String参数的show1(): s = 刘德华
***************获取私有的show4()方法******************
private java.lang.String com.blazemaple.reflect.StudentMethod.show4(int)
调用了,私有的,并且有返回值的,int参数的show4(): age = 20
返回值:abcd
***************获取私有的show3()方法******************
void com.blazemaple.reflect.StudentMethod.show3()
调用了:默认的,无参的show3()
返回值:null
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 通过反射完成泛型擦除
java是一门伪泛型语言,泛型仅仅是在编译器会进行检查,一旦过了编译泛型就会被擦除,所以假如某些情况下我们不能修改某个类的泛型,且需要为这个为这个指定泛型的变量赋一个非泛型的值时,我们就可以使用反射技术,如下所示:
public class ModifyGenerics {
public static void main(String[] args) throws Exception {
ArrayList<String> strList = new ArrayList<>();
strList.add("aaa");
strList.add("bbb");
// strList.add(100);
//获取ArrayList的Class对象,反向的调用add()方法,添加数据
Class listClass = strList.getClass(); //得到 strList 对象的字节码 对象
//获取add()方法
Method m = listClass.getMethod("add", Object.class);
//调用add()方法
m.invoke(strList, 100);
//遍历集合
for (Object obj : strList) {
System.out.println(obj);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 反射的工作原理
new
的过程很简单,大体来说就是看看方法区
有没有关于class
解析的对象的信息,如果没有则先去加载,完成后拿着这些信息完成对象创建。 而反射则是通过方法区
在堆区创建一个对应类的唯一一个Class
对象,通过这个对象获取类中各种信息并封装成一个个Method
、Field
等对象使用。
# 反射机制的优缺点
- 优点:可以让代码更加灵活、为各种框架提供开箱即用的功能提供了便利
- 缺点:让我们在运行时有了分析操作类的能力,这同样也增加了安全问题。比如可以无视泛型参数的安全检查(泛型参数的安全检查发生在编译时)。另外,反射的性能也要稍差点。
# 基于反射的两种代理模式
# 代理模式
代理模式是一种比较好理解的设计模式。简单来说就是 我们使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。代理模式的主要作用是扩展目标对象的功能,比如说在目标对象的某个方法执行前后你可以增加一些自定义的操作。代理模式有静态代理和动态代理两种实现方式。
# 静态代理
从应用层面来说,静态代理中,我们对目标对象的每个方法的增强都是手动完成的,非常不灵活且麻烦(需要对每个目标类都单独写一个代理类)。从 JVM 层面来说,静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。
静态代理实现步骤:
- 定义一个接口及其实现类;
- 创建一个代理类同样实现这个接口
- 将目标对象注入进代理类,然后在代理类的对应方法调用目标类中的对应方法。这样的话,我们就可以通过代理类屏蔽对目标对象的访问,并且可以在目标方法执行前后做一些自己想做的事情。
/**
* 通过接口抽象代理类和被代理类的动作
*/
public interface SmsService {
String send(String message);
}
/**
* 被代理类
*/
public class SmsServiceImpl implements SmsService {
public String send(String message) {
System.out.println("send message:" + message);
return message;
}
}
/**
* 代理类
*/
public class SmsProxy implements SmsService {
/**
* 通过关联关系将被代理对象以成员变量形式组合到代理类中
*/
private final SmsService smsService;
public SmsProxy(SmsService smsService) {
this.smsService = smsService;
}
@Override
public String send(String message) {
//调用方法之前,我们可以添加自己的操作
System.out.println("before method send()");
smsService.send(message);
//调用方法之后,我们同样可以添加自己的操作
System.out.println("after method send()");
return null;
}
}
/**
* 运行代码
*/
public class StaticMain {
public static void main(String[] args) {
SmsService smsService = new SmsServiceImpl();
SmsProxy smsProxy = new SmsProxy(smsService);
smsProxy.send("java");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# 动态代理
相比与静态代理,动态代理则相对灵活一些,它可以在运行时候动态的生成被代理类的字节码并加载到JVM
中,而且与静态代理相比它无需创建接口类去规范代理类的方法(如果被代理类没有实现接口完全可以使用CGLIB
来实现)。
# 基于jdk实现的动态代理
在 Java 动态代理机制中 InvocationHandler
接口和 Proxy
类是核心。Proxy
类中使用频率最高的方法是:newProxyInstance()
,这个方法主要用来生成一个代理对象。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
......
}
2
3
4
5
6
7
这个方法一共有 3 个参数:
- loader :类加载器,用于加载代理对象。
- interfaces : 被代理类实现的一些接口;
- h : 实现了
InvocationHandler
接口的对象;
要实现动态代理的话,还必须需要实现InvocationHandler
来自定义处理逻辑。 当我们的动态代理对象调用一个方法时,这个方法的调用就会被转发到实现InvocationHandler
接口类的 invoke
方法来调用。
public interface InvocationHandler {
/**
* 当你使用代理对象调用方法的时候实际会调用到这个方法
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
2
3
4
5
6
7
8
invoke()
方法有下面三个参数:
- proxy :动态生成的代理类
- method : 与代理类对象调用的方法相对应
- args : 当前 method 方法的参数
也就是说:你通过Proxy
类的 newProxyInstance()
创建的代理对象在调用方法的时候,实际会调用到实现InvocationHandler
接口的类的 invoke()
方法。 你可以在 invoke()
方法中自定义处理逻辑,比如在方法执行前后做什么事情。
实现步骤:
- 定义一个接口及其实现类;
- 自定义
InvocationHandler
并重写invoke
方法,在invoke
方法中我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑; - 通过
Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
方法创建代理对象
示例:
定义接口
public interface SmsService {
String send(String message);
}
2
3
实现类
public class SmsServiceImpl implements SmsService {
public String send(String message) {
System.out.println("send message:" + message);
return message;
}
}
2
3
4
5
6
增强器代码(自定义 InvocationHandler
并重写invoke
方法)
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 通过jdk代理增强被代理方法
*/
public class DebugInvocationHandler implements InvocationHandler {
/**
* 代理类中的真实对象
*/
private final Object target;
public DebugInvocationHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
//调用方法之前,我们可以添加自己的操作
System.out.println("before method " + method.getName());
Object result = method.invoke(target, args);
//调用方法之后,我们同样可以添加自己的操作
System.out.println("after method " + method.getName());
return result;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
代理类代码(获取代理对象的工厂类)
import java.lang.reflect.Proxy;
/**
* jdk代理类 可以看到Obj对象类型任意,真正实现了代码解耦
*/
public class JdkProxyFactory {
public static Object getProxy(Object target) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 目标类的类加载
target.getClass().getInterfaces(), // 代理需要实现的接口,可指定多个
new DebugInvocationHandler(target) // 代理对象对应的自定义 InvocationHandler
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
测试类代码
public class Main {
public static void main(String[] args) {
SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl());
smsService.send("java");
}
}
2
3
4
5
6
结果
before method send
send message:java
after method send
2
3
# 基于CGLIB实现动态代理
JDK
动态代理有一个最致命的问题是其只能代理实现了接口的类。为了解决这个问题,我们可以用 CGLIB
动态代理机制来避免。
**在 CGLIB
动态代理机制中 MethodInterceptor
接口和 Enhancer
类是核心。**需要自定义 MethodInterceptor
并重写 intercept
方法,intercept
用于拦截增强被代理类的方法。
public interface MethodInterceptor extends Callback{
// 拦截被代理类中的方法
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,MethodProxy proxy) throws Throwable;
}
2
3
4
- obj : 被代理的对象(需要增强的对象)
- method : 被拦截的方法(需要增强的方法)
- args : 方法入参
- proxy : 用于调用原始方法
你可以通过 Enhancer
类来动态获取被代理类,当代理类调用方法的时候,实际调用的是 MethodInterceptor
中的 intercept
方法。
实现步骤:
- 定义一个类;
- 自定义
MethodInterceptor
并重写intercept
方法,intercept
用于拦截增强被代理类的方法,和 JDK 动态代理中的invoke
方法类似; - 通过
Enhancer
类的create()
创建代理类;
示例:
不同于 JDK 动态代理不需要额外的依赖。CGLIBopen in new window (opens new window)(Code Generation Library) 实际是属于一个开源项目,如果你要使用它的话,需要手动添加相关依赖。
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
2
3
4
5
定义被代理的类
/**
* 需要被代理的类
*/
public class AliSmsService {
public String send(String message) {
System.out.println("send message:" + message);
return message;
}
}
2
3
4
5
6
7
8
9
自定义 MethodInterceptor
(方法拦截增强器)
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* 自定义MethodInterceptor
*/
public class DebugMethodInterceptor implements MethodInterceptor {
/**
* @param o 代理对象(增强的对象)
* @param method 被拦截的方法(需要增强的方法)
* @param args 方法入参
* @param methodProxy 用于调用原始方法
*/
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//调用方法之前,我们可以添加自己的操作
System.out.println("before method " + method.getName());
Object object = methodProxy.invokeSuper(o, args);
//调用方法之后,我们同样可以添加自己的操作
System.out.println("after method " + method.getName());
return object;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
代理类
import net.sf.cglib.proxy.Enhancer;
/**
* 代理类
*/
public 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();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
测试类
public class Main {
public static void main(String[] args) {
AliSmsService aliSmsService = (AliSmsService) CglibProxyFactory.getProxy(AliSmsService.class);
aliSmsService.send("java");
}
}
2
3
4
5
6
输出
before method send
send message:java
after method send
2
3
# JDK
动态代理和 CGLIB
动态代理对比
JDK
动态代理只能代理实现了接口的类或者直接代理接口,而CGLIB
可以代理未实现任何接口的类。 另外,CGLIB
动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法。- 就二者的效率来说,大部分情况都是
JDK
动态代理更优秀,随着JDK
版本的升级,这个优势更加明显。
# 静态代理和动态代理的对比
灵活性:动态代理更加灵活,不需要必须实现接口,可以直接代理实现类,并且可以不需要针对每个目标类都创建一个代理类。另外,静态代理中,接口一旦新增加方法,目标对象和代理对象都要进行修改。
JVM
层面:静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的class
文件。而动态代理是在运行时动态生成类字节码,并加载到JVM
中的。
# Java中实现回调的几种方式
- 反射,性能较差,不太推荐
/**
* 基于反射实现回调1
*/
public class Demo1 {
public static void main(String[] args) {
Request request=new Request();
new Thread(()->{
try {
request.send(CallBackClass.class,CallBackClass.class.getMethod("process"));
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
class Request {
private static Logger logger = LoggerFactory.getLogger(Request.class);
public void send(Class clazz, Method method) throws Exception {
// 模拟等待响应
Thread.sleep(3000);
logger.info("收到用户的请求,处理业务逻辑后发起回调");
method.invoke(clazz.newInstance());
logger.info("回调结束");
}
}
class CallBackClass {
private static Logger logger = LoggerFactory.getLogger(CallBackClass.class);
public void process() {
logger.info("这个是回调方法哦");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
- 直接将回调方法封装成一个对象传入
/**
* 直接调用,耦合度比较高
*/
public class Demo2 {
public static void main(String[] args) {
Request1 request=new Request1();
new Thread(()->{
try {
request.send(new CallBackClass1());
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
class Request1 {
private static Logger logger = LoggerFactory.getLogger(Request.class);
public void send(CallBackClass1 callBackClass) throws Exception {
// 模拟等待响应
Thread.sleep(3000);
logger.info("收到用户的请求,处理业务逻辑后发起回调");
callBackClass.process();
logger.info("回调结束");
}
}
class CallBackClass1 {
private static Logger logger = LoggerFactory.getLogger(CallBackClass.class);
public void process() {
logger.info("这个是回调方法哦");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
- 将回调行为抽成一个接口作为参数,真正的回调继承接口实现就好了
/**
* 接口调用
*/
public class Demo3 {
public static void main(String[] args) {
CallBackInterface callBackInterface=new CallBackImpl();
Request2 request2=new Request2();
new Thread(()->{
try {
request2.send(callBackInterface);
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
class Request2 {
private static Logger logger = LoggerFactory.getLogger(Request.class);
public void send(CallBackInterface CallBackInterface) throws Exception {
// 模拟等待响应
Thread.sleep(3000);
logger.info("收到用户的请求,处理业务逻辑后发起回调");
CallBackInterface.process();
logger.info("回调结束");
}
}
interface CallBackInterface{
void process();
}
class CallBackImpl implements CallBackInterface{
private static Logger logger = LoggerFactory.getLogger(CallBackImpl.class);
@Override
public void process() {
logger.info("处理回调");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
- 准确来说并不是第4种方法,这种就是基于方法3的接口用lambda实现而已
/**
* lambda处理回调
*/
public class Demo4 {
private static Logger logger = LoggerFactory.getLogger(Demo4.class);
public static void main(String[] args) {
CallBackInterface callBackInterface=new CallBackImpl();
Request2 request2=new Request2();
new Thread(()->{
try {
request2.send(()->{logger.info("lambda处理回调");});
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17