commit
f71d0dbe18
|
@ -46,4 +46,5 @@ src/test/resources/font
|
|||
src/main/resources/WEB-INF/vm/outFile
|
||||
target/
|
||||
*.back
|
||||
src/main/old_src/
|
||||
|
||||
|
|
|
@ -47,6 +47,8 @@ jQuery().ready(function(){
|
|||
}
|
||||
}
|
||||
|
||||
addRowBack(2, configObj);
|
||||
|
||||
changeDetailFieldReadOnly(detailTable, detail2ComPayDateId, detail2PaymentTypeId, computeDatePayType)
|
||||
|
||||
// 主表字段发生变化
|
||||
|
|
|
@ -42,6 +42,8 @@ function getDate(time) {
|
|||
return y + "-" + (m < 10 ? "0" + m : m) + "-" + (d < 10 ? "0" + d : d);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 校验比例是否等于100
|
||||
* @param detailTable 明细表
|
||||
|
@ -153,6 +155,18 @@ function changeDetailPayDate(obj){
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段联动后添加明细行后执行的逻辑
|
||||
* @param detail
|
||||
* @param configObj
|
||||
*/
|
||||
function addRowBack(detail, configObj){
|
||||
WfForm.registerAction(WfForm.ACTION_ADDROW + detail, function(index){
|
||||
configObj.index = index;
|
||||
changeDetailPayDate(configObj);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 改变明细表字段只读
|
||||
* @param detailTable
|
||||
|
@ -173,3 +187,5 @@ function changeDetailFieldReadOnly(detailTable, detailComDateField, detailPaymen
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
// 存货分类编码
|
||||
const chflbmId = WfForm.convertFieldNameToId("chflbm");
|
||||
// 流程类型
|
||||
const workflowType = WfForm.convertFieldNameToId("lcxz");
|
||||
// 唯一编码
|
||||
const unqieCode = WfForm.convertFieldNameToId("wybm");
|
||||
// 帐套查询
|
||||
const zt = WfForm.convertFieldNameToId("ztcx");
|
||||
// 存货编码
|
||||
const chbm = WfForm.convertFieldNameToId("chbm2");
|
||||
|
||||
changeVal();
|
||||
WfForm.bindFieldChangeEvent(chflbmId, function(obj,id,value){
|
||||
changeVal();
|
||||
});
|
||||
|
||||
function changeVal(){
|
||||
let value = WfForm.getFieldValue(chflbmId)
|
||||
console.log('value: ', value);
|
||||
if(!value){
|
||||
return;
|
||||
}
|
||||
let firstVal = value.charAt(0);
|
||||
if(firstVal == 0){
|
||||
value = value.substring(1, value.length);
|
||||
console.log('修改后的val ', value);
|
||||
setTimeout(()=>{
|
||||
$(`#${chflbmId}span`)[0].innerHTML = value;
|
||||
},5);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
// 主表预计付款日期
|
||||
const yjfksj = WfForm.convertFieldNameToId('yjfksj');
|
||||
// 主表比例
|
||||
const bl = WfForm.convertFieldNameToId('bcfkbl');
|
||||
// 明细要求付款日期
|
||||
const detail3Yqfkrq = WfForm.convertFieldNameToId('yqfkrq',"detail_3");
|
||||
// 明细本次付款比例
|
||||
const detailBl = WfForm.convertFieldNameToId('bcfkbl',"detail_3");
|
||||
|
||||
WfForm.registerCheckEvent(WfForm.OPER_ADDROW + "3", function (callback) {
|
||||
callback();
|
||||
initDeatail3Date();
|
||||
});
|
||||
|
||||
WfForm.bindFieldChangeEvent(`${yjfksj},${bl}`, function(obj,id,value){
|
||||
initDeatail3Date();
|
||||
});
|
||||
|
||||
function initDeatail3Date(){
|
||||
let dateVal = WfForm.getFieldValue(yjfksj);
|
||||
let blVal = WfForm.getFieldValue(bl);
|
||||
console.log('dateVal ', dateVal);
|
||||
console.log('blVal ', blVal);
|
||||
let detail3RowArr = WfForm.getDetailAllRowIndexStr('detail_3').split(",");
|
||||
for (let i = 0; i < detail3RowArr.length; i++) {
|
||||
let rowIndex = detail3RowArr[i];
|
||||
if (rowIndex !== "") {
|
||||
WfForm.changeFieldValue(`${detail3Yqfkrq}_${rowIndex}`,{value: dateVal});
|
||||
WfForm.changeFieldValue(`${detailBl}_${rowIndex}`,{value: blVal});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,217 @@
|
|||
var m_xmmc = WfForm.convertFieldNameToId("xmmc");//项目名称
|
||||
var m_bmwyz = WfForm.convertFieldNameToId("glkcjlb");//关联勘察
|
||||
var m_bmwyz1 = WfForm.convertFieldNameToId("glxsxqd");//关联销售
|
||||
var m_bmwyz2 = WfForm.convertFieldNameToId("chdadx");//唯一值多选
|
||||
var dt2_tyrq = WfForm.convertFieldNameToId("tyrq", "detail_2");//明细2停用日期
|
||||
jQuery(document).ready(function () {
|
||||
setTimeout("bindFieldChange()", "500");
|
||||
});
|
||||
|
||||
function bindFieldChange() {
|
||||
WfForm.bindFieldChangeEvent(m_xmmc, function (obj, id, value) {
|
||||
WfForm.changeFieldValue(m_bmwyz, {value: ''});
|
||||
WfForm.changeFieldValue(m_bmwyz1, {value: ''});
|
||||
WfForm.changeFieldValue(m_bmwyz2, {value: ''});
|
||||
});
|
||||
//提交检验
|
||||
WfForm.registerCheckEvent(WfForm.OPER_SUBMIT, function (callback) {
|
||||
var flag = true;
|
||||
var rowArr = WfForm.getDetailAllRowIndexStr("detail_2").split(",");
|
||||
for (var i = 0; i < rowArr.length; i++) {
|
||||
var rowid = rowArr[i];
|
||||
var dt2_tyrq_v = WfForm.getFieldValue(dt2_tyrq + "_" + rowid);
|
||||
if (dt2_tyrq_v != "") {
|
||||
WfForm.showMessage("明细2第" + (i + 1) + "行设备已在" + dt2_tyrq_v + "停用!", 2, 5);
|
||||
flag = flase;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (flag) {
|
||||
callback(); //继续提交需调用callback,不调用代表阻断
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var rowArr = WfForm.getDetailAllRowIndexStr("detail_3").split(",");
|
||||
// WfForm.delDetailRow("detail_2", "all");
|
||||
for (var i = 0; i < rowArr.length; i++) {
|
||||
var rowIndex = rowArr[i];
|
||||
if (rowIndex !== "") {
|
||||
var fieldMark = "field12566" + rowIndex;
|
||||
if (fieldMark === "0") {
|
||||
var count = WfForm.getFieldValue("field12562" + rowIndex);
|
||||
var field7108_ = WfForm.getFieldValue("field6117_" + rowIndex);
|
||||
WfForm.addDetailRow("detail_2", {field8567: {value: count}, field7583: {value: field7108_},});
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
dev-1 王宣然 限制明细表选择的bom不能重复
|
||||
*/
|
||||
const detailBomField = WfForm.convertFieldNameToId('bombm', 'detail_1');
|
||||
/**
|
||||
* 主表辅助字段
|
||||
*/
|
||||
const mainAttrField = WfForm.convertFieldNameToId('ycbmpzx');
|
||||
/**
|
||||
* 明细1数量
|
||||
*/
|
||||
const detail1NumFieldId = WfForm.convertFieldNameToId('sl1', "detail_1");
|
||||
/**
|
||||
* 明细3 明细2数量
|
||||
*/
|
||||
const detail3NumFieldId = WfForm.convertFieldNameToId('mx2sl', "detail_3");
|
||||
/**
|
||||
* 明细3数量
|
||||
*/
|
||||
const detail3NumFieldId2 = WfForm.convertFieldNameToId('sl1', "detail_3");
|
||||
/**
|
||||
* 明细3需求数量
|
||||
*/
|
||||
const detail3NumFieldId3 = WfForm.convertFieldNameToId('sl', "detail_3");
|
||||
/**
|
||||
* 明细3bom编码
|
||||
*/
|
||||
const detail3BomFieldId = WfForm.convertFieldNameToId('bombm', "detail_3");
|
||||
/**
|
||||
* 明细1bom编码
|
||||
*/
|
||||
const detail1BomFieldId = WfForm.convertFieldNameToId('bombm', "detail_1");
|
||||
/*
|
||||
*已经选择的bom
|
||||
*/
|
||||
let choiceArr = new Set();
|
||||
let updateNum = {};
|
||||
|
||||
const yjdhrq = WfForm.convertFieldNameToId('yjdhrq');
|
||||
console.log('id ', yjdhrq)
|
||||
// 明细到货日期
|
||||
const detail1yjdhsj = WfForm.convertFieldNameToId('yjdhsj', "detail_2");
|
||||
|
||||
WfForm.bindFieldChangeEvent(yjdhrq, function (obj, id, value) {
|
||||
initDeatail2Date(value);
|
||||
});
|
||||
|
||||
WfForm.registerCheckEvent(WfForm.OPER_ADDROW + "2", function (callback) {
|
||||
callback();
|
||||
let value = WfForm.getFieldValue(yjdhrq);
|
||||
initDeatail2Date(value);
|
||||
});
|
||||
|
||||
WfForm.bindDetailFieldChangeEvent(detailBomField, function (id, rowIndex, value) {
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
if (choiceArr.has(value)) {
|
||||
WfForm.showMessage('不能选择重复的bom,请重新选择!');
|
||||
setTimeout(() => {
|
||||
WfForm.changeFieldValue(`${detailBomField}_${rowIndex}`, {value: '', specialobj: []});
|
||||
}, 5);
|
||||
}
|
||||
initChoiceArr();
|
||||
getBomArr();
|
||||
});
|
||||
|
||||
|
||||
// 数量变化自动计算
|
||||
WfForm.bindDetailFieldChangeEvent(`${detail1NumFieldId},${detailBomField}`, function (id, rowIndex, value) {
|
||||
let detail1RowArr = WfForm.getDetailAllRowIndexStr('detail_1').split(",");
|
||||
console.log(detail1RowArr);
|
||||
for (let i = 0; i < detail1RowArr.length; i++) {
|
||||
let rowIndex = detail1RowArr[i];
|
||||
if (rowIndex !== "") {
|
||||
let num = WfForm.getFieldValue(`${detail1NumFieldId}_${rowIndex}`);
|
||||
// bom编码
|
||||
let bom = WfForm.getFieldValue(`${detail1BomFieldId}_${rowIndex}`);
|
||||
setTimeout(() => {
|
||||
computeNum(bom, num);
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
function initDeatail2Date(value) {
|
||||
let detail2RowArr = WfForm.getDetailAllRowIndexStr('detail_2').split(",");
|
||||
for (let i = 0; i < detail2RowArr.length; i++) {
|
||||
let rowIndex = detail2RowArr[i];
|
||||
if (rowIndex !== "") {
|
||||
WfForm.changeFieldValue(`${detail1yjdhsj}_${rowIndex}`, {value: value});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算明细3数量
|
||||
*/
|
||||
function computeNum(detail1Bom, detail1Num) {
|
||||
let detail3RowArr = WfForm.getDetailAllRowIndexStr('detail_3').split(",");
|
||||
console.log(detail3RowArr);
|
||||
for (let i = 0; i < detail3RowArr.length; i++) {
|
||||
let rowIndex = detail3RowArr[i];
|
||||
if (rowIndex !== "") {
|
||||
// bom编码
|
||||
let bom = WfForm.getFieldValue(`${detail3BomFieldId}_${rowIndex}`);
|
||||
if (bom != detail1Bom) {
|
||||
continue;
|
||||
}
|
||||
console.log('需要改变的index ', rowIndex)
|
||||
WfForm.changeFieldValue(`${detail3NumFieldId}_${rowIndex}`, {value: detail1Num});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化已经选择的数组
|
||||
*/
|
||||
function initChoiceArr() {
|
||||
choiceArr = new Set();
|
||||
let rowArr = WfForm.getDetailAllRowIndexStr('detail_1').split(",");
|
||||
for (let i = 0; i < rowArr.length; i++) {
|
||||
let rowIndex = rowArr[i];
|
||||
if (rowIndex !== "") {
|
||||
let bom = WfForm.getFieldValue(`${detailBomField}_${rowIndex}`);
|
||||
// console.log('bom ', bom);
|
||||
if (!bom) {
|
||||
continue;
|
||||
}
|
||||
choiceArr.add(bom);
|
||||
}
|
||||
}
|
||||
console.log('choiceArr ', choiceArr)
|
||||
}
|
||||
|
||||
WfForm.registerCheckEvent(WfForm.OPER_DELROW + "1", function (callback) {
|
||||
callback();
|
||||
getBomArr();
|
||||
initChoiceArr();
|
||||
});
|
||||
|
||||
function getBomArr() {
|
||||
let arr = [];
|
||||
let rowArr = WfForm.getDetailAllRowIndexStr('detail_1').split(",");
|
||||
for (let i = 0; i < rowArr.length; i++) {
|
||||
let rowIndex = rowArr[i];
|
||||
if (rowIndex !== "") {
|
||||
arr.push(WfForm.getFieldValue(`${detailBomField}_${rowIndex}`));
|
||||
}
|
||||
}
|
||||
let bom = new Set(arr);
|
||||
let arr2 = [...bom];
|
||||
let obj = {};
|
||||
obj.value = arr2.join(',');
|
||||
let specialobjArr = [];
|
||||
for (let i = 0; i < arr2.length; i++) {
|
||||
let obj = {};
|
||||
obj.id = arr2[i];
|
||||
obj.name = arr2[i];
|
||||
specialobjArr.push(obj);
|
||||
}
|
||||
obj.specialobj = specialobjArr;
|
||||
let val = WfForm.changeFieldValue(mainAttrField, obj);
|
||||
if (!val) {
|
||||
WfForm.delDetailRow("detail_3", "all");
|
||||
}
|
||||
console.log('主表辅助字段值 : ', WfForm.getFieldValue(mainAttrField));
|
||||
}
|
||||
|
|
@ -51,6 +51,9 @@ function init(){
|
|||
'computeDatePayType': computeDatePayType,
|
||||
'paymentTypeGetValue': paymentTypeGetValue
|
||||
}
|
||||
|
||||
addRowBack(3, obj);
|
||||
|
||||
changeDetailFieldReadOnly(detailTable, detailComPayDateId, detailPaymentTypeId, computeDatePayType)
|
||||
// 主表字段发生变化
|
||||
mainFieldChangeDetailCom(`${mainProjectId},${contractSignDateId}`, detailTable, obj);
|
||||
|
|
|
@ -52,6 +52,7 @@ function init(){
|
|||
'computeDatePayType': computeDatePayType,
|
||||
'paymentTypeGetValue': paymentTypeGetValue
|
||||
}
|
||||
addRowBack(2, obj);
|
||||
changeDetailFieldReadOnly(detailTable, detail2ComPayDateId, detail2PaymentTypeId, computeDatePayType)
|
||||
// 主表字段发生变化
|
||||
mainFieldChangeDetailCom(mainProjectId, detailTable, obj);
|
||||
|
|
|
@ -535,8 +535,47 @@ $(() => {
|
|||
callback()
|
||||
})
|
||||
})
|
||||
|
||||
/* ******************* 流程明细字段包含某个字符串时给某个字段赋值为某个东西end ******************* */
|
||||
|
||||
|
||||
/* ******************* 流程提交按钮触发流程抄送start ******************* */
|
||||
|
||||
$(() => {
|
||||
let config = {
|
||||
// 抄送人所在明细表编号,1-明细1 ; 2-明细2
|
||||
detailNo: '1',
|
||||
// 抄送人所在字段字段名
|
||||
ccResourceField: '',
|
||||
// 一级部门负责人字段名
|
||||
firstLevelDepResponsibleField: ''
|
||||
}
|
||||
WfForm.registerCheckEvent(WfForm.OPER_SUBMIT, function (callback) {
|
||||
$.ajax("/api/aiyh/pcn/cc-workflow/trigger", {
|
||||
type: "POST",
|
||||
data: JSON.stringify({
|
||||
detailNo: config.detailNo,
|
||||
requestId: WfForm.getBaseInfo().requestid,
|
||||
ccResourceField: config.ccResourceField,
|
||||
firstLevelDepResponsibleField: config.firstLevelDepResponsibleField
|
||||
}),
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
complete: (res) => {
|
||||
res.then(r => {
|
||||
if (r && r.code === 200) {
|
||||
callback();
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
});
|
||||
})
|
||||
|
||||
/* ******************* 流程提交按钮触发流程抄送end ******************* */
|
||||
|
||||
|
||||
/* ******************* 提交外出流程 ******************* */
|
||||
(function start() {
|
||||
function fillNum(n) {
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCa8TBiSrOPSgJER3nd+gW2eCoSjXHRVxEX54AdxzmtDe4Rn6OlvMVG1GLWfTnjdwbLTVeZUqjTpgZkCdGIp9njgjs6v6oQ7f8+DhQvByp6BSNXBHM+XH+YnmIohcQpnH7qvDT8l41Io2IVZbeNBWMlAtnz7laXWisYkYEDUm8HXOadKLkX+aqWdEYJHGy8zYN9ktNWH7P8BXBUAUd1vtTZhhE6BMd/O0WSqWhgpABbFZ8VKVT/5u4dlXUykKNbCKrsfXoiLE/7plbfHji02sUdaA1LAOF4QOaTePaGZu/55TWCYZPjWKrW2c0Gr8mS/1MQrg1zsWV2c59GqLNMWICtAgMBAAECggEAOS4thvi+j3DmqUAfj3YHybFLBZHBoVoaatH6jALMHDt50nMxt6aUv3D+EN4iEPoKPdkLLQA+Ye1xilW9SEt5s+aJ6UJ2hszuV35moHxqhqGCy0hPJ4KHbFF3NDE5lYm1pPdULqvXbiktt2vUc2y7jBsjSEx7FFwob2azUACKDRL+PkNGGcMmdMeCIHBcQf+j0CTY/VvsPGHqf53I/Ffe2KAE3Y0SugoIB5dqUE9VN5KGC01+X6hufaNUpPLYLbBiaI1eHVtMCHbGNmNnHuFgsEWT4ya4fw1rm9DLS8zJwDHkHmegE5ieqM7EzM2UIvKCN3TQJcy+Ssk3FX+KBFQqmQKBgQDaiC8QFqA7z8DY0K6lwj8PMiuNeu9APJuHLAhGdbDXjAKfdU1cNU4SHfsjjCU4Wymgp8QNrMD8WhardKHV+zr6B0mRMnUYOI6aoAg1+UcGoHYM2PEonZS+OtBHI68wDIsiKQ2Mybsl6GlxoLfo3YdlcclPFHNzXoVc8QFboN0TdwKBgQC1gerb6Anwlj6rJB9RCnoR/Jq5Lyh5wa1eL1C+gZSSM8Mwwxu3VpPohUMca7t1F1vmEDaf/Z2nsQ8IULnYkS94iGYooIcV2ffY5bagi/DkuTYfbf2QBWSZNTI1yHTWoaIXElRG0+svxgL6w98D3Y5lzIt0skz+/ZSGIdiLg8mt+wKBgQCBJCNzxXsxfWeAeWoMKMttJn/YXwLOGkLq0ZmeUeSMrH/MTdzGlfWp/S+xZRuFv1HNT/crAaEWQALPleAhfRLwOKg/9up9wsZ7GAFiLArOHrtEgluZXe5NsKHuuGbJ5U+/gzUvsvM2xq6xaIHmSiu+Rkzpv7MuRXhYYVAlHt4mpwKBgALOOEgf5Q9v8xYIH+fLxqlCg027ed+v67MZ/iCDtj0wSaMWUPZbgzvD246z55jevI/ozj9Y1zgBV58kSEsdq2MskI+uM4hV7yvOGS2QHDAc4MZJl/LC8pQfq2ADcjLjGrNKmDzkB62cXO1tW6Qep5XRPJKYMvJ6DvKn0UYOym5DAoGBALsEOPep1R8CBWCnni8igvbMudBwpzg5e9V6bu7p3MdADhGA/FMkCQgI2VH6fTufQsOA6rYi7YcdMS80r+p1/HMuVskOlqQfPDJ3OG0g0W8nwxf3XbIoEJcaAzLmqDOQMHfhmnoQsPlnCd7YRybbEEkEbKeRjyrDqhMSTSWC8lw4
|
|
@ -0,0 +1 @@
|
|||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmvEwYkqzj0oCREd53foFtngqEo1x0VcRF+eAHcc5rQ3uEZ+jpbzFRtRi1n0543cGy01XmVKo06YGZAnRiKfZ44I7Or+qEO3/Pg4ULwcqegUjVwRzPlx/mJ5iKIXEKZx+6rw0/JeNSKNiFWW3jQVjJQLZ8+5Wl1orGJGBA1JvB1zmnSi5F/mqlnRGCRxsvM2DfZLTVh+z/AVwVAFHdb7U2YYROgTHfztFkqloYKQAWxWfFSlU/+buHZV1MpCjWwiq7H16IixP+6ZW3x44tNrFHWgNSwDheEDmk3j2hmbv+eU1gmGT41iq1tnNBq/Jkv9TEK4Nc7FldnOfRqizTFiArQIDAQAB
|
6
pom.xml
6
pom.xml
|
@ -56,6 +56,12 @@
|
|||
<version>${lombok.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- 图片efi信息,识别ps,贵酒-->
|
||||
<dependency>
|
||||
<groupId>com.drewnoakes</groupId>
|
||||
<artifactId>metadata-extractor</artifactId>
|
||||
<version>${metadata-extractor.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- 单元测试-->
|
||||
|
|
|
@ -732,7 +732,7 @@ public class Util extends weaver.general.Util {
|
|||
StringBuilder buffer = new StringBuilder();
|
||||
for (int i = 0, l = charArray.length; i < l; i++) {
|
||||
// 判断当前字符是否是"_",如果跳出本次循环
|
||||
if (charArray[i] == 95) {
|
||||
if (charArray[i] == '_') {
|
||||
underlineBefore = true;
|
||||
} else if (underlineBefore) {
|
||||
// 如果为true,代表上次的字符是"_",当前字符需要转成大写
|
||||
|
@ -763,10 +763,10 @@ public class Util extends weaver.general.Util {
|
|||
for (int i = 0, l = charArray.length; i < l; i++) {
|
||||
if (charArray[i] >= 65 && charArray[i] <= 90) {
|
||||
if (i == 0) {
|
||||
buffer.append(charArray[i] += 32);
|
||||
buffer.append(Character.toLowerCase(charArray[i]));
|
||||
continue;
|
||||
}
|
||||
buffer.append("_").append(charArray[i] += 32);
|
||||
buffer.append("_").append(Character.toLowerCase(charArray[i]));
|
||||
} else {
|
||||
buffer.append(charArray[i]);
|
||||
}
|
||||
|
@ -2100,7 +2100,12 @@ public class Util extends weaver.general.Util {
|
|||
|
||||
String s;
|
||||
for (Iterator<T> item = coll.iterator(); item.hasNext(); sb.append(s)) {
|
||||
s = (String) item.next();
|
||||
T value = item.next();
|
||||
if (Objects.isNull(value)) {
|
||||
s = "";
|
||||
} else {
|
||||
s = String.valueOf(value);
|
||||
}
|
||||
if (isFirst) {
|
||||
isFirst = false;
|
||||
} else {
|
||||
|
@ -2111,6 +2116,38 @@ public class Util extends weaver.general.Util {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* join方法
|
||||
*
|
||||
* @param coll
|
||||
* @param split
|
||||
* @return
|
||||
*/
|
||||
public static <T> String joinEach(T[] coll, String split) {
|
||||
if (Objects.isNull(coll)) {
|
||||
return "";
|
||||
} else {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
boolean isFirst = true;
|
||||
String s;
|
||||
for (Iterator<T> item = Arrays.stream(coll).iterator(); item.hasNext(); sb.append(s)) {
|
||||
T value = item.next();
|
||||
if (Objects.isNull(value)) {
|
||||
s = "";
|
||||
} else {
|
||||
s = String.valueOf(value);
|
||||
}
|
||||
if (isFirst) {
|
||||
isFirst = false;
|
||||
} else {
|
||||
sb.append(split);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将数组使用指定的分隔符链接为字符串
|
||||
*
|
||||
|
@ -3736,7 +3773,35 @@ public class Util extends weaver.general.Util {
|
|||
return Util.getValueByKeyStr("main." + fieldInfo.getFieldName(), workflowData);
|
||||
}
|
||||
|
||||
public static <K, V> Map<K, V> createFunctionMap() {
|
||||
return null;
|
||||
public static String getIpAddress(HttpServletRequest request) throws IOException {
|
||||
// 获取请求主机IP地址,如果通过代理进来,则透过防火墙获取真实IP地址
|
||||
String ip = request.getHeader("X-Forwarded-For");
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("Proxy-Client-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("WL-Proxy-Client-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("HTTP_CLIENT_IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getRemoteAddr();
|
||||
}
|
||||
} else if (ip.length() > 15) {
|
||||
String[] ips = ip.split(",");
|
||||
for (int index = 0; index < ips.length; index++) {
|
||||
String strIp = ips[index];
|
||||
if (!("unknown".equalsIgnoreCase(strIp))) {
|
||||
ip = strIp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ip;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,8 @@ public @interface Association {
|
|||
|
||||
String column();
|
||||
|
||||
String select();
|
||||
|
||||
String select() default "";
|
||||
|
||||
Id id();
|
||||
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package aiyh.utils.annotation.recordset;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* <h1>批量插入</h1>
|
||||
*
|
||||
* <p>create: 2022-08-09 17:40</p>
|
||||
*
|
||||
* @author aiyh EBU7-dev-1
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@Documented
|
||||
public @interface BatchDeleteOracle {
|
||||
String value() default "";
|
||||
|
||||
// sql是否是在参数中
|
||||
boolean custom() default false;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package aiyh.utils.annotation.recordset;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* <h1>批量插入</h1>
|
||||
*
|
||||
* <p>create: 2022-08-09 17:40</p>
|
||||
*
|
||||
* @author aiyh EBU7-dev-1
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@Documented
|
||||
public @interface BatchInsertOracle {
|
||||
String value() default "";
|
||||
|
||||
// sql是否是在参数中
|
||||
boolean custom() default false;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package aiyh.utils.annotation.recordset;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* <h1>批量插入</h1>
|
||||
*
|
||||
* <p>create: 2022-08-09 17:40</p>
|
||||
*
|
||||
* @author aiyh EBU7-dev-1
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@Documented
|
||||
public @interface BatchUpdateOracle {
|
||||
String value() default "";
|
||||
|
||||
// sql是否是在参数中
|
||||
boolean custom() default false;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package aiyh.utils.annotation.recordset;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* <h1>可以为null</h1>
|
||||
*
|
||||
* <p>create: 2023/3/8 21:41</p>
|
||||
*
|
||||
* @author youHong.ai
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
@Documented
|
||||
public @interface CanBeNull {
|
||||
}
|
|
@ -17,7 +17,7 @@ public @interface CollectionMapping {
|
|||
String property();
|
||||
|
||||
/** 数据库字段名 */
|
||||
String column();
|
||||
String column() default "";
|
||||
|
||||
/** 查询方法全限定类名 */
|
||||
String select() default "";
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package aiyh.utils.annotation.recordset;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* <h1>列名</h1>
|
||||
*
|
||||
* <p>create: 2023/3/10 10:18</p>
|
||||
*
|
||||
* @author youHong.ai
|
||||
*/
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.ANNOTATION_TYPE)
|
||||
@Documented
|
||||
public @interface Column {
|
||||
String value();
|
||||
|
||||
String oracle() default "";
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package aiyh.utils.annotation.recordset;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @author @author EBU7-dev1-ay
|
||||
* create 2021/12/19 0019 15:08
|
||||
*/
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@Documented
|
||||
public @interface DeleteOracle {
|
||||
String value() default "";
|
||||
|
||||
// sql是否是在参数中
|
||||
boolean custom() default false;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package aiyh.utils.annotation.recordset;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @author @author EBU7-dev1-ay
|
||||
* create 2021/12/19 0019 15:08
|
||||
*/
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@Documented
|
||||
public @interface InsertOracle {
|
||||
String value() default "";
|
||||
|
||||
// sql是否是在参数中
|
||||
boolean custom() default false;
|
||||
}
|
|
@ -12,6 +12,7 @@ import java.lang.annotation.*;
|
|||
@Documented
|
||||
public @interface Select {
|
||||
String value() default "";
|
||||
|
||||
// sql是否是在参数中
|
||||
boolean custom() default false;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package aiyh.utils.annotation.recordset;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @author @author EBU7-dev1-ay
|
||||
* create 2021/12/19 0019 15:08
|
||||
*/
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@Documented
|
||||
public @interface SelectOracle {
|
||||
String value() default "";
|
||||
|
||||
boolean custom() default false;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package aiyh.utils.annotation.recordset;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @author @author EBU7-dev1-ay
|
||||
* create 2021/12/19 0019 15:08
|
||||
*/
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@Documented
|
||||
public @interface UpdateOracle {
|
||||
String value() default "";
|
||||
|
||||
// sql是否是在参数中
|
||||
boolean custom() default false;
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package aiyh.utils.entity;
|
||||
|
||||
import aiyh.utils.annotation.recordset.SqlOracleDbFieldAnn;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
|
@ -11,11 +12,33 @@ import lombok.Data;
|
|||
@Data
|
||||
public class DocImageInfo {
|
||||
|
||||
/** docId */
|
||||
@SqlOracleDbFieldAnn("DOC_ID")
|
||||
private Integer docId;
|
||||
/** image id */
|
||||
@SqlOracleDbFieldAnn("IMAGE_FILE_ID")
|
||||
private Integer imageFileId;
|
||||
/** 文件名称 */
|
||||
@SqlOracleDbFieldAnn("IMAGE_FILE_NAME")
|
||||
private String imageFileName;
|
||||
|
||||
/** 创建时间 */
|
||||
@SqlOracleDbFieldAnn("DOC_CREATE_TIME")
|
||||
private String docCreateTime;
|
||||
|
||||
/** 创建日期 */
|
||||
@SqlOracleDbFieldAnn("DOC_CREATE_DATE")
|
||||
private String docCreateDate;
|
||||
/** 明细数据id */
|
||||
@SqlOracleDbFieldAnn("ID")
|
||||
private Integer id;
|
||||
/** 其他id */
|
||||
@SqlOracleDbFieldAnn("DETAIL_ID")
|
||||
private Integer detailId;
|
||||
/** 文件大小 */
|
||||
@SqlOracleDbFieldAnn("FILE_SIZE")
|
||||
private Integer fileSize;
|
||||
/** 文件类型 */
|
||||
@SqlOracleDbFieldAnn("DOC_FILE_TYPE")
|
||||
private Integer docFileType;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import lombok.Setter;
|
|||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* <h1></h1>
|
||||
* <h1>流程建模字段信息</h1>
|
||||
*
|
||||
* <p>create: 2023/2/20 11:01</p>
|
||||
*
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package aiyh.utils.entity;
|
||||
|
||||
import aiyh.utils.annotation.recordset.SqlOracleDbFieldAnn;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* <h1>建模表信息</h1>
|
||||
*
|
||||
* <p>create: 2023/3/14 17:05</p>
|
||||
*
|
||||
* @author youHong.ai
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
public class ModelTableInfo {
|
||||
/** id */
|
||||
@SqlOracleDbFieldAnn("ID")
|
||||
private String id;
|
||||
|
||||
@SqlOracleDbFieldAnn("INDEX_DESC")
|
||||
private String indexDesc;
|
||||
/** 字段表名 */
|
||||
@SqlOracleDbFieldAnn("TABLE_NAME")
|
||||
private String tableName;
|
||||
}
|
|
@ -3,7 +3,7 @@ package aiyh.utils.entity;
|
|||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* <p></p>
|
||||
* <p>下拉框字段信息</p>
|
||||
* <p>create 2022/6/10 21:21</p>
|
||||
*
|
||||
* @author ayh
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package aiyh.utils.fileUtil;
|
||||
|
||||
import aiyh.utils.zwl.common.ToolUtil;
|
||||
import sun.font.FontDesignMetrics;
|
||||
import weaver.conn.RecordSet;
|
||||
import weaver.file.FileUpload;
|
||||
import weaver.file.ImageFileManager;
|
||||
|
@ -76,7 +75,7 @@ public class WritWatermark {
|
|||
}
|
||||
String message = maxStr;
|
||||
Font defaultFont = new Font(fontName, fontStyle, fontSize);
|
||||
int wordWidth = getWordWidth(defaultFont, message);
|
||||
int wordWidth = getWordWidth(defaultFont, message, graphics);
|
||||
// 对字体进行限制
|
||||
if (wordWidth >= Math.min(width, height)) {
|
||||
fontSize = Math.min(width, height) / (pressText.length() + 6);
|
||||
|
@ -151,7 +150,7 @@ public class WritWatermark {
|
|||
file.createNewFile();
|
||||
}
|
||||
// 输出到临时目录
|
||||
FileOutputStream outputStreamTem = new FileOutputStream(URLDecoder.decode(tempPath, "utf-8"));
|
||||
FileOutputStream outputStreamTem = new FileOutputStream(URLDecoder.decode(tempPath, "UTF-8"));
|
||||
ImageIO.write(bufferedImage, suffix, outputStreamTem);
|
||||
outputStreamTem.close();
|
||||
// 保存生成的水印图片到压缩包
|
||||
|
@ -277,7 +276,7 @@ public class WritWatermark {
|
|||
file.createNewFile();
|
||||
}
|
||||
// 输出到临时目录
|
||||
FileOutputStream outputStreamTem = new FileOutputStream(URLDecoder.decode(tempPath, "utf-8"));
|
||||
FileOutputStream outputStreamTem = new FileOutputStream(URLDecoder.decode(tempPath, "UTF-8"));
|
||||
ImageIO.write(bufferedImage, suffix, outputStreamTem);
|
||||
outputStreamTem.close();
|
||||
// 保存生成的水印图片到压缩包
|
||||
|
@ -403,7 +402,7 @@ public class WritWatermark {
|
|||
file.createNewFile();
|
||||
}
|
||||
// 输出到临时目录
|
||||
FileOutputStream outputStreamTem = new FileOutputStream(URLDecoder.decode(tempPath, "utf-8"));
|
||||
FileOutputStream outputStreamTem = new FileOutputStream(URLDecoder.decode(tempPath, "UTF-8"));
|
||||
ImageIO.write(bufferedImage, suffix, outputStreamTem);
|
||||
outputStreamTem.close();
|
||||
// 保存生成的水印图片到压缩包
|
||||
|
@ -540,7 +539,7 @@ public class WritWatermark {
|
|||
file.createNewFile();
|
||||
}
|
||||
// 输出到临时目录
|
||||
FileOutputStream outputStreamTem = new FileOutputStream(URLDecoder.decode(tempPath, "utf-8"));
|
||||
FileOutputStream outputStreamTem = new FileOutputStream(URLDecoder.decode(tempPath, "UTF-8"));
|
||||
ImageIO.write(bufferedImage, suffix, outputStreamTem);
|
||||
outputStreamTem.close();
|
||||
// 保存生成的水印图片到压缩包
|
||||
|
@ -660,7 +659,7 @@ public class WritWatermark {
|
|||
file.createNewFile();
|
||||
}
|
||||
// 输出到临时目录
|
||||
FileOutputStream outputStreamTem = new FileOutputStream(URLDecoder.decode(tempPath, "utf-8"));
|
||||
FileOutputStream outputStreamTem = new FileOutputStream(URLDecoder.decode(tempPath, "UTF-8"));
|
||||
ImageIO.write(bufferedImage, suffix, outputStreamTem);
|
||||
outputStreamTem.close();
|
||||
// 保存生成的水印图片到压缩包
|
||||
|
@ -780,7 +779,7 @@ public class WritWatermark {
|
|||
file.createNewFile();
|
||||
}
|
||||
// 输出到临时目录
|
||||
FileOutputStream outputStreamTem = new FileOutputStream(URLDecoder.decode(tempPath, "utf-8"));
|
||||
FileOutputStream outputStreamTem = new FileOutputStream(URLDecoder.decode(tempPath, "UTF-8"));
|
||||
ImageIO.write(bufferedImage, suffix, outputStreamTem);
|
||||
outputStreamTem.close();
|
||||
// 保存生成的水印图片到压缩包
|
||||
|
@ -810,13 +809,14 @@ public class WritWatermark {
|
|||
return i;
|
||||
}
|
||||
|
||||
public static int getWordWidth(Font font, String content) {
|
||||
FontDesignMetrics metrics = FontDesignMetrics.getMetrics(font);
|
||||
int width = 0;
|
||||
for (int i = 0; i < content.length(); i++) {
|
||||
width += metrics.charWidth(content.charAt(i));
|
||||
}
|
||||
return width;
|
||||
public static int getWordWidth(Font font, String content, Graphics2D graphics) {
|
||||
// FontDesignMetrics metrics = FontDesignMetrics.getMetrics(font);
|
||||
// int width = 0;
|
||||
// for (int i = 0; i < content.length(); i++) {
|
||||
// width += metrics.charWidth(content.charAt(i));
|
||||
// }
|
||||
// return width;
|
||||
return graphics.getFontMetrics(font).stringWidth(content);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@ import weaver.file.ImageFileManager;
|
|||
import weaver.system.SystemComInfo;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
|
@ -136,8 +135,8 @@ public class PdfUtil {
|
|||
// 输出到临时目录
|
||||
FileOutputStream outputStreamTem;
|
||||
try {
|
||||
outputStreamTem = new FileOutputStream(URLDecoder.decode(tempPath, "utf-8"));
|
||||
} catch (FileNotFoundException | UnsupportedEncodingException e) {
|
||||
outputStreamTem = new FileOutputStream(tempPath);
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new CustomerException("创建临时文件流和路径转换失败!", e);
|
||||
}
|
||||
PdfStamper pdfStamper = null;
|
||||
|
|
|
@ -4,7 +4,6 @@ package aiyh.utils.httpUtil;
|
|||
import aiyh.utils.Util;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONException;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
@ -136,11 +135,11 @@ public class ResponeVo implements HttpResponse {
|
|||
} catch (JsonProcessingException ignored) {
|
||||
try {
|
||||
this.resultList = (List<Map>) JSONArray.parseArray(this.getEntityString(), Map.class);
|
||||
} catch (JSONException e) {
|
||||
Util.getLogger().error("Unable to convert the response result to array!" + Util.getErrString(e));
|
||||
} catch (Exception e) {
|
||||
Util.getLogger().error("Unable to convert the response result to array!");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Util.getLogger().error("Unable to convert the response result to map or array!" + Util.getErrString(e));
|
||||
Util.getLogger().error("Unable to convert the response result to map or array!");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -86,6 +86,10 @@ public class HttpUtils {
|
|||
|
||||
}
|
||||
|
||||
public void setCredentialsProvider(CredentialsProvider credentialsProvider) {
|
||||
this.credentialsProvider = credentialsProvider;
|
||||
}
|
||||
|
||||
public HttpUtils(CredentialsProvider credentialsProvider) {
|
||||
this.credentialsProvider = credentialsProvider;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
package aiyh.utils.mapper;
|
||||
|
||||
import aiyh.utils.annotation.recordset.*;
|
||||
import aiyh.utils.entity.DocImageInfo;
|
||||
import aiyh.utils.entity.FieldViewInfo;
|
||||
import aiyh.utils.entity.SelectValueEntity;
|
||||
import aiyh.utils.entity.WorkflowNodeConfig;
|
||||
import aiyh.utils.entity.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -58,17 +55,31 @@ public interface UtilMapper {
|
|||
String selectWorkfowMainTable(@ParamMapper("workflowId") String workflowId);
|
||||
|
||||
|
||||
@Select("select id,docid doc_id,imagefileid image_file_id,imagefilename image_file_name from docimagefile where docid = #{docId}")
|
||||
@Select("select di.id id,dc.id doc_id," +
|
||||
"di.imagefileid image_file_id," +
|
||||
"dc.DOCCREATEDATE doc_create_date,dc.DOCCREATETIME doc_create_time," +
|
||||
"(case when imagefilename = '' or imagefilename is null then dc.DOCSUBJECT else imagefilename end) image_file_name " +
|
||||
"from DocDetail dc " +
|
||||
"left join docimagefile di on (dc.id = #{docId} and dc.id = di.docid) " +
|
||||
"where dc.id = #{docId} ")
|
||||
DocImageInfo selectDocImageInfo(@ParamMapper("docId") String docId);
|
||||
|
||||
@Select("select id,docid doc_id," +
|
||||
"imagefileid image_file_id, docfiletype doc_file_type," +
|
||||
"imagefilename image_file_name from docimagefile where docid in ($t{docIds})")
|
||||
@Select("select di.id id,dc.id doc_id," +
|
||||
"di.imagefileid image_file_id, di.docfiletype doc_file_type," +
|
||||
"dc.DOCCREATEDATE doc_create_date,dc.DOCCREATETIME doc_create_time," +
|
||||
"(case when imagefilename = '' or imagefilename is null then dc.DOCSUBJECT else imagefilename end) image_file_name " +
|
||||
"from DocDetail dc " +
|
||||
"left join docimagefile di on (dc.id in ($t{docIds}) and dc.id = di.docid )" +
|
||||
"where dc.id in ($t{docIds})")
|
||||
List<DocImageInfo> selectDocImageInfos(@ParamMapper("docIds") String docIds);
|
||||
|
||||
@Select("select id,docid doc_id,imagefileid image_file_id," +
|
||||
"docfiletype doc_file_type," +
|
||||
"imagefilename image_file_name from docimagefile where docid in (${docIds})")
|
||||
@Select("select di.id id,dc.id doc_id," +
|
||||
"di.imagefileid image_file_id, di.docfiletype doc_file_type," +
|
||||
"dc.DOCCREATEDATE doc_create_date,dc.DOCCREATETIME doc_create_time," +
|
||||
"(case when imagefilename = '' or imagefilename is null then dc.DOCSUBJECT else imagefilename end) image_file_name " +
|
||||
"from DocDetail dc " +
|
||||
"left join docimagefile di on (dc.id in ($t{docIds}) and dc.id = di.docid )" +
|
||||
"where dc.id in ($t{docIds})")
|
||||
List<DocImageInfo> selectDocImageInfos(@ParamMapper("docIds") String[] docIds);
|
||||
|
||||
|
||||
|
@ -176,4 +187,14 @@ public interface UtilMapper {
|
|||
" fieldhtmltype field_html_type\n" +
|
||||
"from workflow_field_table_view where id = #{id}")
|
||||
FieldViewInfo selectFieldInfo(Integer id);
|
||||
|
||||
/**
|
||||
* <h2>查询建模表表信息</h2>
|
||||
*
|
||||
* @param id id
|
||||
* @return 建模表信息
|
||||
*/
|
||||
|
||||
@Select("select id, tablename table_name, indexdesc index_desc from mode_bill_info_view where id = #{id}")
|
||||
ModelTableInfo selectModelTableInfo(Integer id);
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package aiyh.utils.recordset;
|
||||
|
||||
import aiyh.utils.Util;
|
||||
import aiyh.utils.annotation.recordset.CanBeNull;
|
||||
import weaver.conn.RecordSet;
|
||||
import weaver.conn.RecordSetTrans;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* <p>float处理</p>
|
||||
|
@ -18,8 +20,13 @@ public class DoubleTypeHandler implements TypeHandler {
|
|||
public Object getValue(RecordSet rs, String fieldName, Field declaredField) {
|
||||
String string = Util.null2String(GetRsValueUtil.getRsValue(rs, fieldName, declaredField));
|
||||
if ("".equals(string)) {
|
||||
if (!Objects.isNull(declaredField)) {
|
||||
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return 0.0D;
|
||||
}
|
||||
return Double.parseDouble(string);
|
||||
}
|
||||
|
||||
|
@ -27,8 +34,13 @@ public class DoubleTypeHandler implements TypeHandler {
|
|||
public Object getValue(RecordSet rs, int index, Field declaredField) {
|
||||
String string = Util.null2String(rs.getString(index));
|
||||
if ("".equals(string)) {
|
||||
if (!Objects.isNull(declaredField)) {
|
||||
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return 0.0D;
|
||||
}
|
||||
return Double.parseDouble(string);
|
||||
}
|
||||
|
||||
|
@ -36,8 +48,13 @@ public class DoubleTypeHandler implements TypeHandler {
|
|||
public Object getValue(RecordSetTrans rs, String fieldName, Field declaredField) {
|
||||
String string = Util.null2String(GetRsValueUtil.getRsValue(rs, fieldName, declaredField));
|
||||
if ("".equals(string)) {
|
||||
if (!Objects.isNull(declaredField)) {
|
||||
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return 0.0D;
|
||||
}
|
||||
return Double.parseDouble(string);
|
||||
}
|
||||
|
||||
|
@ -45,8 +62,13 @@ public class DoubleTypeHandler implements TypeHandler {
|
|||
public Object getValue(RecordSetTrans rs, int index, Field declaredField) {
|
||||
String string = Util.null2String(rs.getString(index));
|
||||
if ("".equals(string)) {
|
||||
if (!Objects.isNull(declaredField)) {
|
||||
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return 0.0D;
|
||||
}
|
||||
return Double.parseDouble(string);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package aiyh.utils.recordset;
|
||||
|
||||
import aiyh.utils.Util;
|
||||
import aiyh.utils.annotation.recordset.CanBeNull;
|
||||
import weaver.conn.RecordSet;
|
||||
import weaver.conn.RecordSetTrans;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* <p>float处理</p>
|
||||
|
@ -18,8 +20,13 @@ public class FloatTypeHandler implements TypeHandler {
|
|||
public Object getValue(RecordSet rs, String fieldName, Field declaredField) {
|
||||
String string = Util.null2String(GetRsValueUtil.getRsValue(rs, fieldName, declaredField));
|
||||
if ("".equals(string)) {
|
||||
if (!Objects.isNull(declaredField)) {
|
||||
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return 0.0F;
|
||||
}
|
||||
return Float.parseFloat(string);
|
||||
}
|
||||
|
||||
|
@ -27,8 +34,13 @@ public class FloatTypeHandler implements TypeHandler {
|
|||
public Object getValue(RecordSet rs, int index, Field declaredField) {
|
||||
String string = Util.null2String(rs.getString(index), "0.0");
|
||||
if ("".equals(string)) {
|
||||
if (!Objects.isNull(declaredField)) {
|
||||
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return 0.0F;
|
||||
}
|
||||
return Float.parseFloat(string);
|
||||
}
|
||||
|
||||
|
@ -36,8 +48,13 @@ public class FloatTypeHandler implements TypeHandler {
|
|||
public Object getValue(RecordSetTrans rs, String fieldName, Field declaredField) {
|
||||
String string = Util.null2String(GetRsValueUtil.getRsValue(rs, fieldName, declaredField));
|
||||
if ("".equals(string)) {
|
||||
if (!Objects.isNull(declaredField)) {
|
||||
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return 0.0F;
|
||||
}
|
||||
return Float.parseFloat(string);
|
||||
}
|
||||
|
||||
|
@ -45,8 +62,13 @@ public class FloatTypeHandler implements TypeHandler {
|
|||
public Object getValue(RecordSetTrans rs, int index, Field declaredField) {
|
||||
String string = Util.null2String(rs.getString(index));
|
||||
if ("".equals(string)) {
|
||||
if (!Objects.isNull(declaredField)) {
|
||||
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return 0.0F;
|
||||
}
|
||||
return Float.parseFloat(string);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
package aiyh.utils.recordset;
|
||||
|
||||
import com.alibaba.druid.sql.SQLUtils;
|
||||
import com.alibaba.druid.sql.parser.ParserException;
|
||||
import weaver.conn.RecordSet;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* <h1>格式化sql字符串</h1>
|
||||
*
|
||||
* <p>create: 2023/3/5 11:45</p>
|
||||
*
|
||||
* @author youHong.ai
|
||||
*/
|
||||
public class FormatSqlUtil {
|
||||
private static String dbType = null;
|
||||
private static RecordSet recordSet = null;
|
||||
|
||||
public static String formatSql(String sql) {
|
||||
|
||||
if (Objects.isNull(FormatSqlUtil.dbType)) {
|
||||
if (recordSet == null) {
|
||||
recordSet = new RecordSet();
|
||||
}
|
||||
FormatSqlUtil.dbType = recordSet.getDBType();
|
||||
}
|
||||
try {
|
||||
return SQLUtils.format(sql, dbType);
|
||||
} catch (ParserException e) {
|
||||
return toSqlString(sql);
|
||||
}
|
||||
}
|
||||
|
||||
private static String toSqlString(String sql) {
|
||||
return sql;
|
||||
}
|
||||
}
|
|
@ -1,10 +1,12 @@
|
|||
package aiyh.utils.recordset;
|
||||
|
||||
import aiyh.utils.Util;
|
||||
import aiyh.utils.annotation.recordset.CanBeNull;
|
||||
import weaver.conn.RecordSet;
|
||||
import weaver.conn.RecordSetTrans;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author EBU7-dev1-ayh create 2021/12/21 0021 13:10
|
||||
|
@ -16,8 +18,11 @@ public class IntegerTypeHandler implements TypeHandler {
|
|||
public Object getValue(RecordSet rs, String fieldName, Field declaredField) {
|
||||
String string = Util.null2String(GetRsValueUtil.getRsValue(rs, fieldName, declaredField));
|
||||
if ("".equals(string)) {
|
||||
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
|
||||
return null;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if (string.contains(".")) {
|
||||
string = string.substring(0, string.indexOf("."));
|
||||
}
|
||||
|
@ -28,8 +33,13 @@ public class IntegerTypeHandler implements TypeHandler {
|
|||
public Object getValue(RecordSet rs, int index, Field declaredField) {
|
||||
String string = Util.null2String(rs.getString(index));
|
||||
if ("".equals(string)) {
|
||||
if (!Objects.isNull(declaredField)) {
|
||||
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if (string.contains(".")) {
|
||||
string = string.substring(0, string.indexOf("."));
|
||||
}
|
||||
|
@ -40,8 +50,13 @@ public class IntegerTypeHandler implements TypeHandler {
|
|||
public Object getValue(RecordSetTrans rs, String fieldName, Field declaredField) {
|
||||
String string = Util.null2String(GetRsValueUtil.getRsValue(rs, fieldName, declaredField));
|
||||
if ("".equals(string)) {
|
||||
if (!Objects.isNull(declaredField)) {
|
||||
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if (string.contains(".")) {
|
||||
string = string.substring(0, string.indexOf("."));
|
||||
}
|
||||
|
@ -52,8 +67,13 @@ public class IntegerTypeHandler implements TypeHandler {
|
|||
public Object getValue(RecordSetTrans rs, int index, Field declaredField) {
|
||||
String string = Util.null2String(rs.getString(index));
|
||||
if ("".equals(string)) {
|
||||
if (!Objects.isNull(declaredField)) {
|
||||
if (declaredField.isAnnotationPresent(CanBeNull.class)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if (string.contains(".")) {
|
||||
string = string.substring(0, string.indexOf("."));
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import aiyh.utils.sqlUtil.sqlResult.impl.BatchSqlResultImpl;
|
|||
import aiyh.utils.sqlUtil.sqlResult.impl.PrepSqlResultImpl;
|
||||
import weaver.conn.RecordSet;
|
||||
import weaver.conn.RecordSetTrans;
|
||||
import weaver.conn.constant.DBConstant;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
|
@ -59,29 +60,17 @@ public class RecordsetUtil implements InvocationHandler {
|
|||
return invokeRsTrans(proxy, method, args, name);
|
||||
}
|
||||
|
||||
|
||||
private Object invokeRs(Object proxy, Method method, Object[] args, String name) {
|
||||
String mapperKey = method.getDeclaringClass().getName();
|
||||
if (!"".equals(name) && null != name) {
|
||||
mapperKey += "." + name;
|
||||
}
|
||||
RecordSet rs = rsManager.getRs(mapperKey);
|
||||
if (rs == null) {
|
||||
rsManager.setRecordSet(mapperKey);
|
||||
rs = rsManager.getRs(mapperKey);
|
||||
}
|
||||
private Object invokeDefault(int type, String sql, boolean custom, RecordSet rs, Method method, Object[] args) {
|
||||
SqlHandler sqlHandler = new SqlHandler();
|
||||
ResultMapper resultMapper = new ResultMapper();
|
||||
Select select = method.getAnnotation(Select.class);
|
||||
if (select != null) {
|
||||
// 查询
|
||||
String sql = select.value();
|
||||
boolean custom = select.custom();
|
||||
PrepSqlResultImpl handler = sqlHandler.handler(sql, custom, method, args);
|
||||
String invokeMethod = method.getDeclaringClass().getName() + ":" + method.getName() + " : ";
|
||||
switch (type) {
|
||||
case 1: {
|
||||
if (!handler.getSqlStr().trim().toLowerCase().startsWith("select ")) {
|
||||
throw new CustomerException("The sql statement does not match, the @Select annotation can only execute the select statement, please check whether the sql statement matches!");
|
||||
}
|
||||
Util.getLogger(SQL_LOG).info("解析sql===>" + handler);
|
||||
Util.getLogger(SQL_LOG).info(invokeMethod + handler);
|
||||
if (handler.getArgs().isEmpty()) {
|
||||
rs.executeQuery(handler.getSqlStr());
|
||||
} else {
|
||||
|
@ -89,16 +78,11 @@ public class RecordsetUtil implements InvocationHandler {
|
|||
}
|
||||
return resultMapper.mapperResult(rs, method, method.getReturnType(), this);
|
||||
}
|
||||
Update update = method.getAnnotation(Update.class);
|
||||
if (update != null) {
|
||||
// 查询
|
||||
String sql = update.value();
|
||||
boolean custom = update.custom();
|
||||
PrepSqlResultImpl handler = sqlHandler.handler(sql, custom, method, args);
|
||||
case 2: {
|
||||
if (!handler.getSqlStr().trim().toLowerCase().startsWith("update ")) {
|
||||
throw new CustomerException("The sql statement does not match, the @Update annotation can only execute the update statement, please check whether the sql statement matches!");
|
||||
}
|
||||
Util.getLogger(SQL_LOG).info(handler.toString());
|
||||
Util.getLogger(SQL_LOG).info(invokeMethod + handler);
|
||||
Class<?> returnType = method.getReturnType();
|
||||
boolean b;
|
||||
if (handler.getArgs().isEmpty()) {
|
||||
|
@ -120,16 +104,11 @@ public class RecordsetUtil implements InvocationHandler {
|
|||
return b;
|
||||
}
|
||||
}
|
||||
Insert insert = method.getAnnotation(Insert.class);
|
||||
if (insert != null) {
|
||||
// 查询
|
||||
String sql = insert.value();
|
||||
boolean custom = insert.custom();
|
||||
PrepSqlResultImpl handler = sqlHandler.handler(sql, custom, method, args);
|
||||
case 3: {
|
||||
if (!handler.getSqlStr().trim().toLowerCase().startsWith("insert ")) {
|
||||
throw new CustomerException("The sql statement does not match, the @Insert annotation can only execute the insert statement, please check whether the sql statement matches!");
|
||||
}
|
||||
Util.getLogger(SQL_LOG).info(handler.toString());
|
||||
Util.getLogger(SQL_LOG).info(invokeMethod + handler);
|
||||
Class<?> returnType = method.getReturnType();
|
||||
boolean b;
|
||||
if (handler.getArgs().isEmpty()) {
|
||||
|
@ -144,16 +123,11 @@ public class RecordsetUtil implements InvocationHandler {
|
|||
return b;
|
||||
}
|
||||
}
|
||||
Delete delete = method.getAnnotation(Delete.class);
|
||||
if (delete != null) {
|
||||
// 查询
|
||||
String sql = delete.value();
|
||||
boolean custom = delete.custom();
|
||||
PrepSqlResultImpl handler = sqlHandler.handler(sql, custom, method, args);
|
||||
case 4: {
|
||||
if (!handler.getSqlStr().trim().toLowerCase().startsWith("delete ")) {
|
||||
throw new CustomerException("The sql statement does not match, the @Delete annotation can only execute the delete statement, please check whether the sql statement matches!");
|
||||
}
|
||||
Util.getLogger(SQL_LOG).info(handler.toString());
|
||||
Util.getLogger(SQL_LOG).info(invokeMethod + handler);
|
||||
Class<?> returnType = method.getReturnType();
|
||||
boolean b;
|
||||
if (handler.getArgs().isEmpty()) {
|
||||
|
@ -168,14 +142,10 @@ public class RecordsetUtil implements InvocationHandler {
|
|||
return b;
|
||||
}
|
||||
}
|
||||
boolean hasBatchInsert = method.isAnnotationPresent(BatchInsert.class);
|
||||
if (hasBatchInsert) {
|
||||
BatchInsert batchInsert = method.getAnnotation(BatchInsert.class);
|
||||
String sql = batchInsert.value();
|
||||
case 5: {
|
||||
Class<?> returnType = method.getReturnType();
|
||||
boolean custom = batchInsert.custom();
|
||||
BatchSqlResultImpl batchSqlResult = sqlHandler.handlerBatch(sql, custom, method, args);
|
||||
Util.getLogger(SQL_LOG).info(batchSqlResult.toString());
|
||||
Util.getLogger(SQL_LOG).info(invokeMethod + batchSqlResult.toString());
|
||||
if (batchSqlResult.getBatchList().isEmpty()) {
|
||||
throw new CustomerException("execute batch sql error , batch sql args is empty!");
|
||||
}
|
||||
|
@ -189,16 +159,11 @@ public class RecordsetUtil implements InvocationHandler {
|
|||
if (returnType.equals(boolean.class) || returnType.equals(Boolean.class)) {
|
||||
return b;
|
||||
}
|
||||
|
||||
}
|
||||
boolean hasBatchUpdate = method.isAnnotationPresent(BatchUpdate.class);
|
||||
if (hasBatchUpdate) {
|
||||
BatchUpdate batchUpdate = method.getAnnotation(BatchUpdate.class);
|
||||
String sql = batchUpdate.value();
|
||||
case 6: {
|
||||
Class<?> returnType = method.getReturnType();
|
||||
boolean custom = batchUpdate.custom();
|
||||
BatchSqlResultImpl batchSqlResult = sqlHandler.handlerBatch(sql, custom, method, args);
|
||||
Util.getLogger(SQL_LOG).info(batchSqlResult.toString());
|
||||
Util.getLogger(SQL_LOG).info(invokeMethod + batchSqlResult.toString());
|
||||
if (batchSqlResult.getBatchList().isEmpty()) {
|
||||
throw new CustomerException("execute batch sql error , batch sql args is empty!");
|
||||
}
|
||||
|
@ -212,16 +177,11 @@ public class RecordsetUtil implements InvocationHandler {
|
|||
if (returnType.equals(boolean.class) || returnType.equals(Boolean.class)) {
|
||||
return b;
|
||||
}
|
||||
|
||||
}
|
||||
boolean hasBatchDelete = method.isAnnotationPresent(BatchDelete.class);
|
||||
if (hasBatchDelete) {
|
||||
BatchDelete batchDelete = method.getAnnotation(BatchDelete.class);
|
||||
String sql = batchDelete.value();
|
||||
case 7: {
|
||||
Class<?> returnType = method.getReturnType();
|
||||
boolean custom = batchDelete.custom();
|
||||
BatchSqlResultImpl batchSqlResult = sqlHandler.handlerBatch(sql, custom, method, args);
|
||||
Util.getLogger(SQL_LOG).info(batchSqlResult.toString());
|
||||
Util.getLogger(SQL_LOG).info(invokeMethod + batchSqlResult.toString());
|
||||
if (batchSqlResult.getBatchList().isEmpty()) {
|
||||
throw new CustomerException("execute batch sql error , batch sql args is empty!");
|
||||
}
|
||||
|
@ -235,13 +195,117 @@ public class RecordsetUtil implements InvocationHandler {
|
|||
if (returnType.equals(boolean.class) || returnType.equals(Boolean.class)) {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
default:
|
||||
throw new CustomerException("不支持的sql注解类型: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
private Object invokeRs(Object proxy, Method method, Object[] args, String name) {
|
||||
String mapperKey = method.getDeclaringClass().getName();
|
||||
if (!"".equals(name) && null != name) {
|
||||
mapperKey += "." + name;
|
||||
}
|
||||
RecordSet rs = rsManager.getRs(mapperKey);
|
||||
if (rs == null) {
|
||||
rsManager.setRecordSet(mapperKey);
|
||||
rs = rsManager.getRs(mapperKey);
|
||||
}
|
||||
SqlHandler sqlHandler = new SqlHandler();
|
||||
if (DBConstant.DB_TYPE_ORACLE.equals(rs.getDBType())) {
|
||||
SelectOracle selectOracle = method.getAnnotation(SelectOracle.class);
|
||||
if (selectOracle != null) {
|
||||
String sql = selectOracle.value();
|
||||
boolean custom = selectOracle.custom();
|
||||
return invokeDefault(1, sql, custom, rs, method, args);
|
||||
}
|
||||
|
||||
UpdateOracle updateOracle = method.getAnnotation(UpdateOracle.class);
|
||||
if (updateOracle != null) {
|
||||
String sql = updateOracle.value();
|
||||
boolean custom = updateOracle.custom();
|
||||
return invokeDefault(2, sql, custom, rs, method, args);
|
||||
}
|
||||
InsertOracle insertOracle = method.getAnnotation(InsertOracle.class);
|
||||
if (insertOracle != null) {
|
||||
String sql = insertOracle.value();
|
||||
boolean custom = insertOracle.custom();
|
||||
return invokeDefault(3, sql, custom, rs, method, args);
|
||||
}
|
||||
DeleteOracle deleteOracle = method.getAnnotation(DeleteOracle.class);
|
||||
if (deleteOracle != null) {
|
||||
String sql = deleteOracle.value();
|
||||
boolean custom = deleteOracle.custom();
|
||||
return invokeDefault(4, sql, custom, rs, method, args);
|
||||
}
|
||||
BatchInsertOracle batchInsertOracle = method.getAnnotation(BatchInsertOracle.class);
|
||||
if (batchInsertOracle != null) {
|
||||
String sql = batchInsertOracle.value();
|
||||
boolean custom = batchInsertOracle.custom();
|
||||
return invokeDefault(5, sql, custom, rs, method, args);
|
||||
}
|
||||
BatchUpdateOracle batchUpdateOracle = method.getAnnotation(BatchUpdateOracle.class);
|
||||
if (batchUpdateOracle != null) {
|
||||
String sql = batchUpdateOracle.value();
|
||||
boolean custom = batchUpdateOracle.custom();
|
||||
return invokeDefault(6, sql, custom, rs, method, args);
|
||||
}
|
||||
BatchDeleteOracle batchDeleteOracle = method.getAnnotation(BatchDeleteOracle.class);
|
||||
if (batchDeleteOracle != null) {
|
||||
String sql = batchDeleteOracle.value();
|
||||
boolean custom = batchDeleteOracle.custom();
|
||||
return invokeDefault(7, sql, custom, rs, method, args);
|
||||
}
|
||||
}
|
||||
Select select = method.getAnnotation(Select.class);
|
||||
if (select != null) {
|
||||
String sql = select.value();
|
||||
boolean custom = select.custom();
|
||||
return invokeDefault(1, sql, custom, rs, method, args);
|
||||
}
|
||||
Update update = method.getAnnotation(Update.class);
|
||||
if (update != null) {
|
||||
String sql = update.value();
|
||||
boolean custom = update.custom();
|
||||
return invokeDefault(2, sql, custom, rs, method, args);
|
||||
}
|
||||
Insert insert = method.getAnnotation(Insert.class);
|
||||
if (insert != null) {
|
||||
String sql = insert.value();
|
||||
boolean custom = insert.custom();
|
||||
return invokeDefault(3, sql, custom, rs, method, args);
|
||||
}
|
||||
Delete delete = method.getAnnotation(Delete.class);
|
||||
if (delete != null) {
|
||||
String sql = delete.value();
|
||||
boolean custom = delete.custom();
|
||||
return invokeDefault(4, sql, custom, rs, method, args);
|
||||
}
|
||||
BatchInsert batchInsert = method.getAnnotation(BatchInsert.class);
|
||||
if (batchInsert != null) {
|
||||
String sql = batchInsert.value();
|
||||
boolean custom = batchInsert.custom();
|
||||
return invokeDefault(5, sql, custom, rs, method, args);
|
||||
}
|
||||
BatchUpdate batchUpdate = method.getAnnotation(BatchUpdate.class);
|
||||
if (batchUpdate != null) {
|
||||
String sql = batchUpdate.value();
|
||||
boolean custom = batchUpdate.custom();
|
||||
return invokeDefault(6, sql, custom, rs, method, args);
|
||||
}
|
||||
BatchDelete batchDelete = method.getAnnotation(BatchDelete.class);
|
||||
if (batchDelete != null) {
|
||||
String sql = batchDelete.value();
|
||||
boolean custom = batchDelete.custom();
|
||||
return invokeDefault(7, sql, custom, rs, method, args);
|
||||
}
|
||||
throw new CustomerException("该方法没有添加注解!请检查是否正确添加注解!@Select、@Update、@Insert、@Delete、@BatchUpdate、@BatchInsert、@BatchDelete");
|
||||
}
|
||||
|
||||
|
||||
private Object invokeRsTrans(Object proxy, Method method, Object[] args, String name) {
|
||||
String mapperKey = method.getDeclaringClass().getName();
|
||||
String invokeMethod = method.getDeclaringClass().getName() + ":" + method.getName() + " : ";
|
||||
if (!"".equals(name) && null != name) {
|
||||
mapperKey += "." + name;
|
||||
}
|
||||
|
@ -261,7 +325,7 @@ public class RecordsetUtil implements InvocationHandler {
|
|||
if (!handler.getSqlStr().trim().toLowerCase().startsWith("select ")) {
|
||||
throw new CustomerException("The sql statement does not match, the @Select annotation can only execute the select statement, please check whether the sql statement matches!");
|
||||
}
|
||||
Util.getLogger(SQL_LOG).info("解析sql===>" + handler);
|
||||
Util.getLogger(SQL_LOG).info(invokeMethod + handler);
|
||||
try {
|
||||
if (handler.getArgs().isEmpty()) {
|
||||
rs.executeQuery(handler.getSqlStr());
|
||||
|
@ -284,7 +348,7 @@ public class RecordsetUtil implements InvocationHandler {
|
|||
if (!handler.getSqlStr().trim().toLowerCase().startsWith("update ")) {
|
||||
throw new CustomerException("The sql statement does not match, the @Update annotation can only execute the update statement, please check whether the sql statement matches!");
|
||||
}
|
||||
Util.getLogger(SQL_LOG).info(handler.toString());
|
||||
Util.getLogger(SQL_LOG).info(invokeMethod + handler);
|
||||
Class<?> returnType = method.getReturnType();
|
||||
boolean b;
|
||||
try {
|
||||
|
@ -320,7 +384,7 @@ public class RecordsetUtil implements InvocationHandler {
|
|||
if (!handler.getSqlStr().trim().toLowerCase().startsWith("insert ")) {
|
||||
throw new CustomerException("The sql statement does not match, the @Insert annotation can only execute the insert statement, please check whether the sql statement matches!");
|
||||
}
|
||||
Util.getLogger(SQL_LOG).info(handler.toString());
|
||||
Util.getLogger(SQL_LOG).info(invokeMethod + handler);
|
||||
Class<?> returnType = method.getReturnType();
|
||||
boolean b;
|
||||
try {
|
||||
|
@ -349,7 +413,7 @@ public class RecordsetUtil implements InvocationHandler {
|
|||
if (!handler.getSqlStr().trim().toLowerCase().startsWith("delete ")) {
|
||||
throw new CustomerException("The sql statement does not match, the @Delete annotation can only execute the delete statement, please check whether the sql statement matches!");
|
||||
}
|
||||
Util.getLogger(SQL_LOG).info(handler.toString());
|
||||
Util.getLogger(SQL_LOG).info(invokeMethod + handler);
|
||||
Class<?> returnType = method.getReturnType();
|
||||
boolean b;
|
||||
try {
|
||||
|
@ -376,7 +440,7 @@ public class RecordsetUtil implements InvocationHandler {
|
|||
Class<?> returnType = method.getReturnType();
|
||||
boolean custom = batchInsert.custom();
|
||||
BatchSqlResultImpl batchSqlResult = sqlHandler.handlerBatch(sql, custom, method, args);
|
||||
Util.getLogger(SQL_LOG).info(batchSqlResult.toString());
|
||||
Util.getLogger(SQL_LOG).info(invokeMethod + batchSqlResult.toString());
|
||||
List<List> batchList = batchSqlResult.getBatchList();
|
||||
if (batchList.isEmpty()) {
|
||||
throw new CustomerException("execute batch sql error , batch sql args is empty!");
|
||||
|
@ -415,7 +479,7 @@ public class RecordsetUtil implements InvocationHandler {
|
|||
Class<?> returnType = method.getReturnType();
|
||||
boolean custom = batchUpdate.custom();
|
||||
BatchSqlResultImpl batchSqlResult = sqlHandler.handlerBatch(sql, custom, method, args);
|
||||
Util.getLogger(SQL_LOG).info(batchSqlResult.toString());
|
||||
Util.getLogger(SQL_LOG).info(invokeMethod + batchSqlResult.toString());
|
||||
if (batchSqlResult.getBatchList().isEmpty()) {
|
||||
throw new CustomerException("execute batch sql error , batch sql args is empty!");
|
||||
}
|
||||
|
@ -456,7 +520,7 @@ public class RecordsetUtil implements InvocationHandler {
|
|||
Class<?> returnType = method.getReturnType();
|
||||
boolean custom = batchDelete.custom();
|
||||
BatchSqlResultImpl batchSqlResult = sqlHandler.handlerBatch(sql, custom, method, args);
|
||||
Util.getLogger(SQL_LOG).info(batchSqlResult.toString());
|
||||
Util.getLogger(SQL_LOG).info(invokeMethod + batchSqlResult.toString());
|
||||
if (batchSqlResult.getBatchList().isEmpty()) {
|
||||
throw new CustomerException("execute batch sql error , batch sql args is empty!");
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ public class ResultMapper {
|
|||
typeHandler.put(Boolean.class, new BooleanTypeHandler());
|
||||
typeHandler.put(boolean.class, new BooleanTypeHandler());
|
||||
typeHandler.put(Date.class, new DataTypeHandler());
|
||||
typeHandler.put(Float.class, new FloatTypeHandler());
|
||||
typeHandler.put(float.class, new FloatTypeHandler());
|
||||
typeHandler.put(Float.class, new FloatTypeHandler());
|
||||
typeHandler.put(double.class, new DoubleTypeHandler());
|
||||
typeHandler.put(Double.class, new DoubleTypeHandler());
|
||||
|
@ -319,8 +319,12 @@ public class ResultMapper {
|
|||
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getInt(i + 1));
|
||||
continue;
|
||||
}
|
||||
if (DBConstant.DB_TYPE_ORACLE.equals(rs.getDBType())) {
|
||||
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getInt(i + 1));
|
||||
}
|
||||
((Map<? super Object, ? super Object>) o).put(columnName[i].toLowerCase(), rs.getInt(i + 1));
|
||||
((Map<? super Object, ? super Object>) o).put(columnName[i].toUpperCase(), rs.getInt(i + 1));
|
||||
((Map<? super Object, ? super Object>) o).put(columnName[i], rs.getInt(i + 1));
|
||||
continue;
|
||||
}
|
||||
if ("FLOAT".equalsIgnoreCase(columnType) || "DOUBLE".equalsIgnoreCase(columnType) || "DECIMAL".equalsIgnoreCase(columnType)) {
|
||||
|
@ -328,16 +332,24 @@ public class ResultMapper {
|
|||
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getFloat(i + 1));
|
||||
continue;
|
||||
}
|
||||
if (DBConstant.DB_TYPE_ORACLE.equals(rs.getDBType())) {
|
||||
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getFloat(i + 1));
|
||||
}
|
||||
((Map<? super Object, ? super Object>) o).put(columnName[i].toLowerCase(), rs.getFloat(i + 1));
|
||||
((Map<? super Object, ? super Object>) o).put(columnName[i].toUpperCase(), rs.getFloat(i + 1));
|
||||
((Map<? super Object, ? super Object>) o).put(columnName[i], rs.getFloat(i + 1));
|
||||
continue;
|
||||
}
|
||||
if (enable) {
|
||||
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getString(i + 1));
|
||||
continue;
|
||||
}
|
||||
if (DBConstant.DB_TYPE_ORACLE.equals(rs.getDBType())) {
|
||||
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getString(i + 1));
|
||||
}
|
||||
((Map<? super Object, ? super Object>) o).put(columnName[i].toLowerCase(), rs.getString(i + 1));
|
||||
((Map<? super Object, ? super Object>) o).put(columnName[i].toUpperCase(), rs.getString(i + 1));
|
||||
((Map<? super Object, ? super Object>) o).put(columnName[i], rs.getString(i + 1));
|
||||
continue;
|
||||
}
|
||||
return o;
|
||||
|
@ -386,8 +398,10 @@ public class ResultMapper {
|
|||
} else {
|
||||
value = ResultMapper.typeHandler.get(propertyType) == null ? null : ResultMapper.typeHandler.get(propertyType).getValue(rs, fieldName, declaredField);
|
||||
}
|
||||
if (!Objects.isNull(value)) {
|
||||
propertyDescriptor.getWriteMethod().invoke(o, value);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Util.getLogger().error("报错了,写入数据到实体类报错!\n" + Util.getErrString(e));
|
||||
|
@ -449,8 +463,12 @@ public class ResultMapper {
|
|||
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getInt(i + 1));
|
||||
continue;
|
||||
}
|
||||
if (DBConstant.DB_TYPE_ORACLE.equals(rs.getDBType())) {
|
||||
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getInt(i + 1));
|
||||
}
|
||||
((Map<? super Object, ? super Object>) o).put(columnName[i].toLowerCase(), rs.getInt(i + 1));
|
||||
((Map<? super Object, ? super Object>) o).put(columnName[i].toUpperCase(), rs.getInt(i + 1));
|
||||
((Map<? super Object, ? super Object>) o).put(columnName[i], rs.getInt(i + 1));
|
||||
continue;
|
||||
}
|
||||
if ("FLOAT".equalsIgnoreCase(columnType) || "DOUBLE".equalsIgnoreCase(columnType) || "DECIMAL".equalsIgnoreCase(columnType)) {
|
||||
|
@ -458,8 +476,12 @@ public class ResultMapper {
|
|||
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getFloat(i + 1));
|
||||
continue;
|
||||
}
|
||||
if (DBConstant.DB_TYPE_ORACLE.equals(rs.getDBType())) {
|
||||
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getFloat(i + 1));
|
||||
}
|
||||
((Map<? super Object, ? super Object>) o).put(columnName[i].toLowerCase(), rs.getFloat(i + 1));
|
||||
((Map<? super Object, ? super Object>) o).put(columnName[i].toUpperCase(), rs.getFloat(i + 1));
|
||||
((Map<? super Object, ? super Object>) o).put(columnName[i], rs.getFloat(i + 1));
|
||||
continue;
|
||||
}
|
||||
if (method.isAnnotationPresent(Associations.class)) {
|
||||
|
@ -488,8 +510,12 @@ public class ResultMapper {
|
|||
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getString(i + 1));
|
||||
continue;
|
||||
}
|
||||
if (DBConstant.DB_TYPE_ORACLE.equals(rs.getDBType())) {
|
||||
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getString(i + 1));
|
||||
}
|
||||
((Map<? super Object, ? super Object>) o).put(columnName[i].toLowerCase(), rs.getString(i + 1));
|
||||
((Map<? super Object, ? super Object>) o).put(columnName[i].toUpperCase(), rs.getString(i + 1));
|
||||
((Map<? super Object, ? super Object>) o).put(columnName[i], rs.getString(i + 1));
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
@ -528,6 +554,7 @@ public class ResultMapper {
|
|||
Object value = null;
|
||||
String fieldName = propertyDescriptor.getName();
|
||||
|
||||
Field declaredField = o.getClass().getDeclaredField(fieldName);
|
||||
if (Strings.isNullOrEmpty(fieldName)) {
|
||||
fieldName = propertyDescriptor.getDisplayName();
|
||||
}
|
||||
|
@ -536,7 +563,20 @@ public class ResultMapper {
|
|||
if (association != null) {
|
||||
if (association.property().equals(fieldName)) {
|
||||
Object cassociationValue = association(rs, association, method);
|
||||
if (cassociationValue == null) {
|
||||
continue;
|
||||
}
|
||||
if (paramType.containsKey(declaredField.getType())) {
|
||||
cassociationValue = paramType.get(declaredField.getType()).apply(String.valueOf(cassociationValue));
|
||||
}
|
||||
try {
|
||||
propertyDescriptor.getWriteMethod().invoke(o, cassociationValue);
|
||||
} catch (Exception e) {
|
||||
Util.getLogger().error("实体数据写入报错:" + fieldName + " => " + value);
|
||||
if (value != null) {
|
||||
Util.getLogger().error("查询数据类型: " + value.getClass());
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -546,21 +586,37 @@ public class ResultMapper {
|
|||
if (collectionMapping != null) {
|
||||
if (fieldName.equals(collectionMapping.property()) && !"".equals(collectionMapping.property())) {
|
||||
Object collection = collection(rs, collectionMapping, method);
|
||||
try {
|
||||
propertyDescriptor.getWriteMethod().invoke(o, collection);
|
||||
} catch (Exception e) {
|
||||
Util.getLogger().error("实体数据写入报错:" + fieldName + " => " + value);
|
||||
if (value != null) {
|
||||
Util.getLogger().error("查询数据类型: " + value.getClass());
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
Field declaredField = o.getClass().getDeclaredField(fieldName);
|
||||
TypeHandler typeHandler = ResultMapper.typeHandler.get(propertyType);
|
||||
if (enable) {
|
||||
value = ResultMapper.typeHandler.get(propertyType) == null ? null : ResultMapper.typeHandler.get(propertyType).getValue(rs, Util.toUnderlineCase(fieldName), declaredField);
|
||||
value = typeHandler == null ? null : typeHandler.getValue(rs, Util.toUnderlineCase(fieldName), declaredField);
|
||||
} else {
|
||||
value = ResultMapper.typeHandler.get(propertyType) == null ? null : ResultMapper.typeHandler.get(propertyType).getValue(rs, fieldName, declaredField);
|
||||
value = typeHandler == null ? null : typeHandler.getValue(rs, fieldName, declaredField);
|
||||
}
|
||||
try {
|
||||
if (!Objects.isNull(value)) {
|
||||
propertyDescriptor.getWriteMethod().invoke(o, value);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Util.getLogger().error("实体数据写入报错:" + fieldName + " => " + value);
|
||||
if (value != null) {
|
||||
Util.getLogger().error("查询数据类型: " + value.getClass());
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Util.getLogger().error("报错了,写入数据到实体类报错!\n" + Util.getErrString(e));
|
||||
throw new CustomerException(e.getMessage(), e);
|
||||
|
@ -607,6 +663,9 @@ public class ResultMapper {
|
|||
Id id = annotation.id();
|
||||
String column = annotation.column();
|
||||
String columnValue = rs.getString(column);
|
||||
if (Objects.isNull(columnValue) || "".equals(columnValue)) {
|
||||
columnValue = rs.getString(column.toUpperCase());
|
||||
}
|
||||
if (Objects.isNull(columnValue) || "".equals(columnValue)) {
|
||||
return null;
|
||||
}
|
||||
|
@ -665,6 +724,9 @@ public class ResultMapper {
|
|||
Id id = annotation.id();
|
||||
String column = annotation.column();
|
||||
String columnValue = rs.getString(column);
|
||||
if (Objects.isNull(columnValue) || "".equals(columnValue)) {
|
||||
columnValue = rs.getString(column.toUpperCase());
|
||||
}
|
||||
if (Objects.isNull(columnValue) || "".equals(columnValue)) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package aiyh.utils.sqlUtil.sqlResult.impl;
|
||||
|
||||
import aiyh.utils.recordset.FormatSqlUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -29,7 +31,7 @@ public class BatchSqlResultImpl implements aiyh.utils.sqlUtil.sqlResult.SqlResul
|
|||
@Override
|
||||
public String toString() {
|
||||
return "BatchSqlResultImpl{" +
|
||||
"sqlStr='" + sqlStr + '\'' +
|
||||
"sqlStr='\n" + FormatSqlUtil.formatSql(sqlStr) + "\n'" +
|
||||
", batchList=" + batchList +
|
||||
'}';
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package aiyh.utils.sqlUtil.sqlResult.impl;
|
||||
|
||||
import aiyh.utils.recordset.FormatSqlUtil;
|
||||
import aiyh.utils.sqlUtil.sqlResult.SqlResult;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -31,7 +32,7 @@ public class PrepSqlResultImpl implements SqlResult {
|
|||
@Override
|
||||
public String toString() {
|
||||
return "PrepSqlResultImpl{" +
|
||||
"sqlStr='" + sqlStr + '\'' +
|
||||
"sqlStr='\n" + FormatSqlUtil.formatSql(sqlStr) + "\n'" +
|
||||
", args=" + args +
|
||||
'}';
|
||||
}
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.annotation.scanner.AnnotationScanner;
|
||||
import aiyh.utils.tool.cn.hutool.core.collection.CollUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||
import aiyh.utils.tool.cn.hutool.core.map.MapUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* {@link AnnotationSynthesizer}的基本实现
|
||||
*
|
||||
* @author huangchengxing
|
||||
*/
|
||||
public abstract class AbstractAnnotationSynthesizer<T> implements AnnotationSynthesizer {
|
||||
|
||||
/**
|
||||
* 合成注解来源最初来源
|
||||
*/
|
||||
protected final T source;
|
||||
|
||||
/**
|
||||
* 包含根注解以及其元注解在内的全部注解实例
|
||||
*/
|
||||
protected final Map<Class<? extends Annotation>, SynthesizedAnnotation> synthesizedAnnotationMap;
|
||||
|
||||
/**
|
||||
* 已经合成过的注解对象
|
||||
*/
|
||||
private final Map<Class<? extends Annotation>, Annotation> synthesizedProxyAnnotations;
|
||||
|
||||
/**
|
||||
* 合成注解选择器
|
||||
*/
|
||||
protected final SynthesizedAnnotationSelector annotationSelector;
|
||||
|
||||
/**
|
||||
* 合成注解属性处理器
|
||||
*/
|
||||
protected final Collection<SynthesizedAnnotationPostProcessor> postProcessors;
|
||||
|
||||
/**
|
||||
* 注解扫描器
|
||||
*/
|
||||
protected final AnnotationScanner annotationScanner;
|
||||
|
||||
/**
|
||||
* 构造一个注解合成器
|
||||
*
|
||||
* @param source 当前查找的注解对象
|
||||
* @param annotationSelector 合成注解选择器
|
||||
* @param annotationPostProcessors 注解后置处理器
|
||||
* @param annotationScanner 注解扫描器,该扫描器需要支持扫描注解类
|
||||
*/
|
||||
protected AbstractAnnotationSynthesizer(
|
||||
T source,
|
||||
SynthesizedAnnotationSelector annotationSelector,
|
||||
Collection<SynthesizedAnnotationPostProcessor> annotationPostProcessors,
|
||||
AnnotationScanner annotationScanner) {
|
||||
Assert.notNull(source, "source must not null");
|
||||
Assert.notNull(annotationSelector, "annotationSelector must not null");
|
||||
Assert.notNull(annotationPostProcessors, "annotationPostProcessors must not null");
|
||||
Assert.notNull(annotationPostProcessors, "annotationScanner must not null");
|
||||
|
||||
this.source = source;
|
||||
this.annotationSelector = annotationSelector;
|
||||
this.annotationScanner = annotationScanner;
|
||||
this.postProcessors = CollUtil.unmodifiable(
|
||||
CollUtil.sort(annotationPostProcessors, Comparator.comparing(SynthesizedAnnotationPostProcessor::order))
|
||||
);
|
||||
this.synthesizedProxyAnnotations = new LinkedHashMap<>();
|
||||
this.synthesizedAnnotationMap = MapUtil.unmodifiable(loadAnnotations());
|
||||
annotationPostProcessors.forEach(processor ->
|
||||
synthesizedAnnotationMap.values().forEach(synthesized -> processor.process(synthesized, this))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载合成注解的必要属性
|
||||
*
|
||||
* @return 合成注解
|
||||
*/
|
||||
protected abstract Map<Class<? extends Annotation>, SynthesizedAnnotation> loadAnnotations();
|
||||
|
||||
/**
|
||||
* 根据指定的注解类型和对应注解对象,合成最终所需的合成注解
|
||||
*
|
||||
* @param annotationType 注解类型
|
||||
* @param annotation 合成注解对象
|
||||
* @param <A> 注解类型
|
||||
* @return 最终所需的合成注解
|
||||
*/
|
||||
protected abstract <A extends Annotation> A synthesize(Class<A> annotationType, SynthesizedAnnotation annotation);
|
||||
|
||||
/**
|
||||
* 获取合成注解来源最初来源
|
||||
*
|
||||
* @return 合成注解来源最初来源
|
||||
*/
|
||||
@Override
|
||||
public T getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合成注解选择器
|
||||
*
|
||||
* @return 注解选择器
|
||||
*/
|
||||
@Override
|
||||
public SynthesizedAnnotationSelector getAnnotationSelector() {
|
||||
return annotationSelector;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取合成注解后置处理器
|
||||
*
|
||||
* @return 合成注解后置处理器
|
||||
*/
|
||||
@Override
|
||||
public Collection<SynthesizedAnnotationPostProcessor> getAnnotationPostProcessors() {
|
||||
return postProcessors;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取已合成的注解
|
||||
*
|
||||
* @param annotationType 注解类型
|
||||
* @return 已合成的注解
|
||||
*/
|
||||
@Override
|
||||
public SynthesizedAnnotation getSynthesizedAnnotation(Class<?> annotationType) {
|
||||
return synthesizedAnnotationMap.get(annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全部的合成注解
|
||||
*
|
||||
* @return 合成注解
|
||||
*/
|
||||
@Override
|
||||
public Map<Class<? extends Annotation>, SynthesizedAnnotation> getAllSynthesizedAnnotation() {
|
||||
return synthesizedAnnotationMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取合成注解
|
||||
*
|
||||
* @param annotationType 注解类型
|
||||
* @param <A> 注解类型
|
||||
* @return 类型
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <A extends Annotation> A synthesize(Class<A> annotationType) {
|
||||
return (A)synthesizedProxyAnnotations.computeIfAbsent(annotationType, type -> {
|
||||
final SynthesizedAnnotation synthesizedAnnotation = synthesizedAnnotationMap.get(annotationType);
|
||||
return ObjectUtil.isNull(synthesizedAnnotation) ?
|
||||
null : synthesize(annotationType, synthesizedAnnotation);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.Opt;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ArrayUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* {@link SynthesizedAnnotationPostProcessor}的基本实现,
|
||||
* 用于处理注解中带有{@link Link}注解的属性。
|
||||
*
|
||||
* @author huangchengxing
|
||||
* @see MirrorLinkAnnotationPostProcessor
|
||||
* @see AliasLinkAnnotationPostProcessor
|
||||
*/
|
||||
public abstract class AbstractLinkAnnotationPostProcessor implements SynthesizedAnnotationPostProcessor {
|
||||
|
||||
/**
|
||||
* 若一个注解属性上存在{@link Link}注解,注解的{@link Link#type()}返回值在{@link #processTypes()}中存在,
|
||||
* 且此{@link Link}指定的注解对象在当前的{@link SynthesizedAggregateAnnotation}中存在,
|
||||
* 则从聚合器中获取类型对应的合成注解对象,与该对象中的指定属性,然后将全部关联数据交给
|
||||
* {@link #processLinkedAttribute}处理。
|
||||
*
|
||||
* @param synthesizedAnnotation 合成的注解
|
||||
* @param synthesizer 合成注解聚合器
|
||||
*/
|
||||
@Override
|
||||
public void process(SynthesizedAnnotation synthesizedAnnotation, AnnotationSynthesizer synthesizer) {
|
||||
final Map<String, AnnotationAttribute> attributeMap = new HashMap<>(synthesizedAnnotation.getAttributes());
|
||||
attributeMap.forEach((originalAttributeName, originalAttribute) -> {
|
||||
// 获取注解
|
||||
final Link link = getLinkAnnotation(originalAttribute, processTypes());
|
||||
if (ObjectUtil.isNull(link)) {
|
||||
return;
|
||||
}
|
||||
// 获取注解属性
|
||||
final SynthesizedAnnotation linkedAnnotation = getLinkedAnnotation(link, synthesizer, synthesizedAnnotation.annotationType());
|
||||
if (ObjectUtil.isNull(linkedAnnotation)) {
|
||||
return;
|
||||
}
|
||||
final AnnotationAttribute linkedAttribute = linkedAnnotation.getAttributes().get(link.attribute());
|
||||
// 处理
|
||||
processLinkedAttribute(
|
||||
synthesizer, link,
|
||||
synthesizedAnnotation, synthesizedAnnotation.getAttributes().get(originalAttributeName),
|
||||
linkedAnnotation, linkedAttribute
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// =========================== 抽象方法 ===========================
|
||||
|
||||
/**
|
||||
* 当属性上存在{@link Link}注解时,仅当{@link Link#type()}在本方法返回值内存在时才进行处理
|
||||
*
|
||||
* @return 支持处理的{@link RelationType}类型
|
||||
*/
|
||||
protected abstract RelationType[] processTypes();
|
||||
|
||||
/**
|
||||
* 对关联的合成注解对象及其关联属性的处理
|
||||
*
|
||||
* @param synthesizer 注解合成器
|
||||
* @param annotation {@code originalAttribute}上的{@link Link}注解对象
|
||||
* @param originalAnnotation 当前正在处理的{@link SynthesizedAnnotation}对象
|
||||
* @param originalAttribute {@code originalAnnotation}上的待处理的属性
|
||||
* @param linkedAnnotation {@link Link}指向的关联注解对象
|
||||
* @param linkedAttribute {@link Link}指向的{@code originalAnnotation}中的关联属性,该参数可能为空
|
||||
*/
|
||||
protected abstract void processLinkedAttribute(
|
||||
AnnotationSynthesizer synthesizer, Link annotation,
|
||||
SynthesizedAnnotation originalAnnotation, AnnotationAttribute originalAttribute,
|
||||
SynthesizedAnnotation linkedAnnotation, AnnotationAttribute linkedAttribute
|
||||
);
|
||||
|
||||
// =========================== @Link注解的处理 ===========================
|
||||
|
||||
/**
|
||||
* 从注解属性上获取指定类型的{@link Link}注解
|
||||
*
|
||||
* @param attribute 注解属性
|
||||
* @param relationTypes 类型
|
||||
* @return 注解
|
||||
*/
|
||||
protected Link getLinkAnnotation(AnnotationAttribute attribute, RelationType... relationTypes) {
|
||||
return Opt.ofNullable(attribute)
|
||||
.map(t -> AnnotationUtil.getSynthesizedAnnotation(attribute.getAttribute(), Link.class))
|
||||
.filter(a -> ArrayUtil.contains(relationTypes, a.type()))
|
||||
.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从合成注解中获取{@link Link#type()}指定的注解对象
|
||||
*
|
||||
* @param annotation {@link Link}注解
|
||||
* @param synthesizer 注解合成器
|
||||
* @param defaultType 默认类型
|
||||
* @return {@link SynthesizedAnnotation}
|
||||
*/
|
||||
protected SynthesizedAnnotation getLinkedAnnotation(Link annotation, AnnotationSynthesizer synthesizer, Class<? extends Annotation> defaultType) {
|
||||
final Class<?> targetAnnotationType = getLinkedAnnotationType(annotation, defaultType);
|
||||
return synthesizer.getSynthesizedAnnotation(targetAnnotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 若{@link Link#annotation()}获取的类型{@code Annotation#getClass()},则返回{@code defaultType},
|
||||
* 否则返回{@link Link#annotation()}指定的类型
|
||||
*
|
||||
* @param annotation {@link Link}注解
|
||||
* @param defaultType 默认注解类型
|
||||
* @return 注解类型
|
||||
*/
|
||||
protected Class<?> getLinkedAnnotationType(Link annotation, Class<?> defaultType) {
|
||||
return ObjectUtil.equals(annotation.annotation(), Annotation.class) ?
|
||||
defaultType : annotation.annotation();
|
||||
}
|
||||
|
||||
// =========================== 注解属性的校验 ===========================
|
||||
|
||||
/**
|
||||
* 校验两个注解属性的返回值类型是否一致
|
||||
*
|
||||
* @param original 原属性
|
||||
* @param alias 别名属性
|
||||
*/
|
||||
protected void checkAttributeType(AnnotationAttribute original, AnnotationAttribute alias) {
|
||||
Assert.equals(
|
||||
original.getAttributeType(), alias.getAttributeType(),
|
||||
"return type of the linked attribute [{}] is inconsistent with the original [{}]",
|
||||
original.getAttribute(), alias.getAttribute()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查{@link Link}指向的注解属性是否就是本身
|
||||
*
|
||||
* @param original {@link Link}注解的属性
|
||||
* @param linked {@link Link}指向的注解属性
|
||||
*/
|
||||
protected void checkLinkedSelf(AnnotationAttribute original, AnnotationAttribute linked) {
|
||||
boolean linkSelf = (original == linked) || ObjectUtil.equals(original.getAttribute(), linked.getAttribute());
|
||||
Assert.isFalse(linkSelf, "cannot link self [{}]", original.getAttribute());
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查{@link Link}指向的注解属性是否存在
|
||||
*
|
||||
* @param original {@link Link}注解的属性
|
||||
* @param linkedAttribute {@link Link}指向的注解属性
|
||||
* @param annotation {@link Link}注解
|
||||
*/
|
||||
protected void checkLinkedAttributeNotNull(AnnotationAttribute original, AnnotationAttribute linkedAttribute, Link annotation) {
|
||||
Assert.notNull(linkedAttribute, "cannot find linked attribute [{}] of original [{}] in [{}]",
|
||||
original.getAttribute(), annotation.attribute(),
|
||||
getLinkedAnnotationType(annotation, original.getAnnotationType())
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@link WrappedAnnotationAttribute}的基本实现
|
||||
*
|
||||
* @author huangchengxing
|
||||
* @see ForceAliasedAnnotationAttribute
|
||||
* @see AliasedAnnotationAttribute
|
||||
* @see MirroredAnnotationAttribute
|
||||
*/
|
||||
public abstract class AbstractWrappedAnnotationAttribute implements WrappedAnnotationAttribute {
|
||||
|
||||
protected final AnnotationAttribute original;
|
||||
protected final AnnotationAttribute linked;
|
||||
|
||||
protected AbstractWrappedAnnotationAttribute(AnnotationAttribute original, AnnotationAttribute linked) {
|
||||
Assert.notNull(original, "target must not null");
|
||||
Assert.notNull(linked, "linked must not null");
|
||||
this.original = original;
|
||||
this.linked = linked;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationAttribute getOriginal() {
|
||||
return original;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationAttribute getLinked() {
|
||||
return linked;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationAttribute getNonWrappedOriginal() {
|
||||
AnnotationAttribute curr = null;
|
||||
AnnotationAttribute next = original;
|
||||
while (next != null) {
|
||||
curr = next;
|
||||
next = next.isWrapped() ? ((WrappedAnnotationAttribute)curr).getOriginal() : null;
|
||||
}
|
||||
return curr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<AnnotationAttribute> getAllLinkedNonWrappedAttributes() {
|
||||
List<AnnotationAttribute> leafAttributes = new ArrayList<>();
|
||||
collectLeafAttribute(this, leafAttributes);
|
||||
return leafAttributes;
|
||||
}
|
||||
|
||||
private void collectLeafAttribute(AnnotationAttribute curr, List<AnnotationAttribute> leafAttributes) {
|
||||
if (ObjectUtil.isNull(curr)) {
|
||||
return;
|
||||
}
|
||||
if (!curr.isWrapped()) {
|
||||
leafAttributes.add(curr);
|
||||
return;
|
||||
}
|
||||
WrappedAnnotationAttribute wrappedAttribute = (WrappedAnnotationAttribute)curr;
|
||||
collectLeafAttribute(wrappedAttribute.getOriginal(), leafAttributes);
|
||||
collectLeafAttribute(wrappedAttribute.getLinked(), leafAttributes);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
/**
|
||||
* 表示一组被聚合在一起的注解对象
|
||||
*
|
||||
* @author huangchengxing
|
||||
*/
|
||||
public interface AggregateAnnotation extends Annotation {
|
||||
|
||||
/**
|
||||
* 在聚合中是否存在的指定类型注解对象
|
||||
*
|
||||
* @param annotationType 注解类型
|
||||
* @return 是否
|
||||
*/
|
||||
boolean isAnnotationPresent(Class<? extends Annotation> annotationType);
|
||||
|
||||
/**
|
||||
* 获取聚合中的全部注解对象
|
||||
*
|
||||
* @return 注解对象
|
||||
*/
|
||||
Annotation[] getAnnotations();
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 别名注解,使用此注解的字段、方法、参数等会有一个别名,用于Bean拷贝、Bean转Map等
|
||||
*
|
||||
* @author Looly
|
||||
* @since 5.1.1
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
|
||||
public @interface Alias {
|
||||
|
||||
/**
|
||||
* 别名值,即使用此注解要替换成的别名名称
|
||||
*
|
||||
* @return 别名值
|
||||
*/
|
||||
String value();
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.Opt;
|
||||
import aiyh.utils.tool.cn.hutool.core.map.ForestMap;
|
||||
import aiyh.utils.tool.cn.hutool.core.map.LinkedForestMap;
|
||||
import aiyh.utils.tool.cn.hutool.core.map.TreeEntry;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ClassUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>用于处理注解对象中带有{@link Alias}注解的属性。<br>
|
||||
* 当该处理器执行完毕后,{@link Alias}注解指向的目标注解的属性将会被包装并替换为
|
||||
* {@link ForceAliasedAnnotationAttribute}。
|
||||
*
|
||||
* @author huangchengxing
|
||||
* @see Alias
|
||||
* @see ForceAliasedAnnotationAttribute
|
||||
*/
|
||||
public class AliasAnnotationPostProcessor implements SynthesizedAnnotationPostProcessor {
|
||||
|
||||
@Override
|
||||
public int order() {
|
||||
return Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(SynthesizedAnnotation synthesizedAnnotation, AnnotationSynthesizer synthesizer) {
|
||||
final Map<String, AnnotationAttribute> attributeMap = synthesizedAnnotation.getAttributes();
|
||||
|
||||
// 记录别名与属性的关系
|
||||
final ForestMap<String, AnnotationAttribute> attributeAliasMappings = new LinkedForestMap<>(false);
|
||||
attributeMap.forEach((attributeName, attribute) -> {
|
||||
final String alias = Opt.ofNullable(attribute.getAnnotation(Alias.class))
|
||||
.map(Alias::value)
|
||||
.orElse(null);
|
||||
if (ObjectUtil.isNull(alias)) {
|
||||
return;
|
||||
}
|
||||
final AnnotationAttribute aliasAttribute = attributeMap.get(alias);
|
||||
Assert.notNull(aliasAttribute, "no method for alias: [{}]", alias);
|
||||
attributeAliasMappings.putLinkedNodes(alias, aliasAttribute, attributeName, attribute);
|
||||
});
|
||||
|
||||
// 处理别名
|
||||
attributeMap.forEach((attributeName, attribute) -> {
|
||||
final AnnotationAttribute resolvedAttribute = Opt.ofNullable(attributeName)
|
||||
.map(attributeAliasMappings::getRootNode)
|
||||
.map(TreeEntry::getValue)
|
||||
.orElse(attribute);
|
||||
Assert.isTrue(
|
||||
ObjectUtil.isNull(resolvedAttribute)
|
||||
|| ClassUtil.isAssignable(attribute.getAttributeType(), resolvedAttribute.getAttributeType()),
|
||||
"return type of the root alias method [{}] is inconsistent with the original [{}]",
|
||||
resolvedAttribute.getClass(), attribute.getAttributeType()
|
||||
);
|
||||
if (attribute != resolvedAttribute) {
|
||||
attributeMap.put(attributeName, new ForceAliasedAnnotationAttribute(attribute, resolvedAttribute));
|
||||
}
|
||||
});
|
||||
synthesizedAnnotation.setAttributes(attributeMap);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* <p>{@link Link}的子注解。表示“原始属性”将作为“关联属性”的别名。
|
||||
* <ul>
|
||||
* <li>当“原始属性”为默认值时,获取“关联属性”将返回“关联属性”本身的值;</li>
|
||||
* <li>当“原始属性”不为默认值时,获取“关联属性”将返回“原始属性”的值;</li>
|
||||
* </ul>
|
||||
* <b>注意,该注解与{@link Link}、{@link ForceAliasFor}或{@link MirrorFor}一起使用时,将只有被声明在最上面的注解会生效</b>
|
||||
*
|
||||
* @author huangchengxing
|
||||
* @see Link
|
||||
* @see RelationType#ALIAS_FOR
|
||||
*/
|
||||
@Link(type = RelationType.ALIAS_FOR)
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
|
||||
public @interface AliasFor {
|
||||
|
||||
/**
|
||||
* 产生关联的注解类型,当不指定时,默认指注释的属性所在的类
|
||||
*
|
||||
* @return 注解类型
|
||||
*/
|
||||
@Link(annotation = Link.class, attribute = "annotation", type = RelationType.FORCE_ALIAS_FOR)
|
||||
Class<? extends Annotation> annotation() default Annotation.class;
|
||||
|
||||
/**
|
||||
* {@link #annotation()}指定注解中关联的属性
|
||||
*
|
||||
* @return 关联属性
|
||||
*/
|
||||
@Link(annotation = Link.class, attribute = "attribute", type = RelationType.FORCE_ALIAS_FOR)
|
||||
String attribute() default "";
|
||||
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.Opt;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import java.util.function.BinaryOperator;
|
||||
|
||||
/**
|
||||
* <p>用于处理注解对象中带有{@link Link}注解,且{@link Link#type()}为
|
||||
* {@link RelationType#ALIAS_FOR}或{@link RelationType#FORCE_ALIAS_FOR}的属性。<br>
|
||||
* 当该处理器执行完毕后,{@link Link}注解指向的目标注解的属性将会被包装并替换为
|
||||
* {@link AliasedAnnotationAttribute}或{@link ForceAliasedAnnotationAttribute}。
|
||||
*
|
||||
* @author huangchengxing
|
||||
* @see RelationType#ALIAS_FOR
|
||||
* @see AliasedAnnotationAttribute
|
||||
* @see RelationType#FORCE_ALIAS_FOR
|
||||
* @see ForceAliasedAnnotationAttribute
|
||||
*/
|
||||
public class AliasLinkAnnotationPostProcessor extends AbstractLinkAnnotationPostProcessor {
|
||||
|
||||
private static final RelationType[] PROCESSED_RELATION_TYPES = new RelationType[]{ RelationType.ALIAS_FOR, RelationType.FORCE_ALIAS_FOR };
|
||||
|
||||
@Override
|
||||
public int order() {
|
||||
return Integer.MIN_VALUE + 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* 该处理器只处理{@link Link#type()}类型为{@link RelationType#ALIAS_FOR}和{@link RelationType#FORCE_ALIAS_FOR}的注解属性
|
||||
*
|
||||
* @return 含有{@link RelationType#ALIAS_FOR}和{@link RelationType#FORCE_ALIAS_FOR}的数组
|
||||
*/
|
||||
@Override
|
||||
protected RelationType[] processTypes() {
|
||||
return PROCESSED_RELATION_TYPES;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取{@link Link}指向的目标注解属性,并根据{@link Link#type()}的类型是
|
||||
* {@link RelationType#ALIAS_FOR}或{@link RelationType#FORCE_ALIAS_FOR}
|
||||
* 将目标注解属性包装为{@link AliasedAnnotationAttribute}或{@link ForceAliasedAnnotationAttribute},
|
||||
* 然后用包装后注解属性在对应的合成注解中替换原始的目标注解属性
|
||||
*
|
||||
* @param synthesizer 注解合成器
|
||||
* @param annotation {@code originalAttribute}上的{@link Link}注解对象
|
||||
* @param originalAnnotation 当前正在处理的{@link SynthesizedAnnotation}对象
|
||||
* @param originalAttribute {@code originalAnnotation}上的待处理的属性
|
||||
* @param linkedAnnotation {@link Link}指向的关联注解对象
|
||||
* @param linkedAttribute {@link Link}指向的{@code originalAnnotation}中的关联属性,该参数可能为空
|
||||
*/
|
||||
@Override
|
||||
protected void processLinkedAttribute(
|
||||
AnnotationSynthesizer synthesizer, Link annotation,
|
||||
SynthesizedAnnotation originalAnnotation, AnnotationAttribute originalAttribute,
|
||||
SynthesizedAnnotation linkedAnnotation, AnnotationAttribute linkedAttribute) {
|
||||
// 校验别名关系
|
||||
checkAliasRelation(annotation, originalAttribute, linkedAttribute);
|
||||
// 处理aliasFor类型的关系
|
||||
if (RelationType.ALIAS_FOR.equals(annotation.type())) {
|
||||
wrappingLinkedAttribute(synthesizer, originalAttribute, linkedAttribute, AliasedAnnotationAttribute::new);
|
||||
return;
|
||||
}
|
||||
// 处理forceAliasFor类型的关系
|
||||
wrappingLinkedAttribute(synthesizer, originalAttribute, linkedAttribute, ForceAliasedAnnotationAttribute::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对指定注解属性进行包装,若该属性已被包装过,则递归以其为根节点的树结构,对树上全部的叶子节点进行包装
|
||||
*/
|
||||
private void wrappingLinkedAttribute(
|
||||
AnnotationSynthesizer synthesizer, AnnotationAttribute originalAttribute, AnnotationAttribute aliasAttribute, BinaryOperator<AnnotationAttribute> wrapping) {
|
||||
// 不是包装属性
|
||||
if (!aliasAttribute.isWrapped()) {
|
||||
processAttribute(synthesizer, originalAttribute, aliasAttribute, wrapping);
|
||||
return;
|
||||
}
|
||||
// 是包装属性
|
||||
final AbstractWrappedAnnotationAttribute wrapper = (AbstractWrappedAnnotationAttribute)aliasAttribute;
|
||||
wrapper.getAllLinkedNonWrappedAttributes().forEach(
|
||||
t -> processAttribute(synthesizer, originalAttribute, t, wrapping)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定注解属性,然后将其再进行一层包装
|
||||
*/
|
||||
private void processAttribute(
|
||||
AnnotationSynthesizer synthesizer, AnnotationAttribute originalAttribute,
|
||||
AnnotationAttribute target, BinaryOperator<AnnotationAttribute> wrapping) {
|
||||
Opt.ofNullable(target.getAnnotationType())
|
||||
.map(synthesizer::getSynthesizedAnnotation)
|
||||
.ifPresent(t -> t.replaceAttribute(target.getAttributeName(), old -> wrapping.apply(old, originalAttribute)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 基本校验
|
||||
*/
|
||||
private void checkAliasRelation(Link annotation, AnnotationAttribute originalAttribute, AnnotationAttribute linkedAttribute) {
|
||||
checkLinkedAttributeNotNull(originalAttribute, linkedAttribute, annotation);
|
||||
checkAttributeType(originalAttribute, linkedAttribute);
|
||||
checkCircularDependency(originalAttribute, linkedAttribute);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查两个属性是否互为别名
|
||||
*/
|
||||
private void checkCircularDependency(AnnotationAttribute original, AnnotationAttribute alias) {
|
||||
checkLinkedSelf(original, alias);
|
||||
Link annotation = getLinkAnnotation(alias, RelationType.ALIAS_FOR, RelationType.FORCE_ALIAS_FOR);
|
||||
if (ObjectUtil.isNull(annotation)) {
|
||||
return;
|
||||
}
|
||||
final Class<?> aliasAnnotationType = getLinkedAnnotationType(annotation, alias.getAnnotationType());
|
||||
if (ObjectUtil.notEqual(aliasAnnotationType, original.getAnnotationType())) {
|
||||
return;
|
||||
}
|
||||
Assert.notEquals(
|
||||
annotation.attribute(), original.getAttributeName(),
|
||||
"circular reference between the alias attribute [{}] and the original attribute [{}]",
|
||||
alias.getAttribute(), original.getAttribute()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
/**
|
||||
* <p>表示一个具有别名的属性。
|
||||
* 当别名属性值为默认值时,优先返回原属性的值,当别名属性不为默认值时,优先返回别名属性的值
|
||||
*
|
||||
* @author huangchengxing
|
||||
* @see AliasLinkAnnotationPostProcessor
|
||||
* @see RelationType#ALIAS_FOR
|
||||
*/
|
||||
public class AliasedAnnotationAttribute extends AbstractWrappedAnnotationAttribute {
|
||||
|
||||
protected AliasedAnnotationAttribute(AnnotationAttribute origin, AnnotationAttribute linked) {
|
||||
super(origin, linked);
|
||||
}
|
||||
|
||||
/**
|
||||
* 若{@link #linked}为默认值,则返回{@link #original}的值,否则返回{@link #linked}的值
|
||||
*
|
||||
* @return 属性值
|
||||
*/
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return linked.isValueEquivalentToDefaultValue() ? super.getValue() : linked.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 当{@link #original}与{@link #linked}都为默认值时返回{@code true}
|
||||
*
|
||||
* @return 是否
|
||||
*/
|
||||
@Override
|
||||
public boolean isValueEquivalentToDefaultValue() {
|
||||
return linked.isValueEquivalentToDefaultValue() && original.isValueEquivalentToDefaultValue();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ReflectUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* <p>表示注解的某个属性,等同于绑定的调用对象的{@link Method}方法。<br>
|
||||
* 在{@link SynthesizedAggregateAnnotation}的解析以及取值过程中,
|
||||
* 可以通过设置{@link SynthesizedAnnotation}的注解属性,
|
||||
* 从而使得可以从一个注解对象中属性获取另一个注解对象的属性值
|
||||
*
|
||||
* <p>一般情况下,注解属性的处理会发生在{@link SynthesizedAnnotationPostProcessor}调用时
|
||||
*
|
||||
* @author huangchengxing
|
||||
* @see SynthesizedAnnotationPostProcessor
|
||||
* @see WrappedAnnotationAttribute
|
||||
* @see CacheableAnnotationAttribute
|
||||
* @see AbstractWrappedAnnotationAttribute
|
||||
* @see ForceAliasedAnnotationAttribute
|
||||
* @see AliasedAnnotationAttribute
|
||||
* @see MirroredAnnotationAttribute
|
||||
*/
|
||||
public interface AnnotationAttribute {
|
||||
|
||||
/**
|
||||
* 获取注解对象
|
||||
*
|
||||
* @return 注解对象
|
||||
*/
|
||||
Annotation getAnnotation();
|
||||
|
||||
/**
|
||||
* 获取注解属性对应的方法
|
||||
*
|
||||
* @return 注解属性对应的方法
|
||||
*/
|
||||
Method getAttribute();
|
||||
|
||||
/**
|
||||
* 获取声明属性的注解类
|
||||
*
|
||||
* @return 声明注解的注解类
|
||||
*/
|
||||
default Class<?> getAnnotationType() {
|
||||
return getAttribute().getDeclaringClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取属性名称
|
||||
*
|
||||
* @return 属性名称
|
||||
*/
|
||||
default String getAttributeName() {
|
||||
return getAttribute().getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取注解属性
|
||||
*
|
||||
* @return 注解属性
|
||||
*/
|
||||
default Object getValue() {
|
||||
return ReflectUtil.invoke(getAnnotation(), getAttribute());
|
||||
}
|
||||
|
||||
/**
|
||||
* 该注解属性的值是否等于默认值
|
||||
*
|
||||
* @return 该注解属性的值是否等于默认值
|
||||
*/
|
||||
boolean isValueEquivalentToDefaultValue();
|
||||
|
||||
/**
|
||||
* 获取属性类型
|
||||
*
|
||||
* @return 属性类型
|
||||
*/
|
||||
default Class<?> getAttributeType() {
|
||||
return getAttribute().getReturnType();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取属性上的注解
|
||||
*
|
||||
* @param <T> 注解类型
|
||||
* @param annotationType 注解类型
|
||||
* @return 注解对象
|
||||
*/
|
||||
default <T extends Annotation> T getAnnotation(Class<T> annotationType) {
|
||||
return getAttribute().getAnnotation(annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前注解属性是否已经被{@link WrappedAnnotationAttribute}包装
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
default boolean isWrapped() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
/**
|
||||
* 表示一个可以从当前接口的实现类中,获得特定的属性值
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface AnnotationAttributeValueProvider {
|
||||
|
||||
/**
|
||||
* 获取注解属性值
|
||||
*
|
||||
* @param attributeName 属性名称
|
||||
* @param attributeType 属性类型
|
||||
* @return 注解属性值
|
||||
*/
|
||||
Object getAttributeValue(String attributeName, Class<?> attributeType);
|
||||
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ReflectUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 注解代理<br>
|
||||
* 通过代理指定注解,可以自定义调用注解的方法逻辑,如支持{@link Alias} 注解
|
||||
*
|
||||
* @param <T> 注解类型
|
||||
* @since 5.7.23
|
||||
*/
|
||||
public class AnnotationProxy<T extends Annotation> implements Annotation, InvocationHandler, Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final T annotation;
|
||||
private final Class<T> type;
|
||||
private final Map<String, Object> attributes;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param annotation 注解
|
||||
*/
|
||||
public AnnotationProxy(T annotation) {
|
||||
this.annotation = annotation;
|
||||
//noinspection unchecked
|
||||
this.type = (Class<T>) annotation.annotationType();
|
||||
this.attributes = initAttributes();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Class<? extends Annotation> annotationType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
|
||||
// 注解别名
|
||||
Alias alias = method.getAnnotation(Alias.class);
|
||||
if(null != alias){
|
||||
final String name = alias.value();
|
||||
if(StrUtil.isNotBlank(name)){
|
||||
if(false == attributes.containsKey(name)){
|
||||
throw new IllegalArgumentException(StrUtil.format("No method for alias: [{}]", name));
|
||||
}
|
||||
return attributes.get(name);
|
||||
}
|
||||
}
|
||||
|
||||
final Object value = attributes.get(method.getName());
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
return method.invoke(this, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化注解的属性<br>
|
||||
* 此方法预先调用所有注解的方法,将注解方法值缓存于attributes中
|
||||
*
|
||||
* @return 属性(方法结果)映射
|
||||
*/
|
||||
private Map<String, Object> initAttributes() {
|
||||
final Method[] methods = ReflectUtil.getMethods(this.type);
|
||||
final Map<String, Object> attributes = new HashMap<>(methods.length, 1);
|
||||
|
||||
for (Method method : methods) {
|
||||
// 跳过匿名内部类自动生成的方法
|
||||
if (method.isSynthetic()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
attributes.put(method.getName(), ReflectUtil.invoke(this.annotation, method));
|
||||
}
|
||||
|
||||
return attributes;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>注解合成器,用于处理一组给定的与{@link #getSource()}具有直接或间接联系的注解对象,
|
||||
* 并返回与原始注解对象具有不同属性的“合成”注解。
|
||||
*
|
||||
* <p>合成注解一般被用于处理类层级结果中具有直接或间接关联的注解对象,
|
||||
* 当实例被创建时,会获取到这些注解对象,并使用{@link SynthesizedAnnotationSelector}对类型相同的注解进行过滤,
|
||||
* 并最终得到类型不重复的有效注解对象。这些有效注解将被包装为{@link SynthesizedAnnotation},
|
||||
* 然后最终用于“合成”一个{@link SynthesizedAggregateAnnotation}。<br>
|
||||
* {@link SynthesizedAnnotationSelector}是合成注解生命周期中的第一个钩子,
|
||||
* 自定义选择器以拦截原始注解被扫描的过程。
|
||||
*
|
||||
* <p>当合成注解完成对待合成注解的扫描,并完成了必要属性的加载后,
|
||||
* 将会按顺序依次调用{@link SynthesizedAnnotationPostProcessor},
|
||||
* 注解后置处理器允许用于对完成注解的待合成注解进行二次调整,
|
||||
* 该钩子一般用于根据{@link Link}注解对属性进行调整。<br>
|
||||
* {@link SynthesizedAnnotationPostProcessor}是合成注解生命周期中的第二个钩子,
|
||||
* 自定义后置处理器以拦截原始在转为待合成注解后的初始化过程。
|
||||
*
|
||||
* <p>使用{@link #synthesize(Class)}用于获取“合成”后的注解,
|
||||
* 该注解对象的属性可能会与原始的对象属性不同。
|
||||
*
|
||||
* @author huangchengxing
|
||||
*/
|
||||
public interface AnnotationSynthesizer {
|
||||
|
||||
/**
|
||||
* 获取合成注解来源最初来源
|
||||
*
|
||||
* @return 合成注解来源最初来源
|
||||
*/
|
||||
Object getSource();
|
||||
|
||||
/**
|
||||
* 合成注解选择器
|
||||
*
|
||||
* @return 注解选择器
|
||||
*/
|
||||
SynthesizedAnnotationSelector getAnnotationSelector();
|
||||
|
||||
/**
|
||||
* 获取合成注解后置处理器
|
||||
*
|
||||
* @return 合成注解后置处理器
|
||||
*/
|
||||
Collection<SynthesizedAnnotationPostProcessor> getAnnotationPostProcessors();
|
||||
|
||||
/**
|
||||
* 获取已合成的注解
|
||||
*
|
||||
* @param annotationType 注解类型
|
||||
* @return 已合成的注解
|
||||
*/
|
||||
SynthesizedAnnotation getSynthesizedAnnotation(Class<?> annotationType);
|
||||
|
||||
/**
|
||||
* 获取全部的合成注解
|
||||
*
|
||||
* @return 合成注解
|
||||
*/
|
||||
Map<Class<? extends Annotation>, SynthesizedAnnotation> getAllSynthesizedAnnotation();
|
||||
|
||||
/**
|
||||
* 获取合成注解
|
||||
*
|
||||
* @param annotationType 注解类型
|
||||
* @param <T> 注解类型
|
||||
* @return 类型
|
||||
*/
|
||||
<T extends Annotation> T synthesize(Class<T> annotationType);
|
||||
|
||||
}
|
|
@ -0,0 +1,576 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.annotation.scanner.AnnotationScanner;
|
||||
import aiyh.utils.tool.cn.hutool.core.annotation.scanner.MetaAnnotationScanner;
|
||||
import aiyh.utils.tool.cn.hutool.core.annotation.scanner.MethodAnnotationScanner;
|
||||
import aiyh.utils.tool.cn.hutool.core.annotation.scanner.TypeAnnotationScanner;
|
||||
import aiyh.utils.tool.cn.hutool.core.collection.CollUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.exceptions.UtilException;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.Opt;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.func.Func1;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.func.LambdaUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.*;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import java.lang.invoke.SerializedLambda;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 注解工具类<br>
|
||||
* 快速获取注解对象、注解值等工具封装
|
||||
*
|
||||
* @author looly
|
||||
* @since 4.0.9
|
||||
*/
|
||||
public class AnnotationUtil {
|
||||
|
||||
/**
|
||||
* 元注解
|
||||
*/
|
||||
static final Set<Class<? extends Annotation>> META_ANNOTATIONS = CollUtil.newHashSet(Target.class, //
|
||||
Retention.class, //
|
||||
Inherited.class, //
|
||||
Documented.class, //
|
||||
SuppressWarnings.class, //
|
||||
Override.class, //
|
||||
Deprecated.class//
|
||||
);
|
||||
|
||||
/**
|
||||
* 是否为Jdk自带的元注解。<br>
|
||||
* 包括:
|
||||
* <ul>
|
||||
* <li>{@link Target}</li>
|
||||
* <li>{@link Retention}</li>
|
||||
* <li>{@link Inherited}</li>
|
||||
* <li>{@link Documented}</li>
|
||||
* <li>{@link SuppressWarnings}</li>
|
||||
* <li>{@link Override}</li>
|
||||
* <li>{@link Deprecated}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param annotationType 注解类型
|
||||
* @return 是否为Jdk自带的元注解
|
||||
*/
|
||||
public static boolean isJdkMetaAnnotation(Class<? extends Annotation> annotationType) {
|
||||
return META_ANNOTATIONS.contains(annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否不为Jdk自带的元注解。<br>
|
||||
* 包括:
|
||||
* <ul>
|
||||
* <li>{@link Target}</li>
|
||||
* <li>{@link Retention}</li>
|
||||
* <li>{@link Inherited}</li>
|
||||
* <li>{@link Documented}</li>
|
||||
* <li>{@link SuppressWarnings}</li>
|
||||
* <li>{@link Override}</li>
|
||||
* <li>{@link Deprecated}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param annotationType 注解类型
|
||||
* @return 是否为Jdk自带的元注解
|
||||
*/
|
||||
public static boolean isNotJdkMateAnnotation(Class<? extends Annotation> annotationType) {
|
||||
return false == isJdkMetaAnnotation(annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将指定的被注解的元素转换为组合注解元素
|
||||
*
|
||||
* @param annotationEle 注解元素
|
||||
* @return 组合注解元素
|
||||
*/
|
||||
public static CombinationAnnotationElement toCombination(AnnotatedElement annotationEle) {
|
||||
if (annotationEle instanceof CombinationAnnotationElement) {
|
||||
return (CombinationAnnotationElement) annotationEle;
|
||||
}
|
||||
return new CombinationAnnotationElement(annotationEle);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定注解
|
||||
*
|
||||
* @param annotationEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @param isToCombination 是否为转换为组合注解,组合注解可以递归获取注解的注解
|
||||
* @return 注解对象
|
||||
*/
|
||||
public static Annotation[] getAnnotations(AnnotatedElement annotationEle, boolean isToCombination) {
|
||||
return getAnnotations(annotationEle, isToCombination, (Predicate<Annotation>) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组合注解
|
||||
*
|
||||
* @param <T> 注解类型
|
||||
* @param annotationEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @param annotationType 限定的
|
||||
* @return 注解对象数组
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public static <T> T[] getCombinationAnnotations(AnnotatedElement annotationEle, Class<T> annotationType) {
|
||||
return getAnnotations(annotationEle, true, annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定注解
|
||||
*
|
||||
* @param <T> 注解类型
|
||||
* @param annotationEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @param isToCombination 是否为转换为组合注解,组合注解可以递归获取注解的注解
|
||||
* @param annotationType 限定的
|
||||
* @return 注解对象数组
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public static <T> T[] getAnnotations(AnnotatedElement annotationEle, boolean isToCombination, Class<T> annotationType) {
|
||||
final Annotation[] annotations = getAnnotations(annotationEle, isToCombination,
|
||||
(annotation -> null == annotationType || annotationType.isAssignableFrom(annotation.getClass())));
|
||||
|
||||
final T[] result = ArrayUtil.newArray(annotationType, annotations.length);
|
||||
for (int i = 0; i < annotations.length; i++) {
|
||||
//noinspection unchecked
|
||||
result[i] = (T) annotations[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定注解
|
||||
*
|
||||
* @param annotationEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @param isToCombination 是否为转换为组合注解,组合注解可以递归获取注解的注解
|
||||
* @param predicate 过滤器,{@link Predicate#test(Object)}返回{@code true}保留,否则不保留
|
||||
* @return 注解对象,如果提供的{@link AnnotatedElement}为{@code null},返回{@code null}
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public static Annotation[] getAnnotations(AnnotatedElement annotationEle, boolean isToCombination, Predicate<Annotation> predicate) {
|
||||
if (null == annotationEle) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isToCombination) {
|
||||
if (null == predicate) {
|
||||
return toCombination(annotationEle).getAnnotations();
|
||||
}
|
||||
return CombinationAnnotationElement.of(annotationEle, predicate).getAnnotations();
|
||||
}
|
||||
|
||||
final Annotation[] result = annotationEle.getAnnotations();
|
||||
if (null == predicate) {
|
||||
return result;
|
||||
}
|
||||
return ArrayUtil.filter(result, predicate::test);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定注解
|
||||
*
|
||||
* @param <A> 注解类型
|
||||
* @param annotationEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @param annotationType 注解类型
|
||||
* @return 注解对象
|
||||
*/
|
||||
public static <A extends Annotation> A getAnnotation(AnnotatedElement annotationEle, Class<A> annotationType) {
|
||||
return (null == annotationEle) ? null : toCombination(annotationEle).getAnnotation(annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否包含指定注解指定注解
|
||||
*
|
||||
* @param annotationEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @param annotationType 注解类型
|
||||
* @return 是否包含指定注解
|
||||
* @since 5.4.2
|
||||
*/
|
||||
public static boolean hasAnnotation(AnnotatedElement annotationEle, Class<? extends Annotation> annotationType) {
|
||||
return null != getAnnotation(annotationEle, annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定注解默认值<br>
|
||||
* 如果无指定的属性方法返回null
|
||||
*
|
||||
* @param <T> 注解值类型
|
||||
* @param annotationEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @param annotationType 注解类型
|
||||
* @return 注解对象
|
||||
* @throws UtilException 调用注解中的方法时执行异常
|
||||
*/
|
||||
public static <T> T getAnnotationValue(AnnotatedElement annotationEle, Class<? extends Annotation> annotationType) throws UtilException {
|
||||
return getAnnotationValue(annotationEle, annotationType, "value");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定注解属性的值<br>
|
||||
* 如果无指定的属性方法返回null
|
||||
*
|
||||
* @param <T> 注解值类型
|
||||
* @param annotationEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @param annotationType 注解类型
|
||||
* @param propertyName 属性名,例如注解中定义了name()方法,则 此处传入name
|
||||
* @return 注解对象
|
||||
* @throws UtilException 调用注解中的方法时执行异常
|
||||
*/
|
||||
public static <T> T getAnnotationValue(AnnotatedElement annotationEle, Class<? extends Annotation> annotationType, String propertyName) throws UtilException {
|
||||
final Annotation annotation = getAnnotation(annotationEle, annotationType);
|
||||
if (null == annotation) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Method method = ReflectUtil.getMethodOfObj(annotation, propertyName);
|
||||
if (null == method) {
|
||||
return null;
|
||||
}
|
||||
return ReflectUtil.invoke(annotation, method);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定注解属性的值<br>
|
||||
* 如果无指定的属性方法返回null
|
||||
*
|
||||
* @param <A> 注解类型
|
||||
* @param <R> 注解类型值
|
||||
* @param annotationEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @param propertyName 属性名,例如注解中定义了name()方法,则 此处传入name
|
||||
* @return 注解对象
|
||||
* @throws UtilException 调用注解中的方法时执行异常
|
||||
* @since 5.8.9
|
||||
*/
|
||||
public static <A extends Annotation, R> R getAnnotationValue(AnnotatedElement annotationEle, Func1<A, R> propertyName) {
|
||||
if (propertyName == null) {
|
||||
return null;
|
||||
} else {
|
||||
final SerializedLambda lambda = LambdaUtil.resolve(propertyName);
|
||||
final String instantiatedMethodType = lambda.getInstantiatedMethodType();
|
||||
final Class<A> annotationClass = ClassUtil.loadClass(StrUtil.sub(instantiatedMethodType, 2, StrUtil.indexOf(instantiatedMethodType, ';')));
|
||||
return getAnnotationValue(annotationEle, annotationClass, lambda.getImplMethodName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定注解中所有属性值<br>
|
||||
* 如果无指定的属性方法返回null
|
||||
*
|
||||
* @param annotationEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @param annotationType 注解类型
|
||||
* @return 注解对象
|
||||
* @throws UtilException 调用注解中的方法时执行异常
|
||||
*/
|
||||
public static Map<String, Object> getAnnotationValueMap(AnnotatedElement annotationEle, Class<? extends Annotation> annotationType) throws UtilException {
|
||||
final Annotation annotation = getAnnotation(annotationEle, annotationType);
|
||||
if (null == annotation) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Method[] methods = ReflectUtil.getMethods(annotationType, t -> {
|
||||
if (ArrayUtil.isEmpty(t.getParameterTypes())) {
|
||||
// 只读取无参方法
|
||||
final String name = t.getName();
|
||||
// 跳过自有的几个方法
|
||||
return (false == "hashCode".equals(name)) //
|
||||
&& (false == "toString".equals(name)) //
|
||||
&& (false == "annotationType".equals(name));
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
final HashMap<String, Object> result = new HashMap<>(methods.length, 1);
|
||||
for (Method method : methods) {
|
||||
result.put(method.getName(), ReflectUtil.invoke(annotation, method));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取注解类的保留时间,可选值 SOURCE(源码时),CLASS(编译时),RUNTIME(运行时),默认为 CLASS
|
||||
*
|
||||
* @param annotationType 注解类
|
||||
* @return 保留时间枚举
|
||||
*/
|
||||
public static RetentionPolicy getRetentionPolicy(Class<? extends Annotation> annotationType) {
|
||||
final Retention retention = annotationType.getAnnotation(Retention.class);
|
||||
if (null == retention) {
|
||||
return RetentionPolicy.CLASS;
|
||||
}
|
||||
return retention.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取注解类可以用来修饰哪些程序元素,如 TYPE, METHOD, CONSTRUCTOR, FIELD, PARAMETER 等
|
||||
*
|
||||
* @param annotationType 注解类
|
||||
* @return 注解修饰的程序元素数组
|
||||
*/
|
||||
public static ElementType[] getTargetType(Class<? extends Annotation> annotationType) {
|
||||
final Target target = annotationType.getAnnotation(Target.class);
|
||||
if (null == target) {
|
||||
return new ElementType[]{ElementType.TYPE, //
|
||||
ElementType.FIELD, //
|
||||
ElementType.METHOD, //
|
||||
ElementType.PARAMETER, //
|
||||
ElementType.CONSTRUCTOR, //
|
||||
ElementType.LOCAL_VARIABLE, //
|
||||
ElementType.ANNOTATION_TYPE, //
|
||||
ElementType.PACKAGE//
|
||||
};
|
||||
}
|
||||
return target.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否会保存到 Javadoc 文档中
|
||||
*
|
||||
* @param annotationType 注解类
|
||||
* @return 是否会保存到 Javadoc 文档中
|
||||
*/
|
||||
public static boolean isDocumented(Class<? extends Annotation> annotationType) {
|
||||
return annotationType.isAnnotationPresent(Documented.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否可以被继承,默认为 false
|
||||
*
|
||||
* @param annotationType 注解类
|
||||
* @return 是否会保存到 Javadoc 文档中
|
||||
*/
|
||||
public static boolean isInherited(Class<? extends Annotation> annotationType) {
|
||||
return annotationType.isAnnotationPresent(Inherited.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描注解类,以及注解类的{@link Class}层级结构中的注解,将返回除了{@link #META_ANNOTATIONS}中指定的JDK默认注解外,
|
||||
* 按元注解对象与{@code annotationType}的距离和{@link Class#getAnnotations()}顺序排序的注解对象集合
|
||||
*
|
||||
* <p>比如:<br>
|
||||
* 若{@code annotationType}为 A,且A存在元注解B,B又存在元注解C和D,则有:
|
||||
* <pre>
|
||||
* |-> C.class [@a, @b]
|
||||
* A.class -> B.class [@a] -|
|
||||
* |-> D.class [@a, @c]
|
||||
* </pre>
|
||||
* 扫描A,则该方法最终将返回 {@code [@a, @a, @b, @a, @c]}
|
||||
*
|
||||
* @param annotationType 注解类
|
||||
* @return 注解对象集合
|
||||
* @see MetaAnnotationScanner
|
||||
*/
|
||||
public static List<Annotation> scanMetaAnnotation(Class<? extends Annotation> annotationType) {
|
||||
return AnnotationScanner.DIRECTLY_AND_META_ANNOTATION.getAnnotationsIfSupport(annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>扫描类以及类的{@link Class}层级结构中的注解,将返回除了{@link #META_ANNOTATIONS}中指定的JDK默认元注解外,
|
||||
* 全部类/接口的{@link Class#getAnnotations()}方法返回的注解对象。<br>
|
||||
* 层级结构将按广度优先递归,遵循规则如下:
|
||||
* <ul>
|
||||
* <li>同一层级中,优先处理父类,然后再处理父接口;</li>
|
||||
* <li>同一个接口在不同层级出现,优先选择层级距离{@code targetClass}更近的接口;</li>
|
||||
* <li>同一个接口在相同层级出现,优先选择其子类/子接口被先解析的那个;</li>
|
||||
* </ul>
|
||||
* 注解根据其声明类/接口被扫描的顺序排序,若注解都在同一个{@link Class}中被声明,则还会遵循{@link Class#getAnnotations()}的顺序。
|
||||
*
|
||||
* <p>比如:<br>
|
||||
* 若{@code targetClass}为{@code A.class},且{@code A.class}存在父类{@code B.class}、父接口{@code C.class},
|
||||
* 三个类的注解声明情况如下:
|
||||
* <pre>
|
||||
* |-> B.class [@a, @b]
|
||||
* A.class [@a] -|
|
||||
* |-> C.class [@a, @c]
|
||||
* </pre>
|
||||
* 则该方法最终将返回 {@code [@a, @a, @b, @a, @c]}
|
||||
*
|
||||
* @param targetClass 类
|
||||
* @return 注解对象集合
|
||||
* @see TypeAnnotationScanner
|
||||
*/
|
||||
public static List<Annotation> scanClass(Class<?> targetClass) {
|
||||
return AnnotationScanner.TYPE_HIERARCHY.getAnnotationsIfSupport(targetClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>扫描方法,以及该方法所在类的{@link Class}层级结构中的具有相同方法签名的方法,
|
||||
* 将返回除了{@link #META_ANNOTATIONS}中指定的JDK默认元注解外,
|
||||
* 全部匹配方法上{@link Method#getAnnotations()}方法返回的注解对象。<br>
|
||||
* 方法所在类的层级结构将按广度优先递归,遵循规则如下:
|
||||
* <ul>
|
||||
* <li>同一层级中,优先处理父类,然后再处理父接口;</li>
|
||||
* <li>同一个接口在不同层级出现,优先选择层级距离{@code targetClass}更近的接口;</li>
|
||||
* <li>同一个接口在相同层级出现,优先选择其子类/子接口被先解析的那个;</li>
|
||||
* </ul>
|
||||
* 方法上的注解根据方法的声明类/接口被扫描的顺序排序,若注解都在同一个类的同一个方法中被声明,则还会遵循{@link Method#getAnnotations()}的顺序。
|
||||
*
|
||||
* <p>比如:<br>
|
||||
* 若方法X声明于{@code A.class},且重载/重写自父类{@code B.class},并且父类中的方法X由重写至其实现的接口{@code C.class},
|
||||
* 三个类的注解声明情况如下:
|
||||
* <pre>
|
||||
* A#X()[@a] -> B#X()[@b] -> C#X()[@c]
|
||||
* </pre>
|
||||
* 则该方法最终将返回 {@code [@a, @b, @c]}
|
||||
*
|
||||
* @param method 方法
|
||||
* @return 注解对象集合
|
||||
* @see MethodAnnotationScanner
|
||||
*/
|
||||
public static List<Annotation> scanMethod(Method method) {
|
||||
return AnnotationScanner.TYPE_HIERARCHY.getAnnotationsIfSupport(method);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置新的注解的属性(字段)值
|
||||
*
|
||||
* @param annotation 注解对象
|
||||
* @param annotationField 注解属性(字段)名称
|
||||
* @param value 要更新的属性值
|
||||
* @since 5.5.2
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public static void setValue(Annotation annotation, String annotationField, Object value) {
|
||||
final Map memberValues = (Map) ReflectUtil.getFieldValue(Proxy.getInvocationHandler(annotation), "memberValues");
|
||||
memberValues.put(annotationField, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 该注解对象是否为通过代理类生成的合成注解
|
||||
*
|
||||
* @param annotation 注解对象
|
||||
* @return 是否
|
||||
* @see SynthesizedAnnotationProxy#isProxyAnnotation(Class)
|
||||
*/
|
||||
public static boolean isSynthesizedAnnotation(Annotation annotation) {
|
||||
return SynthesizedAnnotationProxy.isProxyAnnotation(annotation.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取别名支持后的注解
|
||||
*
|
||||
* @param annotationEle 被注解的类
|
||||
* @param annotationType 注解类型Class
|
||||
* @param <T> 注解类型
|
||||
* @return 别名支持后的注解
|
||||
* @since 5.7.23
|
||||
*/
|
||||
public static <T extends Annotation> T getAnnotationAlias(AnnotatedElement annotationEle, Class<T> annotationType) {
|
||||
final T annotation = getAnnotation(annotationEle, annotationType);
|
||||
return aggregatingFromAnnotation(annotation).synthesize(annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将指定注解实例与其元注解转为合成注解
|
||||
*
|
||||
* @param annotationType 注解类
|
||||
* @param annotations 注解对象
|
||||
* @param <T> 注解类型
|
||||
* @return 合成注解
|
||||
* @see SynthesizedAggregateAnnotation
|
||||
*/
|
||||
public static <T extends Annotation> T getSynthesizedAnnotation(Class<T> annotationType, Annotation... annotations) {
|
||||
// TODO 缓存合成注解信息,避免重复解析
|
||||
return Opt.ofNullable(annotations)
|
||||
.filter(ArrayUtil::isNotEmpty)
|
||||
.map(AnnotationUtil::aggregatingFromAnnotationWithMeta)
|
||||
.map(a -> a.synthesize(annotationType))
|
||||
.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>获取元素上距离指定元素最接近的合成注解
|
||||
* <ul>
|
||||
* <li>若元素是类,则递归解析全部父类和全部父接口上的注解;</li>
|
||||
* <li>若元素是方法、属性或注解,则只解析其直接声明的注解;</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>注解合成规则如下:
|
||||
* 若{@code AnnotatedEle}按顺序从上到下声明了A,B,C三个注解,且三注解存在元注解如下:
|
||||
* <pre>
|
||||
* A -> M3
|
||||
* B -> M1 -> M2 -> M3
|
||||
* C -> M2 -> M3
|
||||
* </pre>
|
||||
* 此时入参{@code annotationType}类型为{@code M2},则最终将优先返回基于根注解B合成的合成注解
|
||||
*
|
||||
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @param annotationType 注解类
|
||||
* @param <T> 注解类型
|
||||
* @return 合成注解
|
||||
* @see SynthesizedAggregateAnnotation
|
||||
*/
|
||||
public static <T extends Annotation> T getSynthesizedAnnotation(AnnotatedElement annotatedEle, Class<T> annotationType) {
|
||||
T target = annotatedEle.getAnnotation(annotationType);
|
||||
if (ObjectUtil.isNotNull(target)) {
|
||||
return target;
|
||||
}
|
||||
return AnnotationScanner.DIRECTLY
|
||||
.getAnnotationsIfSupport(annotatedEle).stream()
|
||||
.map(annotation -> getSynthesizedAnnotation(annotationType, annotation))
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取元素上所有指定注解
|
||||
* <ul>
|
||||
* <li>若元素是类,则递归解析全部父类和全部父接口上的注解;</li>
|
||||
* <li>若元素是方法、属性或注解,则只解析其直接声明的注解;</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>注解合成规则如下:
|
||||
* 若{@code AnnotatedEle}按顺序从上到下声明了A,B,C三个注解,且三注解存在元注解如下:
|
||||
* <pre>
|
||||
* A -> M1 -> M2
|
||||
* B -> M3 -> M1 -> M2
|
||||
* C -> M2
|
||||
* </pre>
|
||||
* 此时入参{@code annotationType}类型为{@code M1},则最终将返回基于根注解A与根注解B合成的合成注解。
|
||||
*
|
||||
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @param annotationType 注解类
|
||||
* @param <T> 注解类型
|
||||
* @return 合成注解
|
||||
* @see SynthesizedAggregateAnnotation
|
||||
*/
|
||||
public static <T extends Annotation> List<T> getAllSynthesizedAnnotations(AnnotatedElement annotatedEle, Class<T> annotationType) {
|
||||
return AnnotationScanner.DIRECTLY
|
||||
.getAnnotationsIfSupport(annotatedEle).stream()
|
||||
.map(annotation -> getSynthesizedAnnotation(annotationType, annotation))
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 对指定注解对象进行聚合
|
||||
*
|
||||
* @param annotations 注解对象
|
||||
* @return 聚合注解
|
||||
*/
|
||||
public static SynthesizedAggregateAnnotation aggregatingFromAnnotation(Annotation... annotations) {
|
||||
return new GenericSynthesizedAggregateAnnotation(Arrays.asList(annotations), AnnotationScanner.NOTHING);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对指定注解对象及其元注解进行聚合
|
||||
*
|
||||
* @param annotations 注解对象
|
||||
* @return 聚合注解
|
||||
*/
|
||||
public static SynthesizedAggregateAnnotation aggregatingFromAnnotationWithMeta(Annotation... annotations) {
|
||||
return new GenericSynthesizedAggregateAnnotation(Arrays.asList(annotations), AnnotationScanner.DIRECTLY_AND_META_ANNOTATION);
|
||||
}
|
||||
|
||||
/**
|
||||
* 方法是否为注解属性方法。 <br>
|
||||
* 方法无参数,且有返回值的方法认为是注解属性的方法。
|
||||
*
|
||||
* @param method 方法
|
||||
*/
|
||||
static boolean isAttributeMethod(Method method) {
|
||||
return method.getParameterCount() == 0 && method.getReturnType() != void.class;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ReflectUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* {@link AnnotationAttribute}的基本实现
|
||||
*
|
||||
* @author huangchengxing
|
||||
*/
|
||||
public class CacheableAnnotationAttribute implements AnnotationAttribute {
|
||||
|
||||
private boolean valueInvoked;
|
||||
private Object value;
|
||||
|
||||
private boolean defaultValueInvoked;
|
||||
private Object defaultValue;
|
||||
|
||||
private final Annotation annotation;
|
||||
private final Method attribute;
|
||||
|
||||
public CacheableAnnotationAttribute(Annotation annotation, Method attribute) {
|
||||
Assert.notNull(annotation, "annotation must not null");
|
||||
Assert.notNull(attribute, "attribute must not null");
|
||||
this.annotation = annotation;
|
||||
this.attribute = attribute;
|
||||
this.valueInvoked = false;
|
||||
this.defaultValueInvoked = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Annotation getAnnotation() {
|
||||
return this.annotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getAttribute() {
|
||||
return this.attribute;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
if (!valueInvoked) {
|
||||
valueInvoked = true;
|
||||
value = ReflectUtil.invoke(annotation, attribute);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValueEquivalentToDefaultValue() {
|
||||
if (!defaultValueInvoked) {
|
||||
defaultValue = attribute.getDefaultValue();
|
||||
defaultValueInvoked = true;
|
||||
}
|
||||
return ObjectUtil.equals(getValue(), defaultValue);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||
import aiyh.utils.tool.cn.hutool.core.map.multi.RowKeyTable;
|
||||
import aiyh.utils.tool.cn.hutool.core.map.multi.Table;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* <p>带缓存功能的{@link SynthesizedAnnotationAttributeProcessor}实现,
|
||||
* 构建时需要传入比较器,获取属性值时将根据比较器对合成注解进行排序,
|
||||
* 然后选择具有所需属性的,排序最靠前的注解用于获取属性值
|
||||
*
|
||||
* <p>通过该处理器获取合成注解属性值时会出现隐式别名,
|
||||
* 即子注解和元注解中同时存在类型和名称皆相同的属性时,元注解中属性总是会被该属性覆盖,
|
||||
* 并且该覆盖关系并不会通过{@link Alias}或{@link Link}被传递到关联的属性中。
|
||||
*
|
||||
* @author huangchengxing
|
||||
*/
|
||||
public class CacheableSynthesizedAnnotationAttributeProcessor implements SynthesizedAnnotationAttributeProcessor {
|
||||
|
||||
private final Table<String, Class<?>, Object> valueCaches = new RowKeyTable<>();
|
||||
private final Comparator<Hierarchical> annotationComparator;
|
||||
|
||||
/**
|
||||
* 创建一个带缓存的注解值选择器
|
||||
*
|
||||
* @param annotationComparator 注解比较器,排序更靠前的注解将被优先用于获取值
|
||||
*/
|
||||
public CacheableSynthesizedAnnotationAttributeProcessor(Comparator<Hierarchical> annotationComparator) {
|
||||
Assert.notNull(annotationComparator, "annotationComparator must not null");
|
||||
this.annotationComparator = annotationComparator;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个带缓存的注解值选择器,
|
||||
* 默认按{@link SynthesizedAnnotation#getVerticalDistance()}和{@link SynthesizedAnnotation#getHorizontalDistance()}排序,
|
||||
* 越靠前的越优先被取值。
|
||||
*/
|
||||
public CacheableSynthesizedAnnotationAttributeProcessor() {
|
||||
this(Hierarchical.DEFAULT_HIERARCHICAL_COMPARATOR);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T getAttributeValue(String attributeName, Class<T> attributeType, Collection<? extends SynthesizedAnnotation> synthesizedAnnotations) {
|
||||
Object value = valueCaches.get(attributeName, attributeType);
|
||||
// 此处理论上不可能出现缓存值为nul的情况
|
||||
if (ObjectUtil.isNotNull(value)) {
|
||||
return (T)value;
|
||||
}
|
||||
value = synthesizedAnnotations.stream()
|
||||
.filter(ma -> ma.hasAttribute(attributeName, attributeType))
|
||||
.min(annotationComparator)
|
||||
.map(ma -> ma.getAttributeValue(attributeName))
|
||||
.orElse(null);
|
||||
valueCaches.put(attributeName, attributeType, value);
|
||||
return (T)value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.map.TableMap;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* 组合注解 对JDK的原生注解机制做一个增强,支持类似Spring的组合注解。<br>
|
||||
* 核心实现使用了递归获取指定元素上的注解以及注解的注解,以实现复合注解的获取。
|
||||
*
|
||||
* @author Succy, Looly
|
||||
* @since 4.0.9
|
||||
**/
|
||||
|
||||
public class CombinationAnnotationElement implements AnnotatedElement, Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 创建CombinationAnnotationElement
|
||||
*
|
||||
* @param element 需要解析注解的元素:可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @param predicate 过滤器,{@link Predicate#test(Object)}返回{@code true}保留,否则不保留
|
||||
* @return CombinationAnnotationElement
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public static CombinationAnnotationElement of(AnnotatedElement element, Predicate<Annotation> predicate) {
|
||||
return new CombinationAnnotationElement(element, predicate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注解类型与注解对象对应表
|
||||
*/
|
||||
private Map<Class<? extends Annotation>, Annotation> annotationMap;
|
||||
/**
|
||||
* 直接注解类型与注解对象对应表
|
||||
*/
|
||||
private Map<Class<? extends Annotation>, Annotation> declaredAnnotationMap;
|
||||
/**
|
||||
* 过滤器
|
||||
*/
|
||||
private final Predicate<Annotation> predicate;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param element 需要解析注解的元素:可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
*/
|
||||
public CombinationAnnotationElement(AnnotatedElement element) {
|
||||
this(element, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param element 需要解析注解的元素:可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @param predicate 过滤器,{@link Predicate#test(Object)}返回{@code true}保留,否则不保留
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public CombinationAnnotationElement(AnnotatedElement element, Predicate<Annotation> predicate) {
|
||||
this.predicate = predicate;
|
||||
init(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
|
||||
return annotationMap.containsKey(annotationClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
|
||||
Annotation annotation = annotationMap.get(annotationClass);
|
||||
return (annotation == null) ? null : (T) annotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Annotation[] getAnnotations() {
|
||||
final Collection<Annotation> annotations = this.annotationMap.values();
|
||||
return annotations.toArray(new Annotation[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Annotation[] getDeclaredAnnotations() {
|
||||
final Collection<Annotation> annotations = this.declaredAnnotationMap.values();
|
||||
return annotations.toArray(new Annotation[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*
|
||||
* @param element 元素
|
||||
*/
|
||||
private void init(AnnotatedElement element) {
|
||||
final Annotation[] declaredAnnotations = element.getDeclaredAnnotations();
|
||||
this.declaredAnnotationMap = new TableMap<>();
|
||||
parseDeclared(declaredAnnotations);
|
||||
|
||||
final Annotation[] annotations = element.getAnnotations();
|
||||
if (Arrays.equals(declaredAnnotations, annotations)) {
|
||||
this.annotationMap = this.declaredAnnotationMap;
|
||||
} else {
|
||||
this.annotationMap = new TableMap<>();
|
||||
parse(annotations);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 进行递归解析注解,直到全部都是元注解为止
|
||||
*
|
||||
* @param annotations Class, Method, Field等
|
||||
*/
|
||||
private void parseDeclared(Annotation[] annotations) {
|
||||
Class<? extends Annotation> annotationType;
|
||||
// 直接注解
|
||||
for (Annotation annotation : annotations) {
|
||||
annotationType = annotation.annotationType();
|
||||
// issue#I5FQGW@Gitee:跳过元注解和已经处理过的注解,防止递归调用
|
||||
if (AnnotationUtil.isNotJdkMateAnnotation(annotationType)
|
||||
&& false == declaredAnnotationMap.containsKey(annotationType)) {
|
||||
if(test(annotation)){
|
||||
declaredAnnotationMap.put(annotationType, annotation);
|
||||
}
|
||||
// 测试不通过的注解,不影响继续递归
|
||||
parseDeclared(annotationType.getDeclaredAnnotations());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 进行递归解析注解,直到全部都是元注解为止
|
||||
*
|
||||
* @param annotations Class, Method, Field等
|
||||
*/
|
||||
private void parse(Annotation[] annotations) {
|
||||
Class<? extends Annotation> annotationType;
|
||||
for (Annotation annotation : annotations) {
|
||||
annotationType = annotation.annotationType();
|
||||
// issue#I5FQGW@Gitee:跳过元注解和已经处理过的注解,防止递归调用
|
||||
if (AnnotationUtil.isNotJdkMateAnnotation(annotationType)
|
||||
&& false == declaredAnnotationMap.containsKey(annotationType)) {
|
||||
if(test(annotation)){
|
||||
annotationMap.put(annotationType, annotation);
|
||||
}
|
||||
// 测试不通过的注解,不影响继续递归
|
||||
parse(annotationType.getAnnotations());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查给定的注解是否符合过滤条件
|
||||
*
|
||||
* @param annotation 注解对象
|
||||
* @return 是否符合条件
|
||||
*/
|
||||
private boolean test(Annotation annotation) {
|
||||
return null == this.predicate || this.predicate.test(annotation);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* <p>{@link Link}的子注解。表示“原始属性”将强制作为“关联属性”的别名。效果等同于在“原始属性”上添加{@link Alias}注解,
|
||||
* 任何情况下,获取“关联属性”的值都将直接返回“原始属性”的值
|
||||
* <b>注意,该注解与{@link Link}、{@link AliasFor}或{@link MirrorFor}一起使用时,将只有被声明在最上面的注解会生效</b>
|
||||
*
|
||||
* @author huangchengxing
|
||||
* @see Link
|
||||
* @see RelationType#FORCE_ALIAS_FOR
|
||||
*/
|
||||
@Link(type = RelationType.FORCE_ALIAS_FOR)
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
|
||||
public @interface ForceAliasFor {
|
||||
|
||||
/**
|
||||
* 产生关联的注解类型,当不指定时,默认指注释的属性所在的类
|
||||
*
|
||||
* @return 关联注解类型
|
||||
*/
|
||||
@Link(annotation = Link.class, attribute = "annotation", type = RelationType.FORCE_ALIAS_FOR)
|
||||
Class<? extends Annotation> annotation() default Annotation.class;
|
||||
|
||||
/**
|
||||
* {@link #annotation()}指定注解中关联的属性
|
||||
*
|
||||
* @return 关联的属性
|
||||
*/
|
||||
@Link(annotation = Link.class, attribute = "attribute", type = RelationType.FORCE_ALIAS_FOR)
|
||||
String attribute() default "";
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
/**
|
||||
* 表示一个被指定了强制别名的注解属性。
|
||||
* 当调用{@link #getValue()}时,总是返回{@link #linked}的值
|
||||
*
|
||||
* @author huangchengxing
|
||||
* @see AliasAnnotationPostProcessor
|
||||
* @see AliasLinkAnnotationPostProcessor
|
||||
* @see RelationType#ALIAS_FOR
|
||||
* @see RelationType#FORCE_ALIAS_FOR
|
||||
*/
|
||||
public class ForceAliasedAnnotationAttribute extends AbstractWrappedAnnotationAttribute {
|
||||
|
||||
protected ForceAliasedAnnotationAttribute(AnnotationAttribute origin, AnnotationAttribute linked) {
|
||||
super(origin, linked);
|
||||
}
|
||||
|
||||
/**
|
||||
* 总是返回{@link #linked}的{@link AnnotationAttribute#getValue()}的返回值
|
||||
*
|
||||
* @return {@link #linked}的{@link AnnotationAttribute#getValue()}的返回值
|
||||
*/
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return linked.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 总是返回{@link #linked}的{@link AnnotationAttribute#isValueEquivalentToDefaultValue()}的返回值
|
||||
*
|
||||
* @return {@link #linked}的{@link AnnotationAttribute#isValueEquivalentToDefaultValue()}的返回值
|
||||
*/
|
||||
@Override
|
||||
public boolean isValueEquivalentToDefaultValue() {
|
||||
return linked.isValueEquivalentToDefaultValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 总是返回{@link #linked}的{@link AnnotationAttribute#getAttributeType()}的返回值
|
||||
*
|
||||
* @return {@link #linked}的{@link AnnotationAttribute#getAttributeType()}的返回值
|
||||
*/
|
||||
@Override
|
||||
public Class<?> getAttributeType() {
|
||||
return linked.getAttributeType();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,318 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.annotation.scanner.AnnotationScanner;
|
||||
import aiyh.utils.tool.cn.hutool.core.annotation.scanner.MetaAnnotationScanner;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.Opt;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* {@link SynthesizedAggregateAnnotation}的基本实现,表示基于多个注解对象,
|
||||
* 或多个根注解对象与他们的多层元注解对象的聚合得到的注解。
|
||||
*
|
||||
* <p>假设现有注解A,若指定的{@link #annotationScanner}支持扫描注解A的元注解,
|
||||
* 且A上存在元注解B,B上存在元注解C,则对注解A进行解析,将得到包含根注解A,以及其元注解B、C在内的合成元注解聚合{@link GenericSynthesizedAggregateAnnotation}。
|
||||
* 从{@link AnnotatedElement}的角度来说,得到的合成注解是一个同时承载有ABC三个注解对象的被注解元素,
|
||||
* 因此通过调用{@link AnnotatedElement}的相关方法将返回对应符合语义的注解对象。
|
||||
*
|
||||
* <p>在扫描指定根注解及其元注解时,若在不同的层级出现了类型相同的注解实例,
|
||||
* 将会根据实例化时指定的{@link SynthesizedAnnotationSelector}选择最优的注解,
|
||||
* 完成对根注解及其元注解的扫描后,合成注解中每种类型的注解对象都将有且仅有一个。<br>
|
||||
* 默认情况下,将使用{@link SynthesizedAnnotationSelector#NEAREST_AND_OLDEST_PRIORITY}作为选择器,
|
||||
* 此时若出现扫描时得到了多个同类型的注解对象,有且仅有最接近根注解的注解对象会被作为有效注解。
|
||||
*
|
||||
* <p>当扫描的注解对象经过{@link SynthesizedAnnotationSelector}处理后,
|
||||
* 将会被转为{@link MetaAnnotation},并使用在实例化时指定的{@link AliasAnnotationPostProcessor}
|
||||
* 进行后置处理。<br>
|
||||
* 默认情况下,将注册以下后置处理器以对{@link Alias}与{@link Link}和其扩展注解提供支持:
|
||||
* <ul>
|
||||
* <li>{@link AliasAnnotationPostProcessor};</li>
|
||||
* <li>{@link MirrorLinkAnnotationPostProcessor};</li>
|
||||
* <li>{@link AliasLinkAnnotationPostProcessor};</li>
|
||||
* </ul>
|
||||
* 若用户需要自行扩展,则需要保证上述三个处理器被正确注入当前实例。
|
||||
*
|
||||
* <p>{@link GenericSynthesizedAggregateAnnotation}支持通过{@link #getAttributeValue(String, Class)},
|
||||
* 或通过{@link #synthesize(Class)}获得注解代理对象后获取指定类型的注解属性值,
|
||||
* 返回的属性值将根据合成注解中对应原始注解属性上的{@link Alias}与{@link Link}注解而有所变化。
|
||||
* 通过当前实例获取属性值时,将经过{@link SynthesizedAnnotationAttributeProcessor}的处理。<br>
|
||||
* 默认情况下,实例将会注册{@link CacheableSynthesizedAnnotationAttributeProcessor},
|
||||
* 该处理器将令元注解中与子注解类型与名称皆一致的属性被子注解的属性覆盖,并且缓存最终获取到的属性值。
|
||||
*
|
||||
* @author huangchengxing
|
||||
* @see AnnotationUtil
|
||||
* @see SynthesizedAnnotationProxy
|
||||
* @see SynthesizedAnnotationSelector
|
||||
* @see SynthesizedAnnotationAttributeProcessor
|
||||
* @see SynthesizedAnnotationPostProcessor
|
||||
* @see AnnotationSynthesizer
|
||||
* @see AnnotationScanner
|
||||
*/
|
||||
public class GenericSynthesizedAggregateAnnotation
|
||||
extends AbstractAnnotationSynthesizer<List<Annotation>>
|
||||
implements SynthesizedAggregateAnnotation {
|
||||
|
||||
/**
|
||||
* 根对象
|
||||
*/
|
||||
private final Object root;
|
||||
|
||||
/**
|
||||
* 距离根对象的垂直距离
|
||||
*/
|
||||
private final int verticalDistance;
|
||||
|
||||
/**
|
||||
* 距离根对象的水平距离
|
||||
*/
|
||||
private final int horizontalDistance;
|
||||
|
||||
/**
|
||||
* 合成注解属性处理器
|
||||
*/
|
||||
private final SynthesizedAnnotationAttributeProcessor attributeProcessor;
|
||||
|
||||
/**
|
||||
* 基于指定根注解,为其与其元注解的层级结构中的全部注解构造一个合成注解。
|
||||
* 当层级结构中出现了相同的注解对象时,将优先选择以距离根注解最近,且优先被扫描的注解对象,
|
||||
* 当获取值时,同样遵循该规则。
|
||||
*
|
||||
* @param source 源注解
|
||||
*/
|
||||
public GenericSynthesizedAggregateAnnotation(Annotation... source) {
|
||||
this(Arrays.asList(source), new MetaAnnotationScanner());
|
||||
}
|
||||
|
||||
/**
|
||||
* 基于指定根注解,为其层级结构中的全部注解构造一个合成注解。
|
||||
* 若扫描器支持对注解的层级结构进行扫描,则若层级结构中出现了相同的注解对象时,
|
||||
* 将优先选择以距离根注解最近,且优先被扫描的注解对象,并且当获取注解属性值时同样遵循该规则。
|
||||
*
|
||||
* @param source 源注解
|
||||
* @param annotationScanner 注解扫描器,该扫描器必须支持扫描注解类
|
||||
*/
|
||||
public GenericSynthesizedAggregateAnnotation(List<Annotation> source, AnnotationScanner annotationScanner) {
|
||||
this(
|
||||
source, SynthesizedAnnotationSelector.NEAREST_AND_OLDEST_PRIORITY,
|
||||
new CacheableSynthesizedAnnotationAttributeProcessor(),
|
||||
Arrays.asList(
|
||||
SynthesizedAnnotationPostProcessor.ALIAS_ANNOTATION_POST_PROCESSOR,
|
||||
SynthesizedAnnotationPostProcessor.MIRROR_LINK_ANNOTATION_POST_PROCESSOR,
|
||||
SynthesizedAnnotationPostProcessor.ALIAS_LINK_ANNOTATION_POST_PROCESSOR
|
||||
),
|
||||
annotationScanner
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 基于指定根注解,为其层级结构中的全部注解构造一个合成注解
|
||||
*
|
||||
* @param source 当前查找的注解对象
|
||||
* @param annotationSelector 合成注解选择器
|
||||
* @param attributeProcessor 注解属性处理器
|
||||
* @param annotationPostProcessors 注解后置处理器
|
||||
* @param annotationScanner 注解扫描器,该扫描器必须支持扫描注解类
|
||||
*/
|
||||
public GenericSynthesizedAggregateAnnotation(
|
||||
List<Annotation> source,
|
||||
SynthesizedAnnotationSelector annotationSelector,
|
||||
SynthesizedAnnotationAttributeProcessor attributeProcessor,
|
||||
Collection<SynthesizedAnnotationPostProcessor> annotationPostProcessors,
|
||||
AnnotationScanner annotationScanner) {
|
||||
this(
|
||||
null, 0, 0,
|
||||
source, annotationSelector, attributeProcessor, annotationPostProcessors, annotationScanner
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 基于指定根注解,为其层级结构中的全部注解构造一个合成注解
|
||||
*
|
||||
* @param root 根对象
|
||||
* @param verticalDistance 距离根对象的水平距离
|
||||
* @param horizontalDistance 距离根对象的垂直距离
|
||||
* @param source 当前查找的注解对象
|
||||
* @param annotationSelector 合成注解选择器
|
||||
* @param attributeProcessor 注解属性处理器
|
||||
* @param annotationPostProcessors 注解后置处理器
|
||||
* @param annotationScanner 注解扫描器,该扫描器必须支持扫描注解类
|
||||
*/
|
||||
GenericSynthesizedAggregateAnnotation(
|
||||
Object root, int verticalDistance, int horizontalDistance,
|
||||
List<Annotation> source,
|
||||
SynthesizedAnnotationSelector annotationSelector,
|
||||
SynthesizedAnnotationAttributeProcessor attributeProcessor,
|
||||
Collection<SynthesizedAnnotationPostProcessor> annotationPostProcessors,
|
||||
AnnotationScanner annotationScanner) {
|
||||
super(source, annotationSelector, annotationPostProcessors, annotationScanner);
|
||||
Assert.notNull(attributeProcessor, "attributeProcessor must not null");
|
||||
|
||||
this.root = ObjectUtil.defaultIfNull(root, this);
|
||||
this.verticalDistance = verticalDistance;
|
||||
this.horizontalDistance = horizontalDistance;
|
||||
this.attributeProcessor = attributeProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取根对象
|
||||
*
|
||||
* @return 根对象
|
||||
*/
|
||||
@Override
|
||||
public Object getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取与根对象的垂直距离
|
||||
*
|
||||
* @return 与根对象的垂直距离
|
||||
*/
|
||||
@Override
|
||||
public int getVerticalDistance() {
|
||||
return verticalDistance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取与根对象的水平距离
|
||||
*
|
||||
* @return 获取与根对象的水平距离
|
||||
*/
|
||||
@Override
|
||||
public int getHorizontalDistance() {
|
||||
return horizontalDistance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按广度优先扫描{@link #source}上的元注解
|
||||
*/
|
||||
@Override
|
||||
protected Map<Class<? extends Annotation>, SynthesizedAnnotation> loadAnnotations() {
|
||||
Map<Class<? extends Annotation>, SynthesizedAnnotation> annotationMap = new LinkedHashMap<>();
|
||||
|
||||
// 根注解默认水平坐标为0,根注解的元注解坐标从1开始
|
||||
for (int i = 0; i < source.size(); i++) {
|
||||
final Annotation sourceAnnotation = source.get(i);
|
||||
Assert.isFalse(AnnotationUtil.isSynthesizedAnnotation(sourceAnnotation), "source [{}] has been synthesized");
|
||||
annotationMap.put(sourceAnnotation.annotationType(), new MetaAnnotation(sourceAnnotation, sourceAnnotation, 0, i));
|
||||
Assert.isTrue(
|
||||
annotationScanner.support(sourceAnnotation.annotationType()),
|
||||
"annotation scanner [{}] cannot support scan [{}]",
|
||||
annotationScanner, sourceAnnotation.annotationType()
|
||||
);
|
||||
annotationScanner.scan(
|
||||
(index, annotation) -> {
|
||||
SynthesizedAnnotation oldAnnotation = annotationMap.get(annotation.annotationType());
|
||||
SynthesizedAnnotation newAnnotation = new MetaAnnotation(sourceAnnotation, annotation, index + 1, annotationMap.size());
|
||||
if (ObjectUtil.isNull(oldAnnotation)) {
|
||||
annotationMap.put(annotation.annotationType(), newAnnotation);
|
||||
} else {
|
||||
annotationMap.put(annotation.annotationType(), annotationSelector.choose(oldAnnotation, newAnnotation));
|
||||
}
|
||||
},
|
||||
sourceAnnotation.annotationType(), null
|
||||
);
|
||||
}
|
||||
return annotationMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取合成注解属性处理器
|
||||
*
|
||||
* @return 合成注解属性处理器
|
||||
*/
|
||||
@Override
|
||||
public SynthesizedAnnotationAttributeProcessor getAnnotationAttributeProcessor() {
|
||||
return this.attributeProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据指定的属性名与属性类型获取对应的属性值,若存在{@link Alias}则获取{@link Alias#value()}指定的别名属性的值
|
||||
* <p>当不同层级的注解之间存在同名同类型属性时,将优先获取更接近根注解的属性
|
||||
*
|
||||
* @param attributeName 属性名
|
||||
* @param attributeType 属性类型
|
||||
* @return 属性
|
||||
*/
|
||||
@Override
|
||||
public Object getAttributeValue(String attributeName, Class<?> attributeType) {
|
||||
return attributeProcessor.getAttributeValue(attributeName, attributeType, synthesizedAnnotationMap.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取合成注解中包含的指定注解
|
||||
*
|
||||
* @param annotationType 注解类型
|
||||
* @param <T> 注解类型
|
||||
* @return 注解对象
|
||||
*/
|
||||
@Override
|
||||
public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
|
||||
return Opt.ofNullable(annotationType)
|
||||
.map(synthesizedAnnotationMap::get)
|
||||
.map(SynthesizedAnnotation::getAnnotation)
|
||||
.map(annotationType::cast)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前合成注解中是否存在指定元注解
|
||||
*
|
||||
* @param annotationType 注解类型
|
||||
* @return 是否
|
||||
*/
|
||||
@Override
|
||||
public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
|
||||
return synthesizedAnnotationMap.containsKey(annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取合成注解中包含的全部注解
|
||||
*
|
||||
* @return 注解对象
|
||||
*/
|
||||
@Override
|
||||
public Annotation[] getAnnotations() {
|
||||
return synthesizedAnnotationMap.values().stream()
|
||||
.map(SynthesizedAnnotation::getAnnotation)
|
||||
.toArray(Annotation[]::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* 若合成注解在存在指定元注解,则使用动态代理生成一个对应的注解实例
|
||||
*
|
||||
* @param annotationType 注解类型
|
||||
* @return 合成注解对象
|
||||
* @see SynthesizedAnnotationProxy#create(Class, AnnotationAttributeValueProvider, SynthesizedAnnotation)
|
||||
*/
|
||||
@Override
|
||||
public <T extends Annotation> T synthesize(Class<T> annotationType, SynthesizedAnnotation annotation) {
|
||||
return SynthesizedAnnotationProxy.create(annotationType, this, annotation);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注解包装类,表示{@link #source}以及{@link #source}所属层级结构中的全部关联注解对象
|
||||
*
|
||||
* @author huangchengxing
|
||||
*/
|
||||
public static class MetaAnnotation extends GenericSynthesizedAnnotation<Annotation, Annotation> {
|
||||
|
||||
/**
|
||||
* 创建一个合成注解
|
||||
*
|
||||
* @param root 根对象
|
||||
* @param annotation 被合成的注解对象
|
||||
* @param verticalDistance 距离根对象的水平距离
|
||||
* @param horizontalDistance 距离根对象的垂直距离
|
||||
*/
|
||||
protected MetaAnnotation(Annotation root, Annotation annotation, int verticalDistance, int horizontalDistance) {
|
||||
super(root, annotation, verticalDistance, horizontalDistance);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,197 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.Opt;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ClassUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.UnaryOperator;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* {@link SynthesizedAnnotation}的基本实现
|
||||
*
|
||||
* @param <R> 根对象类型
|
||||
* @param <T> 注解类型
|
||||
* @author huangchengxing
|
||||
*/
|
||||
public class GenericSynthesizedAnnotation<R, T extends Annotation> implements SynthesizedAnnotation {
|
||||
|
||||
private final R root;
|
||||
private final T annotation;
|
||||
private final Map<String, AnnotationAttribute> attributeMethodCaches;
|
||||
private final int verticalDistance;
|
||||
private final int horizontalDistance;
|
||||
|
||||
/**
|
||||
* 创建一个合成注解
|
||||
*
|
||||
* @param root 根对象
|
||||
* @param annotation 被合成的注解对象
|
||||
* @param verticalDistance 距离根对象的水平距离
|
||||
* @param horizontalDistance 距离根对象的垂直距离
|
||||
*/
|
||||
protected GenericSynthesizedAnnotation(
|
||||
R root, T annotation, int verticalDistance, int horizontalDistance) {
|
||||
this.root = root;
|
||||
this.annotation = annotation;
|
||||
this.verticalDistance = verticalDistance;
|
||||
this.horizontalDistance = horizontalDistance;
|
||||
this.attributeMethodCaches = new HashMap<>();
|
||||
this.attributeMethodCaches.putAll(loadAttributeMethods());
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载注解属性
|
||||
*
|
||||
* @return 注解属性
|
||||
*/
|
||||
protected Map<String, AnnotationAttribute> loadAttributeMethods() {
|
||||
return Stream.of(ClassUtil.getDeclaredMethods(annotation.annotationType()))
|
||||
.filter(AnnotationUtil::isAttributeMethod)
|
||||
.collect(Collectors.toMap(Method::getName, method -> new CacheableAnnotationAttribute(annotation, method)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 元注解是否存在该属性
|
||||
*
|
||||
* @param attributeName 属性名
|
||||
* @return 是否存在该属性
|
||||
*/
|
||||
public boolean hasAttribute(String attributeName) {
|
||||
return attributeMethodCaches.containsKey(attributeName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 元注解是否存在该属性,且该属性的值类型是指定类型或其子类
|
||||
*
|
||||
* @param attributeName 属性名
|
||||
* @param returnType 返回值类型
|
||||
* @return 是否存在该属性
|
||||
*/
|
||||
@Override
|
||||
public boolean hasAttribute(String attributeName, Class<?> returnType) {
|
||||
return Opt.ofNullable(attributeMethodCaches.get(attributeName))
|
||||
.filter(method -> ClassUtil.isAssignable(returnType, method.getAttributeType()))
|
||||
.isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取该注解的全部属性
|
||||
*
|
||||
* @return 注解属性
|
||||
*/
|
||||
@Override
|
||||
public Map<String, AnnotationAttribute> getAttributes() {
|
||||
return this.attributeMethodCaches;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置属性值
|
||||
*
|
||||
* @param attributeName 属性名称
|
||||
* @param attribute 注解属性
|
||||
*/
|
||||
@Override
|
||||
public void setAttribute(String attributeName, AnnotationAttribute attribute) {
|
||||
attributeMethodCaches.put(attributeName, attribute);
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换属性值
|
||||
*
|
||||
* @param attributeName 属性名
|
||||
* @param operator 替换操作
|
||||
*/
|
||||
@Override
|
||||
public void replaceAttribute(String attributeName, UnaryOperator<AnnotationAttribute> operator) {
|
||||
AnnotationAttribute old = attributeMethodCaches.get(attributeName);
|
||||
if (ObjectUtil.isNotNull(old)) {
|
||||
attributeMethodCaches.put(attributeName, operator.apply(old));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取属性值
|
||||
*
|
||||
* @param attributeName 属性名
|
||||
* @return 属性值
|
||||
*/
|
||||
@Override
|
||||
public Object getAttributeValue(String attributeName) {
|
||||
return Opt.ofNullable(attributeMethodCaches.get(attributeName))
|
||||
.map(AnnotationAttribute::getValue)
|
||||
.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取该合成注解对应的根节点
|
||||
*
|
||||
* @return 合成注解对应的根节点
|
||||
*/
|
||||
@Override
|
||||
public R getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取被合成的注解对象
|
||||
*
|
||||
* @return 注解对象
|
||||
*/
|
||||
@Override
|
||||
public T getAnnotation() {
|
||||
return annotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取该合成注解与根对象的垂直距离。
|
||||
* 默认情况下,该距离即为当前注解与根对象之间相隔的层级数。
|
||||
*
|
||||
* @return 合成注解与根对象的垂直距离
|
||||
*/
|
||||
@Override
|
||||
public int getVerticalDistance() {
|
||||
return verticalDistance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取该合成注解与根对象的水平距离。
|
||||
* 默认情况下,该距离即为当前注解与根对象之间相隔的已经被扫描到的注解数。
|
||||
*
|
||||
* @return 合成注解与根对象的水平距离
|
||||
*/
|
||||
@Override
|
||||
public int getHorizontalDistance() {
|
||||
return horizontalDistance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取被合成的注解类型
|
||||
*
|
||||
* @return 被合成的注解类型
|
||||
*/
|
||||
@Override
|
||||
public Class<? extends Annotation> annotationType() {
|
||||
return annotation.annotationType();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取注解属性值
|
||||
*
|
||||
* @param attributeName 属性名称
|
||||
* @param attributeType 属性类型
|
||||
* @return 注解属性值
|
||||
*/
|
||||
@Override
|
||||
public Object getAttributeValue(String attributeName, Class<?> attributeType) {
|
||||
return Opt.ofNullable(attributeMethodCaches.get(attributeName))
|
||||
.filter(method -> ClassUtil.isAssignable(attributeType, method.getAttributeType()))
|
||||
.map(AnnotationAttribute::getValue)
|
||||
.get();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* <p>描述以一个参照物为对象,存在于该参照物的层级结构中的对象。
|
||||
*
|
||||
* <p>该对象可通过{@link #getVerticalDistance()}与{@link #getHorizontalDistance()}
|
||||
* 描述其在以参照物为基点的坐标坐标系中的位置。<br>
|
||||
* 在需要对该接口的实现类进行按优先级排序时,距离{@link #getRoot()}对象越近,则该实现类的优先级越高。
|
||||
* 默认提供了{@link #DEFAULT_HIERARCHICAL_COMPARATOR}用于实现该比较规则。<br>
|
||||
* 一般情况下,{@link #getRoot()}返回值相同的对象之间的比较才有意义。
|
||||
*
|
||||
* <p>此外,还提供了{@link Selector}接口用于根据一定的规则从两个{@link Hierarchical}实现类中选择并返回一个最合适的对象,
|
||||
* 默认提供了四个实现类:
|
||||
* <ul>
|
||||
* <li>{@link Selector#NEAREST_AND_OLDEST_PRIORITY}: 返回距离根对象更近的对象,当距离一样时优先返回旧对象;</li>
|
||||
* <li>{@link Selector#NEAREST_AND_NEWEST_PRIORITY}: 返回距离根对象更近的对象,当距离一样时优先返回新对象;</li>
|
||||
* <li>{@link Selector#FARTHEST_AND_OLDEST_PRIORITY}: 返回距离根对象更远的对象,当距离一样时优先返回旧对象;</li>
|
||||
* <li>{@link Selector#FARTHEST_AND_NEWEST_PRIORITY}: 返回距离根对象更远的对象,当距离一样时优先返回新对象;</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author huangchengxing
|
||||
*/
|
||||
public interface Hierarchical extends Comparable<Hierarchical> {
|
||||
|
||||
// ====================== compare ======================
|
||||
|
||||
/**
|
||||
* 默认{@link #getHorizontalDistance()}与{@link #getVerticalDistance()}排序的比较器
|
||||
*/
|
||||
Comparator<Hierarchical> DEFAULT_HIERARCHICAL_COMPARATOR = Comparator
|
||||
.comparing(Hierarchical::getVerticalDistance)
|
||||
.thenComparing(Hierarchical::getHorizontalDistance);
|
||||
|
||||
/**
|
||||
* 按{@link #getVerticalDistance()}和{@link #getHorizontalDistance()}排序
|
||||
*
|
||||
* @param o {@link SynthesizedAnnotation}对象
|
||||
* @return 比较值
|
||||
*/
|
||||
@Override
|
||||
default int compareTo(Hierarchical o) {
|
||||
return DEFAULT_HIERARCHICAL_COMPARATOR.compare(this, o);
|
||||
}
|
||||
|
||||
// ====================== hierarchical ======================
|
||||
|
||||
/**
|
||||
* 参照物,即坐标为{@code (0, 0)}的对象。
|
||||
* 当对象本身即为参照物时,该方法应当返回其本身
|
||||
*
|
||||
* @return 参照物
|
||||
*/
|
||||
Object getRoot();
|
||||
|
||||
/**
|
||||
* 获取该对象与参照物的垂直距离。
|
||||
* 默认情况下,该距离即为当前对象与参照物之间相隔的层级数。
|
||||
*
|
||||
* @return 合成注解与根对象的垂直距离
|
||||
*/
|
||||
int getVerticalDistance();
|
||||
|
||||
/**
|
||||
* 获取该对象与参照物的水平距离。
|
||||
* 默认情况下,该距离即为当前对象在与参照物{@link #getVerticalDistance()}相同的情况下条,
|
||||
* 该对象被扫描到的顺序。
|
||||
*
|
||||
* @return 合成注解与根对象的水平距离
|
||||
*/
|
||||
int getHorizontalDistance();
|
||||
|
||||
// ====================== selector ======================
|
||||
|
||||
/**
|
||||
* {@link Hierarchical}选择器,用于根据一定的规则从两个{@link Hierarchical}实现类中选择并返回一个最合适的对象
|
||||
*/
|
||||
@FunctionalInterface
|
||||
interface Selector {
|
||||
|
||||
/**
|
||||
* 返回距离根对象更近的对象,当距离一样时优先返回旧对象
|
||||
*/
|
||||
Selector NEAREST_AND_OLDEST_PRIORITY = new NearestAndOldestPrioritySelector();
|
||||
|
||||
/**
|
||||
* 返回距离根对象更近的对象,当距离一样时优先返回新对象
|
||||
*/
|
||||
Selector NEAREST_AND_NEWEST_PRIORITY = new NearestAndNewestPrioritySelector();
|
||||
|
||||
/**
|
||||
* 返回距离根对象更远的对象,当距离一样时优先返回旧对象
|
||||
*/
|
||||
Selector FARTHEST_AND_OLDEST_PRIORITY = new FarthestAndOldestPrioritySelector();
|
||||
|
||||
/**
|
||||
* 返回距离根对象更远的对象,当距离一样时优先返回新对象
|
||||
*/
|
||||
Selector FARTHEST_AND_NEWEST_PRIORITY = new FarthestAndNewestPrioritySelector();
|
||||
|
||||
/**
|
||||
* 比较两个被合成的对象,选择其中的一个并返回
|
||||
*
|
||||
* @param <T> 复合注解类型
|
||||
* @param prev 上一对象,该参数不允许为空
|
||||
* @param next 下一对象,该参数不允许为空
|
||||
* @return 对象
|
||||
*/
|
||||
<T extends Hierarchical> T choose(T prev, T next);
|
||||
|
||||
/**
|
||||
* 返回距离根对象更近的注解,当距离一样时优先返回旧注解
|
||||
*/
|
||||
class NearestAndOldestPrioritySelector implements Selector {
|
||||
@Override
|
||||
public <T extends Hierarchical> T choose(T oldAnnotation, T newAnnotation) {
|
||||
return newAnnotation.getVerticalDistance() < oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回距离根对象更近的注解,当距离一样时优先返回新注解
|
||||
*/
|
||||
class NearestAndNewestPrioritySelector implements Selector {
|
||||
@Override
|
||||
public <T extends Hierarchical> T choose(T oldAnnotation, T newAnnotation) {
|
||||
return newAnnotation.getVerticalDistance() <= oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回距离根对象更远的注解,当距离一样时优先返回旧注解
|
||||
*/
|
||||
class FarthestAndOldestPrioritySelector implements Selector {
|
||||
@Override
|
||||
public <T extends Hierarchical> T choose(T oldAnnotation, T newAnnotation) {
|
||||
return newAnnotation.getVerticalDistance() > oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回距离根对象更远的注解,当距离一样时优先返回新注解
|
||||
*/
|
||||
class FarthestAndNewestPrioritySelector implements Selector {
|
||||
@Override
|
||||
public <T extends Hierarchical> T choose(T oldAnnotation, T newAnnotation) {
|
||||
return newAnnotation.getVerticalDistance() >= oldAnnotation.getVerticalDistance() ? newAnnotation : oldAnnotation;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* <p>用于在同一注解中,或具有一定关联的不同注解的属性中,表明这些属性之间具有特定的关联关系。
|
||||
* 在通过{@link SynthesizedAggregateAnnotation}获取合成注解后,合成注解获取属性值时会根据该注解进行调整。<br>
|
||||
*
|
||||
* <p>该注解存在三个字注解:{@link MirrorFor}、{@link ForceAliasFor}或{@link AliasFor},
|
||||
* 使用三个子注解等同于{@link Link}。但是需要注意的是,
|
||||
* 当注解中的属性同时存在多个{@link Link}或基于{@link Link}的子注解时,
|
||||
* 仅有声明在被注解的属性最上方的注解会生效,其余注解都将被忽略。
|
||||
*
|
||||
* <b>注意:该注解的优先级低于{@link Alias}</b>
|
||||
*
|
||||
* @author huangchengxing
|
||||
* @see SynthesizedAggregateAnnotation
|
||||
* @see RelationType
|
||||
* @see AliasFor
|
||||
* @see MirrorFor
|
||||
* @see ForceAliasFor
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
|
||||
public @interface Link {
|
||||
|
||||
/**
|
||||
* 产生关联的注解类型,当不指定时,默认指注释的属性所在的类
|
||||
*
|
||||
* @return 关联的注解类型
|
||||
*/
|
||||
Class<? extends Annotation> annotation() default Annotation.class;
|
||||
|
||||
/**
|
||||
* {@link #annotation()}指定注解中关联的属性
|
||||
*
|
||||
* @return 属性名
|
||||
*/
|
||||
String attribute() default "";
|
||||
|
||||
/**
|
||||
* {@link #attribute()}指定属性与当前注解的属性建的关联关系类型
|
||||
*
|
||||
* @return 关系类型
|
||||
*/
|
||||
RelationType type() default RelationType.MIRROR_FOR;
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* <p>{@link Link}的子注解。表示注解的属性与指定的属性互为镜像,通过一个属性将能够获得对方的值。<br>
|
||||
* 它们遵循下述规则:
|
||||
* <ul>
|
||||
* <li>互为镜像的两个属性,必须同时通过指定模式为{@code MIRROR_FOR}的{@link Link}注解指定对方;</li>
|
||||
* <li>互为镜像的两个属性,类型必须一致;</li>
|
||||
* <li>互为镜像的两个属性在获取值,且两者的值皆不同时,必须且仅允许有一个非默认值,该值被优先返回;</li>
|
||||
* <li>互为镜像的两个属性,在值都为默认值或都不为默认值时,两者的值必须相等;</li>
|
||||
* </ul>
|
||||
* <b>注意,该注解与{@link Link}、{@link ForceAliasFor}或{@link AliasFor}一起使用时,将只有被声明在最上面的注解会生效</b>
|
||||
*
|
||||
* @author huangchengxing
|
||||
* @see Link
|
||||
* @see RelationType#MIRROR_FOR
|
||||
*/
|
||||
@Link(type = RelationType.MIRROR_FOR)
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
|
||||
public @interface MirrorFor {
|
||||
|
||||
/**
|
||||
* 产生关联的注解类型,当不指定时,默认指注释的属性所在的类
|
||||
*
|
||||
* @return 关联的注解类型
|
||||
*/
|
||||
@Link(annotation = Link.class, attribute = "annotation", type = RelationType.FORCE_ALIAS_FOR)
|
||||
Class<? extends Annotation> annotation() default Annotation.class;
|
||||
|
||||
/**
|
||||
* {@link #annotation()}指定注解中关联的属性
|
||||
*
|
||||
* @return 属性名
|
||||
*/
|
||||
@Link(annotation = Link.class, attribute = "attribute", type = RelationType.FORCE_ALIAS_FOR)
|
||||
String attribute() default "";
|
||||
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||
import aiyh.utils.tool.cn.hutool.core.text.CharSequenceUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
/**
|
||||
* <p>用于处理注解对象中带有{@link Link}注解,且{@link Link#type()}为{@link RelationType#MIRROR_FOR}的属性。<br>
|
||||
* 当该处理器执行完毕后,原始合成注解中被{@link Link}注解的属性与{@link Link}注解指向的目标注解的属性,
|
||||
* 都将会被被包装并替换为{@link MirroredAnnotationAttribute}。
|
||||
*
|
||||
* @author huangchengxing
|
||||
* @see RelationType#MIRROR_FOR
|
||||
* @see MirroredAnnotationAttribute
|
||||
*/
|
||||
public class MirrorLinkAnnotationPostProcessor extends AbstractLinkAnnotationPostProcessor {
|
||||
|
||||
private static final RelationType[] PROCESSED_RELATION_TYPES = new RelationType[]{ RelationType.MIRROR_FOR };
|
||||
|
||||
@Override
|
||||
public int order() {
|
||||
return Integer.MIN_VALUE + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 该处理器只处理{@link Link#type()}类型为{@link RelationType#MIRROR_FOR}的注解属性
|
||||
*
|
||||
* @return 仅有{@link RelationType#MIRROR_FOR}数组
|
||||
*/
|
||||
@Override
|
||||
protected RelationType[] processTypes() {
|
||||
return PROCESSED_RELATION_TYPES;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将存在镜像关系的合成注解属性分别包装为{@link MirroredAnnotationAttribute}对象,
|
||||
* 并使用包装后{@link MirroredAnnotationAttribute}替换在它们对应合成注解实例中的{@link AnnotationAttribute}
|
||||
*
|
||||
* @param synthesizer 注解合成器
|
||||
* @param annotation {@code originalAttribute}上的{@link Link}注解对象
|
||||
* @param originalAnnotation 当前正在处理的{@link SynthesizedAnnotation}对象
|
||||
* @param originalAttribute {@code originalAnnotation}上的待处理的属性
|
||||
* @param linkedAnnotation {@link Link}指向的关联注解对象
|
||||
* @param linkedAttribute {@link Link}指向的{@code originalAnnotation}中的关联属性,该参数可能为空
|
||||
*/
|
||||
@Override
|
||||
protected void processLinkedAttribute(
|
||||
AnnotationSynthesizer synthesizer, Link annotation,
|
||||
SynthesizedAnnotation originalAnnotation, AnnotationAttribute originalAttribute,
|
||||
SynthesizedAnnotation linkedAnnotation, AnnotationAttribute linkedAttribute) {
|
||||
|
||||
// 镜像属性必然成对出现,因此此处必定存在三种情况:
|
||||
// 1.两属性都不为镜像属性,此时继续进行后续处理;
|
||||
// 2.两属性都为镜像属性,并且指向对方,此时无需后续处理;
|
||||
// 3.两属性仅有任意一属性为镜像属性,此时镜像属性必然未指向当前原始属性,此时应该抛出异常;
|
||||
if (originalAttribute instanceof MirroredAnnotationAttribute
|
||||
|| linkedAttribute instanceof MirroredAnnotationAttribute) {
|
||||
checkMirrored(originalAttribute, linkedAttribute);
|
||||
return;
|
||||
}
|
||||
|
||||
// 校验镜像关系
|
||||
checkMirrorRelation(annotation, originalAttribute, linkedAttribute);
|
||||
// 包装这一对镜像属性,并替换原注解中的对应属性
|
||||
final AnnotationAttribute mirroredOriginalAttribute = new MirroredAnnotationAttribute(originalAttribute, linkedAttribute);
|
||||
originalAnnotation.setAttribute(originalAttribute.getAttributeName(), mirroredOriginalAttribute);
|
||||
final AnnotationAttribute mirroredTargetAttribute = new MirroredAnnotationAttribute(linkedAttribute, originalAttribute);
|
||||
linkedAnnotation.setAttribute(annotation.attribute(), mirroredTargetAttribute);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查映射关系是否正确
|
||||
*/
|
||||
private void checkMirrored(AnnotationAttribute original, AnnotationAttribute mirror) {
|
||||
final boolean originalAttributeMirrored = original instanceof MirroredAnnotationAttribute;
|
||||
final boolean mirrorAttributeMirrored = mirror instanceof MirroredAnnotationAttribute;
|
||||
|
||||
// 校验通过
|
||||
final boolean passed = originalAttributeMirrored && mirrorAttributeMirrored
|
||||
&& ObjectUtil.equals(((MirroredAnnotationAttribute)original).getLinked(), ((MirroredAnnotationAttribute)mirror).getOriginal());
|
||||
if (passed) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 校验失败,拼装异常信息用于抛出异常
|
||||
String errorMsg;
|
||||
// 原始字段已经跟其他字段形成镜像
|
||||
if (originalAttributeMirrored && !mirrorAttributeMirrored) {
|
||||
errorMsg = CharSequenceUtil.format(
|
||||
"attribute [{}] cannot mirror for [{}], because it's already mirrored for [{}]",
|
||||
original.getAttribute(), mirror.getAttribute(), ((MirroredAnnotationAttribute)original).getLinked()
|
||||
);
|
||||
}
|
||||
// 镜像字段已经跟其他字段形成镜像
|
||||
else if (!originalAttributeMirrored && mirrorAttributeMirrored) {
|
||||
errorMsg = CharSequenceUtil.format(
|
||||
"attribute [{}] cannot mirror for [{}], because it's already mirrored for [{}]",
|
||||
mirror.getAttribute(), original.getAttribute(), ((MirroredAnnotationAttribute)mirror).getLinked()
|
||||
);
|
||||
}
|
||||
// 两者都形成了镜像,但是都未指向对方,理论上不会存在该情况
|
||||
else {
|
||||
errorMsg = CharSequenceUtil.format(
|
||||
"attribute [{}] cannot mirror for [{}], because [{}] already mirrored for [{}] and [{}] already mirrored for [{}]",
|
||||
mirror.getAttribute(), original.getAttribute(),
|
||||
mirror.getAttribute(), ((MirroredAnnotationAttribute)mirror).getLinked(),
|
||||
original.getAttribute(), ((MirroredAnnotationAttribute)original).getLinked()
|
||||
);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(errorMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 基本校验
|
||||
*/
|
||||
private void checkMirrorRelation(Link annotation, AnnotationAttribute original, AnnotationAttribute mirror) {
|
||||
// 镜像属性必须存在
|
||||
checkLinkedAttributeNotNull(original, mirror, annotation);
|
||||
// 镜像属性返回值必须一致
|
||||
checkAttributeType(original, mirror);
|
||||
// 镜像属性上必须存在对应的注解
|
||||
final Link mirrorAttributeAnnotation = getLinkAnnotation(mirror, RelationType.MIRROR_FOR);
|
||||
Assert.isTrue(
|
||||
ObjectUtil.isNotNull(mirrorAttributeAnnotation) && RelationType.MIRROR_FOR.equals(mirrorAttributeAnnotation.type()),
|
||||
"mirror attribute [{}] of original attribute [{}] must marked by @Link, and also @LinkType.type() must is [{}]",
|
||||
mirror.getAttribute(), original.getAttribute(), RelationType.MIRROR_FOR
|
||||
);
|
||||
checkLinkedSelf(original, mirror);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||
|
||||
/**
|
||||
* 表示存在对应镜像属性的注解属性,当获取值时将根据{@link RelationType#MIRROR_FOR}的规则进行处理
|
||||
*
|
||||
* @author huangchengxing
|
||||
* @see MirrorLinkAnnotationPostProcessor
|
||||
* @see RelationType#MIRROR_FOR
|
||||
*/
|
||||
public class MirroredAnnotationAttribute extends AbstractWrappedAnnotationAttribute {
|
||||
|
||||
public MirroredAnnotationAttribute(AnnotationAttribute origin, AnnotationAttribute linked) {
|
||||
super(origin, linked);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
final boolean originIsDefault = original.isValueEquivalentToDefaultValue();
|
||||
final boolean targetIsDefault = linked.isValueEquivalentToDefaultValue();
|
||||
final Object originValue = original.getValue();
|
||||
final Object targetValue = linked.getValue();
|
||||
|
||||
// 都为默认值,或都为非默认值时,两方法的返回值必须相等
|
||||
if (originIsDefault == targetIsDefault) {
|
||||
Assert.equals(
|
||||
originValue, targetValue,
|
||||
"the values of attributes [{}] and [{}] that mirror each other are different: [{}] <==> [{}]",
|
||||
original.getAttribute(), linked.getAttribute(), originValue, targetValue
|
||||
);
|
||||
return originValue;
|
||||
}
|
||||
|
||||
// 两者有一者不为默认值时,优先返回非默认值
|
||||
return originIsDefault ? targetValue : originValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当{@link #original}与{@link #linked}都为默认值时返回{@code true}
|
||||
*
|
||||
* @return 是否
|
||||
*/
|
||||
@Override
|
||||
public boolean isValueEquivalentToDefaultValue() {
|
||||
return original.isValueEquivalentToDefaultValue() && linked.isValueEquivalentToDefaultValue();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 属性忽略注解,使用此注解的字段等会被忽略,主要用于Bean拷贝、Bean转Map等<br>
|
||||
* 此注解应用于字段时,忽略读取和设置属性值,应用于setXXX方法忽略设置值,应用于getXXX忽略读取值
|
||||
*
|
||||
* @author Looly
|
||||
* @since 5.4.2
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
|
||||
public @interface PropIgnore {
|
||||
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
/**
|
||||
* <p>注解属性的关系类型 <br>
|
||||
* 若将被{@link Link}注解的属性称为“原始属性”,而在{@link Link}注解中指向的注解属性称为“关联属性”,
|
||||
* 则该枚举用于描述“原始属性”与“关联属性”在{@link SynthesizedAggregateAnnotation}处理过程中的作用关系。<br>
|
||||
* 根据在{@link Link#type()}中指定的关系类型的不同,通过{@link SynthesizedAggregateAnnotation}合成的注解的属性值也将有所变化。
|
||||
*
|
||||
* <p>当一个注解中的所有属性同时具备多种关系时,将依次按下述顺序处理:
|
||||
* <ol>
|
||||
* <li>属性上的{@link Alias}注解;</li>
|
||||
* <li>属性上的{@link Link}注解,且{@link Link#type()}为{@link #MIRROR_FOR};</li>
|
||||
* <li>属性上的{@link Link}注解,且{@link Link#type()}为{@link #FORCE_ALIAS_FOR};</li>
|
||||
* <li>属性上的{@link Link}注解,且{@link Link#type()}为{@link #ALIAS_FOR};</li>
|
||||
* </ol>
|
||||
*
|
||||
* @author huangchengxing
|
||||
* @see SynthesizedAggregateAnnotation
|
||||
* @see Link
|
||||
*/
|
||||
public enum RelationType {
|
||||
|
||||
/**
|
||||
* <p>表示注解的属性与指定的属性互为镜像,通过一个属性将能够获得对方的值。<br>
|
||||
* 它们遵循下述规则:
|
||||
* <ul>
|
||||
* <li>互为镜像的两个属性,必须同时通过指定模式为{@code MIRROR_FOR}的{@link Link}注解指定对方;</li>
|
||||
* <li>互为镜像的两个属性,类型必须一致;</li>
|
||||
* <li>互为镜像的两个属性在获取值,且两者的值皆不同时,必须且仅允许有一个非默认值,该值被优先返回;</li>
|
||||
* <li>互为镜像的两个属性,在值都为默认值或都不为默认值时,两者的值必须相等;</li>
|
||||
* </ul>
|
||||
*/
|
||||
MIRROR_FOR,
|
||||
|
||||
/**
|
||||
* <p>表示“原始属性”将作为“关联属性”的别名。
|
||||
* <ul>
|
||||
* <li>当“原始属性”为默认值时,获取“关联属性”将返回“关联属性”本身的值;</li>
|
||||
* <li>当“原始属性”不为默认值时,获取“关联属性”将返回“原始属性”的值;</li>
|
||||
* </ul>
|
||||
*/
|
||||
ALIAS_FOR,
|
||||
|
||||
/**
|
||||
* <p>表示“原始属性”将强制作为“关联属性”的别名。效果等同于在“原始属性”上添加{@link Alias}注解,
|
||||
* 任何情况下,获取“关联属性”的值都将直接返回“原始属性”的值
|
||||
*/
|
||||
FORCE_ALIAS_FOR;
|
||||
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
/**
|
||||
* <p>表示基于特定规则聚合,将一组注解聚合而来的注解对象,
|
||||
* 该注解对象允许根据一定规则“合成”一些跟原始注解属性不一样合成注解。
|
||||
*
|
||||
* <p>合成注解一般被用于处理类层级结果中具有直接或间接关联的注解对象,
|
||||
* 当实例被创建时,会获取到这些注解对象,并使用{@link SynthesizedAnnotationSelector}对类型相同的注解进行过滤,
|
||||
* 并最终得到类型不重复的有效注解对象。这些有效注解将被包装为{@link SynthesizedAnnotation},
|
||||
* 然后最终用于“合成”一个{@link SynthesizedAggregateAnnotation}。<br>
|
||||
* {@link SynthesizedAnnotationSelector}是合成注解生命周期中的第一个钩子,
|
||||
* 自定义选择器以拦截原始注解被扫描的过程。
|
||||
*
|
||||
* <p>当合成注解完成对待合成注解的扫描,并完成了必要属性的加载后,
|
||||
* 将会按顺序依次调用{@link SynthesizedAnnotationPostProcessor},
|
||||
* 注解后置处理器允许用于对完成注解的待合成注解进行二次调整,
|
||||
* 该钩子一般用于根据{@link Link}注解对属性进行调整。<br>
|
||||
* {@link SynthesizedAnnotationPostProcessor}是合成注解生命周期中的第二个钩子,
|
||||
* 自定义后置处理器以拦截原始在转为待合成注解后的初始化过程。
|
||||
*
|
||||
* <p>合成注解允许通过{@link #synthesize(Class)}合成一个指定的注解对象,
|
||||
* 该方法返回的注解对象可能是原始的注解对象,也有可能通过动态代理的方式生成,
|
||||
* 该对象实例的属性不一定来自对象本身,而是来自于经过{@link SynthesizedAnnotationAttributeProcessor}
|
||||
* 处理后的、用于合成当前实例的全部关联注解的相关属性。<br>
|
||||
* {@link SynthesizedAnnotationAttributeProcessor}是合成注解生命周期中的第三个钩子,
|
||||
* 自定义属性处理器以拦截合成注解的取值过程。
|
||||
*
|
||||
* @author huangchengxing
|
||||
* @see AnnotationSynthesizer
|
||||
* @see SynthesizedAnnotation
|
||||
* @see SynthesizedAnnotationSelector
|
||||
* @see SynthesizedAnnotationAttributeProcessor
|
||||
* @see SynthesizedAnnotationPostProcessor
|
||||
* @see GenericSynthesizedAggregateAnnotation
|
||||
*/
|
||||
public interface SynthesizedAggregateAnnotation extends AggregateAnnotation, Hierarchical, AnnotationSynthesizer, AnnotationAttributeValueProvider {
|
||||
|
||||
// ================== hierarchical ==================
|
||||
|
||||
/**
|
||||
* 距离{@link #getRoot()}返回值的垂直距离,
|
||||
* 默认聚合注解即为根对象,因此返回0
|
||||
*
|
||||
* @return 距离{@link #getRoot()}返回值的水平距离,
|
||||
*/
|
||||
@Override
|
||||
default int getVerticalDistance() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 距离{@link #getRoot()}返回值的水平距离,
|
||||
* 默认聚合注解即为根对象,因此返回0
|
||||
*
|
||||
* @return 距离{@link #getRoot()}返回值的水平距离,
|
||||
*/
|
||||
@Override
|
||||
default int getHorizontalDistance() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ================== synthesize ==================
|
||||
|
||||
/**
|
||||
* 获取在聚合中存在的指定注解对象
|
||||
*
|
||||
* @param annotationType 注解类型
|
||||
* @param <T> 注解类型
|
||||
* @return 注解对象
|
||||
*/
|
||||
<T extends Annotation> T getAnnotation(Class<T> annotationType);
|
||||
|
||||
/**
|
||||
* 获取合成注解属性处理器
|
||||
*
|
||||
* @return 合成注解属性处理器
|
||||
*/
|
||||
SynthesizedAnnotationAttributeProcessor getAnnotationAttributeProcessor();
|
||||
|
||||
/**
|
||||
* 获取当前的注解类型
|
||||
*
|
||||
* @return 注解类型
|
||||
*/
|
||||
@Override
|
||||
default Class<? extends Annotation> annotationType() {
|
||||
return this.getClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从聚合中获取指定类型的属性值
|
||||
*
|
||||
* @param attributeName 属性名称
|
||||
* @param attributeType 属性类型
|
||||
* @return 属性值
|
||||
*/
|
||||
@Override
|
||||
Object getAttributeValue(String attributeName, Class<?> attributeType);
|
||||
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.collection.CollUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Map;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
/**
|
||||
* <p>用于在{@link SynthesizedAggregateAnnotation}中表示一个处于合成状态的注解对象。<br>
|
||||
* 当对多个合成注解排序时,默认使用{@link #DEFAULT_HIERARCHICAL_COMPARATOR}进行排序,
|
||||
* 从保证合成注解按{@link #getVerticalDistance()}与{@link #getHorizontalDistance()}的返回值保持有序,
|
||||
* 从而使得距离根元素更接近的注解对象在被处理是具有更高的优先级。
|
||||
*
|
||||
* @author huangchengxing
|
||||
* @see SynthesizedAggregateAnnotation
|
||||
*/
|
||||
public interface SynthesizedAnnotation extends Annotation, Hierarchical, AnnotationAttributeValueProvider {
|
||||
|
||||
/**
|
||||
* 获取被合成的注解对象
|
||||
*
|
||||
* @return 注解对象
|
||||
*/
|
||||
Annotation getAnnotation();
|
||||
|
||||
/**
|
||||
* 获取该合成注解与根对象的垂直距离。
|
||||
* 默认情况下,该距离即为当前注解与根对象之间相隔的层级数。
|
||||
*
|
||||
* @return 合成注解与根对象的垂直距离
|
||||
*/
|
||||
@Override
|
||||
int getVerticalDistance();
|
||||
|
||||
/**
|
||||
* 获取该合成注解与根对象的水平距离。
|
||||
* 默认情况下,该距离即为当前注解与根对象之间相隔的已经被扫描到的注解数。
|
||||
*
|
||||
* @return 合成注解与根对象的水平距离
|
||||
*/
|
||||
@Override
|
||||
int getHorizontalDistance();
|
||||
|
||||
/**
|
||||
* 注解是否存在该名称相同,且类型一致的属性
|
||||
*
|
||||
* @param attributeName 属性名
|
||||
* @param returnType 返回值类型
|
||||
* @return 是否存在该属性
|
||||
*/
|
||||
boolean hasAttribute(String attributeName, Class<?> returnType);
|
||||
|
||||
/**
|
||||
* 获取该注解的全部属性
|
||||
*
|
||||
* @return 注解属性
|
||||
*/
|
||||
Map<String, AnnotationAttribute> getAttributes();
|
||||
|
||||
/**
|
||||
* 设置该注解的全部属性
|
||||
*
|
||||
* @param attributes 注解属性
|
||||
*/
|
||||
default void setAttributes(Map<String, AnnotationAttribute> attributes) {
|
||||
if (CollUtil.isNotEmpty(attributes)) {
|
||||
attributes.forEach(this::setAttribute);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置属性值
|
||||
*
|
||||
* @param attributeName 属性名称
|
||||
* @param attribute 注解属性
|
||||
*/
|
||||
void setAttribute(String attributeName, AnnotationAttribute attribute);
|
||||
|
||||
/**
|
||||
* 替换属性值
|
||||
*
|
||||
* @param attributeName 属性名
|
||||
* @param operator 替换操作
|
||||
*/
|
||||
void replaceAttribute(String attributeName, UnaryOperator<AnnotationAttribute> operator);
|
||||
|
||||
/**
|
||||
* 获取属性值
|
||||
*
|
||||
* @param attributeName 属性名
|
||||
* @return 属性值
|
||||
*/
|
||||
Object getAttributeValue(String attributeName);
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 合成注解属性选择器。用于在{@link SynthesizedAggregateAnnotation}中从指定类型的合成注解里获取到对应的属性值
|
||||
*
|
||||
* @author huangchengxing
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface SynthesizedAnnotationAttributeProcessor {
|
||||
|
||||
/**
|
||||
* 从一批被合成注解中,获取指定名称与类型的属性值
|
||||
*
|
||||
* @param attributeName 属性名称
|
||||
* @param attributeType 属性类型
|
||||
* @param synthesizedAnnotations 被合成的注解
|
||||
* @param <R> 属性类型
|
||||
* @return 属性值
|
||||
*/
|
||||
<R> R getAttributeValue(String attributeName, Class<R> attributeType, Collection<? extends SynthesizedAnnotation> synthesizedAnnotations);
|
||||
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.comparator.CompareUtil;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* <p>被合成注解后置处理器,用于在{@link SynthesizedAggregateAnnotation}加载完所有待合成注解后,
|
||||
* 再对加载好的{@link SynthesizedAnnotation}进行后置处理。<br>
|
||||
* 当多个{@link SynthesizedAnnotationPostProcessor}需要一起执行时,将按照{@link #order()}的返回值进行排序,
|
||||
* 该值更小的处理器将被优先执行。
|
||||
*
|
||||
* <p>该接口存在多个实现类,调用者应当保证在任何时候,对一批后置处理器的调用顺序都符合:
|
||||
* <ul>
|
||||
* <li>{@link AliasAnnotationPostProcessor};</li>
|
||||
* <li>{@link MirrorLinkAnnotationPostProcessor};</li>
|
||||
* <li>{@link AliasLinkAnnotationPostProcessor};</li>
|
||||
* <li>其他后置处理器;</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author huangchengxing
|
||||
* @see AliasAnnotationPostProcessor
|
||||
* @see MirrorLinkAnnotationPostProcessor
|
||||
* @see AliasLinkAnnotationPostProcessor
|
||||
*/
|
||||
public interface SynthesizedAnnotationPostProcessor extends Comparable<SynthesizedAnnotationPostProcessor> {
|
||||
|
||||
/**
|
||||
* 属性上带有{@link Alias}的注解对象的后置处理器
|
||||
*/
|
||||
AliasAnnotationPostProcessor ALIAS_ANNOTATION_POST_PROCESSOR = new AliasAnnotationPostProcessor();
|
||||
|
||||
/**
|
||||
* 属性上带有{@link Link},且与其他注解的属性存在镜像关系的注解对象的后置处理器
|
||||
*/
|
||||
MirrorLinkAnnotationPostProcessor MIRROR_LINK_ANNOTATION_POST_PROCESSOR = new MirrorLinkAnnotationPostProcessor();
|
||||
|
||||
/**
|
||||
* 属性上带有{@link Link},且与其他注解的属性存在别名关系的注解对象的后置处理器
|
||||
*/
|
||||
AliasLinkAnnotationPostProcessor ALIAS_LINK_ANNOTATION_POST_PROCESSOR = new AliasLinkAnnotationPostProcessor();
|
||||
|
||||
/**
|
||||
* 在一组后置处理器中被调用的顺序,越小越靠前
|
||||
*
|
||||
* @return 排序值
|
||||
*/
|
||||
default int order() {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较两个后置处理器的{@link #order()}返回值
|
||||
*
|
||||
* @param o 比较对象
|
||||
* @return 大小
|
||||
*/
|
||||
@Override
|
||||
default int compareTo(SynthesizedAnnotationPostProcessor o) {
|
||||
return CompareUtil.compare(this, o, Comparator.comparing(SynthesizedAnnotationPostProcessor::order));
|
||||
}
|
||||
|
||||
/**
|
||||
* 给定指定被合成注解与其所属的合成注解聚合器实例,经过处理后返回最终
|
||||
*
|
||||
* @param synthesizedAnnotation 合成的注解
|
||||
* @param synthesizer 注解合成器
|
||||
*/
|
||||
void process(SynthesizedAnnotation synthesizedAnnotation, AnnotationSynthesizer synthesizer);
|
||||
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.Opt;
|
||||
import aiyh.utils.tool.cn.hutool.core.text.CharSequenceUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ClassUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ReflectUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 合成注解代理类,用于为{@link SynthesizedAnnotation}生成对应的合成注解代理对象
|
||||
*
|
||||
* @author huangchengxing
|
||||
* @see SynthesizedAnnotation
|
||||
* @see AnnotationAttributeValueProvider
|
||||
*/
|
||||
public class SynthesizedAnnotationProxy implements InvocationHandler {
|
||||
|
||||
private final AnnotationAttributeValueProvider annotationAttributeValueProvider;
|
||||
private final SynthesizedAnnotation annotation;
|
||||
private final Map<String, BiFunction<Method, Object[], Object>> methods;
|
||||
|
||||
/**
|
||||
* 创建一个代理注解,生成的代理对象将是{@link SyntheticProxyAnnotation}与指定的注解类的子类。
|
||||
*
|
||||
* @param <T> 注解类型
|
||||
* @param annotationType 注解类型
|
||||
* @param annotationAttributeValueProvider 注解属性值获取器
|
||||
* @param annotation 合成注解
|
||||
* @return 代理注解
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends Annotation> T create(
|
||||
Class<T> annotationType,
|
||||
AnnotationAttributeValueProvider annotationAttributeValueProvider,
|
||||
SynthesizedAnnotation annotation) {
|
||||
if (ObjectUtil.isNull(annotation)) {
|
||||
return null;
|
||||
}
|
||||
final SynthesizedAnnotationProxy proxyHandler = new SynthesizedAnnotationProxy(annotationAttributeValueProvider, annotation);
|
||||
if (ObjectUtil.isNull(annotation)) {
|
||||
return null;
|
||||
}
|
||||
return (T) Proxy.newProxyInstance(
|
||||
annotationType.getClassLoader(),
|
||||
new Class[]{annotationType, SyntheticProxyAnnotation.class},
|
||||
proxyHandler
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个代理注解,生成的代理对象将是{@link SyntheticProxyAnnotation}与指定的注解类的子类。
|
||||
*
|
||||
* @param <T> 注解类型
|
||||
* @param annotationType 注解类型
|
||||
* @param annotation 合成注解
|
||||
* @return 代理注解
|
||||
*/
|
||||
public static <T extends Annotation> T create(
|
||||
Class<T> annotationType, SynthesizedAnnotation annotation) {
|
||||
return create(annotationType, annotation, annotation);
|
||||
}
|
||||
|
||||
/**
|
||||
* 该类是否为通过{@code SynthesizedAnnotationProxy}生成的代理类
|
||||
*
|
||||
* @param annotationType 注解类型
|
||||
* @return 是否
|
||||
*/
|
||||
public static boolean isProxyAnnotation(Class<?> annotationType) {
|
||||
return ClassUtil.isAssignable(SyntheticProxyAnnotation.class, annotationType);
|
||||
}
|
||||
|
||||
SynthesizedAnnotationProxy(AnnotationAttributeValueProvider annotationAttributeValueProvider, SynthesizedAnnotation annotation) {
|
||||
Assert.notNull(annotationAttributeValueProvider, "annotationAttributeValueProvider must not null");
|
||||
Assert.notNull(annotation, "annotation must not null");
|
||||
this.annotationAttributeValueProvider = annotationAttributeValueProvider;
|
||||
this.annotation = annotation;
|
||||
this.methods = new HashMap<>(9);
|
||||
loadMethods();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
return Opt.ofNullable(methods.get(method.getName()))
|
||||
.map(m -> m.apply(method, args))
|
||||
.orElseGet(() -> ReflectUtil.invoke(this, method, args));
|
||||
}
|
||||
|
||||
// ========================= 代理方法 =========================
|
||||
|
||||
void loadMethods() {
|
||||
methods.put("toString", (method, args) -> proxyToString());
|
||||
methods.put("hashCode", (method, args) -> proxyHashCode());
|
||||
methods.put("getSynthesizedAnnotation", (method, args) -> proxyGetSynthesizedAnnotation());
|
||||
methods.put("getRoot", (method, args) -> annotation.getRoot());
|
||||
methods.put("getVerticalDistance", (method, args) -> annotation.getVerticalDistance());
|
||||
methods.put("getHorizontalDistance", (method, args) -> annotation.getHorizontalDistance());
|
||||
methods.put("hasAttribute", (method, args) -> annotation.hasAttribute((String) args[0], (Class<?>) args[1]));
|
||||
methods.put("getAttributes", (method, args) -> annotation.getAttributes());
|
||||
methods.put("setAttribute", (method, args) -> {
|
||||
throw new UnsupportedOperationException("proxied annotation can not reset attributes");
|
||||
});
|
||||
methods.put("getAttributeValue", (method, args) -> annotation.getAttributeValue((String) args[0]));
|
||||
methods.put("annotationType", (method, args) -> annotation.annotationType());
|
||||
for (final Method declaredMethod : ClassUtil.getDeclaredMethods(annotation.getAnnotation().annotationType())) {
|
||||
methods.put(declaredMethod.getName(), (method, args) -> proxyAttributeValue(method));
|
||||
}
|
||||
}
|
||||
|
||||
private String proxyToString() {
|
||||
final String attributes = Stream.of(ClassUtil.getDeclaredMethods(annotation.getAnnotation().annotationType()))
|
||||
.filter(AnnotationUtil::isAttributeMethod)
|
||||
.map(method -> CharSequenceUtil.format(
|
||||
"{}={}", method.getName(), proxyAttributeValue(method))
|
||||
)
|
||||
.collect(Collectors.joining(", "));
|
||||
return CharSequenceUtil.format("@{}({})", annotation.annotationType().getName(), attributes);
|
||||
}
|
||||
|
||||
private int proxyHashCode() {
|
||||
return Objects.hash(annotationAttributeValueProvider, annotation);
|
||||
}
|
||||
|
||||
private Object proxyGetSynthesizedAnnotation() {
|
||||
return annotation;
|
||||
}
|
||||
|
||||
private Object proxyAttributeValue(Method attributeMethod) {
|
||||
return annotationAttributeValueProvider.getAttributeValue(attributeMethod.getName(), attributeMethod.getReturnType());
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过代理类生成的合成注解
|
||||
*
|
||||
* @author huangchengxing
|
||||
*/
|
||||
interface SyntheticProxyAnnotation extends SynthesizedAnnotation {
|
||||
|
||||
/**
|
||||
* 获取该代理注解对应的已合成注解
|
||||
*
|
||||
* @return 理注解对应的已合成注解
|
||||
*/
|
||||
SynthesizedAnnotation getSynthesizedAnnotation();
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
/**
|
||||
* 注解选择器,指定两个注解,选择其中一个返回。<br>
|
||||
* 该接口用于在{@link SynthesizedAggregateAnnotation}中用于从一批相同的注解对象中筛选最终用于合成注解对象。
|
||||
*
|
||||
* @author huangchengxing
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface SynthesizedAnnotationSelector {
|
||||
|
||||
/**
|
||||
* 返回距离根对象更近的注解,当距离一样时优先返回旧注解
|
||||
*/
|
||||
SynthesizedAnnotationSelector NEAREST_AND_OLDEST_PRIORITY = new NearestAndOldestPrioritySelector();
|
||||
|
||||
/**
|
||||
* 返回距离根对象更近的注解,当距离一样时优先返回新注解
|
||||
*/
|
||||
SynthesizedAnnotationSelector NEAREST_AND_NEWEST_PRIORITY = new NearestAndNewestPrioritySelector();
|
||||
|
||||
/**
|
||||
* 返回距离根对象更远的注解,当距离一样时优先返回旧注解
|
||||
*/
|
||||
SynthesizedAnnotationSelector FARTHEST_AND_OLDEST_PRIORITY = new FarthestAndOldestPrioritySelector();
|
||||
|
||||
/**
|
||||
* 返回距离根对象更远的注解,当距离一样时优先返回新注解
|
||||
*/
|
||||
SynthesizedAnnotationSelector FARTHEST_AND_NEWEST_PRIORITY = new FarthestAndNewestPrioritySelector();
|
||||
|
||||
/**
|
||||
* 比较两个被合成的注解,选择其中的一个并返回
|
||||
*
|
||||
* @param <T> 复合注解类型
|
||||
* @param oldAnnotation 已存在的注解,该参数不允许为空
|
||||
* @param newAnnotation 新获取的注解,该参数不允许为空
|
||||
* @return 被合成的注解
|
||||
*/
|
||||
<T extends SynthesizedAnnotation> T choose(T oldAnnotation, T newAnnotation);
|
||||
|
||||
/**
|
||||
* 返回距离根对象更近的注解,当距离一样时优先返回旧注解
|
||||
*/
|
||||
class NearestAndOldestPrioritySelector implements SynthesizedAnnotationSelector {
|
||||
@Override
|
||||
public <T extends SynthesizedAnnotation> T choose(T oldAnnotation, T newAnnotation) {
|
||||
return Hierarchical.Selector.NEAREST_AND_OLDEST_PRIORITY.choose(oldAnnotation, newAnnotation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回距离根对象更近的注解,当距离一样时优先返回新注解
|
||||
*/
|
||||
class NearestAndNewestPrioritySelector implements SynthesizedAnnotationSelector {
|
||||
@Override
|
||||
public <T extends SynthesizedAnnotation> T choose(T oldAnnotation, T newAnnotation) {
|
||||
return Hierarchical.Selector.NEAREST_AND_NEWEST_PRIORITY.choose(oldAnnotation, newAnnotation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回距离根对象更远的注解,当距离一样时优先返回旧注解
|
||||
*/
|
||||
class FarthestAndOldestPrioritySelector implements SynthesizedAnnotationSelector {
|
||||
@Override
|
||||
public <T extends SynthesizedAnnotation> T choose(T oldAnnotation, T newAnnotation) {
|
||||
return Hierarchical.Selector.FARTHEST_AND_OLDEST_PRIORITY.choose(oldAnnotation, newAnnotation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回距离根对象更远的注解,当距离一样时优先返回新注解
|
||||
*/
|
||||
class FarthestAndNewestPrioritySelector implements SynthesizedAnnotationSelector {
|
||||
@Override
|
||||
public <T extends SynthesizedAnnotation> T choose(T oldAnnotation, T newAnnotation) {
|
||||
return Hierarchical.Selector.FARTHEST_AND_NEWEST_PRIORITY.choose(oldAnnotation, newAnnotation);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* <p>表示一个被包装过的{@link AnnotationAttribute},
|
||||
* 该实例中的一些方法可能会被代理到另一个注解属性对象中,
|
||||
* 从而使得通过原始的注解属性的方法获取到另一注解属性的值。<br>
|
||||
* 除了{@link #getValue()}以外,其他方法的返回值应当尽可能与{@link #getOriginal()}
|
||||
* 返回的{@link AnnotationAttribute}对象的方法返回值一致。
|
||||
*
|
||||
* <p>当包装类被包装了多层后,则规则生效优先级按包装的先后顺序倒序排序,
|
||||
* 比如a、b互为镜像,此时a、b两属性应当都被{@link MirroredAnnotationAttribute}包装,
|
||||
* 若再指定c为a的别名字段,则c、a、b都要在原基础上再次包装一层{@link AliasedAnnotationAttribute}。<br>
|
||||
* 此时a、b同时被包装了两层,则执行时,优先执行{@link AliasedAnnotationAttribute}的逻辑,
|
||||
* 当该规则不生效时,比如c只有默认值,此时上一次的{@link MirroredAnnotationAttribute}的逻辑才会生效。
|
||||
*
|
||||
* <p>被包装的{@link AnnotationAttribute}实际结构为一颗二叉树,
|
||||
* 当包装类再次被包装时,实际上等于又添加了一个新的根节点,
|
||||
* 此时需要同时更新树的全部关联叶子节点。
|
||||
*
|
||||
* @author huangchengxing
|
||||
* @see AnnotationAttribute
|
||||
* @see ForceAliasedAnnotationAttribute
|
||||
* @see AliasedAnnotationAttribute
|
||||
* @see MirroredAnnotationAttribute
|
||||
*/
|
||||
public interface WrappedAnnotationAttribute extends AnnotationAttribute {
|
||||
|
||||
// =========================== 新增方法 ===========================
|
||||
|
||||
/**
|
||||
* 获取被包装的{@link AnnotationAttribute}对象,该对象也可能是{@link AnnotationAttribute}
|
||||
*
|
||||
* @return 被包装的{@link AnnotationAttribute}对象
|
||||
*/
|
||||
AnnotationAttribute getOriginal();
|
||||
|
||||
/**
|
||||
* 获取最初的被包装的{@link AnnotationAttribute}
|
||||
*
|
||||
* @return 最初的被包装的{@link AnnotationAttribute}
|
||||
*/
|
||||
AnnotationAttribute getNonWrappedOriginal();
|
||||
|
||||
/**
|
||||
* 获取包装{@link #getOriginal()}的{@link AnnotationAttribute}对象,该对象也可能是{@link AnnotationAttribute}
|
||||
*
|
||||
* @return 包装对象
|
||||
*/
|
||||
AnnotationAttribute getLinked();
|
||||
|
||||
/**
|
||||
* 遍历以当前实例为根节点的树结构,获取所有未被包装的属性
|
||||
*
|
||||
* @return 叶子节点
|
||||
*/
|
||||
Collection<AnnotationAttribute> getAllLinkedNonWrappedAttributes();
|
||||
|
||||
// =========================== 代理实现 ===========================
|
||||
|
||||
/**
|
||||
* 获取注解对象
|
||||
*
|
||||
* @return 注解对象
|
||||
*/
|
||||
@Override
|
||||
default Annotation getAnnotation() {
|
||||
return getOriginal().getAnnotation();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取注解属性对应的方法
|
||||
*
|
||||
* @return 注解属性对应的方法
|
||||
*/
|
||||
@Override
|
||||
default Method getAttribute() {
|
||||
return getOriginal().getAttribute();
|
||||
}
|
||||
|
||||
/**
|
||||
* 该注解属性的值是否等于默认值 <br>
|
||||
* 默认仅当{@link #getOriginal()}与{@link #getLinked()}返回的注解属性
|
||||
* 都为默认值时,才返回{@code true}
|
||||
*
|
||||
* @return 该注解属性的值是否等于默认值
|
||||
*/
|
||||
@Override
|
||||
boolean isValueEquivalentToDefaultValue();
|
||||
|
||||
/**
|
||||
* 获取属性类型
|
||||
*
|
||||
* @return 属性类型
|
||||
*/
|
||||
@Override
|
||||
default Class<?> getAttributeType() {
|
||||
return getOriginal().getAttributeType();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取属性上的注解
|
||||
*
|
||||
* @param annotationType 注解类型
|
||||
* @return 注解对象
|
||||
*/
|
||||
@Override
|
||||
default <T extends Annotation> T getAnnotation(Class<T> annotationType) {
|
||||
return getOriginal().getAnnotation(annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前注解属性是否已经被{@link WrappedAnnotationAttribute}包装
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
@Override
|
||||
default boolean isWrapped() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
/**
|
||||
* 注解包,提供增强型注解和注解工具类
|
||||
*
|
||||
* @author looly
|
||||
*
|
||||
*/
|
||||
package aiyh.utils.tool.cn.hutool.core.annotation;
|
|
@ -0,0 +1,288 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation.scanner;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.annotation.AnnotationUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.collection.CollUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ArrayUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.*;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
/**
|
||||
* 为需要从类的层级结构中获取注解的{@link AnnotationScanner}提供基本实现
|
||||
*
|
||||
* @author huangchengxing
|
||||
*/
|
||||
public abstract class AbstractTypeAnnotationScanner<T extends AbstractTypeAnnotationScanner<T>> implements AnnotationScanner {
|
||||
|
||||
/**
|
||||
* 是否允许扫描父类
|
||||
*/
|
||||
private boolean includeSuperClass;
|
||||
|
||||
/**
|
||||
* 是否允许扫描父接口
|
||||
*/
|
||||
private boolean includeInterfaces;
|
||||
|
||||
/**
|
||||
* 过滤器,若类型无法通过该过滤器,则该类型及其树结构将直接不被查找
|
||||
*/
|
||||
private Predicate<Class<?>> filter;
|
||||
|
||||
/**
|
||||
* 排除的类型,以上类型及其树结构将直接不被查找
|
||||
*/
|
||||
private final Set<Class<?>> excludeTypes;
|
||||
|
||||
/**
|
||||
* 转换器
|
||||
*/
|
||||
private final List<UnaryOperator<Class<?>>> converters;
|
||||
|
||||
/**
|
||||
* 是否有转换器
|
||||
*/
|
||||
private boolean hasConverters;
|
||||
|
||||
/**
|
||||
* 当前实例
|
||||
*/
|
||||
private final T typedThis;
|
||||
|
||||
/**
|
||||
* 构造一个类注解扫描器
|
||||
*
|
||||
* @param includeSuperClass 是否允许扫描父类
|
||||
* @param includeInterfaces 是否允许扫描父接口
|
||||
* @param filter 过滤器
|
||||
* @param excludeTypes 不包含的类型
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected AbstractTypeAnnotationScanner(boolean includeSuperClass, boolean includeInterfaces, Predicate<Class<?>> filter, Set<Class<?>> excludeTypes) {
|
||||
Assert.notNull(filter, "filter must not null");
|
||||
Assert.notNull(excludeTypes, "excludeTypes must not null");
|
||||
this.includeSuperClass = includeSuperClass;
|
||||
this.includeInterfaces = includeInterfaces;
|
||||
this.filter = filter;
|
||||
this.excludeTypes = excludeTypes;
|
||||
this.converters = new ArrayList<>();
|
||||
this.typedThis = (T) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否允许扫描父类
|
||||
*
|
||||
* @return 是否允许扫描父类
|
||||
*/
|
||||
public boolean isIncludeSuperClass() {
|
||||
return includeSuperClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否允许扫描父接口
|
||||
*
|
||||
* @return 是否允许扫描父接口
|
||||
*/
|
||||
public boolean isIncludeInterfaces() {
|
||||
return includeInterfaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置过滤器,若类型无法通过该过滤器,则该类型及其树结构将直接不被查找
|
||||
*
|
||||
* @param filter 过滤器
|
||||
* @return 当前实例
|
||||
*/
|
||||
public T setFilter(Predicate<Class<?>> filter) {
|
||||
Assert.notNull(filter, "filter must not null");
|
||||
this.filter = filter;
|
||||
return typedThis;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加不扫描的类型,该类型及其树结构将直接不被查找
|
||||
*
|
||||
* @param excludeTypes 不扫描的类型
|
||||
* @return 当前实例
|
||||
*/
|
||||
public T addExcludeTypes(Class<?>... excludeTypes) {
|
||||
CollUtil.addAll(this.excludeTypes, excludeTypes);
|
||||
return typedThis;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加转换器
|
||||
*
|
||||
* @param converter 转换器
|
||||
* @return 当前实例
|
||||
* @see JdkProxyClassConverter
|
||||
*/
|
||||
public T addConverters(UnaryOperator<Class<?>> converter) {
|
||||
Assert.notNull(converter, "converter must not null");
|
||||
this.converters.add(converter);
|
||||
if (!this.hasConverters) {
|
||||
this.hasConverters = CollUtil.isNotEmpty(this.converters);
|
||||
}
|
||||
return typedThis;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否允许扫描父类
|
||||
*
|
||||
* @param includeSuperClass 是否
|
||||
* @return 当前实例
|
||||
*/
|
||||
protected T setIncludeSuperClass(boolean includeSuperClass) {
|
||||
this.includeSuperClass = includeSuperClass;
|
||||
return typedThis;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否允许扫描父接口
|
||||
*
|
||||
* @param includeInterfaces 是否
|
||||
* @return 当前实例
|
||||
*/
|
||||
protected T setIncludeInterfaces(boolean includeInterfaces) {
|
||||
this.includeInterfaces = includeInterfaces;
|
||||
return typedThis;
|
||||
}
|
||||
|
||||
/**
|
||||
* 则根据广度优先递归扫描类的层级结构,并对层级结构中类/接口声明的层级索引和它们声明的注解对象进行处理
|
||||
*
|
||||
* @param consumer 对获取到的注解和注解对应的层级索引的处理
|
||||
* @param annotatedEle 注解元素
|
||||
* @param filter 注解过滤器,无法通过过滤器的注解不会被处理。该参数允许为空。
|
||||
*/
|
||||
@Override
|
||||
public void scan(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
|
||||
filter = ObjectUtil.defaultIfNull(filter, a -> annotation -> true);
|
||||
final Class<?> sourceClass = getClassFormAnnotatedElement(annotatedEle);
|
||||
final Deque<List<Class<?>>> classDeque = CollUtil.newLinkedList(CollUtil.newArrayList(sourceClass));
|
||||
final Set<Class<?>> accessedTypes = new LinkedHashSet<>();
|
||||
int index = 0;
|
||||
while (!classDeque.isEmpty()) {
|
||||
final List<Class<?>> currClassQueue = classDeque.removeFirst();
|
||||
final List<Class<?>> nextClassQueue = new ArrayList<>();
|
||||
for (Class<?> targetClass : currClassQueue) {
|
||||
targetClass = convert(targetClass);
|
||||
// 过滤不需要处理的类
|
||||
if (isNotNeedProcess(accessedTypes, targetClass)) {
|
||||
continue;
|
||||
}
|
||||
accessedTypes.add(targetClass);
|
||||
// 扫描父类
|
||||
scanSuperClassIfNecessary(nextClassQueue, targetClass);
|
||||
// 扫描接口
|
||||
scanInterfaceIfNecessary(nextClassQueue, targetClass);
|
||||
// 处理层级索引和注解
|
||||
final Annotation[] targetAnnotations = getAnnotationsFromTargetClass(annotatedEle, index, targetClass);
|
||||
for (final Annotation annotation : targetAnnotations) {
|
||||
if (AnnotationUtil.isNotJdkMateAnnotation(annotation.annotationType()) && filter.test(annotation)) {
|
||||
consumer.accept(index, annotation);
|
||||
}
|
||||
}
|
||||
index++;
|
||||
}
|
||||
if (CollUtil.isNotEmpty(nextClassQueue)) {
|
||||
classDeque.addLast(nextClassQueue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从要搜索的注解元素上获得要递归的类型
|
||||
*
|
||||
* @param annotatedElement 注解元素
|
||||
* @return 要递归的类型
|
||||
*/
|
||||
protected abstract Class<?> getClassFormAnnotatedElement(AnnotatedElement annotatedElement);
|
||||
|
||||
/**
|
||||
* 从类上获取最终所需的目标注解
|
||||
*
|
||||
* @param source 最初的注解元素
|
||||
* @param index 类的层级索引
|
||||
* @param targetClass 类
|
||||
* @return 最终所需的目标注解
|
||||
*/
|
||||
protected abstract Annotation[] getAnnotationsFromTargetClass(AnnotatedElement source, int index, Class<?> targetClass);
|
||||
|
||||
/**
|
||||
* 当前类是否不需要处理
|
||||
*
|
||||
* @param accessedTypes 访问类型
|
||||
* @param targetClass 目标类型
|
||||
* @return 是否不需要处理
|
||||
*/
|
||||
protected boolean isNotNeedProcess(Set<Class<?>> accessedTypes, Class<?> targetClass) {
|
||||
return ObjectUtil.isNull(targetClass)
|
||||
|| accessedTypes.contains(targetClass)
|
||||
|| excludeTypes.contains(targetClass)
|
||||
|| filter.negate().test(targetClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 若{@link #includeInterfaces}为{@code true},则将目标类的父接口也添加到nextClasses
|
||||
*
|
||||
* @param nextClasses 下一个类集合
|
||||
* @param targetClass 目标类型
|
||||
*/
|
||||
protected void scanInterfaceIfNecessary(List<Class<?>> nextClasses, Class<?> targetClass) {
|
||||
if (includeInterfaces) {
|
||||
final Class<?>[] interfaces = targetClass.getInterfaces();
|
||||
if (ArrayUtil.isNotEmpty(interfaces)) {
|
||||
CollUtil.addAll(nextClasses, interfaces);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 若{@link #includeSuperClass}为{@code true},则将目标类的父类也添加到nextClasses
|
||||
*
|
||||
* @param nextClassQueue 下一个类队列
|
||||
* @param targetClass 目标类型
|
||||
*/
|
||||
protected void scanSuperClassIfNecessary(List<Class<?>> nextClassQueue, Class<?> targetClass) {
|
||||
if (includeSuperClass) {
|
||||
final Class<?> superClass = targetClass.getSuperclass();
|
||||
if (!ObjectUtil.equals(superClass, Object.class) && ObjectUtil.isNotNull(superClass)) {
|
||||
nextClassQueue.add(superClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 若存在转换器,则使用转换器对目标类进行转换
|
||||
*
|
||||
* @param target 目标类
|
||||
* @return 转换后的类
|
||||
*/
|
||||
protected Class<?> convert(Class<?> target) {
|
||||
if (hasConverters) {
|
||||
for (final UnaryOperator<Class<?>> converter : converters) {
|
||||
target = converter.apply(target);
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* 若类型为jdk代理类,则尝试转换为原始被代理类
|
||||
*/
|
||||
public static class JdkProxyClassConverter implements UnaryOperator<Class<?>> {
|
||||
@Override
|
||||
public Class<?> apply(Class<?> sourceClass) {
|
||||
return Proxy.isProxyClass(sourceClass) ? apply(sourceClass.getSuperclass()) : sourceClass;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,198 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation.scanner;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.annotation.AnnotationUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ArrayUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* <p>注解扫描器,用于从支持的可注解元素上获取所需注解
|
||||
*
|
||||
* <p>默认提供了以下扫描方式:
|
||||
* <ul>
|
||||
* <li>{@link #NOTHING}:什么都不做,什么注解都不扫描;</li>
|
||||
* <li>{@link #DIRECTLY}:扫描元素本身直接声明的注解,包括父类带有{@link Inherited}、被传递到元素上的注解;</li>
|
||||
* <li>
|
||||
* {@link #DIRECTLY_AND_META_ANNOTATION}:扫描元素本身直接声明的注解,包括父类带有{@link Inherited}、被传递到元素上的注解,
|
||||
* 以及这些注解的元注解;
|
||||
* </li>
|
||||
* <li>{@link #SUPERCLASS}:扫描元素本身以及父类的层级结构中声明的注解;</li>
|
||||
* <li>{@link #SUPERCLASS_AND_META_ANNOTATION}:扫描元素本身以及父类的层级结构中声明的注解,以及这些注解的元注解;</li>
|
||||
* <li>{@link #INTERFACE}:扫描元素本身以及父接口的层级结构中声明的注解;</li>
|
||||
* <li>{@link #INTERFACE_AND_META_ANNOTATION}:扫描元素本身以及父接口的层级结构中声明的注解,以及这些注解的元注解;</li>
|
||||
* <li>{@link #TYPE_HIERARCHY}:扫描元素本身以及父类、父接口的层级结构中声明的注解;</li>
|
||||
* <li>{@link #TYPE_HIERARCHY_AND_META_ANNOTATION}:扫描元素本身以及父接口、父接口的层级结构中声明的注解,以及这些注解的元注解;</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author huangchengxing
|
||||
* @see TypeAnnotationScanner
|
||||
* @see MethodAnnotationScanner
|
||||
* @see FieldAnnotationScanner
|
||||
* @see MetaAnnotationScanner
|
||||
* @see ElementAnnotationScanner
|
||||
* @see GenericAnnotationScanner
|
||||
*/
|
||||
public interface AnnotationScanner {
|
||||
|
||||
// ============================ 预置的扫描器实例 ============================
|
||||
|
||||
/**
|
||||
* 不扫描任何注解
|
||||
*/
|
||||
AnnotationScanner NOTHING = new EmptyAnnotationScanner();
|
||||
|
||||
/**
|
||||
* 扫描元素本身直接声明的注解,包括父类带有{@link Inherited}、被传递到元素上的注解的扫描器
|
||||
*/
|
||||
AnnotationScanner DIRECTLY = new GenericAnnotationScanner(false, false, false);
|
||||
|
||||
/**
|
||||
* 扫描元素本身直接声明的注解,包括父类带有{@link Inherited}、被传递到元素上的注解,以及这些注解的元注解的扫描器
|
||||
*/
|
||||
AnnotationScanner DIRECTLY_AND_META_ANNOTATION = new GenericAnnotationScanner(true, false, false);
|
||||
|
||||
/**
|
||||
* 扫描元素本身以及父类的层级结构中声明的注解的扫描器
|
||||
*/
|
||||
AnnotationScanner SUPERCLASS = new GenericAnnotationScanner(false, true, false);
|
||||
|
||||
/**
|
||||
* 扫描元素本身以及父类的层级结构中声明的注解,以及这些注解的元注解的扫描器
|
||||
*/
|
||||
AnnotationScanner SUPERCLASS_AND_META_ANNOTATION = new GenericAnnotationScanner(true, true, false);
|
||||
|
||||
/**
|
||||
* 扫描元素本身以及父接口的层级结构中声明的注解的扫描器
|
||||
*/
|
||||
AnnotationScanner INTERFACE = new GenericAnnotationScanner(false, false, true);
|
||||
|
||||
/**
|
||||
* 扫描元素本身以及父接口的层级结构中声明的注解,以及这些注解的元注解的扫描器
|
||||
*/
|
||||
AnnotationScanner INTERFACE_AND_META_ANNOTATION = new GenericAnnotationScanner(true, false, true);
|
||||
|
||||
/**
|
||||
* 扫描元素本身以及父类、父接口的层级结构中声明的注解的扫描器
|
||||
*/
|
||||
AnnotationScanner TYPE_HIERARCHY = new GenericAnnotationScanner(false, true, true);
|
||||
|
||||
/**
|
||||
* 扫描元素本身以及父接口、父接口的层级结构中声明的注解,以及这些注解的元注解的扫描器
|
||||
*/
|
||||
AnnotationScanner TYPE_HIERARCHY_AND_META_ANNOTATION = new GenericAnnotationScanner(true, true, true);
|
||||
|
||||
// ============================ 静态方法 ============================
|
||||
|
||||
/**
|
||||
* 给定一组扫描器,使用第一个支持处理该类型元素的扫描器获取元素上可能存在的注解
|
||||
*
|
||||
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @param scanners 注解扫描器
|
||||
* @return 注解
|
||||
*/
|
||||
static List<Annotation> scanByAnySupported(AnnotatedElement annotatedEle, AnnotationScanner... scanners) {
|
||||
if (ObjectUtil.isNull(annotatedEle) && ArrayUtil.isNotEmpty(scanners)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Stream.of(scanners)
|
||||
.filter(scanner -> scanner.support(annotatedEle))
|
||||
.findFirst()
|
||||
.map(scanner -> scanner.getAnnotations(annotatedEle))
|
||||
.orElseGet(Collections::emptyList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据指定的扫描器,扫描元素上可能存在的注解
|
||||
*
|
||||
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @param scanners 注解扫描器
|
||||
* @return 注解
|
||||
*/
|
||||
static List<Annotation> scanByAllSupported(AnnotatedElement annotatedEle, AnnotationScanner... scanners) {
|
||||
if (ObjectUtil.isNull(annotatedEle) && ArrayUtil.isNotEmpty(scanners)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Stream.of(scanners)
|
||||
.map(scanner -> scanner.getAnnotationsIfSupport(annotatedEle))
|
||||
.flatMap(Collection::stream)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
// ============================ 抽象方法 ============================
|
||||
|
||||
/**
|
||||
* 判断是否支持扫描该注解元素
|
||||
*
|
||||
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @return 是否支持扫描该注解元素
|
||||
*/
|
||||
default boolean support(AnnotatedElement annotatedEle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取注解元素上的全部注解。调用该方法前,需要确保调用{@link #support(AnnotatedElement)}返回为true
|
||||
*
|
||||
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @return 注解
|
||||
*/
|
||||
default List<Annotation> getAnnotations(AnnotatedElement annotatedEle) {
|
||||
final List<Annotation> annotations = new ArrayList<>();
|
||||
scan((index, annotation) -> annotations.add(annotation), annotatedEle, null);
|
||||
return annotations;
|
||||
}
|
||||
|
||||
/**
|
||||
* 若{@link #support(AnnotatedElement)}返回{@code true},
|
||||
* 则调用并返回{@link #getAnnotations(AnnotatedElement)}结果,
|
||||
* 否则返回{@link Collections#emptyList()}
|
||||
*
|
||||
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @return 注解
|
||||
*/
|
||||
default List<Annotation> getAnnotationsIfSupport(AnnotatedElement annotatedEle) {
|
||||
return support(annotatedEle) ? getAnnotations(annotatedEle) : Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描注解元素的层级结构(若存在),然后对获取到的注解和注解对应的层级索引进行处理。
|
||||
* 调用该方法前,需要确保调用{@link #support(AnnotatedElement)}返回为true
|
||||
*
|
||||
* @param consumer 对获取到的注解和注解对应的层级索引的处理
|
||||
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @param filter 注解过滤器,无法通过过滤器的注解不会被处理。该参数允许为空。
|
||||
*/
|
||||
default void scan(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
|
||||
filter = ObjectUtil.defaultIfNull(filter, (a)->annotation -> true);
|
||||
for (final Annotation annotation : annotatedEle.getAnnotations()) {
|
||||
if (AnnotationUtil.isNotJdkMateAnnotation(annotation.annotationType()) && filter.test(annotation)) {
|
||||
consumer.accept(0, annotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 若{@link #support(AnnotatedElement)}返回{@code true},则调用{@link #scan(BiConsumer, AnnotatedElement, Predicate)}
|
||||
*
|
||||
* @param consumer 对获取到的注解和注解对应的层级索引的处理
|
||||
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @param filter 注解过滤器,无法通过过滤器的注解不会被处理。该参数允许为空。
|
||||
*/
|
||||
default void scanIfSupport(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
|
||||
if (support(annotatedEle)) {
|
||||
scan(consumer, annotatedEle, filter);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation.scanner;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 扫描{@link AnnotatedElement}上的注解,不支持处理层级对象
|
||||
*
|
||||
* @author huangchengxing
|
||||
*/
|
||||
public class ElementAnnotationScanner implements AnnotationScanner {
|
||||
|
||||
/**
|
||||
* 判断是否支持扫描该注解元素,仅当注解元素不为空时返回{@code true}
|
||||
*
|
||||
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @return 是否支持扫描该注解元素
|
||||
*/
|
||||
@Override
|
||||
public boolean support(AnnotatedElement annotatedEle) {
|
||||
return ObjectUtil.isNotNull(annotatedEle);
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描{@link AnnotatedElement}上直接声明的注解,调用前需要确保调用{@link #support(AnnotatedElement)}返回为true
|
||||
*
|
||||
* @param consumer 对获取到的注解和注解对应的层级索引的处理
|
||||
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @param filter 注解过滤器,无法通过过滤器的注解不会被处理。该参数允许为空。
|
||||
*/
|
||||
@Override
|
||||
public void scan(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
|
||||
filter = ObjectUtil.defaultIfNull(filter,a-> t -> true);
|
||||
Stream.of(annotatedEle.getAnnotations())
|
||||
.filter(filter)
|
||||
.forEach(annotation -> consumer.accept(0, annotation));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation.scanner;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* 默认不扫描任何元素的扫描器
|
||||
*
|
||||
* @author huangchengxing
|
||||
*/
|
||||
public class EmptyAnnotationScanner implements AnnotationScanner {
|
||||
|
||||
@Override
|
||||
public boolean support(AnnotatedElement annotatedEle) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Annotation> getAnnotations(AnnotatedElement annotatedEle) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scan(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation.scanner;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.annotation.AnnotationUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* 扫描{@link Field}上的注解
|
||||
*
|
||||
* @author huangchengxing
|
||||
*/
|
||||
public class FieldAnnotationScanner implements AnnotationScanner {
|
||||
|
||||
/**
|
||||
* 判断是否支持扫描该注解元素,仅当注解元素是{@link Field}时返回{@code true}
|
||||
*
|
||||
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @return 是否支持扫描该注解元素
|
||||
*/
|
||||
@Override
|
||||
public boolean support(AnnotatedElement annotatedEle) {
|
||||
return annotatedEle instanceof Field;
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描{@link Field}上直接声明的注解,调用前需要确保调用{@link #support(AnnotatedElement)}返回为true
|
||||
*
|
||||
* @param consumer 对获取到的注解和注解对应的层级索引的处理
|
||||
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @param filter 注解过滤器,无法通过过滤器的注解不会被处理。该参数允许为空。
|
||||
*/
|
||||
@Override
|
||||
public void scan(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
|
||||
filter = ObjectUtil.defaultIfNull(filter, a -> annotation -> true);
|
||||
for (final Annotation annotation : annotatedEle.getAnnotations()) {
|
||||
if (AnnotationUtil.isNotJdkMateAnnotation(annotation.annotationType()) && filter.test(annotation)) {
|
||||
consumer.accept(0, annotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation.scanner;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.map.multi.ListValueMap;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* <p>通用注解扫描器,支持按不同的层级结构扫描{@link AnnotatedElement}上的注解。
|
||||
*
|
||||
* <p>当{@link AnnotatedElement}类型不同时,“层级结构”指向的对象将有所区别:
|
||||
* <ul>
|
||||
* <li>
|
||||
* 当元素为{@link Method}时,此处层级结构指声明方法的类的层级结构,
|
||||
* 扫描器将从层级结构中寻找与该方法签名相同的方法,并对其进行扫描;
|
||||
* </li>
|
||||
* <li>
|
||||
* 当元素为{@link Class}时,此处层级结构即指类本身与其父类、父接口共同构成的层级结构,
|
||||
* 扫描器将扫描层级结构中类、接口声明的注解;
|
||||
* </li>
|
||||
* <li>当元素不为{@link Method}或{@link Class}时,则其层级结构仅有其本身一层;</li>
|
||||
* </ul>
|
||||
* 此外,扫描器支持在获取到层级结构中的注解对象后,再对注解对象的元注解进行扫描。
|
||||
*
|
||||
* @author huangchengxing
|
||||
* @see TypeAnnotationScanner
|
||||
* @see MethodAnnotationScanner
|
||||
* @see MetaAnnotationScanner
|
||||
* @see ElementAnnotationScanner
|
||||
*/
|
||||
public class GenericAnnotationScanner implements AnnotationScanner {
|
||||
|
||||
/**
|
||||
* 类型扫描器
|
||||
*/
|
||||
private final AnnotationScanner typeScanner;
|
||||
|
||||
/**
|
||||
* 方法扫描器
|
||||
*/
|
||||
private final AnnotationScanner methodScanner;
|
||||
|
||||
/**
|
||||
* 元注解扫描器
|
||||
*/
|
||||
private final AnnotationScanner metaScanner;
|
||||
|
||||
/**
|
||||
* 普通元素扫描器
|
||||
*/
|
||||
private final AnnotationScanner elementScanner;
|
||||
|
||||
/**
|
||||
* 通用注解扫描器支持扫描所有类型的{@link AnnotatedElement}
|
||||
*
|
||||
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @return 是否支持扫描该注解元素
|
||||
*/
|
||||
@Override
|
||||
public boolean support(AnnotatedElement annotatedEle) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造一个通用注解扫描器
|
||||
*
|
||||
* @param enableScanMetaAnnotation 是否扫描注解上的元注解
|
||||
* @param enableScanSupperClass 是否扫描父类
|
||||
* @param enableScanSupperInterface 是否扫描父接口
|
||||
*/
|
||||
public GenericAnnotationScanner(
|
||||
boolean enableScanMetaAnnotation,
|
||||
boolean enableScanSupperClass,
|
||||
boolean enableScanSupperInterface) {
|
||||
|
||||
this.metaScanner = enableScanMetaAnnotation ? new MetaAnnotationScanner() : new EmptyAnnotationScanner();
|
||||
this.typeScanner = new TypeAnnotationScanner(
|
||||
enableScanSupperClass, enableScanSupperInterface, a -> true, Collections.emptySet()
|
||||
);
|
||||
this.methodScanner = new MethodAnnotationScanner(
|
||||
enableScanSupperClass, enableScanSupperInterface, a -> true, Collections.emptySet()
|
||||
);
|
||||
this.elementScanner = new ElementAnnotationScanner();
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描注解元素的层级结构(若存在),然后对获取到的注解和注解对应的层级索引进行处理
|
||||
*
|
||||
* @param consumer 对获取到的注解和注解对应的层级索引的处理
|
||||
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @param filter 注解过滤器,无法通过过滤器的注解不会被处理。该参数允许为空。
|
||||
*/
|
||||
@Override
|
||||
public void scan(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
|
||||
filter = ObjectUtil.defaultIfNull(filter, a -> t -> true);
|
||||
if (ObjectUtil.isNull(annotatedEle)) {
|
||||
return;
|
||||
}
|
||||
// 注解元素是类
|
||||
if (annotatedEle instanceof Class) {
|
||||
scanElements(typeScanner, consumer, annotatedEle, filter);
|
||||
}
|
||||
// 注解元素是方法
|
||||
else if (annotatedEle instanceof Method) {
|
||||
scanElements(methodScanner, consumer, annotatedEle, filter);
|
||||
}
|
||||
// 注解元素是其他类型
|
||||
else {
|
||||
scanElements(elementScanner, consumer, annotatedEle, filter);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描注解类的层级结构(若存在),然后对获取到的注解和注解对应的层级索引进行处理
|
||||
*
|
||||
* @param scanner 使用的扫描器
|
||||
* @param consumer 对获取到的注解和注解对应的层级索引的处理
|
||||
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @param filter 注解过滤器,无法通过过滤器的注解不会被处理。该参数允许为空。
|
||||
*/
|
||||
private void scanElements(
|
||||
AnnotationScanner scanner,
|
||||
BiConsumer<Integer, Annotation> consumer,
|
||||
AnnotatedElement annotatedEle,
|
||||
Predicate<Annotation> filter) {
|
||||
// 扫描类上注解
|
||||
final ListValueMap<Integer, Annotation> classAnnotations = new ListValueMap<>(new LinkedHashMap<>());
|
||||
scanner.scan((index, annotation) -> {
|
||||
if (filter.test(annotation)) {
|
||||
classAnnotations.putValue(index, annotation);
|
||||
}
|
||||
}, annotatedEle, filter);
|
||||
|
||||
// 扫描元注解
|
||||
classAnnotations.forEach((index, annotations) ->
|
||||
annotations.forEach(annotation -> {
|
||||
consumer.accept(index, annotation);
|
||||
metaScanner.scan(consumer, annotation.annotationType(), filter);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation.scanner;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.annotation.AnnotationUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.collection.CollUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ClassUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.util.*;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 扫描注解类上存在的注解,支持处理枚举实例或枚举类型
|
||||
* 需要注意,当待解析是枚举类时,有可能与{@link TypeAnnotationScanner}冲突
|
||||
*
|
||||
* @author huangchengxing
|
||||
* @see TypeAnnotationScanner
|
||||
*/
|
||||
public class MetaAnnotationScanner implements AnnotationScanner {
|
||||
|
||||
/**
|
||||
* 获取当前注解的元注解后,是否继续递归扫描的元注解的元注解
|
||||
*/
|
||||
private final boolean includeSupperMetaAnnotation;
|
||||
|
||||
/**
|
||||
* 构造一个元注解扫描器
|
||||
*
|
||||
* @param includeSupperMetaAnnotation 获取当前注解的元注解后,是否继续递归扫描的元注解的元注解
|
||||
*/
|
||||
public MetaAnnotationScanner(boolean includeSupperMetaAnnotation) {
|
||||
this.includeSupperMetaAnnotation = includeSupperMetaAnnotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造一个元注解扫描器,默认在扫描当前注解上的元注解后,并继续递归扫描元注解
|
||||
*/
|
||||
public MetaAnnotationScanner() {
|
||||
this(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否支持扫描该注解元素,仅当注解元素是{@link Annotation}接口的子类{@link Class}时返回{@code true}
|
||||
*
|
||||
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @return 是否支持扫描该注解元素
|
||||
*/
|
||||
@Override
|
||||
public boolean support(AnnotatedElement annotatedEle) {
|
||||
return (annotatedEle instanceof Class && ClassUtil.isAssignable(Annotation.class, (Class<?>) annotatedEle));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取注解元素上的全部注解。调用该方法前,需要确保调用{@link #support(AnnotatedElement)}返回为true
|
||||
*
|
||||
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @return 注解
|
||||
*/
|
||||
@Override
|
||||
public List<Annotation> getAnnotations(AnnotatedElement annotatedEle) {
|
||||
final List<Annotation> annotations = new ArrayList<>();
|
||||
scan(
|
||||
(index, annotation) -> annotations.add(annotation), annotatedEle,
|
||||
annotation -> ObjectUtil.notEqual(annotation, annotatedEle)
|
||||
);
|
||||
return annotations;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按广度优先扫描指定注解上的元注解,对扫描到的注解与层级索引进行操作
|
||||
*
|
||||
* @param consumer 当前层级索引与操作
|
||||
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @param filter 过滤器
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void scan(BiConsumer<Integer, Annotation> consumer, AnnotatedElement annotatedEle, Predicate<Annotation> filter) {
|
||||
filter = ObjectUtil.defaultIfNull(filter, a -> t -> true);
|
||||
Set<Class<? extends Annotation>> accessed = new HashSet<>();
|
||||
final Deque<List<Class<? extends Annotation>>> deque = CollUtil.newLinkedList(CollUtil.newArrayList((Class<? extends Annotation>) annotatedEle));
|
||||
int distance = 0;
|
||||
do {
|
||||
final List<Class<? extends Annotation>> annotationTypes = deque.removeFirst();
|
||||
for (final Class<? extends Annotation> type : annotationTypes) {
|
||||
final List<Annotation> metaAnnotations = Stream.of(type.getAnnotations())
|
||||
.filter(a -> !AnnotationUtil.isJdkMetaAnnotation(a.annotationType()))
|
||||
.filter(filter)
|
||||
.collect(Collectors.toList());
|
||||
for (final Annotation metaAnnotation : metaAnnotations) {
|
||||
consumer.accept(distance, metaAnnotation);
|
||||
}
|
||||
accessed.add(type);
|
||||
List<Class<? extends Annotation>> next = metaAnnotations.stream()
|
||||
.map(Annotation::annotationType)
|
||||
.filter(t -> !accessed.contains(t))
|
||||
.collect(Collectors.toList());
|
||||
if (CollUtil.isNotEmpty(next)) {
|
||||
deque.addLast(next);
|
||||
}
|
||||
}
|
||||
distance++;
|
||||
} while (includeSupperMetaAnnotation && !deque.isEmpty());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation.scanner;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.collection.CollUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ArrayUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ClassUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 扫描{@link Method}上的注解
|
||||
*
|
||||
* @author huangchengxing
|
||||
*/
|
||||
public class MethodAnnotationScanner extends AbstractTypeAnnotationScanner<MethodAnnotationScanner> implements AnnotationScanner {
|
||||
|
||||
/**
|
||||
* 构造一个类注解扫描器,仅扫描该方法上直接声明的注解
|
||||
*/
|
||||
public MethodAnnotationScanner() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造一个类注解扫描器
|
||||
*
|
||||
* @param scanSameSignatureMethod 是否扫描类层级结构中具有相同方法签名的方法
|
||||
*/
|
||||
public MethodAnnotationScanner(boolean scanSameSignatureMethod) {
|
||||
this(scanSameSignatureMethod, targetClass -> true, CollUtil.newLinkedHashSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造一个方法注解扫描器
|
||||
*
|
||||
* @param scanSameSignatureMethod 是否扫描类层级结构中具有相同方法签名的方法
|
||||
* @param filter 过滤器
|
||||
* @param excludeTypes 不包含的类型
|
||||
*/
|
||||
public MethodAnnotationScanner(boolean scanSameSignatureMethod, Predicate<Class<?>> filter, Set<Class<?>> excludeTypes) {
|
||||
super(scanSameSignatureMethod, scanSameSignatureMethod, filter, excludeTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造一个方法注解扫描器
|
||||
*
|
||||
* @param includeSuperClass 是否允许扫描父类中具有相同方法签名的方法
|
||||
* @param includeInterfaces 是否允许扫描父接口中具有相同方法签名的方法
|
||||
* @param filter 过滤器
|
||||
* @param excludeTypes 不包含的类型
|
||||
*/
|
||||
public MethodAnnotationScanner(boolean includeSuperClass, boolean includeInterfaces, Predicate<Class<?>> filter, Set<Class<?>> excludeTypes) {
|
||||
super(includeSuperClass, includeInterfaces, filter, excludeTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否支持扫描该注解元素,仅当注解元素是{@link Method}时返回{@code true}
|
||||
*
|
||||
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @return boolean 是否支持扫描该注解元素
|
||||
*/
|
||||
@Override
|
||||
public boolean support(AnnotatedElement annotatedEle) {
|
||||
return annotatedEle instanceof Method;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取声明该方法的类
|
||||
*
|
||||
* @param annotatedElement 注解元素
|
||||
* @return 要递归的类型
|
||||
* @see Method#getDeclaringClass()
|
||||
*/
|
||||
@Override
|
||||
protected Class<?> getClassFormAnnotatedElement(AnnotatedElement annotatedElement) {
|
||||
return ((Method)annotatedElement).getDeclaringClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* 若父类/父接口中方法具有相同的方法签名,则返回该方法上的注解
|
||||
*
|
||||
* @param source 原始方法
|
||||
* @param index 类的层级索引
|
||||
* @param targetClass 类
|
||||
* @return 最终所需的目标注解
|
||||
*/
|
||||
@Override
|
||||
protected Annotation[] getAnnotationsFromTargetClass(AnnotatedElement source, int index, Class<?> targetClass) {
|
||||
final Method sourceMethod = (Method) source;
|
||||
return Stream.of(ClassUtil.getDeclaredMethods(targetClass))
|
||||
.filter(superMethod -> !superMethod.isBridge())
|
||||
.filter(superMethod -> hasSameSignature(sourceMethod, superMethod))
|
||||
.map(AnnotatedElement::getAnnotations)
|
||||
.flatMap(Stream::of)
|
||||
.toArray(Annotation[]::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否扫描类层级结构中具有相同方法签名的方法
|
||||
*
|
||||
* @param scanSuperMethodIfOverride 是否扫描类层级结构中具有相同方法签名的方法
|
||||
* @return 当前实例
|
||||
*/
|
||||
public MethodAnnotationScanner setScanSameSignatureMethod(boolean scanSuperMethodIfOverride) {
|
||||
setIncludeInterfaces(scanSuperMethodIfOverride);
|
||||
setIncludeSuperClass(scanSuperMethodIfOverride);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 该方法是否具备与扫描的方法相同的方法签名
|
||||
*/
|
||||
private boolean hasSameSignature(Method sourceMethod, Method superMethod) {
|
||||
if (false == StrUtil.equals(sourceMethod.getName(), superMethod.getName())) {
|
||||
return false;
|
||||
}
|
||||
final Class<?>[] sourceParameterTypes = sourceMethod.getParameterTypes();
|
||||
final Class<?>[] targetParameterTypes = superMethod.getParameterTypes();
|
||||
if (sourceParameterTypes.length != targetParameterTypes.length) {
|
||||
return false;
|
||||
}
|
||||
if (!ArrayUtil.containsAll(sourceParameterTypes, targetParameterTypes)) {
|
||||
return false;
|
||||
}
|
||||
return ClassUtil.isAssignable(superMethod.getReturnType(), sourceMethod.getReturnType());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.annotation.scanner;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.collection.CollUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
/**
|
||||
* 扫描{@link Class}上的注解
|
||||
*
|
||||
* @author huangchengxing
|
||||
*/
|
||||
public class TypeAnnotationScanner extends AbstractTypeAnnotationScanner<TypeAnnotationScanner> implements AnnotationScanner {
|
||||
|
||||
/**
|
||||
* 构造一个类注解扫描器
|
||||
*
|
||||
* @param includeSupperClass 是否允许扫描父类
|
||||
* @param includeInterfaces 是否允许扫描父接口
|
||||
* @param filter 过滤器
|
||||
* @param excludeTypes 不包含的类型
|
||||
*/
|
||||
public TypeAnnotationScanner(boolean includeSupperClass, boolean includeInterfaces, Predicate<Class<?>> filter, Set<Class<?>> excludeTypes) {
|
||||
super(includeSupperClass, includeInterfaces, filter, excludeTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建一个类注解扫描器,默认允许扫描指定元素的父类以及父接口
|
||||
*/
|
||||
public TypeAnnotationScanner() {
|
||||
this(true, true, t -> true, CollUtil.newLinkedHashSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否支持扫描该注解元素,仅当注解元素是{@link Class}接时返回{@code true}
|
||||
*
|
||||
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @return 是否支持扫描该注解元素
|
||||
*/
|
||||
@Override
|
||||
public boolean support(AnnotatedElement annotatedEle) {
|
||||
return annotatedEle instanceof Class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将注解元素转为{@link Class}
|
||||
*
|
||||
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
|
||||
* @return 要递归的类型
|
||||
*/
|
||||
@Override
|
||||
protected Class<?> getClassFormAnnotatedElement(AnnotatedElement annotatedEle) {
|
||||
return (Class<?>)annotatedEle;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取{@link Class#getAnnotations()}
|
||||
*
|
||||
* @param source 最初的注解元素
|
||||
* @param index 类的层级索引
|
||||
* @param targetClass 类
|
||||
* @return 类上直接声明的注解
|
||||
*/
|
||||
@Override
|
||||
protected Annotation[] getAnnotationsFromTargetClass(AnnotatedElement source, int index, Class<?> targetClass) {
|
||||
return targetClass.getAnnotations();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否允许扫描父类
|
||||
*
|
||||
* @param includeSuperClass 是否允许扫描父类
|
||||
* @return 当前实例
|
||||
*/
|
||||
@Override
|
||||
public TypeAnnotationScanner setIncludeSuperClass(boolean includeSuperClass) {
|
||||
return super.setIncludeSuperClass(includeSuperClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否允许扫描父接口
|
||||
*
|
||||
* @param includeInterfaces 是否允许扫描父类
|
||||
* @return 当前实例
|
||||
*/
|
||||
@Override
|
||||
public TypeAnnotationScanner setIncludeInterfaces(boolean includeInterfaces) {
|
||||
return super.setIncludeInterfaces(includeInterfaces);
|
||||
}
|
||||
|
||||
/**
|
||||
* 若类型为jdk代理类,则尝试转换为原始被代理类
|
||||
*/
|
||||
public static class JdkProxyClassConverter implements UnaryOperator<Class<?>> {
|
||||
@Override
|
||||
public Class<?> apply(Class<?> sourceClass) {
|
||||
return Proxy.isProxyClass(sourceClass) ? apply(sourceClass.getSuperclass()) : sourceClass;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
/**
|
||||
* 注解包扫描封装
|
||||
*
|
||||
* @author looly
|
||||
*
|
||||
*/
|
||||
package aiyh.utils.tool.cn.hutool.core.annotation.scanner;
|
|
@ -0,0 +1,324 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.bean;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||
import aiyh.utils.tool.cn.hutool.core.map.CaseInsensitiveMap;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.BooleanUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ModifierUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ReflectUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Bean信息描述做为BeanInfo替代方案,此对象持有JavaBean中的setters和getters等相关信息描述<br>
|
||||
* 查找Getter和Setter方法时会:
|
||||
*
|
||||
* <pre>
|
||||
* 1. 忽略字段和方法名的大小写
|
||||
* 2. Getter查找getXXX、isXXX、getIsXXX
|
||||
* 3. Setter查找setXXX、setIsXXX
|
||||
* 4. Setter忽略参数值与字段值不匹配的情况,因此有多个参数类型的重载时,会调用首次匹配的
|
||||
* </pre>
|
||||
*
|
||||
* @author looly
|
||||
* @since 3.1.2
|
||||
*/
|
||||
public class BeanDesc implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Bean类
|
||||
*/
|
||||
private final Class<?> beanClass;
|
||||
/**
|
||||
* 属性Map
|
||||
*/
|
||||
private final Map<String, PropDesc> propMap = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param beanClass Bean类
|
||||
*/
|
||||
public BeanDesc(Class<?> beanClass) {
|
||||
Assert.notNull(beanClass);
|
||||
this.beanClass = beanClass;
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Bean的全类名
|
||||
*
|
||||
* @return Bean的类名
|
||||
*/
|
||||
public String getName() {
|
||||
return this.beanClass.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Bean的简单类名
|
||||
*
|
||||
* @return Bean的类名
|
||||
*/
|
||||
public String getSimpleName() {
|
||||
return this.beanClass.getSimpleName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字段名-字段属性Map
|
||||
*
|
||||
* @param ignoreCase 是否忽略大小写,true为忽略,false不忽略
|
||||
* @return 字段名-字段属性Map
|
||||
*/
|
||||
public Map<String, PropDesc> getPropMap(boolean ignoreCase) {
|
||||
return ignoreCase ? new CaseInsensitiveMap<>(1, this.propMap) : this.propMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字段属性列表
|
||||
*
|
||||
* @return {@link PropDesc} 列表
|
||||
*/
|
||||
public Collection<PropDesc> getProps() {
|
||||
return this.propMap.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取属性,如果不存在返回null
|
||||
*
|
||||
* @param fieldName 字段名
|
||||
* @return {@link PropDesc}
|
||||
*/
|
||||
public PropDesc getProp(String fieldName) {
|
||||
return this.propMap.get(fieldName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得字段名对应的字段对象,如果不存在返回null
|
||||
*
|
||||
* @param fieldName 字段名
|
||||
* @return 字段值
|
||||
*/
|
||||
public Field getField(String fieldName) {
|
||||
final PropDesc desc = this.propMap.get(fieldName);
|
||||
return null == desc ? null : desc.getField();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Getter方法,如果不存在返回null
|
||||
*
|
||||
* @param fieldName 字段名
|
||||
* @return Getter方法
|
||||
*/
|
||||
public Method getGetter(String fieldName) {
|
||||
final PropDesc desc = this.propMap.get(fieldName);
|
||||
return null == desc ? null : desc.getGetter();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Setter方法,如果不存在返回null
|
||||
*
|
||||
* @param fieldName 字段名
|
||||
* @return Setter方法
|
||||
*/
|
||||
public Method getSetter(String fieldName) {
|
||||
final PropDesc desc = this.propMap.get(fieldName);
|
||||
return null == desc ? null : desc.getSetter();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------ Private method start
|
||||
|
||||
/**
|
||||
* 初始化<br>
|
||||
* 只有与属性关联的相关Getter和Setter方法才会被读取,无关的getXXX和setXXX都被忽略
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
private BeanDesc init() {
|
||||
final Method[] gettersAndSetters = ReflectUtil.getMethods(this.beanClass, ReflectUtil::isGetterOrSetterIgnoreCase);
|
||||
PropDesc prop;
|
||||
for (Field field : ReflectUtil.getFields(this.beanClass)) {
|
||||
// 排除静态属性和对象子类
|
||||
if (false == ModifierUtil.isStatic(field) && false == ReflectUtil.isOuterClassField(field)) {
|
||||
prop = createProp(field, gettersAndSetters);
|
||||
// 只有不存在时才放入,防止父类属性覆盖子类属性
|
||||
this.propMap.putIfAbsent(prop.getFieldName(), prop);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据字段创建属性描述<br>
|
||||
* 查找Getter和Setter方法时会:
|
||||
*
|
||||
* <pre>
|
||||
* 1. 忽略字段和方法名的大小写
|
||||
* 2. Getter查找getXXX、isXXX、getIsXXX
|
||||
* 3. Setter查找setXXX、setIsXXX
|
||||
* 4. Setter忽略参数值与字段值不匹配的情况,因此有多个参数类型的重载时,会调用首次匹配的
|
||||
* </pre>
|
||||
*
|
||||
* @param field 字段
|
||||
* @param methods 类中所有的方法
|
||||
* @return {@link PropDesc}
|
||||
* @since 4.0.2
|
||||
*/
|
||||
private PropDesc createProp(Field field, Method[] methods) {
|
||||
final PropDesc prop = findProp(field, methods, false);
|
||||
// 忽略大小写重新匹配一次
|
||||
if (null == prop.getter || null == prop.setter) {
|
||||
final PropDesc propIgnoreCase = findProp(field, methods, true);
|
||||
if (null == prop.getter) {
|
||||
prop.getter = propIgnoreCase.getter;
|
||||
}
|
||||
if (null == prop.setter) {
|
||||
prop.setter = propIgnoreCase.setter;
|
||||
}
|
||||
}
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找字段对应的Getter和Setter方法
|
||||
*
|
||||
* @param field 字段
|
||||
* @param gettersOrSetters 类中所有的Getter或Setter方法
|
||||
* @param ignoreCase 是否忽略大小写匹配
|
||||
* @return PropDesc
|
||||
*/
|
||||
private PropDesc findProp(Field field, Method[] gettersOrSetters, boolean ignoreCase) {
|
||||
final String fieldName = field.getName();
|
||||
final Class<?> fieldType = field.getType();
|
||||
final boolean isBooleanField = BooleanUtil.isBoolean(fieldType);
|
||||
|
||||
Method getter = null;
|
||||
Method setter = null;
|
||||
String methodName;
|
||||
for (Method method : gettersOrSetters) {
|
||||
methodName = method.getName();
|
||||
if (method.getParameterCount() == 0) {
|
||||
// 无参数,可能为Getter方法
|
||||
if (isMatchGetter(methodName, fieldName, isBooleanField, ignoreCase)) {
|
||||
// 方法名与字段名匹配,则为Getter方法
|
||||
getter = method;
|
||||
}
|
||||
} else if (isMatchSetter(methodName, fieldName, isBooleanField, ignoreCase)) {
|
||||
// setter方法的参数类型和字段类型必须一致,或参数类型是字段类型的子类
|
||||
if(fieldType.isAssignableFrom(method.getParameterTypes()[0])){
|
||||
setter = method;
|
||||
}
|
||||
}
|
||||
if (null != getter && null != setter) {
|
||||
// 如果Getter和Setter方法都找到了,不再继续寻找
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new PropDesc(field, getter, setter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 方法是否为Getter方法<br>
|
||||
* 匹配规则如下(忽略大小写):
|
||||
*
|
||||
* <pre>
|
||||
* 字段名 -》 方法名
|
||||
* isName -》 isName
|
||||
* isName -》 isIsName
|
||||
* isName -》 getIsName
|
||||
* name -》 isName
|
||||
* name -》 getName
|
||||
* </pre>
|
||||
*
|
||||
* @param methodName 方法名
|
||||
* @param fieldName 字段名
|
||||
* @param isBooleanField 是否为Boolean类型字段
|
||||
* @param ignoreCase 匹配是否忽略大小写
|
||||
* @return 是否匹配
|
||||
*/
|
||||
private boolean isMatchGetter(String methodName, String fieldName, boolean isBooleanField, boolean ignoreCase) {
|
||||
final String handledFieldName;
|
||||
if (ignoreCase) {
|
||||
// 全部转为小写,忽略大小写比较
|
||||
methodName = methodName.toLowerCase();
|
||||
handledFieldName = fieldName.toLowerCase();
|
||||
fieldName = handledFieldName;
|
||||
} else {
|
||||
handledFieldName = StrUtil.upperFirst(fieldName);
|
||||
}
|
||||
|
||||
// 针对Boolean类型特殊检查
|
||||
if (isBooleanField) {
|
||||
if (fieldName.startsWith("is")) {
|
||||
// 字段已经是is开头
|
||||
if (methodName.equals(fieldName) // isName -》 isName
|
||||
|| ("get" + handledFieldName).equals(methodName)// isName -》 getIsName
|
||||
|| ("is" + handledFieldName).equals(methodName)// isName -》 isIsName
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
} else if (("is" + handledFieldName).equals(methodName)) {
|
||||
// 字段非is开头, name -》 isName
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 包括boolean的任何类型只有一种匹配情况:name -》 getName
|
||||
return ("get" + handledFieldName).equals(methodName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 方法是否为Setter方法<br>
|
||||
* 匹配规则如下(忽略大小写):
|
||||
*
|
||||
* <pre>
|
||||
* 字段名 -》 方法名
|
||||
* isName -》 setName
|
||||
* isName -》 setIsName
|
||||
* name -》 setName
|
||||
* </pre>
|
||||
*
|
||||
* @param methodName 方法名
|
||||
* @param fieldName 字段名
|
||||
* @param isBooleanField 是否为Boolean类型字段
|
||||
* @param ignoreCase 匹配是否忽略大小写
|
||||
* @return 是否匹配
|
||||
*/
|
||||
private boolean isMatchSetter(String methodName, String fieldName, boolean isBooleanField, boolean ignoreCase) {
|
||||
final String handledFieldName;
|
||||
if (ignoreCase) {
|
||||
// 全部转为小写,忽略大小写比较
|
||||
methodName = methodName.toLowerCase();
|
||||
handledFieldName = fieldName.toLowerCase();
|
||||
fieldName = handledFieldName;
|
||||
} else {
|
||||
handledFieldName = StrUtil.upperFirst(fieldName);
|
||||
}
|
||||
|
||||
// 非标准Setter方法跳过
|
||||
if (false == methodName.startsWith("set")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 针对Boolean类型特殊检查
|
||||
if (isBooleanField && fieldName.startsWith("is")) {
|
||||
// 字段是is开头
|
||||
if (("set" + StrUtil.removePrefix(fieldName, "is")).equals(methodName)// isName -》 setName
|
||||
|| ("set" + handledFieldName).equals(methodName)// isName -》 setIsName
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 包括boolean的任何类型只有一种匹配情况:name -》 setName
|
||||
return ("set" + handledFieldName).equals(methodName);
|
||||
}
|
||||
// ------------------------------------------------------------------------------------------------------ Private method end
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.bean;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.func.Func0;
|
||||
import aiyh.utils.tool.cn.hutool.core.map.WeakConcurrentMap;
|
||||
|
||||
/**
|
||||
* Bean属性缓存<br>
|
||||
* 缓存用于防止多次反射造成的性能问题
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public enum BeanDescCache {
|
||||
INSTANCE;
|
||||
|
||||
private final WeakConcurrentMap<Class<?>, BeanDesc> bdCache = new WeakConcurrentMap<>();
|
||||
|
||||
/**
|
||||
* 获得属性名和{@link BeanDesc}Map映射
|
||||
*
|
||||
* @param beanClass Bean的类
|
||||
* @param supplier 对象不存在时创建对象的函数
|
||||
* @return 属性名和{@link BeanDesc}映射
|
||||
* @since 5.4.2
|
||||
*/
|
||||
public BeanDesc getBeanDesc(Class<?> beanClass, Func0<BeanDesc> supplier) {
|
||||
return bdCache.computeIfAbsent(beanClass, (key)->supplier.callWithRuntimeException());
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空全局的Bean属性缓存
|
||||
*
|
||||
* @since 5.7.21
|
||||
*/
|
||||
public void clear() {
|
||||
this.bdCache.clear();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.bean;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.exceptions.ExceptionUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||
|
||||
/**
|
||||
* Bean异常
|
||||
* @author xiaoleilu
|
||||
*/
|
||||
public class BeanException extends RuntimeException{
|
||||
private static final long serialVersionUID = -8096998667745023423L;
|
||||
|
||||
public BeanException(Throwable e) {
|
||||
super(ExceptionUtil.getMessage(e), e);
|
||||
}
|
||||
|
||||
public BeanException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public BeanException(String messageTemplate, Object... params) {
|
||||
super(StrUtil.format(messageTemplate, params));
|
||||
}
|
||||
|
||||
public BeanException(String message, Throwable throwable) {
|
||||
super(message, throwable);
|
||||
}
|
||||
|
||||
public BeanException(Throwable throwable, String messageTemplate, Object... params) {
|
||||
super(StrUtil.format(messageTemplate, params), throwable);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.bean;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.func.Func0;
|
||||
import aiyh.utils.tool.cn.hutool.core.map.ReferenceConcurrentMap;
|
||||
import aiyh.utils.tool.cn.hutool.core.map.WeakConcurrentMap;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Bean属性缓存<br>
|
||||
* 缓存用于防止多次反射造成的性能问题
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public enum BeanInfoCache {
|
||||
INSTANCE;
|
||||
|
||||
private final WeakConcurrentMap<Class<?>, Map<String, PropertyDescriptor>> pdCache = new WeakConcurrentMap<>();
|
||||
private final WeakConcurrentMap<Class<?>, Map<String, PropertyDescriptor>> ignoreCasePdCache = new WeakConcurrentMap<>();
|
||||
|
||||
/**
|
||||
* 获得属性名和{@link PropertyDescriptor}Map映射
|
||||
*
|
||||
* @param beanClass Bean的类
|
||||
* @param ignoreCase 是否忽略大小写
|
||||
* @return 属性名和{@link PropertyDescriptor}Map映射
|
||||
*/
|
||||
public Map<String, PropertyDescriptor> getPropertyDescriptorMap(Class<?> beanClass, boolean ignoreCase) {
|
||||
return getCache(ignoreCase).get(beanClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得属性名和{@link PropertyDescriptor}Map映射
|
||||
*
|
||||
* @param beanClass Bean的类
|
||||
* @param ignoreCase 是否忽略大小写
|
||||
* @param supplier 缓存对象产生函数
|
||||
* @return 属性名和{@link PropertyDescriptor}Map映射
|
||||
* @since 5.4.1
|
||||
*/
|
||||
public Map<String, PropertyDescriptor> getPropertyDescriptorMap(
|
||||
Class<?> beanClass,
|
||||
boolean ignoreCase,
|
||||
Func0<Map<String, PropertyDescriptor>> supplier) {
|
||||
return getCache(ignoreCase).computeIfAbsent(beanClass, (key)->supplier.callWithRuntimeException());
|
||||
}
|
||||
|
||||
/**
|
||||
* 加入缓存
|
||||
*
|
||||
* @param beanClass Bean的类
|
||||
* @param fieldNamePropertyDescriptorMap 属性名和{@link PropertyDescriptor}Map映射
|
||||
* @param ignoreCase 是否忽略大小写
|
||||
*/
|
||||
public void putPropertyDescriptorMap(Class<?> beanClass, Map<String, PropertyDescriptor> fieldNamePropertyDescriptorMap, boolean ignoreCase) {
|
||||
getCache(ignoreCase).put(beanClass, fieldNamePropertyDescriptorMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空缓存
|
||||
*
|
||||
* @since 5.7.21
|
||||
*/
|
||||
public void clear() {
|
||||
this.pdCache.clear();
|
||||
this.ignoreCasePdCache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据是否忽略字段名的大小写,返回不用Cache对象
|
||||
*
|
||||
* @param ignoreCase 是否忽略大小写
|
||||
* @return {@link ReferenceConcurrentMap}
|
||||
* @since 5.4.1
|
||||
*/
|
||||
private ReferenceConcurrentMap<Class<?>, Map<String, PropertyDescriptor>> getCache(boolean ignoreCase) {
|
||||
return ignoreCase ? ignoreCasePdCache : pdCache;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,324 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.bean;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.collection.CollUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.collection.ListUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.convert.Convert;
|
||||
import aiyh.utils.tool.cn.hutool.core.map.MapUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ArrayUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.CharUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.NumberUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Bean路径表达式,用于获取多层嵌套Bean中的字段值或Bean对象<br>
|
||||
* 根据给定的表达式,查找Bean中对应的属性值对象。 表达式分为两种:
|
||||
* <ol>
|
||||
* <li>.表达式,可以获取Bean对象中的属性(字段)值或者Map中key对应的值</li>
|
||||
* <li>[]表达式,可以获取集合等对象中对应index的值</li>
|
||||
* </ol>
|
||||
* <p>
|
||||
* 表达式栗子:
|
||||
*
|
||||
* <pre>
|
||||
* persion
|
||||
* persion.name
|
||||
* persons[3]
|
||||
* person.friends[5].name
|
||||
* ['person']['friends'][5]['name']
|
||||
* </pre>
|
||||
*
|
||||
* @author Looly
|
||||
* @since 4.0.6
|
||||
*/
|
||||
public class BeanPath implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 表达式边界符号数组
|
||||
*/
|
||||
private static final char[] EXP_CHARS = {CharUtil.DOT, CharUtil.BRACKET_START, CharUtil.BRACKET_END};
|
||||
|
||||
private boolean isStartWith = false;
|
||||
protected List<String> patternParts;
|
||||
|
||||
/**
|
||||
* 解析Bean路径表达式为Bean模式<br>
|
||||
* Bean表达式,用于获取多层嵌套Bean中的字段值或Bean对象<br>
|
||||
* 根据给定的表达式,查找Bean中对应的属性值对象。 表达式分为两种:
|
||||
* <ol>
|
||||
* <li>.表达式,可以获取Bean对象中的属性(字段)值或者Map中key对应的值</li>
|
||||
* <li>[]表达式,可以获取集合等对象中对应index的值</li>
|
||||
* </ol>
|
||||
* <p>
|
||||
* 表达式栗子:
|
||||
*
|
||||
* <pre>
|
||||
* persion
|
||||
* persion.name
|
||||
* persons[3]
|
||||
* person.friends[5].name
|
||||
* ['person']['friends'][5]['name']
|
||||
* </pre>
|
||||
*
|
||||
* @param expression 表达式
|
||||
* @return BeanPath
|
||||
*/
|
||||
public static BeanPath create(final String expression) {
|
||||
return new BeanPath(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param expression 表达式
|
||||
*/
|
||||
public BeanPath(final String expression) {
|
||||
init(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取表达式解析后的分段列表
|
||||
*
|
||||
* @return 表达式分段列表
|
||||
*/
|
||||
public List<String> getPatternParts() {
|
||||
return this.patternParts;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Bean中对应表达式的值
|
||||
*
|
||||
* @param bean Bean对象或Map或List等
|
||||
* @return 值,如果对应值不存在,则返回null
|
||||
*/
|
||||
public Object get(final Object bean) {
|
||||
return get(this.patternParts, bean, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置表达式指定位置(或filed对应)的值<br>
|
||||
* 若表达式指向一个List则设置其坐标对应位置的值,若指向Map则put对应key的值,Bean则设置字段的值<br>
|
||||
* 注意:
|
||||
*
|
||||
* <pre>
|
||||
* 1. 如果为List,如果下标不大于List长度,则替换原有值,否则追加值
|
||||
* 2. 如果为数组,如果下标不大于数组长度,则替换原有值,否则追加值
|
||||
* </pre>
|
||||
*
|
||||
* @param bean Bean、Map或List
|
||||
* @param value 值
|
||||
*/
|
||||
public void set(final Object bean, final Object value) {
|
||||
set(bean, this.patternParts, lastIsNumber(this.patternParts), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.patternParts.toString();
|
||||
}
|
||||
|
||||
//region Private Methods
|
||||
|
||||
/**
|
||||
* 设置表达式指定位置(或filed对应)的值<br>
|
||||
* 若表达式指向一个List则设置其坐标对应位置的值,若指向Map则put对应key的值,Bean则设置字段的值<br>
|
||||
* 注意:
|
||||
*
|
||||
* <pre>
|
||||
* 1. 如果为List,如果下标不大于List长度,则替换原有值,否则追加值
|
||||
* 2. 如果为数组,如果下标不大于数组长度,则替换原有值,否则追加值
|
||||
* </pre>
|
||||
*
|
||||
* @param bean Bean、Map或List
|
||||
* @param patternParts 表达式块列表
|
||||
* @param value 值
|
||||
* @return 值
|
||||
*/
|
||||
private void set(Object bean, List<String> patternParts, boolean nextNumberPart, Object value) {
|
||||
Object subBean = this.get(patternParts, bean, true);
|
||||
if (null == subBean) {
|
||||
final List<String> parentParts = getParentParts(patternParts);
|
||||
this.set(bean, parentParts, lastIsNumber(parentParts), nextNumberPart ? new ArrayList<>() : new HashMap<>());
|
||||
//set中有可能做过转换,因此此处重新获取bean
|
||||
subBean = this.get(patternParts, bean, true);
|
||||
}
|
||||
BeanUtil.setFieldValue(subBean, patternParts.get(patternParts.size() - 1), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断path列表中末尾的标记是否为数字
|
||||
*
|
||||
* @param patternParts path列表
|
||||
* @return 是否为数字
|
||||
*/
|
||||
private static boolean lastIsNumber(List<String> patternParts) {
|
||||
return NumberUtil.isInteger(patternParts.get(patternParts.size() - 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取父级路径列表
|
||||
*
|
||||
* @param patternParts 路径列表
|
||||
* @return 父级路径列表
|
||||
*/
|
||||
private static List<String> getParentParts(List<String> patternParts) {
|
||||
return patternParts.subList(0, patternParts.size() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Bean中对应表达式的值
|
||||
*
|
||||
* @param patternParts 表达式分段列表
|
||||
* @param bean Bean对象或Map或List等
|
||||
* @param ignoreLast 是否忽略最后一个值,忽略最后一个值则用于set,否则用于read
|
||||
* @return 值,如果对应值不存在,则返回null
|
||||
*/
|
||||
private Object get(final List<String> patternParts, final Object bean, final boolean ignoreLast) {
|
||||
int length = patternParts.size();
|
||||
if (ignoreLast) {
|
||||
length--;
|
||||
}
|
||||
Object subBean = bean;
|
||||
boolean isFirst = true;
|
||||
String patternPart;
|
||||
for (int i = 0; i < length; i++) {
|
||||
patternPart = patternParts.get(i);
|
||||
subBean = getFieldValue(subBean, patternPart);
|
||||
if (null == subBean) {
|
||||
// 支持表达式的第一个对象为Bean本身(若用户定义表达式$开头,则不做此操作)
|
||||
if (isFirst && false == this.isStartWith && BeanUtil.isMatchName(bean, patternPart, true)) {
|
||||
subBean = bean;
|
||||
isFirst = false;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return subBean;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Object getFieldValue(final Object bean, final String expression) {
|
||||
if (StrUtil.isBlank(expression)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (StrUtil.contains(expression, ':')) {
|
||||
// [start:end:step] 模式
|
||||
final List<String> parts = StrUtil.splitTrim(expression, ':');
|
||||
final int start = Integer.parseInt(parts.get(0));
|
||||
final int end = Integer.parseInt(parts.get(1));
|
||||
int step = 1;
|
||||
if (3 == parts.size()) {
|
||||
step = Integer.parseInt(parts.get(2));
|
||||
}
|
||||
if (bean instanceof Collection) {
|
||||
return CollUtil.sub((Collection<?>) bean, start, end, step);
|
||||
} else if (ArrayUtil.isArray(bean)) {
|
||||
return ArrayUtil.sub(bean, start, end, step);
|
||||
}
|
||||
} else if (StrUtil.contains(expression, ',')) {
|
||||
// [num0,num1,num2...]模式或者['key0','key1']模式
|
||||
final List<String> keys = StrUtil.splitTrim(expression, ',');
|
||||
if (bean instanceof Collection) {
|
||||
return CollUtil.getAny((Collection<?>) bean, Convert.convert(int[].class, keys));
|
||||
} else if (ArrayUtil.isArray(bean)) {
|
||||
return ArrayUtil.getAny(bean, Convert.convert(int[].class, keys));
|
||||
} else {
|
||||
final String[] unWrappedKeys = new String[keys.size()];
|
||||
for (int i = 0; i < unWrappedKeys.length; i++) {
|
||||
unWrappedKeys[i] = StrUtil.unWrap(keys.get(i), '\'');
|
||||
}
|
||||
if (bean instanceof Map) {
|
||||
// 只支持String为key的Map
|
||||
return MapUtil.getAny((Map<String, ?>) bean, unWrappedKeys);
|
||||
} else {
|
||||
final Map<String, Object> map = BeanUtil.beanToMap(bean);
|
||||
return MapUtil.getAny(map, unWrappedKeys);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 数字或普通字符串
|
||||
return BeanUtil.getFieldValue(bean, expression);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*
|
||||
* @param expression 表达式
|
||||
*/
|
||||
private void init(final String expression) {
|
||||
final List<String> localPatternParts = new ArrayList<>();
|
||||
final int length = expression.length();
|
||||
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
char c;
|
||||
boolean isNumStart = false;// 下标标识符开始
|
||||
boolean isInWrap = false; //标识是否在引号内
|
||||
for (int i = 0; i < length; i++) {
|
||||
c = expression.charAt(i);
|
||||
if (0 == i && '$' == c) {
|
||||
// 忽略开头的$符,表示当前对象
|
||||
isStartWith = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ('\'' == c) {
|
||||
// 结束
|
||||
isInWrap = (false == isInWrap);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (false == isInWrap && ArrayUtil.contains(EXP_CHARS, c)) {
|
||||
// 处理边界符号
|
||||
if (CharUtil.BRACKET_END == c) {
|
||||
// 中括号(数字下标)结束
|
||||
if (false == isNumStart) {
|
||||
throw new IllegalArgumentException(StrUtil.format("Bad expression '{}':{}, we find ']' but no '[' !", expression, i));
|
||||
}
|
||||
isNumStart = false;
|
||||
// 中括号结束加入下标
|
||||
} else {
|
||||
if (isNumStart) {
|
||||
// 非结束中括号情况下发现起始中括号报错(中括号未关闭)
|
||||
throw new IllegalArgumentException(StrUtil.format("Bad expression '{}':{}, we find '[' but no ']' !", expression, i));
|
||||
} else if (CharUtil.BRACKET_START == c) {
|
||||
// 数字下标开始
|
||||
isNumStart = true;
|
||||
}
|
||||
// 每一个边界符之前的表达式是一个完整的KEY,开始处理KEY
|
||||
}
|
||||
if (builder.length() > 0) {
|
||||
localPatternParts.add(builder.toString());
|
||||
}
|
||||
builder.setLength(0);
|
||||
} else {
|
||||
// 非边界符号,追加字符
|
||||
builder.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
// 末尾边界符检查
|
||||
if (isNumStart) {
|
||||
throw new IllegalArgumentException(StrUtil.format("Bad expression '{}':{}, we find '[' but no ']' !", expression, length - 1));
|
||||
} else {
|
||||
if (builder.length() > 0) {
|
||||
localPatternParts.add(builder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// 不可变List
|
||||
this.patternParts = ListUtil.unmodifiable(localPatternParts);
|
||||
}
|
||||
//endregion
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,226 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.bean;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.clone.CloneSupport;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ClassUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ReflectUtil;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 动态Bean,通过反射对Bean的相关方法做操作<br>
|
||||
* 支持Map和普通Bean
|
||||
*
|
||||
* @author Looly
|
||||
* @since 3.0.7
|
||||
*/
|
||||
public class DynaBean extends CloneSupport<DynaBean> implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final Class<?> beanClass;
|
||||
private final Object bean;
|
||||
|
||||
/**
|
||||
* 创建一个DynaBean
|
||||
*
|
||||
* @param bean 普通Bean
|
||||
* @return DynaBean
|
||||
*/
|
||||
public static DynaBean create(Object bean) {
|
||||
return new DynaBean(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个DynaBean
|
||||
*
|
||||
* @param beanClass Bean类
|
||||
* @return DynaBean
|
||||
*/
|
||||
public static DynaBean create(Class<?> beanClass) {
|
||||
return new DynaBean(beanClass);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建一个DynaBean
|
||||
*
|
||||
* @param beanClass Bean类
|
||||
* @param params 构造Bean所需要的参数
|
||||
* @return DynaBean
|
||||
*/
|
||||
public static DynaBean create(Class<?> beanClass, Object... params) {
|
||||
return new DynaBean(beanClass, params);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------ Constructor start
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param beanClass Bean类
|
||||
* @param params 构造Bean所需要的参数
|
||||
*/
|
||||
public DynaBean(Class<?> beanClass, Object... params) {
|
||||
this(ReflectUtil.newInstance(beanClass, params));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param beanClass Bean类
|
||||
*/
|
||||
public DynaBean(Class<?> beanClass) {
|
||||
this(ReflectUtil.newInstance(beanClass));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param bean 原始Bean
|
||||
*/
|
||||
public DynaBean(Object bean) {
|
||||
Assert.notNull(bean);
|
||||
if (bean instanceof DynaBean) {
|
||||
bean = ((DynaBean) bean).getBean();
|
||||
}
|
||||
this.bean = bean;
|
||||
this.beanClass = ClassUtil.getClass(bean);
|
||||
}
|
||||
//------------------------------------------------------------------------ Constructor end
|
||||
|
||||
/**
|
||||
* 获得字段对应值
|
||||
*
|
||||
* @param <T> 属性值类型
|
||||
* @param fieldName 字段名
|
||||
* @return 字段值
|
||||
* @throws BeanException 反射获取属性值或字段值导致的异常
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T get(String fieldName) throws BeanException {
|
||||
if (Map.class.isAssignableFrom(beanClass)) {
|
||||
return (T) ((Map<?, ?>) bean).get(fieldName);
|
||||
} else {
|
||||
final PropDesc prop = BeanUtil.getBeanDesc(beanClass).getProp(fieldName);
|
||||
if (null == prop) {
|
||||
throw new BeanException("No public field or get method for {}", fieldName);
|
||||
}
|
||||
return (T) prop.getValue(bean);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否有指定名称的bean属性
|
||||
*
|
||||
* @param fieldName 字段名
|
||||
* @return 是否有bean属性
|
||||
* @since 5.4.2
|
||||
*/
|
||||
public boolean containsProp(String fieldName) {
|
||||
if (Map.class.isAssignableFrom(beanClass)) {
|
||||
return ((Map<?, ?>) bean).containsKey(fieldName);
|
||||
} else{
|
||||
return null != BeanUtil.getBeanDesc(beanClass).getProp(fieldName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得字段对应值,获取异常返回{@code null}
|
||||
*
|
||||
* @param <T> 属性值类型
|
||||
* @param fieldName 字段名
|
||||
* @return 字段值
|
||||
* @since 3.1.1
|
||||
*/
|
||||
public <T> T safeGet(String fieldName) {
|
||||
try {
|
||||
return get(fieldName);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置字段值
|
||||
*
|
||||
* @param fieldName 字段名
|
||||
* @param value 字段值
|
||||
* @throws BeanException 反射获取属性值或字段值导致的异常
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public void set(String fieldName, Object value) throws BeanException {
|
||||
if (Map.class.isAssignableFrom(beanClass)) {
|
||||
((Map) bean).put(fieldName, value);
|
||||
} else {
|
||||
final PropDesc prop = BeanUtil.getBeanDesc(beanClass).getProp(fieldName);
|
||||
if (null == prop) {
|
||||
throw new BeanException("No public field or set method for {}", fieldName);
|
||||
}
|
||||
prop.setValue(bean, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行原始Bean中的方法
|
||||
*
|
||||
* @param methodName 方法名
|
||||
* @param params 参数
|
||||
* @return 执行结果,可能为null
|
||||
*/
|
||||
public Object invoke(String methodName, Object... params) {
|
||||
return ReflectUtil.invoke(this.bean, methodName, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得原始Bean
|
||||
*
|
||||
* @param <T> Bean类型
|
||||
* @return bean
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getBean() {
|
||||
return (T) this.bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得Bean的类型
|
||||
*
|
||||
* @param <T> Bean类型
|
||||
* @return Bean类型
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Class<T> getBeanClass() {
|
||||
return (Class<T>) this.beanClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((bean == null) ? 0 : bean.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final DynaBean other = (DynaBean) obj;
|
||||
if (bean == null) {
|
||||
return other.bean == null;
|
||||
} else return bean.equals(other.bean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.bean.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.bean;
|
||||
|
||||
/**
|
||||
* 为了解决反射过程中,需要传递null参数,但是会丢失参数类型而设立的包装类
|
||||
*
|
||||
* @param <T> Null值对应的类型
|
||||
* @author Lillls
|
||||
* @since 5.5.0
|
||||
*/
|
||||
public class NullWrapperBean<T> {
|
||||
|
||||
private final Class<T> clazz;
|
||||
|
||||
/**
|
||||
* @param clazz null的类型
|
||||
*/
|
||||
public NullWrapperBean(Class<T> clazz) {
|
||||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取null值对应的类型
|
||||
*
|
||||
* @return 类型
|
||||
*/
|
||||
public Class<T> getWrappedClass() {
|
||||
return clazz;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,404 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.bean;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.annotation.AnnotationUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.annotation.PropIgnore;
|
||||
import aiyh.utils.tool.cn.hutool.core.convert.Convert;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ClassUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ModifierUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ReflectUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.TypeUtil;
|
||||
|
||||
import java.beans.Transient;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* 属性描述,包括了字段、getter、setter和相应的方法执行
|
||||
*
|
||||
* @author looly
|
||||
*/
|
||||
public class PropDesc {
|
||||
|
||||
/**
|
||||
* 字段
|
||||
*/
|
||||
final Field field;
|
||||
/**
|
||||
* Getter方法
|
||||
*/
|
||||
protected Method getter;
|
||||
/**
|
||||
* Setter方法
|
||||
*/
|
||||
protected Method setter;
|
||||
|
||||
/**
|
||||
* 构造<br>
|
||||
* Getter和Setter方法设置为默认可访问
|
||||
*
|
||||
* @param field 字段
|
||||
* @param getter get方法
|
||||
* @param setter set方法
|
||||
*/
|
||||
public PropDesc(Field field, Method getter, Method setter) {
|
||||
this.field = field;
|
||||
this.getter = ClassUtil.setAccessible(getter);
|
||||
this.setter = ClassUtil.setAccessible(setter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字段名,如果存在Alias注解,读取注解的值作为名称
|
||||
*
|
||||
* @return 字段名
|
||||
*/
|
||||
public String getFieldName() {
|
||||
return ReflectUtil.getFieldName(this.field);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字段名称
|
||||
*
|
||||
* @return 字段名
|
||||
* @since 5.1.6
|
||||
*/
|
||||
public String getRawFieldName() {
|
||||
return null == this.field ? null : this.field.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字段
|
||||
*
|
||||
* @return 字段
|
||||
*/
|
||||
public Field getField() {
|
||||
return this.field;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得字段类型<br>
|
||||
* 先获取字段的类型,如果字段不存在,则获取Getter方法的返回类型,否则获取Setter的第一个参数类型
|
||||
*
|
||||
* @return 字段类型
|
||||
*/
|
||||
public Type getFieldType() {
|
||||
if (null != this.field) {
|
||||
return TypeUtil.getType(this.field);
|
||||
}
|
||||
return findPropType(getter, setter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得字段类型<br>
|
||||
* 先获取字段的类型,如果字段不存在,则获取Getter方法的返回类型,否则获取Setter的第一个参数类型
|
||||
*
|
||||
* @return 字段类型
|
||||
*/
|
||||
public Class<?> getFieldClass() {
|
||||
if (null != this.field) {
|
||||
return TypeUtil.getClass(this.field);
|
||||
}
|
||||
return findPropClass(getter, setter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Getter方法,可能为{@code null}
|
||||
*
|
||||
* @return Getter方法
|
||||
*/
|
||||
public Method getGetter() {
|
||||
return this.getter;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Setter方法,可能为{@code null}
|
||||
*
|
||||
* @return {@link Method}Setter 方法对象
|
||||
*/
|
||||
public Method getSetter() {
|
||||
return this.setter;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查属性是否可读(即是否可以通过{@link #getValue(Object)}获取到值)
|
||||
*
|
||||
* @param checkTransient 是否检查Transient关键字或注解
|
||||
* @return 是否可读
|
||||
* @since 5.4.2
|
||||
*/
|
||||
public boolean isReadable(boolean checkTransient) {
|
||||
// 检查是否有getter方法或是否为public修饰
|
||||
if (null == this.getter && false == ModifierUtil.isPublic(this.field)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查transient关键字和@Transient注解
|
||||
if (checkTransient && isTransientForGet()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查@PropIgnore注解
|
||||
return false == isIgnoreGet();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取属性值<br>
|
||||
* 首先调用字段对应的Getter方法获取值,如果Getter方法不存在,则判断字段如果为public,则直接获取字段值<br>
|
||||
* 此方法不检查任何注解,使用前需调用 {@link #isReadable(boolean)} 检查是否可读
|
||||
*
|
||||
* @param bean Bean对象
|
||||
* @return 字段值
|
||||
* @since 4.0.5
|
||||
*/
|
||||
public Object getValue(Object bean) {
|
||||
if (null != this.getter) {
|
||||
return ReflectUtil.invoke(bean, this.getter);
|
||||
} else if (ModifierUtil.isPublic(this.field)) {
|
||||
return ReflectUtil.getFieldValue(bean, this.field);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取属性值,自动转换属性值类型<br>
|
||||
* 首先调用字段对应的Getter方法获取值,如果Getter方法不存在,则判断字段如果为public,则直接获取字段值
|
||||
*
|
||||
* @param bean Bean对象
|
||||
* @param targetType 返回属性值需要转换的类型,null表示不转换
|
||||
* @param ignoreError 是否忽略错误,包括转换错误和注入错误
|
||||
* @return this
|
||||
* @since 5.4.2
|
||||
*/
|
||||
public Object getValue(Object bean, Type targetType, boolean ignoreError) {
|
||||
Object result = null;
|
||||
try {
|
||||
result = getValue(bean);
|
||||
} catch (Exception e) {
|
||||
if (false == ignoreError) {
|
||||
throw new BeanException(e, "Get value of [{}] error!", getFieldName());
|
||||
}
|
||||
}
|
||||
|
||||
if (null != result && null != targetType) {
|
||||
// 尝试将结果转换为目标类型,如果转换失败,返回null,即跳过此属性值。
|
||||
// 来自:issues#I41WKP@Gitee,当忽略错误情况下,目标类型转换失败应返回null
|
||||
// 如果返回原值,在集合注入时会成功,但是集合取值时会报类型转换错误
|
||||
return Convert.convertWithCheck(targetType, result, null, ignoreError);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查属性是否可读(即是否可以通过{@link #getValue(Object)}获取到值)
|
||||
*
|
||||
* @param checkTransient 是否检查Transient关键字或注解
|
||||
* @return 是否可读
|
||||
* @since 5.4.2
|
||||
*/
|
||||
public boolean isWritable(boolean checkTransient) {
|
||||
// 检查是否有getter方法或是否为public修饰
|
||||
if (null == this.setter && false == ModifierUtil.isPublic(this.field)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查transient关键字和@Transient注解
|
||||
if (checkTransient && isTransientForSet()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查@PropIgnore注解
|
||||
return false == isIgnoreSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Bean的字段值<br>
|
||||
* 首先调用字段对应的Setter方法,如果Setter方法不存在,则判断字段如果为public,则直接赋值字段值<br>
|
||||
* 此方法不检查任何注解,使用前需调用 {@link #isWritable(boolean)} 检查是否可写
|
||||
*
|
||||
* @param bean Bean对象
|
||||
* @param value 值,必须与字段值类型匹配
|
||||
* @return this
|
||||
* @since 4.0.5
|
||||
*/
|
||||
public PropDesc setValue(Object bean, Object value) {
|
||||
if (null != this.setter) {
|
||||
ReflectUtil.invoke(bean, this.setter, value);
|
||||
} else if (ModifierUtil.isPublic(this.field)) {
|
||||
ReflectUtil.setFieldValue(bean, this.field, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置属性值,可以自动转换字段类型为目标类型
|
||||
*
|
||||
* @param bean Bean对象
|
||||
* @param value 属性值,可以为任意类型
|
||||
* @param ignoreNull 是否忽略{@code null}值,true表示忽略
|
||||
* @param ignoreError 是否忽略错误,包括转换错误和注入错误
|
||||
* @return this
|
||||
* @since 5.4.2
|
||||
*/
|
||||
public PropDesc setValue(Object bean, Object value, boolean ignoreNull, boolean ignoreError) {
|
||||
return setValue(bean, value, ignoreNull, ignoreError, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置属性值,可以自动转换字段类型为目标类型
|
||||
*
|
||||
* @param bean Bean对象
|
||||
* @param value 属性值,可以为任意类型
|
||||
* @param ignoreNull 是否忽略{@code null}值,true表示忽略
|
||||
* @param ignoreError 是否忽略错误,包括转换错误和注入错误
|
||||
* @param override 是否覆盖目标值,如果不覆盖,会先读取bean的值,{@code null}则写,否则忽略。如果覆盖,则不判断直接写
|
||||
* @return this
|
||||
* @since 5.7.17
|
||||
*/
|
||||
public PropDesc setValue(Object bean, Object value, boolean ignoreNull, boolean ignoreError, boolean override) {
|
||||
if (null == value && ignoreNull) {
|
||||
return this;
|
||||
}
|
||||
|
||||
// issue#I4JQ1N@Gitee
|
||||
// 非覆盖模式下,如果目标值存在,则跳过
|
||||
if (false == override && null != getValue(bean)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
// 当类型不匹配的时候,执行默认转换
|
||||
if (null != value) {
|
||||
final Class<?> propClass = getFieldClass();
|
||||
if (false == propClass.isInstance(value)) {
|
||||
value = Convert.convertWithCheck(propClass, value, null, ignoreError);
|
||||
}
|
||||
}
|
||||
|
||||
// 属性赋值
|
||||
if (null != value || false == ignoreNull) {
|
||||
try {
|
||||
this.setValue(bean, value);
|
||||
} catch (Exception e) {
|
||||
if (false == ignoreError) {
|
||||
throw new BeanException(e, "Set value of [{}] error!", getFieldName());
|
||||
}
|
||||
// 忽略注入失败
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------ Private method start
|
||||
|
||||
/**
|
||||
* 通过Getter和Setter方法中找到属性类型
|
||||
*
|
||||
* @param getter Getter方法
|
||||
* @param setter Setter方法
|
||||
* @return {@link Type}
|
||||
*/
|
||||
private Type findPropType(Method getter, Method setter) {
|
||||
Type type = null;
|
||||
if (null != getter) {
|
||||
type = TypeUtil.getReturnType(getter);
|
||||
}
|
||||
if (null == type && null != setter) {
|
||||
type = TypeUtil.getParamType(setter, 0);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过Getter和Setter方法中找到属性类型
|
||||
*
|
||||
* @param getter Getter方法
|
||||
* @param setter Setter方法
|
||||
* @return {@link Type}
|
||||
*/
|
||||
private Class<?> findPropClass(Method getter, Method setter) {
|
||||
Class<?> type = null;
|
||||
if (null != getter) {
|
||||
type = TypeUtil.getReturnClass(getter);
|
||||
}
|
||||
if (null == type && null != setter) {
|
||||
type = TypeUtil.getFirstParamClass(setter);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查字段是否被忽略写,通过{@link PropIgnore} 注解完成,规则为:
|
||||
* <pre>
|
||||
* 1. 在字段上有{@link PropIgnore} 注解
|
||||
* 2. 在setXXX方法上有{@link PropIgnore} 注解
|
||||
* </pre>
|
||||
*
|
||||
* @return 是否忽略写
|
||||
* @since 5.4.2
|
||||
*/
|
||||
private boolean isIgnoreSet() {
|
||||
return AnnotationUtil.hasAnnotation(this.field, PropIgnore.class)
|
||||
|| AnnotationUtil.hasAnnotation(this.setter, PropIgnore.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查字段是否被忽略读,通过{@link PropIgnore} 注解完成,规则为:
|
||||
* <pre>
|
||||
* 1. 在字段上有{@link PropIgnore} 注解
|
||||
* 2. 在getXXX方法上有{@link PropIgnore} 注解
|
||||
* </pre>
|
||||
*
|
||||
* @return 是否忽略读
|
||||
* @since 5.4.2
|
||||
*/
|
||||
private boolean isIgnoreGet() {
|
||||
return AnnotationUtil.hasAnnotation(this.field, PropIgnore.class)
|
||||
|| AnnotationUtil.hasAnnotation(this.getter, PropIgnore.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段和Getter方法是否为Transient关键字修饰的
|
||||
*
|
||||
* @return 是否为Transient关键字修饰的
|
||||
* @since 5.3.11
|
||||
*/
|
||||
private boolean isTransientForGet() {
|
||||
boolean isTransient = ModifierUtil.hasModifier(this.field, ModifierUtil.ModifierType.TRANSIENT);
|
||||
|
||||
// 检查Getter方法
|
||||
if (false == isTransient && null != this.getter) {
|
||||
isTransient = ModifierUtil.hasModifier(this.getter, ModifierUtil.ModifierType.TRANSIENT);
|
||||
|
||||
// 检查注解
|
||||
if (false == isTransient) {
|
||||
isTransient = AnnotationUtil.hasAnnotation(this.getter, Transient.class);
|
||||
}
|
||||
}
|
||||
|
||||
return isTransient;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段和Getter方法是否为Transient关键字修饰的
|
||||
*
|
||||
* @return 是否为Transient关键字修饰的
|
||||
* @since 5.3.11
|
||||
*/
|
||||
private boolean isTransientForSet() {
|
||||
boolean isTransient = ModifierUtil.hasModifier(this.field, ModifierUtil.ModifierType.TRANSIENT);
|
||||
|
||||
// 检查Getter方法
|
||||
if (false == isTransient && null != this.setter) {
|
||||
isTransient = ModifierUtil.hasModifier(this.setter, ModifierUtil.ModifierType.TRANSIENT);
|
||||
|
||||
// 检查注解
|
||||
if (false == isTransient) {
|
||||
isTransient = AnnotationUtil.hasAnnotation(this.setter, Transient.class);
|
||||
}
|
||||
}
|
||||
|
||||
return isTransient;
|
||||
}
|
||||
//------------------------------------------------------------------------------------ Private method end
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.bean.copier;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.copier.Copier;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
/**
|
||||
* 抽象的对象拷贝封装,提供来源对象、目标对象持有
|
||||
*
|
||||
* @param <S> 来源对象类型
|
||||
* @param <T> 目标对象类型
|
||||
* @author looly
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public abstract class AbsCopier<S, T> implements Copier<T> {
|
||||
|
||||
protected final S source;
|
||||
protected final T target;
|
||||
/**
|
||||
* 拷贝选项
|
||||
*/
|
||||
protected final CopyOptions copyOptions;
|
||||
|
||||
public AbsCopier(S source, T target, CopyOptions copyOptions) {
|
||||
this.source = source;
|
||||
this.target = target;
|
||||
this.copyOptions = ObjectUtil.defaultIfNull(copyOptions, CopyOptions::create);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.bean.copier;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.copier.Copier;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Bean拷贝,提供:
|
||||
*
|
||||
* <pre>
|
||||
* 1. Bean 转 Bean
|
||||
* 2. Bean 转 Map
|
||||
* 3. Map 转 Bean
|
||||
* 4. Map 转 Map
|
||||
* </pre>
|
||||
*
|
||||
* @author looly
|
||||
*
|
||||
* @param <T> 目标对象类型
|
||||
* @since 3.2.3
|
||||
*/
|
||||
public class BeanCopier<T> implements Copier<T>, Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final Copier<T> copier;
|
||||
|
||||
/**
|
||||
* 创建BeanCopier
|
||||
*
|
||||
* @param <T> 目标Bean类型
|
||||
* @param source 来源对象,可以是Bean或者Map
|
||||
* @param target 目标Bean对象
|
||||
* @param copyOptions 拷贝属性选项
|
||||
* @return BeanCopier
|
||||
*/
|
||||
public static <T> BeanCopier<T> create(Object source, T target, CopyOptions copyOptions) {
|
||||
return create(source, target, target.getClass(), copyOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建BeanCopier
|
||||
*
|
||||
* @param <T> 目标Bean类型
|
||||
* @param source 来源对象,可以是Bean或者Map
|
||||
* @param target 目标Bean对象
|
||||
* @param destType 目标的泛型类型,用于标注有泛型参数的Bean对象
|
||||
* @param copyOptions 拷贝属性选项
|
||||
* @return BeanCopier
|
||||
*/
|
||||
public static <T> BeanCopier<T> create(Object source, T target, Type destType, CopyOptions copyOptions) {
|
||||
return new BeanCopier<>(source, target, destType, copyOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param source 来源对象,可以是Bean或者Map
|
||||
* @param target 目标Bean对象
|
||||
* @param targetType 目标的泛型类型,用于标注有泛型参数的Bean对象
|
||||
* @param copyOptions 拷贝属性选项
|
||||
*/
|
||||
public BeanCopier(Object source, T target, Type targetType, CopyOptions copyOptions) {
|
||||
Assert.notNull(source, "Source bean must be not null!");
|
||||
Assert.notNull(target, "Target bean must be not null!");
|
||||
Copier<T> copier;
|
||||
if (source instanceof Map) {
|
||||
if (target instanceof Map) {
|
||||
//noinspection unchecked
|
||||
copier = (Copier<T>) new MapToMapCopier((Map<?, ?>) source, (Map<?, ?>) target, targetType, copyOptions);
|
||||
} else {
|
||||
copier = new MapToBeanCopier<>((Map<?, ?>) source, target, targetType, copyOptions);
|
||||
}
|
||||
}else if(source instanceof ValueProvider){
|
||||
//noinspection unchecked
|
||||
copier = new ValueProviderToBeanCopier<>((ValueProvider<String>) source, target, targetType, copyOptions);
|
||||
} else {
|
||||
if (target instanceof Map) {
|
||||
//noinspection unchecked
|
||||
copier = (Copier<T>) new BeanToMapCopier(source, (Map<?, ?>) target, targetType, copyOptions);
|
||||
} else {
|
||||
copier = new BeanToBeanCopier<>(source, target, targetType, copyOptions);
|
||||
}
|
||||
}
|
||||
this.copier = copier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T copy() {
|
||||
return copier.copy();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
package aiyh.utils.tool.cn.hutool.core.bean.copier;
|
||||
|
||||
import aiyh.utils.tool.cn.hutool.core.bean.BeanUtil;
|
||||
import aiyh.utils.tool.cn.hutool.core.bean.PropDesc;
|
||||
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||
import aiyh.utils.tool.cn.hutool.core.util.TypeUtil;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Bean属性拷贝到Bean中的拷贝器
|
||||
*
|
||||
* @param <S> 源Bean类型
|
||||
* @param <T> 目标Bean类型
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public class BeanToBeanCopier<S, T> extends AbsCopier<S, T> {
|
||||
|
||||
/**
|
||||
* 目标的类型(用于泛型类注入)
|
||||
*/
|
||||
private final Type targetType;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param source 来源Map
|
||||
* @param target 目标Bean对象
|
||||
* @param targetType 目标泛型类型
|
||||
* @param copyOptions 拷贝选项
|
||||
*/
|
||||
public BeanToBeanCopier(S source, T target, Type targetType, CopyOptions copyOptions) {
|
||||
super(source, target, copyOptions);
|
||||
this.targetType = targetType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T copy() {
|
||||
Class<?> actualEditable = target.getClass();
|
||||
if (null != copyOptions.editable) {
|
||||
// 检查限制类是否为target的父类或接口
|
||||
Assert.isTrue(copyOptions.editable.isInstance(target),
|
||||
"Target class [{}] not assignable to Editable class [{}]", actualEditable.getName(), copyOptions.editable.getName());
|
||||
actualEditable = copyOptions.editable;
|
||||
}
|
||||
final Map<String, PropDesc> targetPropDescMap = BeanUtil.getBeanDesc(actualEditable).getPropMap(copyOptions.ignoreCase);
|
||||
|
||||
final Map<String, PropDesc> sourcePropDescMap = BeanUtil.getBeanDesc(source.getClass()).getPropMap(copyOptions.ignoreCase);
|
||||
sourcePropDescMap.forEach((sFieldName, sDesc) -> {
|
||||
if (null == sFieldName || false == sDesc.isReadable(copyOptions.transientSupport)) {
|
||||
// 字段空或不可读,跳过
|
||||
return;
|
||||
}
|
||||
|
||||
sFieldName = copyOptions.editFieldName(sFieldName);
|
||||
// 对key做转换,转换后为null的跳过
|
||||
if (null == sFieldName) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 忽略不需要拷贝的 key,
|
||||
if (false == copyOptions.testKeyFilter(sFieldName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查目标字段可写性
|
||||
final PropDesc tDesc = targetPropDescMap.get(sFieldName);
|
||||
if (null == tDesc || false == tDesc.isWritable(this.copyOptions.transientSupport)) {
|
||||
// 字段不可写,跳过之
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查源对象属性是否过滤属性
|
||||
Object sValue = sDesc.getValue(this.source);
|
||||
if (false == copyOptions.testPropertyFilter(sDesc.getField(), sValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取目标字段真实类型并转换源值
|
||||
final Type fieldType = TypeUtil.getActualType(this.targetType, tDesc.getFieldType());
|
||||
//sValue = Convert.convertWithCheck(fieldType, sValue, null, this.copyOptions.ignoreError);
|
||||
sValue = this.copyOptions.convertField(fieldType, sValue);
|
||||
sValue = copyOptions.editFieldValue(sFieldName, sValue);
|
||||
|
||||
// 目标赋值
|
||||
tDesc.setValue(this.target, sValue, copyOptions.ignoreNullValue, copyOptions.ignoreError, copyOptions.override);
|
||||
});
|
||||
return this.target;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue