Java 反射和动态代理

Java 反射和动态代理

1. 类加载机制

1.1 类加载过程

大体来说,可以分为三个阶段:加载 $rightarrow$ 链接 $rightarrow$ 初始化。具体过程如图:1

类加载时机:

1.创建类的实例,也就是new一个对象

2.访问某个类或接口的静态变量,或者对该静态变量赋值

3.调用类的静态方法

4.反射

5.初始化一个类的子类(会首先初始化子类的父类)

6.虚拟机启动时标明的启动类,即文件名和类名相同的那个类

1.2 加载

​ 将class字节码文件内容加载到内存中,并将这些静态数据转换为方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据(Class Metadata)的返回入口。

Class对象代表啥:

​ 每当一个类加载到内存中后,这个类便成为运行时类,虚拟机会在堆区创建一个有关这个类的Class对象。

1.1 类加载过程

1.3 类加载器

  • Bootstrap Class loader
  • Extension Class loader
  • Application Class loader

双亲委托模型

​ 如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式。

​ 即每个儿子都很懒,每次有活就丢给父亲去干,直到父亲说这件事我也干不了时,儿子自己才想办法去完成。

为什么采用这种模式

  • 避免类的重复加载
  • 保证安全, Java中定义的核心类不会被随意替换

2. 反射

​ 反射到底是干啥的?答:不用new也可以获取到一个对象的实例。可以在运行时构造任意一个类的对象,可以在运行时处理注解、获取泛型信息等。

​ 反射相关的apijava.lang.reflect包下。以下是通过反射调用show方法的代码清单。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.nefu.reflect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class {
public () {

}
public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {

Class<?> clazz = Class.forName("com.nefu.reflect.Main");
//2. 创建一个Main类的实例对象
Object obj = clazz.getDeclaredConstructor().newInstance();
Method show = clazz.getDeclaredMethod("show");
//3. 避免权限不够
show.setAccessible(true);
System.out.println(show.getReturnType());
//4. 调用obj的show方法
show.invoke(obj);
}
public void show() {
System.out.println("hello world");
}
}

3. 动态代理

​ 代理类可以增强被代理类对象方法。

3.1 静态代理

1
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 class Test01 {
public static void main(String[] args) {
NikeClothFactory nikeClothFactory = new NikeClothFactory();
NikeClothFactoryProxy proxy = new NikeClothFactoryProxy(nikeClothFactory);
proxy.invoke();
}
大专栏  Java 反射和动态代理class="line">}
interface ClothFactory{
void product();
}
///被代理类
class NikeClothFactory implements ClothFactory{

public void product() {
System.out.println("Nike 开始生产....");
}
}
//代理类
class NikeClothFactoryProxy{
ClothFactory clothFactory;

public NikeClothFactoryProxy(ClothFactory clothFactory) {

this.clothFactory = clothFactory;
}

public void invoke() {
System.out.println("前置处理");
clothFactory.product();
System.out.println("后置处理");
}
}

​ 以上代码可以看出,当再有一个类实现ClothFactory接口,我们得继续编写一个对应的代理类进行增强处理。静态代理在编译期就确定了代理对象

3.2 动态代理

​ 在Java中,动态代理实现有JDK自带的动态代理,CGLib动态代理。 通过动态代理,可以无需声明代理类。是使用反射和字节码的技术,在运行期创建指定接口或类的子类(即动态代理类)以及其实例对象的技术。通过动态代理技术可以无侵入地对代码进行增强。

​ 两种动态代理的最大的区别是:JDK动态代理要求被代理对象必须基于接口来实现。动态代理类和被代理类必须实现同一个接口。动态代理只能对接口中声明的方法进行代理。对那些没有实现接口的bean。JDK动态代理无法代理。而CGLib通过继承被代理类的方式实现代理。

​ 在JDK动态代理中,主要调用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。依然以静态代理中的ClothFactory为例,编写动态代理。

1
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
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//被代理类
class PumaClothFactory implements ClothFactory{

public void product() {
System.out.println("Puma 源自南美!");
}
}
//测试类
public class Test02 {
public static void main(String[] args) {
PumaClothFactory obj = new PumaClothFactory();
ClothFactory proxy = (ClothFactory)Proxy
.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),
new PumaInvocationHandler(obj));
proxy.product();
}
}
//每一个动态代理实例都有一个关联的InvocationHandler
class PumaInvocationHandler implements InvocationHandler{
ClothFactory obj;
public PumaInvocationHandler(ClothFactory clothFactory) {
// TODO Auto-generated constructor stub
this.obj = clothFactory;
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
System.out.println("前置处理");
//真正调用被代理类的方法
method.invoke(obj, args);
System.out.println("后置处理");
return null;
}
}

​ 可以看出,上述代码中并没有显示的编写代理类,而是调用了Proxy.newProxyInstance方法来动态创建代理类。

猜你喜欢

转载自www.cnblogs.com/lijianming180/p/12410226.html
今日推荐