The last post introduced the basic syntax of ONGL. Now we will move on to the actual usage of ONGL. We will combine some practical cases to demonstrate how ogNL can be supported

Before reading this article, it is strongly recommended to familiarize yourself with what OGNL is and its grammar features, reducing dyslexia, the five-minute Introduction series: 191129- OgNL Grammar Basics tutorial

I. Basic use

1. The configuration

We chose a Java development environment and used Maven for package management, first adding dependencies to poM files

<! -- https://mvnrepository.com/artifact/ognl/ognl -->
<dependency>
    <groupId>ognl</groupId>
    <artifactId>ognl</artifactId>
    <version>3.2.11</version>
</dependency>
Copy the code

2. Basic use

For the use of Ognl, the key is to get the OgnlContext, saving instances in this context to support the Ognl syntax

So the usual prior action with OGNL is to create the OgnlContext, then throw our instance into the context, receive the OGNL expression, and finally execute and get the result

The pseudocode is as follows

// Build an OgnlContext object
OgnlContext context = (OgnlContext) Ognl.createDefaultContext(this.new DefaultMemberAccess(true), 
        new DefaultClassResolver(),
        new DefaultTypeConverter());


// Set the root node and initialize some instance objects
context.setRoot(this);
context.put("Instance name", obj); .// ognl expression execution
Object expression = Ognl.parseExpression("#a.name")
Object result = Ognl.getValue(expression, context, context.getRoot());
Copy the code

II. Example demonstration

To move on to the example demonstration, first we need to create two test objects to populate the OgnlContext

0. Prepare

Two normal objects, one static class

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ADemo {

    private String name;

    private Integer age;

}

@Data
public class PrintDemo {

    private String prefix;

    private ADemo aDemo;

    public void sayHello(String name, int age) {
        System.out.println("name: " + name + " age: " + age);
    }

    private void print(ADemo a) {
        System.out.println(prefix + "= >" + a);
    }

    public <T> T print(String str, Class<T> clz) {
        T obj = JSON.parseObject(str, clz);
        System.out.println("class: " + obj);
        return obj;
    }

    public void print(String str, String clz) {
        System.out.println("str2a: " + str + " clz: " + clz);
    }

    public void print(String str, OgnlEnum ognlEnum) {
        System.out.println("enum: " + str + ":" + ognlEnum);
    }

    public void print(String str, ADemo a) {
        System.out.println("obj: " + str + ":" + a);
    }

    public void show(Class clz) { System.out.println(clz.getName()); }}public class StaticDemo {

    private static int num = (int) (Math.random() * 100);

    public static int showDemo(int a) {
        System.out.println("static show demo: " + a);
        returna; }}public enum OgnlEnum {
    CONSOLE, FILE;
}
Copy the code

When creating OgnlContext, there is a DefaultMemberAccess class, which is used to set the access permissions of the members

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

    public DefaultMemberAccess(boolean allowAllAccess) {
        this(allowAllAccess, allowAllAccess, allowAllAccess);
    }

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

    @Override
    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.TRUE; accessible.setAccessible(true); }}return result;
    }

    @Override
    public void restore(Map context, Object target, Member member, String propertyName, Object state) {
        if(state ! =null) { ((AccessibleObject) member).setAccessible((Boolean) state); }}/** * Returns true if the given member is accessible or can be made accessible by this object. */
    @Override
    public boolean isAccessible(Map context, Object target, Member member, String propertyName) {
        int modifiers = member.getModifiers();
        if (Modifier.isPublic(modifiers)) {
            return true;
        } else if (Modifier.isPrivate(modifiers)) {
            return this.allowPrivateAccess;
        } else if (Modifier.isProtected(modifiers)) {
            return this.allowProtectedAccess;
        } else {
            return this.allowPackageProtectedAccess; }}}Copy the code

Next we create our OgnlContext object

ADemo a = new ADemo();
a.setName("yihui");
a.setAge(10);

PrintDemo print = new PrintDemo();
print.setPrefix("ognl");
print.setADemo(a);


// Build an OgnlContext object
// Extension to support passing in arguments of type class
OgnlContext context = (OgnlContext) Ognl.createDefaultContext(this.new DefaultMemberAccess(true), new DefaultClassResolver(), new DefaultTypeConverter());
context.setRoot(print);
context.put("print", print);
context.put("a", a);
Copy the code

At this point, we are ready to move on to the actual case section

1. Instance access

Our instance access is divided into two types: method calls of instances; Instance property access

A. Instance method call

For example, if we want to execute the sayHello method of print, we can use it as follows

Object ans = Ognl.getValue(Ognl.parseExpression("#print.sayHello(\"一灰灰blog\", 18)"), context, context.getRoot());
System.out.println("Instance method execution:" + ans);
Copy the code

SayHello (” blog”, 18), where print is the instance name corresponding to the context.put(“print”, print) executed after the OgnlContext object is constructed; This line of code

Output result:

Name: an ash blog age: 18 Instance method execution: nullCopy the code

B. Instance member attribute access

Access to member properties can be divided into diameter getting member property values and setting member property values, which can be used as follows

ans = Ognl.getValue(Ognl.parseExpression("#a.name=\" "), context, context.getRoot());
System.out.println("Instance Property Settings:" + ans);

ans = Ognl.getValue(Ognl.parseExpression("#a.name"), context, context.getRoot());
System.out.println("Instance property Access:" + ans);
Copy the code

The output

Instance property Settings: a gray BlogInstance property access: a gray Blog
Copy the code

Can I access private members of the parent class?

To verify this, we create a new instance that inherits from ADemo and registers with the OgnlContext context

@Data
public class BDemo extends ADemo {
    private String address;
}

// Register with ognlContext
BDemo b = new BDemo();
b.setName("b name");
b.setAge(20);
b.setAddress("The test ing");
context.put("b", b);

/ / test case
ans = Ognl.getValue(Ognl.parseExpression("#b.name"), context, context.getRoot());
System.out.println("Instance parent property access:" + ans);
Copy the code

The output is as follows

Instance parent class attribute access: b nameCopy the code

Note:

We can directly access private members, private methods, and private members of the parent class thanks to our custom DefaultMemberAccess, which is set to true (private, protected, default access).

Static class access

Instance members need to be registered with OgnlContext before they can be accessed by instance name, but static classes do not need to be. By default, all static classes loaded by the current ClassLoader are supported. Let’s go to an example demonstration

A. Static class method calls

The important thing to note about static class access is that you pass in the full path, starting with @, and separating the class from the method with @

ans = Ognl.getValue(Ognl.parseExpression("@git.hui.fix.test.ognl.bean.StaticDemo@showDemo(20)"), context,
        context.getRoot());
System.out.println(Static class method execution: + ans);
Copy the code

The output

static show demo: 20
Copy the code

A. Static class member access

Again we have member access and modification

ans = Ognl.getValue(Ognl.parseExpression("@git.hui.fix.test.ognl.bean.StaticDemo@num"), context,
        context.getRoot());
System.out.println(Static class Member Access: + ans);

ans = Ognl.getValue(Ognl.parseExpression("@git.hui.fix.test.ognl.bean.StaticDemo@num=1314"), context,
        context.getRoot());
System.out.println("Static Class Member Settings:" + ans);
Copy the code

The output is as follows

Static class methods: 20 ognl. InappropriateExpressionException: Inappropriate ognl expression: @git.hui.fix.test.ognl.bean.StaticDemo@num at ognl.SimpleNode.setValueBody(SimpleNode.java:312) at ognl.SimpleNode.evaluateSetValueBody(SimpleNode.java:220) at ognl.SimpleNode.setValue(SimpleNode.java:301) at ognl.ASTAssign.getValueBody(ASTAssign.java:53) at ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212)Copy the code

Raise the move directly set static variables, warehouse, prompt InappropriateExpressionException

Can static class members be changed? I’ll leave you with a question

3. Special parameter transmission

