zoukankan      html  css  js  c++  java
  • Spring中获取被代理的对象

    Spring中获取被代理的对象

    Spring中获取被代理的对象

    获取Spring被代理对象的JAVA工具类

    ​ Spring采用CGLIB或者JDK动态代理来实现AOP,那如何获取 被代理对象?通过ApplicationContext.getBean()获取到的对象都是 利用字节码动态生成的 增强对象,那假如我们有场景获取 被代理的对象,方式如下: (封装到工具类形式里面,直接通过getTrueTargetFrom0即可调用,需要强转一下类型.)

    import org.springframework.aop.TargetSource;
    import org.springframework.aop.framework.AdvisedSupport;
    import org.springframework.cglib.proxy.MethodInterceptor;
    
    import java.lang.reflect.Field;
    
    public class SpringUtils {
    
        public static Object getTrueTargetFrom0(Object obj){
            try {
                //获取第一个拦截器
                Field field = obj.getClass().getDeclaredField("CGLIB$CALLBACK_0");
                field.setAccessible(true);
                MethodInterceptor interceptor = (MethodInterceptor) field.get(obj);
    
                //获取拦截器的属性advised
                Field advised = interceptor.getClass().getDeclaredField("advised");
                advised.setAccessible(true);
                AdvisedSupport advisedSupport = (AdvisedSupport) advised.get(interceptor);
                TargetSource targetSource=null;
                if (advisedSupport!=null) {
                    targetSource = advisedSupport.getTargetSource();
                }
               return targetSource!=null?targetSource.getTarget():null;
            } catch (Exception e) {
                e.printStackTrace();
            }
               return null;
        }
    
    
        public static Object getTrueTargetFrom1(Object obj){
            try {
                //获取第二个拦截器
                Field field = obj.getClass().getDeclaredField("CGLIB$CALLBACK_1");
                field.setAccessible(true);
                MethodInterceptor interceptor = (MethodInterceptor) field.get(obj);
    
                //获取拦截器的属性advised
                Field advised = interceptor.getClass().getDeclaredField("target");
                advised.setAccessible(true);
                Object result =  advised.get(interceptor);
                return result;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
        public static Object getTrueTargetFrom3(Object obj){
            try {
                //获取第四个拦截器
                Field field = obj.getClass().getDeclaredField("CGLIB$CALLBACK_3");
                field.setAccessible(true);
                MethodInterceptor interceptor = (MethodInterceptor) field.get(obj);
    
                //获取拦截器的属性advised
                Field advised = interceptor.getClass().getDeclaredField("target");
                advised.setAccessible(true);
                Object result =  advised.get(interceptor);
                return result;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    

    效果截图

    ​ 效果说明: 三个方法效果一样.

    1. System.setProperty("cglib.debugLocation","E:\data\spring")用来指定 代理类class文件生成位置,在CGLIB中也可以这么用。
    2. getTrueTargetFrom0等方法是如何获取被代理对象UserService的,有一定CGLIB基础之后,代理类中存在回调类,属性CGLIB$CALLBACK_x(x 为数字),Spring生成 代理类时候会将 被代理的UserService保存起来在某些CGLIB$CALLBACK_x中. 这些需要查看源码才能有个轮廓的了解。

    获取Spring被代理对象什么时候可能会用到?

    ​ CGLIB代理是基于继承或者实现接口的方式,我们可能只需要知道 class 属性就能生成代理类,这样做带来的问题:
    父类(被代理类)的属性可能我们 只能通过 方法 来获取,比如有个dao属性,不是private修饰类型的,我们不想通过getDao来获取,想直接调用 属性 ,那可能就是空的. 下面看下例子,

    @Service
    public class UserService {
        @Autowired
        public UserDao dao;
    
        public void addUser(){
            System.out.println("添加用户");
        }
    
    
        public UserDao getDao() {
            return dao;
        }
    }
    

    这样一个类可能不符合代码编写,但是用来介绍实验效果够了。 比如我们 ApplicationContext.getBean(UserService.class).getDao是能够获取到注入的DAO,但是ApplicationContext.getBean(UserService.class).dao输出的就是null. 原因很简单,CGLIB增强的是方法,dao肯定就是空的,getDao被代理了之后就进入到了真正的UserServicegetDao 方法.

    ​ 提示:代码不规范,同事两行泪,这种写法不太规范仅供出现问题时定位.

  • 相关阅读:
    今天查看了java文档中的sort方法
    记录下git简单使用(以码云为例)
    今天是leetcode300
    今天了解了一下摩尔投票法
    # 今天学习了一下java8的lambda表达式
    make命令的-j -f参数说明
    shell中单引号和双引号区别
    判断字符串相似度
    shell计算时间差
    hive cli转hive beeline的几个例子
  • 原文地址:https://www.cnblogs.com/lvbinbin2yujie/p/11143402.html
Copyright © 2011-2022 走看看