参考文献

反射

img

获取Class对象的四种方式

  • 知道具体类的情况下可以使用:

    1
    Class klass = Person.class;
  • 通过 Class.forName()传入类的全路径获取:

    1
    Class klass = Class.forName("cn.holelin.Person");
  • 通过对象实例instance.getClass()获取:

    1
    2
    Person o = new Person();
    Class klass = o.getClass();
  • 通过类加载器xxxClassLoader.loadClass()传入类路径获取:

    1
    ClassLoader.getSystemClassLoader().loadClass("cn.holelin.Person");
    • 通过类加载器获取 Class 对象不会进行初始化,意味着不进行包括初始化等一系列步骤,静态代码块和静态对象不会得到执行

使用Class对象

  • 一旦得到了 Class 对象,便可以正式地使用反射功能了:
    • 使用newInstance()来生成一个该类的实例.它要求该类中拥有一个无参数的构造器.
    • 使用 isInstance(Object) 来判断一个对象是否该类的实例,语法上等同于 instanceof 关键字.
    • 使用 Array.newInstance(Class,int) 来构造该类型的数组.
    • 使用 getFields()/getConstructors()/getMethods() 来访问该类的成员.除了这三个之外,Class 类还提供了许多其他方法.需要注意的是,方法名中带 Declared 的不会返回父类的成员,但是会返回私有成员;而不带Declared的则相反.
  • 当获得了类成员之后,可以进一步做如下操作:
    • 使用 Constructor/Field/Method.setAccessible(true) 来绕开 Java 语言的访问限制.
    • 使用 Constructor.newInstance(Object[]) 来生成该类的实例.
    • 使用 Field.get/set(Object) 来访问字段的值.
    • 使用 Method.invoke(Object, Object[])来调用方法.

反射的作用

  • 在运行时获取类的信息

代理模式

  • 代理模式的主要作用是扩展目标对象的功能,比如说在目标对象的某个方法执行前后你可以增加一些自定义的操作。

代理模式的分类

  • 代理根据创建代理类的时间点的不同,可以分为静态代理动态代理

静态代理

  • 由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来.在程序运行之前,代理类的.class文件就已经生成.

动态代理

  • 基于接口的动态代理:基于接口的动态代理是指代理类和目标类都要实现同一个接口,代理类通过实现InvocationHandler接口,实现代理类对目标类的代理逻辑。在运行时,使用Proxy类的静态方法newProxyInstance()创建代理类的实例。这种方式只能为接口创建代理对象。

  • 基于类的动态代理:基于类的动态代理是指代理类和目标类都是普通的 Java 类,代理类通过继承目标类,覆盖其中的方法,实现代理类对目标类的代理逻辑。在运行时,使用 ByteBuddy、ASM 等字节码工具生成代理类的字节码,并通过类加载器加载代理类。这种方式可以为类创建代理对象。

  • Java中,动态代理常见的又有两种实现方式: JDK动态代理和CGLIB代理

    • JDK动态代理其实就是运用了反射机制;
      • JDK动态代理会帮我们实现接口的方法,通过invokeHandler所需要的方法进行增强;
      • JDK中的Proxy只能为接口生成代理类,如果想给某个类创建代理类,那么Proxy是无能为力的,
    • CGLIB代理则是用ASM框架,通过修改其字节码生成子类来处理

JDK动态代理实现步骤

  • 在Java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象.

    1
    2
    java.lang.reflect.Proxy
    java.lang.reflect.InvocationHandler
  1. 通过实现InvocationHandler接口来自定义自己的InvocationHandler;

    1
    2
    //创建一个与代理对象相关联的InvocationHandler
    InvocationHandler stuHandler = new MyInvocationHandler<Person>(stu);
  2. 通过Proxy.getProxyClass获得动态代理类

    1
    Class<?> stuProxyClass = Proxy.getProxyClass(Person.class.getClassLoader(), new Class<?>[] {Person.class});
  3. 通过反射机制获得代理类的构造方法,方法签名为getConstructor(InvocationHandler.class)

    1
    Constructor<?> constructor = PersonProxy.getConstructor(InvocationHandler.class);
  4. 通过构造函数获得代理对象并将自定义的InvocationHandler实例对象传为参数传入

    1
    Person stuProxy = (Person) cons.newInstance(stuHandler);
  5. 通过代理对象调用目标方法

  • 其中上述步骤可以简化一下,如下所示

    1
    2
    3
    4
    5
     //创建一个与代理对象相关联的InvocationHandler
    InvocationHandler stuHandler = new MyInvocationHandler<Person>(stu);
    //创建一个代理对象stuProxy,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
    Person stuProxy = (Person) Proxy.newProxyInstance(
    Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);

CGLIB动态代理实现

拦截所有方法

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
40
41
42
43
import org.junit.Test;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

public class CglibTest {
   @Test
   public void test1() {
       //使用Enhancer来给某个类创建代理类,步骤
       //1.创建Enhancer对象
       Enhancer enhancer = new Enhancer();
       //2.通过setSuperclass来设置父类型,即需要给哪个类创建代理类
       enhancer.setSuperclass(Service1.class);
       /*3.设置回调,需实现org.springframework.cglib.proxy.Callback接口,
       此处我们使用的是org.springframework.cglib.proxy.MethodInterceptor,也是一个接口,实现了Callback接口,
       当调用代理对象的任何方法的时候,都会被MethodInterceptor接口的intercept方法处理*/
       enhancer.setCallback(new MethodInterceptor() {
           /**
            * 代理对象方法拦截器
            * @param o 代理对象
            * @param method 被代理的类的方法,即Service1中的方法
            * @param objects 调用方法传递的参数
            * @param methodProxy 方法代理对象
            * @return
            * @throws Throwable
            */
           @Override
           public Object intercept(Object o, Method method, Object[] objects,
MethodProxy methodProxy) throws Throwable {
               System.out.println("调用方法:" + method);
               //可以调用MethodProxy的invokeSuper调用被代理类的方法
               Object result = methodProxy.invokeSuper(o, objects);
               return result;
      }
      });
       //4.获取代理对象,调用enhancer.create方法获取代理对象,这个方法返回的是Object类型的,所以需要强转一下
       Service1 proxy = (Service1) enhancer.create();
       //5.调用代理对象的方法
       proxy.m1();
       proxy.m2();
 }
}

直接放行,不做任何操作(NoOp.INSTANCE)

1
2
3
4
5
6
7
8
9
@Test
public void test() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Service3.class);
enhancer.setCallback(NoOp.INSTANCE);
Service3 proxy = (Service3) enhancer.create();
System.out.println(proxy.m1());
System.out.println(proxy.m2());
}

不同的方法使用不同的拦截器(CallbackFilter)

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
@Test
public void test() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Service4.class);
//创建2个Callback
Callback[] callbacks = {
//这个用来拦截所有query开头的方法
new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[]
objects, MethodProxy methodProxy) throws Throwable {
long starTime = System.nanoTime();
Object result = methodProxy.invokeSuper(o, objects);
long endTime = System.nanoTime();
System.out.println(method + ",耗时(纳秒):" + (endTime -
starTime));
return result;
}
},
//下面这个用来拦截所有get开头的方法,返回固定值的
new FixedValue() {
@Override
public Object loadObject() throws Exception {
return "FixedValue";
}
}
};
enhancer.setCallbackFilter(new CallbackFilter() {
@Override
public int accept(Method method) {
return 0;
}
});
//调用enhancer的setCallbacks传递Callback数组
enhancer.setCallbacks(callbacks);
/**
* 设置过滤器CallbackFilter
* CallbackFilter用来判断调用方法的时候使用callbacks数组中的哪个Callback来处理当前方法
* 返回的是callbacks数组的下标
*/
enhancer.setCallbackFilter(new CallbackFilter() {
@Override
public int accept(Method method) {
//获取当前调用的方法的名称
String methodName = method.getName();
/**
* 方法名称以query开头,
* 返回callbacks中的第1个Callback对象来处理当前方法,
* 否则使用第二个Callback处理被调用的方法
*/
return methodName.startsWith("query") ? 0 : 1;
}
});
Service4 proxy = (Service4) enhancer.create();
System.out.println("---------------");
proxy.query1();
System.out.println("---------------");
proxy.query2();
System.out.println("---------------");
System.out.println(proxy.del1());
System.out.println("---------------");
System.out.println(proxy.del2());
}