General Java operations, nothing more than method call, attribute access two kinds, next we focus on the method call; If a method takes some primitive type of object, it is relatively simple to use. But what about the other scenarios?

A. Class type parameter

As in our previous PrintDemo, one of the methods is as follows

public <T> T print(String str, Class<T> clz) {
    T obj = JSON.parseObject(str, clz);
    System.out.println("class: " + obj);
    return obj;
}
Copy the code

What can be done with the CLZ arguments if the above methods are called?

ans = Ognl.getValue(Ognl.parseExpression(
        "#print.print(\"{'name':'xx', 'age': 20}\", @git.hui.fix.test.ognl.bean.ADemo@class)"), context,
        context.getRoot());
System.out.println("Class parameter method execution:" + ans);

/ / class participation
ans = Ognl.getValue(Ognl.parseExpression("#print.print(\"{'name':'haha', 'age': 10}\", #a.getClass())"),
        context, context.getRoot());
System.out.println("Class parameter method execution:" + ans);
Copy the code

There are two ways to get a class from an existing object, or to get a class directly from a static class

Class: ADemo(name=xx, age=20) class: ADemo(name=haha, age=10) Class parameter Method Run: ADemo(name=haha, age=10)Copy the code

B. Enumerate parameters

As in PrintDemo, where the second argument is an enumeration

public void print(String str, OgnlEnum ognlEnum) {
    System.out.println("enum: " + str + ":" + ognlEnum);
}
Copy the code

Combined with the pose above, this is not too difficult

ans = Ognl.getValue(
        Ognl.parseExpression("#print.print(\"print enum\", @git.hui.fix.test.ognl.model.OgnlEnum@CONSOLE)"),
        context, context.getRoot());
System.out.println("Enumerating parameter method execution:" + ans);
Copy the code

The output

enum: printEnum :CONSOLE enumeration parameter Method execution: NULLCopy the code

C. a null reference

The target method is as follows

private void print(ADemo a) {
    System.out.println(prefix + "= >" + a);
}
Copy the code

Since we need to pass the parameter as an empty object, which is a little bit special, OGNL supports this. Pass the parameter as null

ans = Ognl.getValue(Ognl.parseExpression("#print.print(null)"), context, context.getRoot());
System.out.println("Null pass parameter:" + ans);
Copy the code

The output is as follows

Ognl => NULL Null parameter: NULLCopy the code

In PrintDemo, the print method has multiple overloaded cases, so both arguments are passed null. Which method will be executed?

public <T> T print(String str, Class<T> clz) {
    T obj = JSON.parseObject(str, clz);
    System.out.println("class: " + obj);
    return obj;
}

public void print(String str, String clz) {
    System.out.println("str2a: " + str + " clz: " + clz);
}

public void print(String str, OgnlEnum ognlEnum) {
    System.out.println("enum: " + str + ":" + ognlEnum);
}

public void print(String str, ADemo a) {
    System.out.println("obj: " + str + ":" + a);
}
Copy the code

Through the actual test, the third method is called, there is no hidden rule, but I did not find it

ans = Ognl.getValue(Ognl.parseExpression("#print.print(null, null)"), context, context.getRoot());
System.out.println("Null pass parameter:" + ans);
Copy the code

The output

Enum: NULL: NULL Null Parameter transmission: NULLCopy the code

D. Object passing

What happens when the pass parameter is a POJO object?

public void print(String str, ADemo a) {
    System.out.println("obj: " + str + ":" + a);
}
Copy the code

If Aemo provides a full property constructor, then you can do the following. If Aemo provides a full property constructor, you can do the following

ex = Ognl.parseExpression("# print. The print (\" construct \ ", the new git. Hui. Fix the test. The ognl. Beans. ADemo (20) \ test \ "",")");
Object ans = Ognl.getValue(ex, context, context.getRoot());
System.out.println("Object pass parameter:" + ans);
Copy the code

Observe the above ognl expressions, among which the key in the new git. Hui. Fix. Test. Ognl. Beans. ADemo (” test “, 20)), to create objects, please specify the full path name

