Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

如果在插件中使用装饰模式增强、并非jdk动态代理时,会导致com.baomidou.mybatisplus.core.toolkit.PluginUtils#realTarget获取实际处理对象失败 #6183

Closed
bugCats opened this issue May 20, 2024 · 3 comments

Comments

@bugCats
Copy link

bugCats commented May 20, 2024

如果在插件中使用装饰模式增强、并非jdk动态代理时,会导致com.baomidou.mybatisplus.core.toolkit.PluginUtils#realTarget获取实际处理对象失败

当前使用版本
mybatisplus所有版本

当前环境信息
Java8 + Mysql5.7

描述bug现象
之前使用mybatis,自定义了很多插件,插件使用装饰模式增强,并非JDK动态代理。
最近打算升级到mybatis-plus,发现在com.baomidou.mybatisplus.core.toolkit.PluginUtils#realTarget处报错,此方法并未分离出来实际对象。

提供问题复现步骤

拦截器示例:使用装饰模式增强StatementHandler.prepare

@Component
public class MyInterceptor implements Interceptor {
    
    @Override
    public Object intercept(Invocation invocation) {
        return null;
    }

    @Override
    public Object plugin(Object target) {
        if ( target instanceof StatementHandler ) {
            return new StatementHandlerWrap((StatementHandler) target); //此处只演示StatementHandler,装饰其他对象也会造成同样异常
        }
        return target;
    }

    private static class StatementHandlerWrap implements StatementHandler {
        private final StatementHandler h; //模拟jdk动态代理后的目标对象属性名
        private StatementHandlerWrap(StatementHandler statementHandler) {
            this.h = statementHandler;
        }

        @Override
        public Statement prepare(Connection connection, Integer timeout) throws SQLException {
            //增强代码
            return h.prepare(connection, timeout);
        }

        @Override
        public void parameterize(Statement statement) throws SQLException {
            h.parameterize(statement);
        }
        @Override
        public void batch(Statement statement) throws SQLException {
            h.batch(statement);
        }
        @Override
        public int update(Statement statement) throws SQLException {
            return h.update(statement);
        }
        @Override
        public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
            return h.query(statement, resultHandler);
        }
        @Override
        public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
            return h.queryCursor(statement);
        }
        @Override
        public BoundSql getBoundSql() {
            return h.getBoundSql();
        }
        @Override
        public ParameterHandler getParameterHandler() {
            return h.getParameterHandler();
        }
    }
}

异常信息:

Caused by: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'delegate' in 'class xxx.xxx.MyInterceptor$StatementHandlerWrap'
	at org.apache.ibatis.reflection.Reflector.getGetInvoker(Reflector.java:374)
	at org.apache.ibatis.reflection.MetaClass.getGetInvoker(MetaClass.java:164)
	at org.apache.ibatis.reflection.wrapper.BeanWrapper.getBeanProperty(BeanWrapper.java:162)
	at org.apache.ibatis.reflection.wrapper.BeanWrapper.get(BeanWrapper.java:49)
	at org.apache.ibatis.reflection.MetaObject.getValue(MetaObject.java:122)
	at com.baomidou.mybatisplus.core.toolkit.PluginUtils.mpStatementHandler(PluginUtils.java:72)
	at com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor.beforePrepare(BlockAttackInnerInterceptor.java:53)
	at com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor.intercept(MybatisPlusInterceptor.java:102)
	...

实际原因:
com.baomidou.mybatisplus.core.toolkit.PluginUtils#realTarget,只有在入参target是JDK代理对象时,才会执行获取实际对象逻辑。

@miemieYaho
Copy link
Member

你自己的东西让别人怎么取?

@bugCats
Copy link
Author

bugCats commented May 20, 2024

你自己的东西让别人怎么取?

至少有2种方式:

1、简单粗暴,直接判断target是否包含h属性。这样就算是使用装饰模式,被装饰对象属性名如果为h,同样可以获取到真实对象:

        MetaObject handler = SystemMetaObject.forObject(target);
        while (handler.hasGetter("h")) {
            Object object = handler.getValue("h");
            handler = SystemMetaObject.forObject(object);
        }
        while (handler.hasGetter("target")) {
            Object object = handler.getValue("target");
            handler = SystemMetaObject.forObject(object);
        }

2、提供获取原始对象接口:如果使用装饰模式,装饰类再实现该接口,返回真实对象;

public interface PluginProxyAdapter {
    Object theProxyObject();
}

//com.baomidou.mybatisplus.core.toolkit.PluginUtils#realTarget,优先判断PluginProxyAdapter
public static <T> T realTarget(Object target) {
	if( target instanceof PluginProxyAdapter){
		return realTarget(((PluginProxyAdapter) target).theProxyObject());
	}
	if (Proxy.isProxyClass(target.getClass())) {
		MetaObject metaObject = SystemMetaObject.forObject(target);
		return realTarget(metaObject.getValue("h.target"));
	}
	return (T) target;
}


private static class StatementHandlerWrap implements StatementHandler, PluginProxyAdapter {
        private final StatementHandler h; //模拟jdk动态代理后的目标对象属性名
        private StatementHandlerWrap(StatementHandler statementHandler) {
            this.h = statementHandler;
        }

        @Override
        public Object theProxyObject() {
            return this.h;
        }

        ......

}

方法总比困难多

@VampireAchao
Copy link
Contributor

问题解决了吗?我将关闭本issue,如需要可以reopen

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants