Spring源码阅读目录
第一章 Spring之最熟悉的陌生人——IOC
第二章 Spring之假如让你来写IOC容器——加载资源篇
第三章 Spring之假如让你来写IOC容器——解析配置文件篇
第四章 Spring之假如让你来写IOC容器——XML配置文件篇
第五章 Spring之假如让你来写IOC容器——BeanFactory和FactoryBean
第六章 Spring之假如让你来写IOC容器——Scope和属性填充
第七章 Spring之假如让你来写IOC容器——属性填充特别篇:SpEL表达式
第八章 Spring之假如让你来写IOC容器——拓展篇
第九章 Spring之源码阅读——环境搭建篇
第十章 Spring之源码阅读——IOC篇
第二部分——AOP篇
第十一章 Spring之不太熟的熟人——AOP
第十二章 Spring之不得不了解的内容——概念篇
前言
对于Spring一直都是既熟悉又陌生,说对它熟悉吧,平时用用没啥问题,但面试的时候被问的一脸懵逼,就很尴尬,都不好意思在简历上写着熟悉Spring了
所以决定花点时间研究研究Spring的源码。主要参考的书籍是:《Spring源码深度解析(第2版)》、《Spring揭秘》、《Spring技术内幕:深入解析Spring架构与设计原理(第2版)》
书接上回,在上篇 第六章 Spring之假如让你来写IOC容器——Scope和属性填充 已经实现了Scope作用域和自定义类型转换器。接下来作为拓展内容,新增了spel表达式相关内容作为 第六章 的补充内容,不关心这部分内容的小伙伴也可以直接跳过,感兴趣的小伙伴就接着往下看吧
尝试动手写IOC容器
出场人物:A君(苦逼的开发)、老大(项目经理)
背景:老大要求A君在一周内开发个简单的IOC容器
前情提要:A君 不满足于现状,希望自己写的ioc容器可以支持spel表达式。。。
属性填充——特别篇:SpEL表达式
随着版本的落下,A君的IOC容器已经可以填充各式各样的属性值,但是A君并不满足于此。因为spring还有一个强大的功能——Spel表达式。这玩意,A君平时工作中也没少用过,像取配置文件中的值:${xxx.xx},执行方法:#{xxx.getXx()},计算表达式:#{1+2+3} 等等
虽然A君心潮澎湃,不过还是需要冷静下来思考如何实现?通常对于这种提取字符串内容的情况,A君的第一反应是用正则匹配,单一的表达式还好,如果像:#{xxx.getXx().xxx + 1 + 2 / 2 + ‘dddd’} 这种混合表达式,就算能写出正则来,怕是也快哭了。既然正则行不通,那只剩一条路了——编译原理
编译原理 涉及到的内容比较多,比如:词法分析、语法分析、语义分析、中间代码生成、代码生成等,具体可以参考:龙书、虎书、鲸书 等相关专业书籍。不过 A君 并不打算直接去看相关原理——容易睡着。A君 打算做的是丐版的,只需要词法分析、语法分析即可,先不用管他们是什么,按照自己的理解做就行了
要实现一个简单的Spel,A君 首先得知道哪些是他需要的内容,比如:#{a.b} + 1+2,真正需要的只有a.b,其他内容就不关 A君 的事了,所以需要定义一个接口,用来规定Spel表达式的前缀、后缀,方便 A君 提取Spel表达式。ParserContext接口代码如下:
/**
* 表达式转换模板
*/
public interface ParserContext {
/**
* 是否启用模板
* true:使用模板解析,例如:#{xxx.xx}
* false:直接解析
*
* @return
*/
boolean isTemplate();
/**
* Spel的前缀
*
* @return
*/
String getExpressionPrefix();
/**
* Spel的后缀
*
* @return
*/
String getExpressionSuffix();
}
A君 提供了个默认实现,以#{}包裹的内容就是Spel表达式,默认实现TemplateParserContext,如下:
import com.hqd.ch03.v11.spel.ParserContext;
/**
* Spel表达式默认模板,以#{}包裹的内容
*/
public class TemplateParserContext implements ParserContext {
private final String expressionPrefix;
private final String expressionSuffix;
public TemplateParserContext() {
this("#{", "}");
}
public TemplateParserContext(String expressionPrefix, String expressionSuffix) {
this.expressionPrefix = expressionPrefix;
this.expressionSuffix = expressionSuffix;
}
@Override
public boolean isTemplate() {
return true;
}
@Override
public String getExpressionPrefix() {
return expressionPrefix;
}
@Override
public String getExpressionSuffix() {
return expressionSuffix;
}
}
有了模板接口之后, A君 就知道字符串中哪些是Spel表达式了。接下来A君 又开始挠头:怎么处理提出来的Spel表达式,对于计算机来说,啥Spel表达式,它可不认识,对它来说都是字符串。当务之急,需要先给表达式进行分类,再进行对应的操作。比如:类型是+就进行加法运算、是&&就进行且判断等等。于是乎,A君 定义个TokenKind枚举类规定Spel表达式支持的类型。代码如下:
/**
* token类型
*/
public enum TokenKind {
LITERAL_INT,
LITERAL_REAL,
LITERAL_STRING,
LPAREN("("),
RPAREN(")"),
COMMA(","),
DOT("."),
IDENTIFIER,
PLUS("+"),
STAR("*"),
MINUS("-"),
DIV("/"),
GE(">="),
GT(">"),
LE("<="),
LT("<"),
EQ("=="),
NE("!="),
MOD("%"),
NOT("!"),
ASSIGN("="),
FACTORY_BEAN_REF("&"),
SYMBOLIC_OR("||"),
SYMBOLIC_AND("&&"),
INC("++"),
DEC("--");
final char[] tokenChars;
private TokenKind(String tokenString) {
this.tokenChars = tokenString.toCharArray();
}
private TokenKind() {
this("");
}
public int getLength() {
return this.tokenChars.length;
}
}
再定一个实体类——Token,用来包装解析出来的内容,代码如下:
/**
*
*/
public class Token {
private int endPos;
private int startPos;
private String data;
private TokenKind kind;
public Token(TokenKind tokenKind, int startPos, int endPos) {
this.kind = tokenKind;
this.startPos = startPos;
this.endPos = endPos;
}
public Token(TokenKind tokenKind, char[] tokenData, int startPos, int endPos) {
this(tokenKind, startPos, endPos);
this.data = new String(tokenData);
}
public boolean isIdentifier() {
return (this.kind == TokenKind.IDENTIFIER);
}
public TokenKind getKind() {
return kind;
}
public int getEndPos() {
return endPos;
}
public int getStartPos() {
return startPos;
}
public boolean isNumericRelationalOperator() {
return (this.kind == TokenKind.GT || this.kind == TokenKind.GE || this.kind == TokenKind.LT ||
this.kind == TokenKind.LE || this.kind == TokenKind.EQ || this.kind == TokenKind.NE);
}
public String getData() {
return data;
}
}
TokenKind 和 Token 都准备好之后,接着就是把获取到的表达式进行归类了,例如:0-9就是数字,+就是加号等(就是一堆switch…case)。Tokenizer 代码如下:
import com.hqd.ch03.v11.spel.exception.InternalParseException;
import com.hqd.ch03.v11.spel.exception.SpelMessage;
import com.hqd.ch03.v11.spel.exception.SpelParseException;
import java.util.ArrayList;
import java.util.List;
/**
* 词法解析:将获取到的表达式解析成Token
*/
public class Tokenizer {
private String expressionString;
private char[] charsToProcess;
private int pos;
private int max;
private List<Token> tokens = new ArrayList<>();
public Tokenizer(String expressionString) {
this.expressionString = expressionString;
this.charsToProcess = this.expressionString.toCharArray();
this.pos = 0;
this.max = this.expressionString.length();
}
/**
* 词法解析
*
* @return
*/
public List<Token> process() {
while (this.pos < this.max) {
char ch = this.charsToProcess[pos++];
if (Character.isLetter(ch)) {
lexIdentifier(ch);
} else {
switch (ch) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
pushNumberToken(ch);
break;
case '+':
pushToken(TokenKind.PLUS);
break;
case '-':
pushToken(TokenKind.MINUS);
break;
case '*':
pushToken(TokenKind.STAR);
break;
case '/':
pushToken(TokenKind.DIV);
break;
case '%':
pushToken(TokenKind.MOD);
break;
case '(':
pushToken(TokenKind.LPAREN);
break;
case ')':
pushToken(TokenKind.RPAREN);
break;
case '.':
pushToken(TokenKind.DOT);
break;
case ',':
pushToken(TokenKind.COMMA);
break;
case '\'':
lexQuotedStringLiteral();
break;
case '=':
if (isTwoCharToken(TokenKind.EQ)) {
pushPairToken(TokenKind.EQ);
} else {
pushToken(TokenKind.ASSIGN);
}
break;
case '<':
if (isTwoCharToken(TokenKind.LE)) {
pushPairToken(TokenKind.LE);
} else {
pushToken(TokenKind.LT);
}
break;
case '>':
if (isTwoCharToken(TokenKind.GE)) {
pushPairToken(TokenKind.GE);
} else {
pushToken(TokenKind.GT);
}
break;
case '&':
if (isTwoCharToken(TokenKind.SYMBOLIC_AND)) {
pushPairToken(TokenKind.SYMBOLIC_AND);
} else {
raiseParseException(pos, SpelMessage.MISSING_CHARACTER, "&");
}
break;
case '|':
if (!isTwoCharToken(TokenKind.SYMBOLIC_OR)) {
raiseParseException(this.pos, SpelMessage.MISSING_CHARACTER, "|");
}
pushPairToken(TokenKind.SYMBOLIC_OR);
break;
case '!':
if (isTwoCharToken(TokenKind.NE)) {
pushPairToken(TokenKind.NE);
} else {
pushToken(TokenKind.NOT);
}
break;
default:
break;
}
}
}
return tokens;
}
private void lexIdentifier(char ch) {
int start = this.pos - 1;
StringBuilder sb = new StringBuilder();
sb.append(ch);
while (pos < max) {
ch = this.charsToProcess[this.pos++];
if (isIdentifier(ch)) {
sb.append(ch);
} else {
this.pos--;
break;
}
}
this.tokens.add(new Token(TokenKind.IDENTIFIER, sb.toString().toCharArray(), start, this.pos));
}
private boolean isIdentifier(char ch) {
return Character.isLetter(ch) || Character.isDigit(ch) || ch == '_' || ch == '$';
}
private void pushPairToken(TokenKind kind) {
this.tokens.add(new Token(kind, this.pos - 1, this.pos + 1));
this.pos++;
}
private void pushToken(TokenKind kind) {
pushToken(kind, this.pos - 1, this.pos);
}
private void pushToken(TokenKind kind, int startPos, int endPos) {
tokens.add(new Token(kind, startPos, endPos));
}
private void lexQuotedStringLiteral() {
int start = pos - 1;
StringBuilder sb = new StringBuilder();
while (pos < max) {
char ch = charsToProcess[pos++];
if (ch != '\'') {
sb.append(ch);
} else {
break;
}
}
int end = this.pos - 1;
tokens.add(new Token(TokenKind.LITERAL_STRING, sb.toString().toCharArray(), start, end));
}
private void pushNumberToken(char ch) {
StringBuilder sb = new StringBuilder();
sb.append(ch);
TokenKind kind = TokenKind.LITERAL_INT;
int start = this.pos - 1;
sb.append(getNumber());
if (pos < max) {
ch = charsToProcess[pos];
if (ch == '.') {
sb.append(ch);
pos++;
sb.append(getNumber());
kind = TokenKind.LITERAL_REAL;
}
}
int end = this.pos - 1;
tokens.add(new Token(kind, sb.toString().toCharArray(), start, end));
}
private String getNumber() {
StringBuilder sb = new StringBuilder();
while (pos < max) {
char ch = charsToProcess[this.pos++];
if (ch >= '0' && ch <= '9') {
sb.append(ch);
} else {
this.pos--;
break;
}
}
return sb.toString();
}
private boolean isTwoCharToken(TokenKind kind) {
return (kind.tokenChars.length == 2 &&
this.charsToProcess[this.pos - 1] == kind.tokenChars[0] &&
this.charsToProcess[this.pos] == kind.tokenChars[1]);
}
private void raiseParseException(int start, SpelMessage msg, Object... inserts) {
throw new InternalParseException(new SpelParseException(this.expressionString, start, msg, inserts));
}
}
将表达式解析成一个个Token之后,按道理说接下来就是解析并计算相应的值了。但是新的问题又出现了:如何计算这些值,先算哪个,后算哪个。比如:3+4/2-2*3,应该先算乘除,再算加减,表达式存在优先级问题。这不经让 A君 一阵犯难
所幸,前辈们已经给出对应的解决方法,那就是抽象语法树(AST)。简单的说,就是把解析出来的Token构建成一棵树形结构,以上边:3 + 4 / 2 - 2 * 3 为例,语法树如下:
A君 依稀记得大学时候的知识:二叉树的遍历可以根据根节点的位置分为前序、中序、后序。多叉树遍历方式主要分为前序、后序、层序。A君 认真观察了下,发现对上边的AST树进行前序遍历就是前缀表达式(波兰表达式):- + 3 / 4 2 * 2 3,中序遍历就是中缀表达式(平时用的表达式):3 + 4 / 2 - 2 * 3 ,后序遍历就是后缀表达式(逆波兰表达式):3 4 2 / + 2 3 * - 。有了以上的铺垫,A君 大概知道怎么实现了,先把Token构建成一棵AST树,而后再进行后序遍历即可
注:AST树不一定是二叉树,也有可能是多叉树。取决于语法规则。可能有些小伙伴对于为什么AST树可以保证优先级有所疑惑,其实跟构建节点时候的顺序有关,构建AST树的时候高优先级先构建,在构建低优先级,这样就保证高优先级是低优先级的子节点。而遍历的时候用的是后序遍历,先子后父,所以高优先级的运算符就会先执行,从而保证了优先级
要想构建一个AST树,先要有对应的节点才行。A君 定义了一个 SpelNode 接口作为树的节点接口,代码如下:
import com.hqd.ch03.v11.spel.standard.ExpressionState;
/**
* ast节点接口
*/
public interface SpelNode {
/**
* 获取节点值
*
* @param expressionState
* @return
*/
Object getValue(ExpressionState expressionState);
/**
* 获取子节点
*
* @param index
* @return
*/
SpelNode getChild(int index);
/**
* 获取起始下标
*
* @return
*/
int getStartPos();
/**
* 获取结束下标
*
* @return
*/
int getEndPos();
}
在接口里看到一个不认识的类——ExpressionState ,这个涉及到取值问题,先按下不表,A君 后边会一并说到。照例,A君 提取一个抽象类,用以封装公共实现。SpelNodeImpl 代码如下:
import com.hqd.ch03.v11.spel.SpelNode;
import com.hqd.ch03.v11.spel.context.StandardEvaluationContext;
import com.hqd.ch03.v11.spel.standard.ExpressionState;
import org.apache.commons.lang3.ObjectUtils;
/**
* ast节点抽象类
*/
public abstract class SpelNodeImpl implements SpelNode {
private final int startPos;
private final int endPos;
protected SpelNodeImpl[] children = new SpelNodeImpl[0];
protected SpelNodeImpl parent;
public SpelNodeImpl(int startPos, int endPos, SpelNodeImpl... operands) {
this.startPos = startPos;
this.endPos = endPos;
if (!ObjectUtils.isEmpty(operands)) {
this.children = operands;
for (SpelNodeImpl operand : operands) {
operand.parent = this;
}
}
}
@Override
public SpelNode getChild(int index) {
return children[index];
}
public Object getValue(StandardEvaluationContext context) {
return getValue(new ExpressionState(context, context.getRootObj()));
}
@Override
public abstract Object getValue(ExpressionState state);
@Override
public int getStartPos() {
return startPos;
}
@Override
public int getEndPos() {
return endPos;
}
}
封装好抽象类之后,接着还需要实现各个具体的运算,而节点类型大致可以分为:运算符、变量/常量。这里A君 打算先做常量/变量,因为相对比较简单。首先A君 定义了个 IntLiteral 节点,用以支持Int类型的常量,代码如下:
import com.hqd.ch03.v11.spel.standard.ExpressionState;
/**
* int类型节点
*/
public class IntLiteral extends SpelNodeImpl {
private Integer val;
public IntLiteral(int startPos, int endPos, String strVal) {
super(startPos, endPos);
this.val = Integer.parseInt(strVal);
}
@Override
public Object getValue(ExpressionState state) {
return val;
}
}
还有String类型,这个也不少了。StringLiteral 代码如下:
import com.hqd.ch03.v11.spel.standard.ExpressionState;
/**
* 字符串节点
*/
public class StringLiteral extends SpelNodeImpl {
private String val;
public StringLiteral(int startPos, int endPos, String strVal) {
super(startPos, endPos);
this.val = strVal;
}
@Override
public Object getValue(ExpressionState state) {
return val;
}
}
其他常量类型与之类似,就不一一列举了。接着 A君 把目光转到了运算符上面,运算符也是多种,大致可分为:一元运算、二元运算、三元运算。比如:!、++、-- 这些就属于一元运算,+、-、&&、|| 这些属于二元运算,三元运算最经典的当属三目运算了。抛去那些晦涩的原理,其实可以根据操作数判断,需要一个操作数就是一元运算,两个就是二元运算,以此类推。A君 先从二元运算入手,由于二元运算都需要两个操作数,分别获取左右节点后在进行运算,也存在着共同点,所以老样子,提取一个抽象类——Operator。其代码如下:
import com.hqd.ch03.v11.spel.standard.ExpressionState;
/**
* 二元运算符抽象类
*/
public abstract class Operator extends SpelNodeImpl {
/**
* 运算符名称
*/
private final String operatorName;
public Operator(String payload, int startPos, int endPos, SpelNodeImpl... operands) {
super(startPos, endPos, operands);
this.operatorName = payload;
}
@Override
public Object getValue(ExpressionState state) {
Object left = getLeftOperand().getValue(state);
Object right = getRightOperand().getValue(state);
return getValueInternal(left, right);
}
protected abstract Object getValueInternal(Object left, Object right);
public SpelNodeImpl getLeftOperand() {
return this.children[0];
}
public SpelNodeImpl getRightOperand() {
return this.children[1];
}
}
A君 先以简单的加减乘除入手,加法节点需要获取左右节点后进行加法运算,如果是数字就直接相加,但是遇到字符串的时候比较特殊,需要进行字符的拼接。所以OpPlus代码如下:
/**
* 加法运算符
*/
public class OpPlus extends Operator {
public OpPlus(int startPos, int endPos, SpelNodeImpl... operands) {
super("+", startPos, endPos, operands);
}
@Override
protected Object getValueInternal(Object left, Object right) {
if (left instanceof Number && right instanceof Number) {
Number leftNumber = (Number) left;
Number rightNumber = (Number) right;
if (leftNumber instanceof Double || rightNumber instanceof Double) {
return leftNumber.doubleValue() + rightNumber.doubleValue();
} else if (leftNumber instanceof Integer || rightNumber instanceof Integer) {
return leftNumber.intValue() + rightNumber.intValue();
}
}
if (left instanceof String && right instanceof String) {
return left + (String) right;
}
if (left instanceof String) {
return left + String.valueOf(right);
}
if (right instanceof String) {
return String.valueOf(left) + right;
}
return null;
}
}
其他的二元运算都是类似的,篇幅有限,A君 这里也不一一列举了。运算时候需要注意下顺序,先左后右
简单的运算符 A君 已经弄得七七八八了,还有个比较蛋疼的问题:如果遇到方法调用,或者属性访问,这可要怎么办?要知道 A君 最终的目的是要整合进IOC容器之中的,必须是要支持像:#{user.name}、#{user.getName()} 这种类型的表达式。 这时候会涉及到对象变量以及操作对象属性的内容,A君 可不能提前预知要传啥对象,所以,依旧定义个接口,用来保存运算时候的变量。EvaluationContext 接口代码如下:
import java.util.List;
/**
* spel表达式上下文接口
* 有可能涉及到变量计算,比如获取a对象里边的b属性:#{a.b}
*/
public interface EvaluationContext {
/**
* 获取根对象
*
* @return
*/
Object getRootObj();
/**
* 获取属性解析器
*
* @return
*/
List<PropertyAccessor> getPropertyAccessors();
}
至于操作对象属性,A君 用PropertyAccessor接口进行规范,遇到一些特殊的对象操作,用户可以实现该接口用以定制化的实现。代码如下:
/**
* 对象访问接口
*/
public interface PropertyAccessor {
/**
* 返回可以解析的类型数组
*
* @return
*/
Class<?>[] getSpecificTargetClasses();
/**
* 检查是否能读取该属性
*
* @param context
* @param target
* @param name
* @return
*/
boolean canRead(EvaluationContext context, Object target, String name);
/**
* 读取属性值
*
* @param context
* @param target
* @param name
* @return
*/
Object read(EvaluationContext context, Object target, String name);
/**
* 检查该属性是否可以写入
*
* @param context
* @param target
* @param name
* @return
*/
boolean canWrite(EvaluationContext context, Object target, String name);
/**
* 写入该属性
*
* @param context
* @param target
* @param name
* @param newValue
*/
void write(EvaluationContext context, Object target, String name, Object newValue);
}
EvaluationContext 的实现类就比较简单了,只需要提供添加对象访问器(PropertyAccessor)和传入操作对象即可,StandardEvaluationContext 代码如下:
import com.hqd.ch03.v11.spel.EvaluationContext;
import com.hqd.ch03.v11.spel.PropertyAccessor;
import java.util.ArrayList;
import java.util.List;
/**
* 运算是上下文默认实现
*/
public class StandardEvaluationContext implements EvaluationContext {
private Object rootObj;
private List<PropertyAccessor> propertyAccessors = new ArrayList<>();
public StandardEvaluationContext() {
}
public StandardEvaluationContext(Object rootObj) {
this.rootObj = rootObj;
}
public void addPropertyAccessor(PropertyAccessor accessor) {
this.propertyAccessors.add(accessor);
}
@Override
public Object getRootObj() {
return rootObj;
}
@Override
public List<PropertyAccessor> getPropertyAccessors() {
return propertyAccessors;
}
}
用了以上内容就足够了吗?A君 不经陷入了沉思:不,还不够。如果只是一级访问,比如:#{a.b} 那上边的内容就够了,但是如果是N级访问,如:#{a.b.c.d},那就芭比Q了。为了能够支持多级访问,还需要用个栈结构来保持运行状态,也就是类 ExpressionState。这也是A君在定义SpelNode接口是为什么会多出来的一个ExpressionState 类的原因了。ExpressionState 的实现也比较简单,包含:压栈,出栈等操作,当遇到多级访问时候,先把用逗号分割成一个个节点,再循环压栈出栈,大致如下图:
ExpressionState 实现代码如下:
import com.hqd.ch03.v11.spel.EvaluationContext;
import org.apache.commons.collections4.CollectionUtils;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.NoSuchElementException;
/**
* 表达式运行时状态
*/
public class ExpressionState {
private final EvaluationContext evaluationContext;
private final Object rootObject;
private Deque<Object> contextObjects = new ArrayDeque<>();
public ExpressionState(EvaluationContext context) {
this(context, context.getRootObj());
}
public ExpressionState(EvaluationContext context, Object rootObject) {
this.evaluationContext = context;
this.rootObject = rootObject;
}
public EvaluationContext getEvaluationContext() {
return evaluationContext;
}
/**
* 获取栈顶元素
*
* @return
*/
public Object getActiveContextObject() {
if (CollectionUtils.isEmpty(this.contextObjects)) {
return this.rootObject;
}
return this.contextObjects.element();
}
/**
* 压栈
*
* @param obj
*/
public void pushActiveContextObject(Object obj) {
this.contextObjects.push(obj);
}
/**
* 出栈
*
* @return
*/
public Object popActiveContextObject() {
try {
return this.contextObjects.pop();
} catch (NoSuchElementException ex) {
throw new IllegalStateException("Cannot pop active context object: stack is empty");
}
}
}
有了上下文(EvaluationContext)和运行时状态(ExpressionState)之后,A君 终于可以添加属性和方法节点了,属性节点由于存在对象访问器(PropertyAccessor),所以获取值时候先要看看对象访问器(PropertyAccessor)是否支持该属性,如果支持则用对象访问器(PropertyAccessor),不支持则用反射直接获取。PropertyOrFieldReference 代码如下:
import com.hqd.ch03.v11.spel.EvaluationContext;
import com.hqd.ch03.v11.spel.PropertyAccessor;
import com.hqd.ch03.v11.spel.standard.ExpressionState;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.ArrayUtils;
import java.util.List;
/**
* 获取类属性
*/
public class PropertyOrFieldReference extends SpelNodeImpl {
private final String name;
public PropertyOrFieldReference(String propertyOrFieldName, int startPos, int endPos) {
super(startPos, endPos);
this.name = propertyOrFieldName;
}
@Override
public Object getValue(ExpressionState state) {
try {
return readProperty(state.getActiveContextObject(), state.getEvaluationContext(), name);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 读取类属性
*
* @param contextObject
* @param evalContext
* @param name
* @return
*/
private Object readProperty(Object contextObject, EvaluationContext evalContext, String name) {
try {
/**
* 获取对象访问器
*/
List<PropertyAccessor> propertyAccessors = evalContext.getPropertyAccessors();
for (PropertyAccessor accessor : propertyAccessors) {
Class<?>[] specificTargetClasses = accessor.getSpecificTargetClasses();
/**
* 如果遇到可以解析的类型,用对象访问器获取相应对象
* 否则,直接获取
*/
if (ArrayUtils.contains(specificTargetClasses, contextObject.getClass())) {
if (accessor.canRead(evalContext, contextObject, name)) {
return accessor.read(evalContext, contextObject, name);
}
}
}
return PropertyUtils.getProperty(contextObject, name);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public String getName() {
return name;
}
}
方法节点也差不多,不过需要注意的是:方法存在参数,所以方法需要额外的子节点存储参数,MethodReference 代码如下:
import com.hqd.ch03.v11.spel.standard.ExpressionState;
import org.apache.commons.beanutils.MethodUtils;
import org.apache.commons.lang3.ArrayUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 方法节点
*/
public class MethodReference extends SpelNodeImpl {
private final String name;
public MethodReference(String methodName, int startPos, int endPos, SpelNodeImpl... args) {
super(startPos, endPos, args);
this.name = methodName;
}
@Override
public Object getValue(ExpressionState state) {
Object activeContextObject = state.getActiveContextObject();
try {
List<Object> params = new ArrayList<>();
if (ArrayUtils.isNotEmpty(children)) {
Arrays.stream(children).forEach(spelNode -> {
params.add(spelNode.getValue(state));
});
}
return MethodUtils.invokeMethod(activeContextObject, name, params.toArray());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public String getName() {
return name;
}
}
MethodReference 和PropertyOrFieldReference 只支持一级访问,所以还需要定义个父节点,用以支持多级访问。CompoundExpression 代码如下:
import com.hqd.ch03.v11.spel.ast.SpelNodeImpl;
import com.hqd.ch03.v11.spel.standard.ExpressionState;
/**
* 属性、方法父节点,支持多级访问
*/
public class CompoundExpression extends SpelNodeImpl {
public CompoundExpression(int startPos, int endPos, SpelNodeImpl... expressionComponents) {
super(startPos, endPos, expressionComponents);
}
@Override
public Object getValue(ExpressionState state) {
if (children.length == 1) {
return children[0].getValue(state);
}
int count = this.children.length - 1;
SpelNodeImpl nextNode = this.children[0];
Object result = nextNode.getValue(state);
for (int i = 1; i < count; i++) {
state.pushActiveContextObject(result);
nextNode = this.children[i];
result = nextNode.getValue(state);
state.popActiveContextObject();
}
state.pushActiveContextObject(result);
nextNode = this.children[count];
result = nextNode.getValue(state);
state.popActiveContextObject();
return result;
}
}
树节点定义完成之后,就可以构建AST树了。不过在此之前,为了方便给别人使用,也为了区别不同的表达式,比如1+2+3 这时候就是个字符串、#{a.b} 就一个spel表达式、#{a.b} 123 #{c.d} 则是混合表达式。故而存在多种实现,需要定义个Expression 接口。代码如下:
/**
* 表达式获取值接口
*/
public interface Expression {
/**
* 获取Spel表达式
*
* @return
*/
String getExpressionString();
/**
* 获取Spel表达式结果
*
* @return
*/
Object getValue();
/**
* 根据传入的上下文,获取spel表达式的值
*
* @param context 上下文
* @return
*/
Object getValue(EvaluationContext context);
/**
* 设置上下文的值
*
* @param context
* @param value
*/
void setValue(EvaluationContext context, Object value);
}
A君 先是实现最简单的常规表达式,不需要任何转换,直接返回就行了。LiteralExpression 代码如下:
import com.hqd.ch03.v11.spel.EvaluationContext;
import com.hqd.ch03.v11.spel.Expression;
/**
* 普通字符串
*/
public class LiteralExpression implements Expression {
private final String literalValue;
public LiteralExpression(String literalValue) {
this.literalValue = literalValue;
}
@Override
public String getExpressionString() {
return literalValue;
}
@Override
public Object getValue() {
return literalValue;
}
@Override
public Object getValue(EvaluationContext context) {
return literalValue;
}
@Override
public void setValue(EvaluationContext context, Object value) {
}
}
接着是单一的Spel表达式,这个只需要给个AST的根节点,获取根节点的值就行了。SpelExpression 代码如下:
import com.hqd.ch03.v11.spel.EvaluationContext;
import com.hqd.ch03.v11.spel.Expression;
import com.hqd.ch03.v11.spel.ast.SpelNodeImpl;
import com.hqd.ch03.v11.spel.context.StandardEvaluationContext;
/**
* 单一的spel表达式
*/
public class SpelExpression implements Expression {
private final String expression;
/**
* 根节点
*/
private final SpelNodeImpl ast;
private EvaluationContext evaluationContext = new StandardEvaluationContext();
public SpelExpression(String expressionString, SpelNodeImpl ast) {
this.expression = expressionString;
this.ast = ast;
}
@Override
public String getExpressionString() {
return expression;
}
@Override
public Object getValue() {
return getValue(getEvaluationContext());
}
@Override
public Object getValue(EvaluationContext context) {
ExpressionState expressionState = new ExpressionState(context);
return this.ast.getValue(expressionState);
}
@Override
public void setValue(EvaluationContext context, Object value) {
}
public EvaluationContext getEvaluationContext() {
return evaluationContext;
}
}
混合表达式实现也简单,用个数组保存各个表达式,循环获取值,而后将结果拼接即可。CompositeStringExpression 代码如下:
import com.hqd.ch03.v11.spel.EvaluationContext;
import com.hqd.ch03.v11.spel.Expression;
import org.apache.commons.lang3.ArrayUtils;
import java.util.Arrays;
/**
* 混合表达式
*/
public class CompositeStringExpression implements Expression {
private final String expressionString;
private final Expression[] expressions;
public CompositeStringExpression(String expressionString, Expression[] expressions) {
this.expressionString = expressionString;
this.expressions = expressions;
}
@Override
public String getExpressionString() {
return expressionString;
}
@Override
public Object getValue() {
return getValue(null);
}
@Override
public Object getValue(EvaluationContext context) {
if (ArrayUtils.isEmpty(expressions)) {
return "";
}
StringBuilder sb = new StringBuilder();
Arrays.stream(expressions).forEach(expr -> {
sb.append(expr.getValue(context));
});
return sb.toString();
}
@Override
public void setValue(EvaluationContext context, Object value) {
}
}
经过前边的一系类铺垫,终于可以构建AST树了,A君 先定义个 ExpressionParser 接口作为构建AST树的规范。代码如下:
/**
* 解析表达式接口
*/
public interface ExpressionParser {
/**
* 解析表达式
*
* @param expressionString
* @return
*/
Expression parseExpression(String expressionString);
/**
* 根据前后缀解析表达式
*
* @param expressionString
* @param context
* @return
*/
Expression parseExpression(String expressionString, ParserContext context);
}
接着就是老调重弹了,定义一个抽象类,提取出公共代码:根据模板提取相应表达式。TemplateAwareExpressionParser 代码如下:
import com.hqd.ch03.v11.spel.Expression;
import com.hqd.ch03.v11.spel.ExpressionParser;
import com.hqd.ch03.v11.spel.ParserContext;
import com.hqd.ch03.v11.spel.express.CompositeStringExpression;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List;
/**
* Spel表达式抽象类
*/
public abstract class TemplateAwareExpressionParser implements ExpressionParser {
@Override
public Expression parseExpression(String expressionString) {
return parseExpression(expressionString, null);
}
@Override
public Expression parseExpression(String expressionString, ParserContext context) {
/**
* 按模板解析
*/
if (context != null && context.isTemplate()) {
return parseTemplate(expressionString, context);
} else {
//不用模板,直接解析
return doParseExpression(expressionString, context);
}
}
protected abstract Expression doParseExpression(String expressionString, ParserContext context);
protected Expression parseTemplate(String expressionString, ParserContext context) {
List<Expression> expressions = new ArrayList<>();
String prefix = context.getExpressionPrefix();
String suffix = context.getExpressionSuffix();
int startIdx = 0;
/**
* 解析字符串中的表达式
*/
while (startIdx < expressionString.length()) {
int prefixIndex = expressionString.indexOf(prefix, startIdx);
if (prefixIndex >= startIdx) {
//说明中间有其他字符,直接解析
if (prefixIndex > startIdx) {
expressions.add(new LiteralExpression(expressionString.substring(startIdx, prefixIndex)));
}
int afterPrefixIndex = prefixIndex + prefix.length();
int suffixIndex = expressionString.indexOf(suffix, afterPrefixIndex);
if (suffixIndex == -1) {
throw new RuntimeException(String.format("缺失:%s", suffix));
}
/**
* 截取子串
*/
String expr = expressionString.substring(prefixIndex + prefix.length(), suffixIndex);
expr = expr.trim();
if (StringUtils.isBlank(expr)) {
throw new RuntimeException("expr 表达式为空");
}
expressions.add(doParseExpression(expr, context));
startIdx = suffixIndex + suffix.length();
} else {
expressions.add(new LiteralExpression(expressionString.substring(startIdx)));
startIdx = expressionString.length();
}
}
if (CollectionUtils.isNotEmpty(expressions)) {
if (expressions.size() == 1) {
return expressions.get(0);
} else {
return new CompositeStringExpression(expressionString, expressions.toArray(new Expression[0]));
}
}
return null;
}
}
接下来就是最终实现了,代码比较多些,但是一个个节点拆开来看,其实并不难,但是核心思想就只有一个,优先级高的运算符节点先生成。InternalSpelExpressionParser 代码如下:
import com.hqd.ch03.v11.spel.Expression;
import com.hqd.ch03.v11.spel.ParserContext;
import com.hqd.ch03.v11.spel.ast.*;
import com.hqd.ch03.v11.spel.express.CompoundExpression;
import com.hqd.ch03.v11.spel.standard.SpelExpression;
import com.hqd.ch03.v11.spel.standard.Token;
import com.hqd.ch03.v11.spel.standard.TokenKind;
import com.hqd.ch03.v11.spel.standard.Tokenizer;
import org.apache.commons.lang3.ArrayUtils;
import java.util.ArrayList;
import java.util.List;
/**
* spel表达式解析
*/
public class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
private String expressionString = "";
private int pos = 0;
private List<Token> tokenStream;
@Override
protected Expression doParseExpression(String expressionString, ParserContext context) {
this.expressionString = expressionString;
Tokenizer tokenizer = new Tokenizer(expressionString);
tokenStream = tokenizer.process();
this.pos = 0;
SpelNodeImpl ast = eatExpression();
return new SpelExpression(expressionString, ast);
}
/**
* 运算符优先级,乘除先,加减后
*
* @return
*/
private SpelNodeImpl eatExpression() {
return eatLogicalOrExpression();
}
/**
* 生成or节点
*
* @return
*/
private SpelNodeImpl eatLogicalOrExpression() {
SpelNodeImpl expr = eatLogicalAndExpression();
while (peekToken(TokenKind.SYMBOLIC_OR)) {
Token token = nextToken();
SpelNodeImpl rhExpr = eatLogicalAndExpression();
expr = new OpOr(token.getStartPos(), token.getEndPos(), expr, rhExpr);
}
return expr;
}
/**
* 生成and节点
*
* @return
*/
private SpelNodeImpl eatLogicalAndExpression() {
SpelNodeImpl expr = eatRelationalExpression();
while (peekToken(TokenKind.SYMBOLIC_AND)) {
Token token = nextToken();
SpelNodeImpl rhExpr = eatRelationalExpression();
expr = new OpAnd(token.getStartPos(), token.getEndPos(), expr, rhExpr);
}
return expr;
}
/**
* 生成 <、>、!=、<=、>=等节点
*
* @return
*/
private SpelNodeImpl eatRelationalExpression() {
SpelNodeImpl expr = eatSumExpression();
Token t = peekToken();
if (t == null) {
return expr;
}
if (t.isNumericRelationalOperator()) {
t = nextToken();
TokenKind tk = t.getKind();
SpelNodeImpl rhExpr = eatSumExpression();
if (tk == TokenKind.GT) {
return new OpGt(t.getStartPos(), t.getEndPos(), expr, rhExpr);
}
if (tk == TokenKind.LT) {
return new OpLt(t.getStartPos(), t.getEndPos(), expr, rhExpr);
}
if (tk == TokenKind.LE) {
return new OpLe(t.getStartPos(), t.getEndPos(), expr, rhExpr);
}
if (tk == TokenKind.GE) {
return new OpGe(t.getStartPos(), t.getEndPos(), expr, rhExpr);
}
if (tk == TokenKind.EQ) {
return new OpEQ(t.getStartPos(), t.getEndPos(), expr, rhExpr);
}
if (tk == TokenKind.NE) {
return new OpNE(t.getStartPos(), t.getEndPos(), expr, rhExpr);
}
}
return expr;
}
/**
* 生成+、-节点
*
* @return
*/
private SpelNodeImpl eatSumExpression() {
SpelNodeImpl expr = eatProductExpression();
while (peekToken(TokenKind.MINUS, TokenKind.PLUS)) {
Token token = nextToken();
SpelNodeImpl rgNode = eatProductExpression();
if (token.getKind() == TokenKind.MINUS) {
expr = new OpMinus(token.getStartPos(), token.getEndPos(), expr, rgNode);
} else if (token.getKind() == TokenKind.PLUS) {
expr = new OpPlus(token.getStartPos(), token.getEndPos(), expr, rgNode);
}
}
return expr;
}
/**
* 生成*、/、%节点
*
* @return
*/
private SpelNodeImpl eatProductExpression() {
SpelNodeImpl expr = eatUnaryExpression();
if (expr == null) {
throw new RuntimeException("表达式左边必须为数字");
}
while (peekToken(TokenKind.DIV, TokenKind.STAR, TokenKind.MOD)) {
Token token = nextToken();
SpelNodeImpl rgLiteralNode = eatLiteralNode();
if (token.getKind() == TokenKind.DIV) {
expr = new OpDiv(token.getStartPos(), token.getEndPos(), expr, rgLiteralNode);
} else if (token.getKind() == TokenKind.STAR) {
expr = new OpStar(token.getStartPos(), token.getEndPos(), expr, rgLiteralNode);
} else if (token.getKind() == TokenKind.MOD) {
expr = new OpMod(token.getStartPos(), token.getEndPos(), expr, rgLiteralNode);
}
}
return expr;
}
/**
* 生成一元表达式节点
*
* @return
*/
private SpelNodeImpl eatUnaryExpression() {
if (peekToken(TokenKind.NOT)) {
Token t = nextToken();
SpelNodeImpl expr = eatUnaryExpression();
if (t.getKind() == TokenKind.NOT) {
return new OpNOT(t.getStartPos(), t.getEndPos(), expr);
}
}
return eatLiteralNode();
}
/**
* 生成常量、变量节点
*
* @return
*/
private SpelNodeImpl eatLiteralNode() {
Token token = nextToken();
if (token.getKind() == TokenKind.LITERAL_INT) {
return new IntLiteral(token.getStartPos(), token.getEndPos(), token.getData());
} else if (token.getKind() == TokenKind.LPAREN) {
SpelNodeImpl spelNode = eatExpression();
token = nextToken();
if (token != null && token.getKind() == TokenKind.RPAREN) {
return spelNode;
}
throw new RuntimeException("没有匹配到')'");
} else if (token.getKind() == TokenKind.LITERAL_REAL) {
return new RealLiteral(token.getStartPos(), token.getEndPos(), token.getData());
} else if (token.getKind() == TokenKind.LITERAL_STRING) {
return new StringLiteral(token.getStartPos(), token.getEndPos(), token.getData());
} else if (token.getKind() == TokenKind.IDENTIFIER) {
return eatIdentifier(token);
}
return null;
}
/**
* 生成变量节点
*
* @param token
* @return
*/
private SpelNodeImpl eatIdentifier(Token token) {
/**
* 有可能是方法
*/
SpelNodeImpl[] args = maybeEatMethodArgs();
SpelNodeImpl startNode;
List<SpelNodeImpl> nodes = new ArrayList<>();
if (args == null) {
startNode = new PropertyOrFieldReference(token.getData(), token.getStartPos(), token.getEndPos());
} else {
startNode = new MethodReference(token.getData(), token.getStartPos(), token.getEndPos(), args);
}
nodes.add(startNode);
eatMethodOrProperty(nodes, token);
return new CompoundExpression(startNode.getStartPos(), nodes.get(nodes.size() - 1).getEndPos(),
nodes.toArray(new SpelNodeImpl[0]));
}
/**
* 生成方法节点
*
* @param nodes
* @param token
*/
private void eatMethodOrProperty(List<SpelNodeImpl> nodes, Token token) {
if (peekToken(TokenKind.DOT)) {
nextToken();
if (peekToken(TokenKind.IDENTIFIER)) {
token = nextToken();
}
SpelNodeImpl[] args = maybeEatMethodArgs();
SpelNodeImpl node;
if (args == null) {
node = new PropertyOrFieldReference(token.getData(), token.getStartPos(), token.getEndPos());
} else {
node = new MethodReference(token.getData(), token.getStartPos(), token.getEndPos(), args);
}
nodes.add(node);
eatMethodOrProperty(nodes, token);
}
}
private SpelNodeImpl[] maybeEatMethodArgs() {
if (!peekToken(TokenKind.LPAREN)) {
return null;
}
nextToken();
List<SpelNodeImpl> accumulatedArguments = new ArrayList<>();
while (!peekToken(TokenKind.RPAREN)) {
accumulatedArguments.add(eatExpression());
if (!peekToken(TokenKind.COMMA)) {
break;
}
nextToken();
}
if (!peekToken(TokenKind.RPAREN)) {
throw new RuntimeException("方法无法匹配')'");
}
nextToken();
return accumulatedArguments.toArray(new SpelNodeImpl[0]);
}
private boolean peekToken(TokenKind... possibles) {
Token t = peekToken();
if (t == null) {
return false;
}
if (ArrayUtils.isEmpty(possibles)) {
return false;
}
return ArrayUtils.contains(possibles, t.getKind());
}
private Token peekToken() {
if (pos >= tokenStream.size()) {
return null;
}
return tokenStream.get(pos);
}
private Token nextToken() {
if (pos >= tokenStream.size()) {
return null;
}
return tokenStream.get(pos++);
}
}
A君 现在已经实现了一个简单的Spel表达式,不过还无法无IOC容器整合到一起。当输入变量时,如:#{user.getName()},应该先从容器中获取user对象,再进行相应的操作。想要从IOC容器中获取对象,无疑需要拿到Bean工厂。为此,A君 先定义一个 BeanExpressionContext 类用来对 Bean工厂 做一个简单的包装。代码如下:
import com.hqd.ch03.v11.factory.ConfigurableBeanFactory;
/**
* 提供BeanFactory上下文,用以获取Bean对象
*/
public class BeanExpressionContext {
private final ConfigurableBeanFactory beanFactory;
public BeanExpressionContext(ConfigurableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public boolean containsObject(String key) {
return this.beanFactory.containsBean(key);
}
public Object getObject(String key) {
return beanFactory.getBean(key);
}
}
有了BeanExpressionContext对象后,A君 可以在运行Spel时指定到Spel的上下文中。但是光指定还不够,由于Spel在获取属性节点时,会根据对象访问器(PropertyAccessor)所支持的对象进行操作,所以还需要实现一个属于IOC容器的对象访问器——BeanExpressionContextAccessor,他仅支持操作BeanExpressionContext 对象。代码如下:
import com.hqd.ch03.v11.spel.EvaluationContext;
import com.hqd.ch03.v11.spel.PropertyAccessor;
import com.hqd.ch03.v11.spel.express.BeanExpressionContext;
/**
* 获取bean对象的属性读取器
*/
public class BeanExpressionContextAccessor implements PropertyAccessor {
@Override
public boolean canRead(EvaluationContext context, Object target, String name) {
return (target instanceof BeanExpressionContext && ((BeanExpressionContext) target).containsObject(name));
}
@Override
public Object read(EvaluationContext context, Object target, String name) {
return ((BeanExpressionContext) target).getObject(name);
}
@Override
public boolean canWrite(EvaluationContext context, Object target, String name) {
return false;
}
@Override
public void write(EvaluationContext context, Object target, String name, Object newValue) {
throw new RuntimeException("Beans in a BeanFactory are read-only");
}
@Override
public Class<?>[] getSpecificTargetClasses() {
return new Class<?>[]{BeanExpressionContext.class};
}
}
现在 A君 一切都准备好了,只剩下最后一步。定义一个总的入口,用来给IOC容器执行Spel表达式,屏蔽掉Spel内部细节。BeanExpressionResolver 代码如下:
import com.hqd.ch03.v11.spel.express.BeanExpressionContext;
/**
* bean中的spel表达式解析接口
*/
public interface BeanExpressionResolver {
/**
* 计算spel表达式
*
* @param value
* @param beanExpressionContext
* @return
*/
Object evaluate(String value, BeanExpressionContext beanExpressionContext);
}
在给BeanExpressionResolver 接口提供个默认实现,他只需要把 BeanExpressionContext 添加到Spel上下文中,并且添加 BeanExpressionContextAccessor 对象访问器即可。StandardBeanExpressionResolver 代码如下:
import com.hqd.ch03.v11.spel.BeanExpressionResolver;
import com.hqd.ch03.v11.spel.Expression;
import com.hqd.ch03.v11.spel.ExpressionParser;
import com.hqd.ch03.v11.spel.ParserContext;
import com.hqd.ch03.v11.spel.accessor.BeanExpressionContextAccessor;
import com.hqd.ch03.v11.spel.context.StandardEvaluationContext;
import com.hqd.ch03.v11.spel.parse.InternalSpelExpressionParser;
import com.hqd.ch03.v11.spel.parse.TemplateParserContext;
import org.apache.commons.lang3.StringUtils;
public class StandardBeanExpressionResolver implements BeanExpressionResolver {
private final ParserContext beanExpressionParserContext = new TemplateParserContext();
private ExpressionParser expressionParser;
public StandardBeanExpressionResolver() {
this.expressionParser = new InternalSpelExpressionParser();
}
@Override
public Object evaluate(String value, BeanExpressionContext beanExpressionContext) {
if (StringUtils.isBlank(value)) {
return value;
}
Expression expr = this.expressionParser.parseExpression(value, this.beanExpressionParserContext);
StandardEvaluationContext sec = new StandardEvaluationContext(beanExpressionContext);
sec.addPropertyAccessor(new BeanExpressionContextAccessor());
return expr.getValue(sec);
}
}
最后,A君 进行最后一步的修改,当设置bean属性时,如果是字符串,就使用 StandardBeanExpressionResolver 进行解析,SpringImitationV11 改造如下:
完整代码:
import com.hqd.ch03.v11.beans.BeanWrapper;
import com.hqd.ch03.v11.beans.BeanWrapperImpl;
import com.hqd.ch03.v11.config.BeanDefinition;
import com.hqd.ch03.v11.config.MutablePropertyValues;
import com.hqd.ch03.v11.config.Scope;
import com.hqd.ch03.v11.factory.BeanFactory;
import com.hqd.ch03.v11.factory.ConfigurableBeanFactory;
import com.hqd.ch03.v11.factory.FactoryBean;
import com.hqd.ch03.v11.io.ResourceLoader;
import com.hqd.ch03.v11.io.support.DefaultResourceLoader;
import com.hqd.ch03.v11.registry.BeanDefinitionRegistry;
import com.hqd.ch03.v11.registry.support.DefaultSingletonBeanRegistry;
import com.hqd.ch03.v11.registry.support.SimpleBeanDefinitionRegistry;
import com.hqd.ch03.v11.spel.BeanExpressionResolver;
import com.hqd.ch03.v11.spel.express.BeanExpressionContext;
import com.hqd.ch03.v11.spel.express.StandardBeanExpressionResolver;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import java.beans.PropertyEditor;
import java.util.HashMap;
import java.util.Map;
public abstract class SpringImitationV11 extends DefaultSingletonBeanRegistry implements ConfigurableBeanFactory {
private final Map<Class<?>, Class<? extends PropertyEditor>> customEditors = new HashMap<>();
protected BeanDefinitionRegistry beanDefinitionRegistry = new SimpleBeanDefinitionRegistry();
protected BeanExpressionResolver beanExpressionResolver = new StandardBeanExpressionResolver();
/**
* 单例缓存
*/
protected Map<String, Scope> scopeCache = new HashMap<>();
/**
* 资源加载器
*/
protected ResourceLoader resourceLoader = new DefaultResourceLoader();
public static boolean isFactoryDereference(String name) {
return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
}
@Override
public void registerCustomEditor(Class<?> requiredType, Class<? extends PropertyEditor> propertyEditorClass) {
customEditors.put(requiredType, propertyEditorClass);
}
/**
* 处理传入的name,去除&开头
*
* @param name
* @return
*/
protected String transformedBeanName(String name) {
if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
return name;
}
String beanName = name;
do {
beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
}
while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
return beanName;
}
/**
* 区分正常bean还是FactoryBean
*
* @param obj
* @param name
* @param beanName
* @param bd
* @return
*/
protected Object getObjectForBeanInstance(Object obj, String name, String beanName, BeanDefinition bd) {
if (isFactoryDereference(name)) {
if (!(obj instanceof FactoryBean)) {
throw new RuntimeException(String.format("%s 未实现FactoryBean接口", beanName));
}
return obj;
}
if (obj instanceof FactoryBean) {
return ((FactoryBean) obj).getObject();
}
return obj;
}
private void initBean(BeanWrapper beanWrapper, BeanDefinition bd) {
try {
MutablePropertyValues properties = bd.getProperties();
properties.getProperties().forEach(propertyValue -> {
boolean isRef = propertyValue.isRef();
Object value = propertyValue.getValue();
if (isRef) {
value = getBean((String) value);
}
try {
if (value instanceof String) {
/**
* 使用Spel进行解析
*/
value = beanExpressionResolver.evaluate((String) value, new BeanExpressionContext(this));
}
beanWrapper.setPropertyValue(propertyValue.getName(), value, isRef);
//BeanUtils.setProperty(bean, propertyValue.getName(), value);
} catch (Exception e) {
throw new RuntimeException(e);
}
});
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private Object createBean(BeanDefinition bd, String beanName) {
try {
Object instance = null;
if (bd.isSingleton()) {//单例
instance = getSingleton(beanName);
if (instance == null) {
instance = doCreate(bd, beanName);
}
addSingleton(beanName, instance);
} else if (bd.isPrototype()) {//多例
instance = doCreate(bd, beanName);
} else {//自定义作用域
String scopeName = bd.getScope();
Scope scope = scopeCache.get(scopeName);
if (scope == null) {
throw new RuntimeException(String.format("[%s]未注册scope", scopeName));
}
instance = scope.get(beanName, () -> doCreate(bd, beanName));
}
return instance;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected Object doCreate(BeanDefinition bd, String beanName) {
String beanClass = bd.getBeanClass();
try {
Object instance = Class.forName(beanClass).getConstructor().newInstance();
BeanWrapper beanWrapper = new BeanWrapperImpl(instance);
//初始化BeanWrapper
initBeanWrapper(beanWrapper);
addSingletonFactory(beanName, () -> beanWrapper.getWrappedInstance());
//doSomething
initBean(beanWrapper, bd);
return instance;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected void initBeanWrapper(BeanWrapper bw) {
/**
* 注册自定义类型转换器
*
*/
registerCustomEditors(bw);
}
@Override
public boolean containsBean(String name) {
String beanName = transformedBeanName(name);
if (containsSingleton(beanName) || containsBeanDefinition(beanName)) {
return true;
}
return false;
}
public boolean containsBeanDefinition(String beanName) {
return this.beanDefinitionRegistry.containsBeanDefinition(beanName);
}
/**
* 是否应该区分全局转换器和局部转换器
* 全局为Factory级别,局部为Bean级别
*
* @param bw
*/
protected void registerCustomEditors(BeanWrapper bw) {
if (MapUtils.isNotEmpty(customEditors)) {
customEditors.forEach((key, val) -> {
try {
bw.registerCustomEditor(key, val.getConstructor().newInstance());
} catch (Exception e) {
e.printStackTrace();
}
});
}
}
@Override
public void registerScope(String scopeName, Scope scope) {
if (StringUtils.isNotBlank(scopeName) && scope != null) {
scopeCache.put(scopeName, scope);
}
}
@Override
public <T> T getBean(String name, Class<T> clazz) {
return (T) doGetBean(name);
}
@Override
public Object getBean(String name) {
return doGetBean(name);
}
protected Object doGetBean(String name) {
String beanName = transformedBeanName(name);
BeanDefinition bd = beanDefinitionRegistry.getBeanDefinition(beanName);
Object bean = createBean(bd, beanName);
bean = getObjectForBeanInstance(bean, name, beanName, bd);
return bean;
}
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}
折腾了这么久,总算完成了。A君 按捺不住激动的心、颤抖的手。对努力的成果进行测试。先对IKun对象添加几个属性,方便测试,代码如下:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class IKun extends User {
private String aliaName;
private boolean handsome;
private double deposit;
private int scope;
private String[] hobbies;
}
接下来就是这次的重头戏了,A君 在xml文件中配置了各式各样的Spel表达式。bean.xml 配置如下:
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="idCard" class="com.hqd.ch03.bean.IdCard">
<property name="id" value="123456"/>
<property name="user" ref="ikun"/>
</bean>
<bean id="test" class="com.hqd.ch03.bean.IdCard">
<property name="id" value="法外狂徒-张三"/>
</bean>
<bean id="ikun" class="com.hqd.ch03.bean.IKun" scope="singleton">
<property name="idCard" ref="idCard"/>
<property name="name" value="良民"/>
<property name="scope" value="#{3+4/2-2*3}"/>
<property name="aliaName" value="#{test.id} (#{idCard.getUser().getName() + 1 + 2 / 2 + 'dddd'}-1)"/>
<property name="age" value="14"/>
<property name="handsome" value="#{1+2 < 3+4}"/>
<property name="deposit" value="6.66"/>
<property name="hobbies" value="唱,跳,rap"/>
</bean>
</beans>
A君 已经将测试案例准备完毕,接下来是骡子是马,跑跑就知道了。运行结果如下:
结果并没有让 A君 失望,努力几天的成果并没有付之东流。A君 又Get到了一个小技能呢。当然,A君 做的还差很多功能,比如:获取配置属性、instanceof、matches、三目运算等,这些就看各位看官发挥了
总结
正所谓树欲静而风不止,欲知后事如何,请看下回分解(✪ω✪)