例如:mybatis對sql中#{name}占位符的替換為羡藐?
主要關(guān)注:GenericTokenParser岁疼、ParameterMappingTokenHandler.handleToken
/**
* 完成對#{}的解析工作:1.將#{}使用吊骤?進行代替缓淹,2.解析出#{}里面的值進行存儲
* @param sql
* @return
*/
private void getBoundSql(String sql) {
//標(biāo)記處理類:配置標(biāo)記解析器來完成對占位符的解析處理工作
ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
// 工具類:3個參數(shù):占位符的 開始標(biāo)記登失、結(jié)束標(biāo)記遏佣、替換為指定 符號'?' 的處理工具
GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
//解析出來的sql
String parseSql = genericTokenParser.parse(sql);
//#{}里面解析出來的參數(shù)名稱
List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();
}
/**
* 開閉原則 定義接口
*/
public interface TokenHandler {
String handleToken(String content);
}
// -----------------------------接口實現(xiàn)類------------------------------------
import java.util.ArrayList;
import java.util.List;
public class ParameterMappingTokenHandler implements TokenHandler {
// 將解析出來的字段放入到集合中,面向?qū)ο蟮乃枷耄喊炎侄畏庋b為對象ParameterMapping
// 方便后續(xù)對占位符參數(shù)的使用
private List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();
// ParameterMapping.context是參數(shù)名稱 #{id}揽浙、#{username}中的 id状婶、username
// 注意!注意馅巷!注意膛虫!=========================================
// TODO 將占位符中的變量用 ?代替
public String handleToken(String content) {
parameterMappings.add(buildParameterMapping(content));
return "?";
}
// 將占位符中的變量封裝為對象
private ParameterMapping buildParameterMapping(String content) {
ParameterMapping parameterMapping = new ParameterMapping(content);
return parameterMapping;
}
// 成員變量get方法
public List<ParameterMapping> getParameterMappings() {
return parameterMappings;
}
// 成員變量set方法
public void setParameterMappings(List<ParameterMapping> parameterMappings) {
this.parameterMappings = parameterMappings;
}
}
// --------------------------ParameterMapping---------------------------------
public class ParameterMapping {
private String content;
// 有參構(gòu)造
public ParameterMapping(String content) {
this.content = content;
}
// 成員變量get方法
public String getContent() {
return content;
}
// 成員變量set方法
public void setContent(String content) {
this.content = content;
}
}
public class GenericTokenParser {
private final String openToken; //開始標(biāo)記
private final String closeToken; //結(jié)束標(biāo)記
private final TokenHandler handler; //標(biāo)記處理器
public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {
this.openToken = openToken;
this.closeToken = closeToken;
this.handler = handler;
}
/**
* 解析${}和#{}
* @param text
* @return
* 該方法主要實現(xiàn)了配置文件钓猬、腳本等片段中占位符的解析稍刀、處理工作,并返回最終需要的數(shù)據(jù)敞曹。
* 其中账月,解析工作由該方法完成,處理工作是由處理器handler的handleToken()方法來實現(xiàn)
*/
public String parse(String text) {
// 驗證參數(shù)問題澳迫,如果是null局齿,就返回空字符串。
if (text == null || text.isEmpty()) {
return "";
}
// 下面繼續(xù)驗證是否包含開始標(biāo)簽橄登,如果不包含抓歼,默認(rèn)不是占位符,直接原樣返回即可拢锹,否則繼續(xù)執(zhí)行谣妻。
int start = text.indexOf(openToken, 0);
if (start == -1) {
return text;
}
// 把text轉(zhuǎn)成字符數(shù)組src,并且定義默認(rèn)偏移量offset=0面褐、存儲最終需要返回字符串的變量builder拌禾,
// text變量中占位符對應(yīng)的變量名expression。判斷start是否大于-1(即text中是否存在openToken)展哭,如果存在就執(zhí)行下面代碼
char[] src = text.toCharArray();
int offset = 0;
final StringBuilder builder = new StringBuilder();
StringBuilder expression = null;
while (start > -1) {
// 判斷如果開始標(biāo)記前如果有轉(zhuǎn)義字符湃窍,就不作為openToken進行處理闻蛀,否則繼續(xù)處理
if (start > 0 && src[start - 1] == '\\') {
builder.append(src, offset, start - offset - 1).append(openToken);
offset = start + openToken.length();
} else {
//重置expression變量,避免空指針或者老數(shù)據(jù)干擾您市。
if (expression == null) {
expression = new StringBuilder();
} else {
expression.setLength(0);
}
builder.append(src, offset, start - offset);
offset = start + openToken.length();
int end = text.indexOf(closeToken, offset);
while (end > -1) {////存在結(jié)束標(biāo)記時
if (end > offset && src[end - 1] == '\\') {//如果結(jié)束標(biāo)記前面有轉(zhuǎn)義字符時
// this close token is escaped. remove the backslash and continue.
expression.append(src, offset, end - offset - 1).append(closeToken);
offset = end + closeToken.length();
end = text.indexOf(closeToken, offset);
} else {//不存在轉(zhuǎn)義字符觉痛,即需要作為參數(shù)進行處理
expression.append(src, offset, end - offset);
offset = end + closeToken.length();
break;
}
}
if (end == -1) {
// close token was not found.
builder.append(src, start, src.length - start);
offset = src.length;
} else {
// 注意!注意茵休!注意薪棒!=====================================================
// TODO 主要關(guān)注點:首先根據(jù)參數(shù)的key(即expression)進行參數(shù)處理,返回?作為占位符
builder.append(handler.handleToken(expression.toString()));
offset = end + closeToken.length();
}
}
start = text.indexOf(openToken, offset);
}
if (offset < src.length) {
builder.append(src, offset, src.length - offset);
}
return builder.toString();
}
}