只看標題應該不知道這在寫什麼,簡單說就是有特定前提下不影響原先撰寫的邏輯在加入其他程式。
我簡單分為
- 不用定義@
- 要定義@
不用定義@ (原有的@)
目的:JPA要在每次資料新增/更新時需要顯示紀錄
不影響原來使用JPA 在CU時維持原來的結果但會顯示log
先來定義哪些@是我們要注意的
// for save
@Pointcut(value = "execution(* org.springframework.data.jpa.repository.JpaRepository+.save(..))))")
public void saveMethodMatch() {
}
@Pointcut(value = "execution(* org.springframework.data.jpa.repository.JpaRepository+.saveAndFlush(..))))")
public void saveAndFlushMethodMatch() {
}
@Pointcut(value = "execution(* org.springframework.data.jpa.repository.JpaRepository+.saveAll(..))))")
public void saveAllMethodMatch() {
}
@Pointcut(value = "execution(* org.springframework.data.jpa.repository.JpaRepository+.saveAllAndFlush(..))))")
public void saveAllAndFlushMethodMatch() {
}
當注意到後我們要做什麼事情(前/中/後)
前
@Before("saveMethodMatch() ||" +
"saveAndFlushMethodMatch() ||" +
"saveAllMethodMatch() ||" +
"saveAllAndFlushMethodMatch()")
public void doBeforeSave(JoinPoint joinPoint) {
Object obj = joinPoint.getArgs()[0];
String instanceStr = joinPoint.getArgs()[0].getClass().getSimpleName();
if (obj instanceof List<?>) {
List<?> list = (List<?>) obj;
instanceStr = instanceStr + StringUtils.SPACE + list.get(0).getClass().getSimpleName();
}
logger.info(("-----" + this.getSignatureMethodName(joinPoint) + " " + instanceStr + "開始執行..."));
}
中
@Around("saveMethodMatch() ||" +
"saveAndFlushMethodMatch() ||" +
"saveAllMethodMatch() ||" +
"saveAllAndFlushMethodMatch()")
public Object onAroundSave(ProceedingJoinPoint joinPoint) throws Throwable {
Object[] params = new Object[1];
long start = System.currentTimeMillis();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
String repositoryName = joinPoint.getSignature().getDeclaringType().getSimpleName();
Object[] args = joinPoint.getArgs(); // change the args if you want to
if (args[0] instanceof List) {
params[0] = this.transformArgs(args);
} else {
params = (Object[]) this.transformArgs(args);
}
Object retVal = joinPoint.proceed(params); // run the actual method (or don't)
long executionTime = System.currentTimeMillis() - start;
logger.info(String.join(" ", repositoryName + ":" + method + " executed in " + executionTime + "ms"));
return retVal;
}
@Around("method1() || method2()") 可以放多個被注意的@要行什麼行為
transformArgs自己寫的轉換參數就不特別寫了
後
@AfterReturning("saveMethodMatch() ||" +
"saveAndFlushMethodMatch() ||" +
"saveAllMethodMatch() ||" +
"saveAllAndFlushMethodMatch()")
public void doAfterSave(JoinPoint joinPoint) {
logger.info("-----" + this.getSignatureMethodName(joinPoint) + "Save執行完畢!");
}
以上就可以達到當有人呼叫JPA 相關SAVE 都會顯示相關紀錄並不影響或未來異動程式
定義@
目的:需要重新刷新資料庫物件時 repository.flush 使用後無效可以使用entityManger去刷新
@名稱與參數
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface FlushAfterOp {
// 無須參數則空白
}
定義注意到後要做什麼
@Component
@Aspect
public class FlushAspect {
/*
如果用H2 則不適用
多DB資料庫來源就需指定哪一個entityManager
為了方便說明我這邊就直接注入不另外抽成Service亦不加入 交易事務
*/
@Autowired
private EntityManager entityManager;
@Pointcut("@annotation(FlushAfterQuery)")
public void flushAfterQuery() {
}
@Transactional
@Around("flushAfterQuery()")
public Object onAround(ProceedingJoinPoint joinPoint) throws Throwable {
Object result = joinPoint.proceed();
Object waitForProceeding = result;
// 若使用JPA提供的Naming query 回傳Optional<?> 或自行使用該型別
// entityManager.refresh 是不接受此型別因此要特別處理
Optional<?> resultFromOpt;
if (result instanceof Optional) {
resultFromOpt = ((Optional<?>) result);
waitForProceeding = resultFromOpt.isPresent()?resultFromOpt.get() : null;
}
if (waitForProceeding instanceof List) {
refreshList(waitForProceeding);
} else {
entityManager.refresh(waitForProceeding);
}
return result;
}
private Object refreshList(Object objList) {
List<?> list = (List) objList;
// 這邊使用遍歷方式
for (Object record : list) {
entityService.refresh(record);
}
return list;
}
}
以上為此兩種AOP 方式介紹
未來有機會在和大家說明他的特性
這不是一個給新手的一個教學過程,也寫的不是很完整
希望大家多多包涵囉~
主要是給自己的一個紀錄,也分享給有需要的夥伴
這是一個心血來潮,產生的文章
若有喜歡或交流的部分都歡迎在下方留言,多多關照。