Mybatis的Ognl上下文和变量解析器

Published on with 0 views and 0 comments

package com.example.demo;

import org.apache.ibatis.ognl.MemberAccess;
import org.apache.ibatis.ognl.Ognl;
import org.apache.ibatis.ognl.OgnlContext;
import org.apache.ibatis.parsing.PropertyParser;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.Properties;

public class ParserDemo {
    public static void main(String[] args) throws Exception{
        Properties variables = new Properties();
        variables.setProperty("id", "123123");

        String parse = PropertyParser.parse("select * from tb where id = ${id}", variables);
        System.out.println(parse);

        User user = new User("1", "zhangsan", "23");
        // 创建一个上下文,root为user
        OgnlContext context = (OgnlContext) Ognl.createDefaultContext(user, new DefaultMemberAccess(true));
        // 上下文中添加userCount属性
        context.put("userCount", 11);
        // 获取userCount,上下文中的属性使用#访问
        Object ans = Ognl.getValue(Ognl.parseExpression("#userCount"), context, context.getRoot());
        System.out.println("userCount = " + ans);
        // 获取root中的age,root中的属性不使用#
        ans = Ognl.getValue(Ognl.parseExpression("age"), context, context.getRoot());
        System.out.println("age = " + ans);
        // 指定值,会调用setName方法
        ans = Ognl.getValue(Ognl.parseExpression("name=\"lisi\""), context, context.getRoot());
        System.out.println("name = " + user.getName());

    }
}


class DefaultMemberAccess implements MemberAccess
{
    public boolean      allowPrivateAccess = false;
    public boolean      allowProtectedAccess = false;
    public boolean      allowPackageProtectedAccess = false;

    /*===================================================================
        Constructors
      ===================================================================*/
    public DefaultMemberAccess(boolean allowAllAccess)
    {
        this(allowAllAccess, allowAllAccess, allowAllAccess);
    }

    public DefaultMemberAccess(boolean allowPrivateAccess, boolean allowProtectedAccess, boolean allowPackageProtectedAccess)
    {
        super();
        this.allowPrivateAccess = allowPrivateAccess;
        this.allowProtectedAccess = allowProtectedAccess;
        this.allowPackageProtectedAccess = allowPackageProtectedAccess;
    }

    /*===================================================================
        Public methods
      ===================================================================*/
    public boolean getAllowPrivateAccess()
    {
        return allowPrivateAccess;
    }

    public void setAllowPrivateAccess(boolean value)
    {
        allowPrivateAccess = value;
    }

    public boolean getAllowProtectedAccess()
    {
        return allowProtectedAccess;
    }

    public void setAllowProtectedAccess(boolean value)
    {
        allowProtectedAccess = value;
    }

    public boolean getAllowPackageProtectedAccess()
    {
        return allowPackageProtectedAccess;
    }

    public void setAllowPackageProtectedAccess(boolean value)
    {
        allowPackageProtectedAccess = value;
    }

    /*===================================================================
        MemberAccess interface
      ===================================================================*/
    public Object setup(Map context, Object target, Member member, String propertyName)
    {
        Object      result = null;

        if (isAccessible(context, target, member, propertyName)) {
            AccessibleObject accessible = (AccessibleObject)member;

            if (!accessible.isAccessible()) {
                result = Boolean.FALSE;
                accessible.setAccessible(true);
            }
        }
        return result;
    }

    public void restore(Map context, Object target, Member member, String propertyName, Object state)
    {
        if (state != null) {
            ((AccessibleObject)member).setAccessible(((Boolean)state).booleanValue());
        }
    }

    /**
     Returns true if the given member is accessible or can be made accessible
     by this object.
     */
    public boolean isAccessible(Map context, Object target, Member member, String propertyName)
    {
        int         modifiers = member.getModifiers();
        boolean     result = Modifier.isPublic(modifiers);

        if (!result) {
            if (Modifier.isPrivate(modifiers)) {
                result = getAllowPrivateAccess();
            } else {
                if (Modifier.isProtected(modifiers)) {
                    result = getAllowProtectedAccess();
                } else {
                    result = getAllowPackageProtectedAccess();
                }
            }
        }
        return result;
    }
}

class User {
    private String id;
    private String name;
    private String age;

    public User(String id, String name, String age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

以下是关于 ognl 和值栈 valuestack 的一些理解。

ognl 可以写很多自定义表达式,比如像 JSP 里面的对象属性和 Java 静态方法,像 MyBatis 的 XML 里头的 if test 判断。

MyBatis 实现的 ognl 逻辑:
ognl 获取数据的时候,需要一个 ognl 表达式,一个上下文 map,和一个 root 对象
通过 ognl 表达式来判断使用具体哪一个解析器,选择解析器的代码相当复杂,看不懂。
解析器也有很多种,简单的就是获取一个 value 值,有的需要运算,有的需要运行方法

struts 实现的 ognl 逻辑:
值栈是放在上下文中的。每个请求创建一个值栈。
request.setAttribute("struts.valueStack",valuestack)
值栈的实现可以参考 Struts2 里的 OgnlValueStack.java 来实现,
值栈的 set 方法就是往栈顶放一个 map
值栈先从根栈中寻找,然后到 context 中寻找


标题:Mybatis的Ognl上下文和变量解析器
作者:cuijianzhe
地址:https://cjzshilong.cn/articles/2022/05/17/1652767269945.html