• 当动态代理遇到ioc (四)真正的cglib


    当动态代理遇到ioc (二)cglib 中的代理仍然不能切子函数,所以有本篇

    1 此前已经解决cglib与asm冲突:当动态代理遇到ioc (三)cglib与asm jar包冲突

    2 事务的try catch模型由exception扩叨叨throwable

    3 抽象层

    由于我们反射调用方法时,不再是method.invoke(origin object, args[]),所以抽象层要修改一下

    protected Object invokeHasTransactional(SCEF_DB_TRANSACTIONAL scef_db_transactional, Method method, Object[] args, MethodProxy methodProxy, Object oCglib) throws Throwable {
    
    protected Object invokeNoTransactional(Method method, Object[] args, MethodProxy methodProxy, Object oCglib) throws Throwable {
    

      

    Object returnValue = aopInvoke(method, args, methodProxy, oCglib);
    

      

    jdk

        protected Object aopInvoke(Method method, Object [] args, MethodProxy methodProxy, Object oCglib) throws Throwable {
            Object returnValue = method.invoke(target, args);
            return returnValue;
        }
    

      

            if(scef_db_transactional != null) {
                res = invokeHasTransactional(scef_db_transactional, method, args, null, null);
            } else {
                res = invokeNoTransactional(method, args, null, null);
            }
    

      

    cglib

        @Override
        protected Object aopInvoke(Method method, Object[] args, MethodProxy methodProxy, Object oCglib) throws Throwable {
    ////////return method.invoke(target, args); return methodProxy.invokeSuper(oCglib, args); }

      

            if (scef_db_transactional != null) {
                res = invokeHasTransactional(scef_db_transactional, null, objects, methodProxy, o);
            } else {
                res = invokeNoTransactional(null, objects, methodProxy, o);
            }
    

      

    4 ioc反哺

    解决了3形成了子函数切面后,报错:

    首次处理 class sun.myproxybean.MyServiceImpl的ioc
    处理 class sun.myproxybean.MyServiceImpl的myMapper1
    处理 class sun.myproxybean.MyServiceImpl的myMapper2
    处理 class sun.myproxybean.MyServiceImpl的myDao
    public void sun.myproxybean.MyServiceImpl.multi()
    @sun.myproxy.MY_TRANSACTIONAL()
    public final void sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d.multi()
    null
    public abstract void sun.myproxybean.MyService.multi()
    null
    Exception in thread "main" java.lang.RuntimeException: java.lang.NullPointerException
    	at sun.myproxy.OdsTransactionProxyFactory.invokeHasTransactional(OdsTransactionProxyFactory.java:112)
    	at sun.myproxy.OdsTransactionCglibFactory.intercept(OdsTransactionCglibFactory.java:65)
    	at sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d.multi(<generated>)
    	at sun.mybatis.Main.main(Main.java:58)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.lang.reflect.Method.invoke(Method.java:498)
    	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
    Caused by: java.lang.NullPointerException
    	at sun.myproxybean.MyServiceImpl.multi(MyServiceImpl.java:29)
    	at sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d.CGLIB$multi$0(<generated>)
    	at sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d$$FastClassByCGLIB$$8fca1f76.invoke(<generated>)
    	at net.sf.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
    	at sun.myproxy.OdsTransactionCglibFactory.aopInvoke(OdsTransactionCglibFactory.java:73)
    	at sun.myproxy.OdsTransactionProxyFactory.invokeHasTransactional(OdsTransactionProxyFactory.java:100)
    	... 8 more
    

    显示ioc null了,故我们回顾一下IOC()中,反哺是反到原始对象

    为了切子函数,不再使用method.invoke(origin object, args[])原对象,而是使用代理对象(子类对象)methodProxy.invokeSuper(oCglib, args);,因此反哺应在子类对象上进行

    protected Object getTarget() {
    return this.target;
    }

    @SuppressWarnings("squid:S3011")
    protected void IOC() throws IllegalAccessException {
    if (!alreadyIOC) {
    Class clProxy = target.getClass();
    Field[] fields = clProxy.getDeclaredFields();
    for (Field field : fields) {

    field.setAccessible(true);
    if (!field.isAnnotationPresent(getBeanInjectAnnotationGuice()))
    continue;
    String name = field.getName();
    Class fieldType = field.getType();
    Object obj = getBeanFromFactoryGuice(fieldType);
    if (obj != null)
    field.set(getTarget(), obj);
    }
    alreadyIOC = true;
    }
    }
    public class OdsTransactionCglibFactory extends OdsTransactionProxyFactory implements MethodInterceptor {

    public OdsTransactionCglibFactory(Object target) {
    super(target);
    }

    private Object targetCglib;

    @Override
    protected Object getTarget() {
    return targetCglib;
    }
    @Override
    public Object getProxyInstance() {
    Enhancer en = new Enhancer();
    en.setSuperclass(target.getClass());
    en.setCallback(this);
    Object oCglib = en.create();
    this.targetCglib = oCglib;
    return oCglib;
    }

    5 发现一个bug

        @Inject
    private MyDao myDao; // https://www.cnblogs.com/silyvin/p/14237270.html 第5点
    // private MyDaoImpl myDao; // 导致myDao不是注入代理类对象,而是new MyDaoImpl,guice会自己newInstance一个

    6 对于接口MyService中没有,仅在MyServiceImpl中有的子函数,处理“迎合某些人习惯将注解加到接口上”的时候去取接口的getMethod时报错

    首次处理 class sun.myproxybean.MyServiceImpl的ioc
    处理 class sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d的myMapper1
    处理 class sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d的myMapper2
    处理 class sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d的myDao
    public void sun.myproxybean.MyServiceImpl.multi()
    @sun.myproxy.MY_TRANSACTIONAL()
    public final void sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d.multi()
    null
    public abstract void sun.myproxybean.MyService.multi()
    null
    Exception in thread "main" java.lang.RuntimeException: java.lang.NoSuchMethodException: sun.myproxybean.MyService.subFunction()
    	at sun.myproxy.OdsTransactionProxyFactory.invokeHasTransactional(OdsTransactionProxyFactory.java:117)
    	at sun.myproxy.OdsTransactionCglibFactory.intercept(OdsTransactionCglibFactory.java:74)
    	at sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d.multi(<generated>)
    	at sun.mybatis.Main.main(Main.java:58)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.lang.reflect.Method.invoke(Method.java:498)
    	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
    Caused by: java.lang.NoSuchMethodException: sun.myproxybean.MyService.subFunction()
    	at java.lang.Class.getMethod(Class.java:1786)
    	at sun.myproxy.OdsTransactionCglibFactory.intercept(OdsTransactionCglibFactory.java:42)
    	at sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d.subFunction(<generated>)
    	at sun.myproxybean.MyServiceImpl.multi(MyServiceImpl.java:30)
    	at sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d.CGLIB$multi$0(<generated>)
    	at sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d$$FastClassByCGLIB$$8fca1f76.invoke(<generated>)
    	at net.sf.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
    	at sun.myproxy.OdsTransactionCglibFactory.aopInvoke(OdsTransactionCglibFactory.java:83)
    	at sun.myproxy.OdsTransactionProxyFactory.invokeHasTransactional(OdsTransactionProxyFactory.java:105)
    	... 8 more
    

    jdk动态代理的子函数没有办法切,被切到的一定是接口中有的方法,所以天然无此问题;而cglib会切到大概率没有事务注解的子函数,所以就暴露了这个问题

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

    IOC();

    // ewirweod
    MY_TRANSACTIONAL scef_db_transactional = method.getAnnotation(MY_TRANSACTIONAL.class);

    // 迎合有些人习惯将注解加到接口上
    Class[] interfaces = target.getClass().getInterfaces();
    if(scef_db_transactional == null) {
    for(Class inter : interfaces) {
    try {
    Method classMethod = inter.getMethod(method.getName(), method.getParameterTypes());
    scef_db_transactional = classMethod.getAnnotation(MY_TRANSACTIONAL.class);
    } catch (NoSuchMethodException e1) {
    ;
    } catch (Throwable e) {
    e.printStackTrace();
    }

    break;
    }
    }

    6.5 又引出了一个知识:

            // MyServiceImpl&&CGLIB.method  uewoqruwiiii
            {
                Method classMethod = o.getClass().getMethod(method.getName(), method.getParameterTypes());
                System.out.println(classMethod);
                MY_TRANSACTIONAL my_transactional = classMethod.getAnnotation(MY_TRANSACTIONAL.class);
                System.out.println(my_transactional);
            }
    

    这个地方居然也报错了

    public class MyServiceImpl implements MyService {
    
        。。。。。
    
        /**
         * public o.getClass().getMethod 可以找到
         * protected 不行
         */
        public void subFunction() {
            System.out.println(this);
        }
    

     原因为:

    getDeclaredMethod*()获取的是类自身声明的所有方法,包含public、protected和private方法。getMethod*()获取的是类的所有共有方法,这就包括自身的所有public方法,和从基类继承的、从接口实现的所有public方法。

    anyway,所有getMethod方法都加上try catch throwable吧,以免影响主体进程

    7 最终的日志

    首次处理 class sun.myproxybean.MyServiceImpl的ioc
    处理 class sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d的myMapper1
    处理 class sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d的myMapper2
    处理 class sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d的myDao
    public void sun.myproxybean.MyServiceImpl.multi()
    @sun.myproxy.MY_TRANSACTIONAL()
    public final void sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d.multi()
    null
    public abstract void sun.myproxybean.MyService.multi()
    null
    protected void sun.myproxybean.MyServiceImpl.subFunction()
    null
    Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
    首次处理 class sun.myproxybean.MyDaoImpl的ioc
    处理 class sun.myproxybean.MyDaoImpl$$EnhancerByCGLIB$$4b5f7f6c的myMapper2
    public void sun.myproxybean.MyDaoImpl.multi()
    @sun.myproxy.MY_TRANSACTIONAL()
    public final void sun.myproxybean.MyDaoImpl$$EnhancerByCGLIB$$4b5f7f6c.multi()
    null
    public abstract void sun.myproxybean.MyDao.multi()
    null
    Exception in thread "main" java.lang.RuntimeException: java.lang.RuntimeException: java.lang.ArithmeticException: / by zero
    	at sun.myproxy.OdsTransactionProxyFactory.invokeHasTransactional(OdsTransactionProxyFactory.java:117)
    	at sun.myproxy.OdsTransactionCglibFactory.intercept(OdsTransactionCglibFactory.java:74)
    	at sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d.multi(<generated>)
    	at sun.mybatis.Main.main(Main.java:58)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.lang.reflect.Method.invoke(Method.java:498)
    	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
    Caused by: java.lang.RuntimeException: java.lang.ArithmeticException: / by zero
    	at sun.myproxy.OdsTransactionProxyFactory.invokeHasTransactional(OdsTransactionProxyFactory.java:117)
    	at sun.myproxy.OdsTransactionCglibFactory.intercept(OdsTransactionCglibFactory.java:74)
    	at sun.myproxybean.MyDaoImpl$$EnhancerByCGLIB$$4b5f7f6c.multi(<generated>)
    	at sun.myproxybean.MyServiceImpl.multi(MyServiceImpl.java:33)
    	at sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d.CGLIB$multi$1(<generated>)
    	at sun.myproxybean.MyServiceImpl$$EnhancerByCGLIB$$5b3ba48d$$FastClassByCGLIB$$8fca1f76.invoke(<generated>)
    	at net.sf.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
    	at sun.myproxy.OdsTransactionCglibFactory.aopInvoke(OdsTransactionCglibFactory.java:83)
    	at sun.myproxy.OdsTransactionProxyFactory.invokeHasTransactional(OdsTransactionProxyFactory.java:105)
    	... 8 more
    Caused by: java.lang.ArithmeticException: / by zero
    	at sun.myproxybean.MyDaoImpl.multi(MyDaoImpl.java:18)
    	at sun.myproxybean.MyDaoImpl$$EnhancerByCGLIB$$4b5f7f6c.CGLIB$multi$0(<generated>)
    	at sun.myproxybean.MyDaoImpl$$EnhancerByCGLIB$$4b5f7f6c$$FastClassByCGLIB$$e94f8239.invoke(<generated>)
    	at net.sf.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
    	at sun.myproxy.OdsTransactionCglibFactory.aopInvoke(OdsTransactionCglibFactory.java:83)
    	at sun.myproxy.OdsTransactionProxyFactory.invokeHasTransactional(OdsTransactionProxyFactory.java:105)
    	... 16 more
    
  • 相关阅读:
    利用搜狐查询接口举例说明
    超有用! 地址栏网址静默更新, 进入新网页也可以后退回去,.
    mouseenter 与 mouseover 区别于选择
    使用querySelector添加移除style和class
    网页修改<title ></title >标签内容
    (超实用)前端地址栏保存&获取参数,地址栏传输中文不在乱码
    html页面在苹果手机内,safari浏览器,微信中滑动不流畅问题解决方案
    python归一化方法
    opencv-python之投影
    matplotlib的用法
  • 原文地址:https://www.cnblogs.com/silyvin/p/14237270.html
Copyright © 2020-2023  润新知