反射

Java 反射,可以获取任意类的名称、package 信息、所有属性、方法、注解、类型、类加载器、现实接口等,并且可以调用任意方法和实例化任意一个类的对象,通过反射我们可以实现动态装配,降低代码的耦合度、实现动态代理等,不过反射的过度使用会严重消耗系统资源
image.png

  1. 反射的优缺点
    1. 可以动态执行,在运行期间根据业务功能动态执行方法、访问属性,最大限度发挥了java的灵活性。
    2. 缺点:对性能有影响,这类操作总是慢于直接执行java代码。
  2. 如何解决反射的性能问题
  • 聊到性能,记得想好怎么回答优化方面的问题,否则就是自己搬起石头砸自己的脚。
  • image.png

    静态代理

    想对方法进行一个增强, 需要修改源代码. 假如有100个方法, 我们需要修改100个方法的源代码来实现方法增强.

    1. /**
    2. * 增删改查方法增强例子
    3. * @author : Shen Hanbo
    4. * @date : 2020/11/9 16:01
    5. */
    6. public class CrudServiceImpl implements CrudService {
    7. public void insert() {
    8. System.out.println("增强新增前置处理");
    9. System.out.println("新增");
    10. System.out.println("增强新增后置处理");
    11. }
    12. public void delete() {
    13. System.out.println("增强删除前置处理");
    14. System.out.println("删除");
    15. System.out.println("增强删除后置处理");
    16. }
    17. public void update() {
    18. System.out.println("增强更新前置处理");
    19. System.out.println("更新");
    20. System.out.println("增强更新后置处理");
    21. }
    22. public void query() {
    23. System.out.println("增强查询前置处理");
    24. System.out.println("查询");
    25. System.out.println("增强查询后置处理");
    26. }
    27. //...假如下面还有一堆方法的话, 每个方法都需要去手动改源代码
    28. }

    解决思路
    生成一个代理类, 代理类要和被代理类拥有一样方法(实现同一个接口), 然后调用方直接调用代理类的方法, 而非被代理类的方法. 在代理类的实现方法中, 增强被代理类的方法, 然后客户端请求代理类.

  • 代码

用房东/中介/租客

  1. 首先房东和中介都需要有”租房”这个方法, 中介来增强房东的”租房”方法,因此需要定义一个租房接口. ```java /**

    • 租房接口
    • @author : Shen Hanbo
    • @date : 2020/11/9 15:52 */ public interface ZuFang {

      /**

      • 房东和中介都需要实现租房接口,因为他们都有租房的功能 */ void zuFang();

}

  1. 2. 房东的"租房"方法, 即被代理的方法
  2. ```java
  3. /**
  4. * 房东类
  5. * @author : Shen Hanbo
  6. * @date : 2020/11/9 15:54
  7. */
  8. public class FangDong implements ZuFang {
  9. /**
  10. * 房东出租房子(被代理方法)
  11. */
  12. public void zuFang() {
  13. System.out.println("房东出租房子");
  14. }
  15. }
  1. 中介的”租房”方法, 即代理方法, 在代理方法中对原方法进行增强 ```java /**

    • 中介(代理类)
    • @author : Shen Hanbo
    • @date : 2020/11/9 16:10 */ public class ZhongJie implements ZuFang {

      /**

      • 需要一个房东 */ private FangDong fangDong;

      public ZhongJie(FangDong fangDong) { this.fangDong = fangDong; }

      /**

      • 中介代理房东出租房子(代理方法) */ public void zuFang() { //前置增强 System.out.println(“前置增强”); //原方法 fangDong.zuFang(); //后置增强 System.out.println(“后置增强”); } }
  1. 4. main
  2. ```java
  3. public static void main(String[] args) {
  4. //房东
  5. FangDong fangDong = new FangDong();
  6. //中介代理房东
  7. ZhongJie zhongJie = new ZhongJie(fangDong);
  8. //调用中介的租房方法
  9. zhongJie.zuFang();
  10. }
  11. /*
  12. 打印结果:
  13. 前置增强
  14. 房东出租房子
  15. 后置增强
  16. */

动态代理

  • 痛点
    • 静态代理需要手动编写代理类, 比如上个例子中的ZhongJie类.
    • 静态代理类只能为特定的接口(Service)服务。如想要为多个接口服务则需要建立很多个代理类, **每个接口都需要自己的代理类**.
  • 解决思路

在上面的示例中**,一个代理只能代理一种类型,而且是在编译期就已经确定被代理的对象。
动态代理是在运行时**,通过反射机制实现动态代理,并且能够代理各种类型的对象

  1. 使用InvocationHandler接口

    1. //Object proxy:被代理的对象
    2. //Method method:要调用的方法
    3. //Object[] args:方法调用时所需要参数
    4. public interface InvocationHandler {
    5. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
    6. }
  2. Proxy类 ```java //用于获取代理类 //CLassLoader loader:类的加载器
    //Class<?> interfaces:得到全部的接口
    //InvocationHandler h:得到InvocationHandler接口的子类的实例

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h);

  1. final Class<?>[] intfs = interfaces.clone();
  2. final SecurityManager sm = System.getSecurityManager();
  3. if (sm != null) {
  4. checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
  5. }
  6. /*
  7. * Look up or generate the designated proxy class.
  8. */
  9. Class<?> cl = getProxyClass0(loader, intfs);
  10. /*
  11. * Invoke its constructor with the designated invocation handler.
  12. */
  13. try {
  14. if (sm != null) {
  15. checkNewProxyPermission(Reflection.getCallerClass(), cl);
  16. }
  17. final Constructor<?> cons = cl.getConstructor(constructorParams);
  18. final InvocationHandler ih = h;
  19. if (!Modifier.isPublic(cl.getModifiers())) {
  20. AccessController.doPrivileged(new PrivilegedAction<Void>() {
  21. public Void run() {
  22. cons.setAccessible(true);
  23. return null;
  24. }
  25. });
  26. }
  27. return cons.newInstance(new Object[]{h});
  28. } catch (IllegalAccessException|InstantiationException e) {
  29. throw new InternalError(e.toString(), e);
  30. } catch (InvocationTargetException e) {
  31. Throwable t = e.getCause();
  32. if (t instanceof RuntimeException) {
  33. throw (RuntimeException) t;
  34. } else {
  35. throw new InternalError(t.toString(), t);
  36. }
  37. } catch (NoSuchMethodException e) {
  38. throw new InternalError(e.toString(), e);
  39. }
  40. }
  1. 3. 实现
  2. 目前动态代理可以提供两种,分为
  3. - JDK 原生动态代理
  4. - 基于接口实现
  5. - 当目标类有接口的时候才会使用JDK动态代理,其实是因为JDK动态代理无法代理一个没有接口的类,因为JDK动态代理是利用反射机制生成一个实现代理接口的匿名类
  6. ![](https://cdn.nlark.com/yuque/0/2021/webp/8429887/1610976255297-e2844563-5896-4616-a624-efe3f51ba990.webp#align=left&display=inline&height=280&margin=%5Bobject%20Object%5D&originHeight=280&originWidth=455&size=0&status=done&style=none&width=455)
  7. ```java
  8. public interface Target {
  9. public String execute();
  10. }
  11. ========================
  12. public class TargetImpl implements Target {
  13. @Override
  14. public String execute() {
  15. System.out.println("TargetImpl execute!");
  16. return "execute";
  17. }
  18. }
  19. =======================
  20. public class DynamicProxyHandler implements InvocationHandler{
  21. private Target target;
  22. public DynamicProxyHandler(Target target) {
  23. this.target = target;
  24. }
  25. @Override
  26. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  27. System.out.println("========before==========");
  28. Object result = method.invoke(target,args);
  29. System.out.println("========after===========");
  30. return result;
  31. }
  32. }
  33. ============================
  34. public class DynamicProxyTest {
  35. public static void main(String[] args) {
  36. Target target = new TargetImpl();
  37. DynamicProxyHandler handler = new DynamicProxyHandler(target);
  38. Target proxySubject = (Target) Proxy.newProxyInstance(
  39. TargetImpl.class.getClassLoader(),
  40. TargetImpl.class.getInterfaces(),
  41. handler);
  42. // 通过原来的方法名调用
  43. String result = proxySubject.execute(); // 返回被代理对象的方法返回值
  44. System.out.println(result);
  45. }
  46. }
  47. 结果:
  48. ========before==========
  49. TargetImpl execute!
  50. ========after===========
  51. execute


  • CGLIB动态代理
    • 基于继承当前类的子类实现的
    • CGLIB是针对类实现代理,主要是对指定的类生成一个子类,并且覆盖其中的方法。

Spring中默认使用的是JDK动态代理,除非目标类没有实现接口,才会转为CGLIB代理,如果想要强行使用CGLIB代理免责需要在Spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true" />
然而在SpringBoot中,从2.0开始就默认使用CGLIB代理。
4反射(不常问) - 图3

CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用(当方法为final修饰时 无法实现代理),顺势织入横切逻辑。

  1. 目标类 是一个类而不是接口了 ```java package com.test.cglib;

public class Target {

  1. public String execute() {
  2. String message = "-----------test------------";
  3. System.out.println(message);
  4. return message;
  5. }

}

  1. 2. 代理类
  2. 代理对象的生成过程由Enhancer类实现,大概步骤如下:<br /> 1、生成代理类Class的二进制字节码;<br /> 2、通过Class.forName加载二进制字节码,生成Class对象;<br /> 3、通过反射机制获取实例构造,并初始化代理类对象。
  3. ```java
  4. package com.test.cglib;
  5. import net.sf.cglib.proxy.MethodInterceptor;
  6. import net.sf.cglib.proxy.MethodProxy;
  7. import java.lang.reflect.Method;
  8. public class MyMethodInterceptor implements MethodInterceptor{
  9. @Override
  10. public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
  11. System.out.println(">>>>MethodInterceptor start...");
  12. Object result = proxy.invokeSuper(obj,args);
  13. System.out.println(">>>>MethodInterceptor ending...");
  14. return "result";
  15. }
  16. }
  1. 测试类 ```java package com.test.cglib;

import net.sf.cglib.proxy.Enhancer;

public class CglibTest {

  1. public static void main(String ... args) {
  2. System.out.println("***************");
  3. Target target = new Target();
  4. CglibTest test = new CglibTest();
  5. Target proxyTarget = (Target) test.createProxy(Target.class);
  6. String res = proxyTarget.execute();
  7. System.out.println(res);
  8. }
  9. /**
  10. 代理对象的生成过程由Enhancer类实现,大概步骤如下:
  11. 1、生成代理类Class的二进制字节码;
  12. 2、通过Class.forName加载二进制字节码,生成Class对象;
  13. 3、通过反射机制获取实例构造,并初始化代理类对象。

作者:冬天里的懒喵 链接:https://www.jianshu.com/p/37d0ac9233b9 来源:简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 **/

  1. public Object createProxy(Class targetClass) {
  2. Enhancer enhancer = new Enhancer();
  3. enhancer.setSuperclass(targetClass);
  4. enhancer.setCallback(new MyMethodInterceptor());
  5. return enhancer.create();
  6. }

} ```