Re: [問題] spring, 與架構問題
※ 引述《ghchen (Seele)》之銘言:
: 主要問題:
: 如何禁止從 Controller 直接呼叫 Repository
: 問題描述:
: 我使用 spring 來開發 webapp
: 採用三層式架構
: Controller -> Service -> Repository
: // Repository 是 Spring Data 使用的術語,可以想像成 DAO
: 原本打算把 jta, auditing log 加在 Service layer 上
: 所以我不希望其他開發人員從 Controller 這裡直接呼叫 Repository
: 但是其他開發人員不可能被我控制
: 他們可以隨意的在 controller 裡面任意 autowired repository
: 接下來就隨便他們搞了
: 後來我加了一個 AOP 來限制 repository 的呼叫
: 如果是從 service package 呼叫的就放行,否則拋出例外
: @Aspect
: public class ModelAdvice {
: private Pattern pattern = Pattern.compile("^demo\\.services\\..*");
: @Before("execution(* demo.repositories..*Repository.*(..))")
: public void protectRepositories(JoinPoint joinPoint) {
: StackTraceElement[] stElements =
: Thread.currentThread().getStackTrace();
: for (StackTraceElement element : stElements) {
: Matcher matcher = pattern.matcher(element.getClassName());
: if (matcher.matches()) {
: return;
: }
: }
: throw new RuntimeException("security violation!");
: }
: }
: 雖然可以動,但是覺得不太漂亮,而且可能會很慢
使用 AOP 來作 policy enforcement 是不錯的選擇,但是應該就不要在 runtime
再去分析 runtime 時的資訊。
試著透過複合 within pointcut 直接把不是在 service classes 範圍下,去 call
repository methods 的部份織上拋出 exception 的碼,等於是在 weaving time 就
直接把不合規定的部份找出來,沒有額外 runtime 成本。
另外,需要考慮使用 reflection 去調用 repository classes 的功能,這部份也
可以透過 AspectJ 去攔截,但是就確實需要在 runtime 去判斷是否有符合你的
policy。
--
※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 218.173.236.72
※ 文章網址: http://www.ptt.cc/bbs/java/M.1399313711.A.CA1.html
推
05/06 09:19, , 1F
05/06 09:19, 1F
推
05/06 19:08, , 2F
05/06 19:08, 2F
推
05/08 11:43, , 3F
05/08 11:43, 3F
第二段的部分在實作上應該還挺直覺的,所以我才沒有直接給出範例。
如果要給個具體的例子,那就拿原帖中應用為例。假設所有 Repository 相關的
classes 都在 package demo.repositories 之下(含 subpackage);所有 Service
相關的 classes 都在 package demo.services 之下(含 subpackage)。
不能直接碰 repository classes 的原則是指,除了 repository classes 與
services classes 之外的所有 class 都不能夠直接去調用 repository classes
的任何 method,也不能存取任何 repository classes 的 field。
要強制遵守這個原則大致上可以這麼做(假設違例處拋出一個帶有提示訊息的
runtime exception 即可達到成效):
public aspect DontTouchModel {
pointcut manipulateRepository() : call(* demo.repositories..*.*(..));
pointcut readFieldOfRepository() : get(* demo.repositories..*.*);
pointcut writeFieldOfRepository() : set(* demo.repositories..*.*);
before() : manipulateRepository()
&& !within(demo.repositories..*) && !within(demo.services..*) {
throw new RuntimeException(
"Please don't touch repositories classes directly.");
}
before() : readFieldOfRepository()
&& !within(demo.repositories..*) && !within(demo.services..*) {
throw new RuntimeException(
"Please don't touch repositories classes directly.");
}
before() : writeFieldOfRepository()
&& !within(demo.repositories..*) && !within(demo.services..*) {
throw new RuntimeException(
"Please don't touch repositories classes directly.");
}
}
把整個 project 的 classes 拿來與 DontTouchModel 織在一起後,非 repository/
service classes 者不論如何拿到 repository classes 的 object/class,只要
他有把 reference casting 成 repository classes/interface 來使用,就會被
揪出來(除非他透過 reflection 去使用這些 reference,這就是第三段的部分)。
這種做法對於那些符合原則的 statement 不會添加 runtime 成本(因為這些碼完全
沒有被加工),只有不符合原則的部分有額外成本(其實也沒有啦,你根本就要他掛掉
了 :D)。
※ 編輯: sbrhsieh (1.172.225.226), 05/09/2014 17:14:33
推
05/10 09:46, , 4F
05/10 09:46, 4F
討論串 (同標題文章)
本文引述了以下文章的的內容:
完整討論串 (本文為第 2 之 2 篇):
java 近期熱門文章
PTT數位生活區 即時熱門文章