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