不同的方法使用不同的拦截器(CallbackHelper)

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
@Test
public void test() {
Enhancer enhancer = new Enhancer();
//创建2个Callback
Callback costTimeCallback = (MethodInterceptor) (Object o, Method method,
Object[] objects, MethodProxy methodProxy) -> {
long starTime = System.nanoTime();
Object result = methodProxy.invokeSuper(o, objects);
long endTime = System.nanoTime();
System.out.println(method + ",耗时(纳秒):" + (endTime - starTime));
return result;
};
//下面这个用来拦截所有get开头的方法,返回固定值的
Callback fixdValueCallback = (FixedValue) () -> "FixedValue";
CallbackHelper callbackHelper = new CallbackHelper(Service.class, null) {
@Override
protected Object getCallback(Method method) {
return method.getName().startsWith("query") ? costTimeCallback :
fixdValueCallback;
}

};
enhancer.setSuperclass(Service.class);
//调用enhancer的setCallbacks传递Callback数组
enhancer.setCallbacks(callbackHelper.getCallbacks());
/**
* 设置CallbackFilter,用来判断某个方法具体走哪个Callback
*/
enhancer.setCallbackFilter(callbackHelper);
Service proxy = (Service) enhancer.create();
System.out.println("---------------");
proxy.query1();
System.out.println("---------------");
proxy.query2();
System.out.println("---------------");
System.out.println(proxy.del1());
System.out.println("---------------");
System.out.println(proxy.del2());
}s

JDK 动态代理的优缺点

使用场景

  • 如果程序需要频繁、反复地创建代理对象,则JDK动态代理在性能上更占优.

优点

  1. 简单易用:JDK 动态代理不需要任何第三方库支持,只需要 JDK 提供的相关类即可.
  2. 安全可靠:JDK 动态代理只能代理实现了接口的类,不会对未实现接口的类进行代理,避免了对未知类型的方法调用.
  3. 高效性能:JDK 动态代理是基于 Java 反射机制实现的,在生成类上比较高效.

缺点

  1. 只能代理实现了接口的类,无法代理未实现接口的类.
  2. 无法代理final类和方法:由于cglib是通过继承来实现代理的,因此无法代理final类和方法.
  3. 只能代理public方法,不能代理privateprotected方法.
  4. 需要实现接口并重写其中的方法,如果接口中方法较多,则需要编写大量的重复代码,增加了代码的复杂性和维护难度.

CGLIB 优缺点

适用场景

  • 不需要频繁创建代理对象的应用,如Spring中默认的单例bean,只需要在容器启动时生成一次代理对象.

优点

  1. 支持代理未实现接口的类,能够代理所有的方法.
  2. 无需实现接口并重写方法,可以直接对目标类进行代理.
  3. 在创建代理对象时不需要传递接口或类类型,相对于 JDK 动态代理更加灵活.
  4. CGLIB使用ASM框架直接对字节码进行操作,在类的执行过程中比较高效

缺点

  1. 无法代理final类和方法:由于cglib是通过继承来实现代理的,因此无法代理final类和方法.
  2. 性能较差:相比于JDK动态代理,cglib的性能较差.这主要是由于cglib是通过创建目标类的子类来实现代理的,而创建子类的过程需要较多的时间和内存空间.
  3. 对于构造函数的处理:cglib无法代理没有无参构造函数的类.如果目标类没有无参构造函数,那么cglib会抛出异常.
  4. 对于私有方法和私有属性的处理:cglib无法访问和修改私有方法和私有属性,因为它是通过继承来实现代理的.