The output

Obj: Object construction :ADemo(name=test, age=20) Object pass parameter: nullCopy the code

If the POJO does not have a full attribute constructor, what can be done?

This is where the chain statement of OGNL syntax is used to create the object with new, set the properties, and throw the object

ex = Ognl.parseExpression("# print. The print (\" construct \ ", (# demo = new git. Hui. Fix. Test. The ognl. Beans. ADemo (), # demo. The elegantly-named setName (\ \ "" one is gray), # demo))");
ans = Ognl.getValue(ex, context, context.getRoot());
System.out.println("Object pass parameter:" + ans);
Copy the code

Core statement in (# demo = new git. Hui. Fix. Test. The ognl. Beans. ADemo (), # demo. The elegantly-named setName (\ \ “” one is gray), # demo), create the object and set properties

The output

Obj: Object construction :ADemo(name= gray, age=null) Object pass parameter: nullCopy the code

Although our requirement scenario is implemented, there is a catch: the property we created will be dropped into the OgnlContext, so it is very likely that the temporary object we created will overwrite the original object

So is there any way to avoid it?

We’ll talk about that later

E. Container parameter transfer

Add methods to the PrintDemo object

public void print(List<Integer> args) {
    System.out.println(args);
}

public void print(Map<String, Integer> args) {
    System.out.println(args);
}
Copy the code

Then our access case is as follows

ex = Ognl.parseExpression("#print.print({1, 3, 5})");
ans = Ognl.getValue(ex, context, context.getRoot());
System.out.println("List pass parameter:" + ans);

ex = Ognl.parseExpression("#print.print(#{\"A\": 1, \"b\": 3, \"c\": 5})");
ans = Ognl.getValue(ex, context, context.getRoot());
System.out.println("Map parameter transfer:" + ans);
Copy the code

The output

[1, 3, 5] List parameter: NULL {A=1, b=3, c=5} Map parameter: nullCopy the code

4. Expression execution

Next comes another category of cases, which perform some simple arithmetic operations or conditional expressions

ans = Ognl.getValue(Ognl.parseExpression("1 + 3 + 4"), context, context.getRoot());
System.out.println("Expression execution:" + ans);

/ / factorial
ans = Ognl.getValue(Ognl.parseExpression("#fact = :[#this<=1? 1 : #this*#fact(#this-1)], #fact(3)"), context, context.getRoot());
System.out.println("Lambda execution:" + ans);
Copy the code

The output

Expression execution: 8 lambda execution: 6Copy the code

III. The summary

Given the length of the article, this post will be limited to how far you can go with basic OGNL, which is easier to use in Java

1. Create OgnlContext and register the instance

// Build an OgnlContext object
OgnlContext context = (OgnlContext) Ognl.createDefaultContext(this.new DefaultMemberAccess(true), 
        new DefaultClassResolver(),
        new DefaultTypeConverter());


// Set the root node and initialize some instance objects
context.setRoot(this);
context.put("Instance name", obj); .Copy the code

2. Compile the OGNL expression and obtain the execution result

// ognl expression execution
Object expression = Ognl.parseExpression("#a.name")
Object result = Ognl.getValue(expression, context, context.getRoot());
Copy the code

3. The legacy

Two questions remain unanswered

  • Static members cannot be modified by default, so is there a way to make them modified
  • How do I avoid the scenario where temporary objects are cached in the OgnlContext when they are created by chaining when objects are passed?

II. The other

1. A gray Blog:liuyueyi.github.io/hexblog

A gray personal blog, recording all the study and work in the blog, welcome everyone to go to stroll

2. Statement

As far as the letter is not as good as, has been the content, purely one’s own words, because of the limited personal ability, it is hard to avoid omissions and mistakes, such as finding bugs or better suggestions, welcome criticism and correction, not grudging gratitude

  • Micro Blog address: Small Gray Blog
  • QQ: a gray /3302797840

3. Scan attention

A gray blog