ページの作成
親となるページを選択してください。
親ページに紐づくページを子ページといいます。
例: 親=スポーツ, 子1=サッカー, 子2=野球
子ページを親ページとして更に子ページを作成することも可能です。
例: 親=サッカー, 子=サッカーのルール
親ページはいつでも変更することが可能なのでとりあえず作ってみましょう!
| この記事の要点 |
|---|
|
AOP の基本概念
AOP (Aspect-Oriented Programming / アスペクト指向プログラミング) は、複数のクラスにまたがる横断的関心事(ログ・認証・トランザクション等)を、本来のビジネスロジックから分離する手法です。
@Before の基本
// 1. AOP を有効化
@Configuration
@EnableAspectJAutoProxy
public class AppConfig { }
// または Spring Boot なら依存追加で自動有効
org.springframework.boot
spring-boot-starter-aop
// 2. Aspect クラス作成
@Aspect
@Component
public class LoggingAspect {
private final Logger log = LoggerFactory.getLogger(LoggingAspect.class);
// すべての Service メソッドの「実行前」にログ出力
@Before("execution(* com.example.service.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
String method = joinPoint.getSignature().toShortString();
Object[] args = joinPoint.getArgs();
log.info("→ {} args={}", method, Arrays.toString(args));
}
}
// 3. 対象クラス (Service)
@Service
public class UserService {
public User findById(Long id) {
// 実行前に LoggingAspect.logBefore() が呼ばれる
return userRepository.findById(id).orElseThrow();
}
}
Pointcut 式
「どのメソッドを対象にするか」を指定する式:
| Pointcut | 意味 |
|---|---|
execution(* com.example.service.*.*(..)) | com.example.service の全クラスの全メソッド |
execution(* *..service.*.find*(..)) | 名前が find で始まるメソッド |
execution(public * *(..)) | すべての public メソッド |
within(com.example.service..*) | service パッケージ以下 |
@annotation(org.springframework.transaction.annotation.Transactional) | @Transactional 付きメソッド |
@within(org.springframework.stereotype.Service) | @Service クラス全体 |
@args(org.springframework.security.access.annotation.Secured) | 引数に特定アノテーション |
args(java.lang.Long, ..) | 第 1 引数が Long |
this(com.example.MyInterface) | プロキシが特定型のインスタンス |
target(com.example.MyClass) | 対象クラス |
Pointcut 式の組み合わせ
// AND
@Before("execution(* com.example.service.*.*(..)) && @annotation(myAnnotation)")
public void log(MyAnnotation myAnnotation) { ... }
// OR
@Before("execution(* *..UserService.*(..)) || execution(* *..OrderService.*(..))")
// NOT
@Before("execution(* com.example..*(..)) && !execution(* *..toString(..))")
// 名前付き Pointcut (再利用)
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}
@Before("serviceMethods()")
public void logBefore(JoinPoint jp) { ... }
JoinPoint と引数取得
@Aspect
@Component
public class AuditAspect {
@Before("@annotation(com.example.AuditLog)")
public void audit(JoinPoint joinPoint) {
// メソッド情報
String className = joinPoint.getTarget().getClass().getSimpleName();
String methodName = joinPoint.getSignature().getName();
// 引数
Object[] args = joinPoint.getArgs();
String[] paramNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
// アノテーション
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
AuditLog annotation = method.getAnnotation(AuditLog.class);
// ログ出力
log.info("Audit: {}.{}({})", className, methodName, Arrays.toString(args));
// メタ情報の活用
for (int i = 0; i < args.length; i++) {
log.info(" {} = {}", paramNames[i], args[i]);
}
}
}
カスタムアノテーション + AOP
// 1. カスタムアノテーション定義
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuditLog {
String value() default "";
}
// 2. Aspect で処理
@Aspect
@Component
public class AuditAspect {
@Before("@annotation(auditLog)")
public void audit(JoinPoint jp, AuditLog auditLog) {
log.info("AUDIT [{}]: {} called", auditLog.value(), jp.getSignature());
}
}
// 3. 利用
@Service
public class OrderService {
@AuditLog("ORDER_CREATE")
public Order createOrder(OrderRequest req) {
// ...
}
}
5 種類のアドバイス
| アノテーション | タイミング |
|---|---|
@Before | 対象メソッド実行前 |
@After | 実行後(正常 / 例外問わず) |
@AfterReturning | 正常終了後(戻り値取得可) |
@AfterThrowing | 例外発生時のみ |
@Around | 前後で完全制御(戻り値・引数を変更可) |
@Aspect
@Component
public class FullAspect {
@Before("serviceMethods()")
public void before() {
log.info("Before");
}
@After("serviceMethods()")
public void after() {
log.info("After (always)");
}
@AfterReturning(pointcut = "serviceMethods()", returning = "result")
public void afterReturning(Object result) {
log.info("AfterReturning, result={}", result);
}
@AfterThrowing(pointcut = "serviceMethods()", throwing = "ex")
public void afterThrowing(Throwable ex) {
log.error("AfterThrowing", ex);
}
@Around("serviceMethods()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
try {
Object result = pjp.proceed(); // 元のメソッド実行
long elapsed = System.currentTimeMillis() - start;
log.info("{} elapsed={}ms", pjp.getSignature(), elapsed);
return result;
} catch (Throwable ex) {
log.error("Exception in {}", pjp.getSignature(), ex);
throw ex;
}
}
}
典型的な用途
① 実行時間計測
@Around("@annotation(timed)")
public Object measure(ProceedingJoinPoint pjp, Timed timed) throws Throwable {
long start = System.currentTimeMillis();
Object result = pjp.proceed();
log.info("{} took {}ms", pjp.getSignature(), System.currentTimeMillis() - start);
return result;
}
② 認証チェック
@Before("@annotation(requiredRole)")
public void checkRole(RequiredRole requiredRole) {
String currentRole = SecurityContextHolder.getContext()
.getAuthentication().getAuthorities().toString();
if (!currentRole.contains(requiredRole.value())) {
throw new AccessDeniedException();
}
}
③ リトライ
@Around("@annotation(retry)")
public Object retry(ProceedingJoinPoint pjp, Retry retry) throws Throwable {
int attempts = 0;
while (attempts < retry.maxAttempts()) {
try {
return pjp.proceed();
} catch (Exception e) {
attempts++;
if (attempts >= retry.maxAttempts()) throw e;
Thread.sleep(retry.delay());
}
}
throw new RuntimeException("リトライ上限");
}
注意点
- 同一クラス内の自己呼び出しは効かない: AOP は Spring プロキシ経由なので、内部呼び出しはバイパス
- private メソッドは対象外: プロキシ経由できない
- final メソッド・クラスは対象外: CGLIB プロキシ生成不可
- パフォーマンス: 多用するとオーバーヘッド、ホットパスでの使用は注意
関連記事
ページの作成
親となるページを選択してください。
親ページに紐づくページを子ページといいます。
例: 親=スポーツ, 子1=サッカー, 子2=野球
子ページを親ページとして更に子ページを作成することも可能です。
例: 親=サッカー, 子=サッカーのルール
親ページはいつでも変更することが可能なのでとりあえず作ってみましょう!
子ページ
子ページはありません
人気ページ
- 1 Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法
- 2 tomcat の起動 / 停止ログと catalina.log・catalina.out の違い
- 3 JavaScript base URL 取得方法|window.location.origin と SSR/Node.js 対応
- 4 YouTube Data API v3 エラー一覧|403/400/404 の主要原因と切り分け
- 5 Spring Frameworkのアノテーション一覧
- 6 Laravel エラー一覧|500/Blade/DB 接続/ルーティングの代表エラー
- 7 3Dグラフィックスとは|モデリング/レンダリング/主要ソフトウェア (Blender / Maya)
- 8 【Spring】@Valueアノテーションとは
- 9 CATALINA_HOME の確認方法 (Linux / Mac)
- 10 【Spring】@Autowiredアノテーションとは
最近更新/作成されたページ
- Laravel キャッシュクリア完全ガイド(cache:clear / config:clear / 2026-05-18 07:42:07
- プロジェクトの作成と削除 2026-05-18 07:42:07
- インストール直後にNetbeansが反応しない 2026-05-18 07:42:07
- 動画やチャンネルの検索 2026-05-18 07:42:07
- APIキー取得方法 2026-05-18 07:42:07
- チャンネル情報の取得 2026-05-18 07:42:07
- API 入門 — Web API(REST / GraphQL / gRPC / 2026-05-18 07:42:07
- インストール(eclipseプラグイン) 2026-05-18 07:42:07
- Laravel「Dotenv values containing spaces must be surrounded 2026-05-18 07:42:07
- エラー一覧 2026-05-18 07:42:07
- curl: (51) SSL: certificate subject name '~' does not match 2026-05-18 07:42:07
- インストール方法(Windows版) 2026-05-18 07:42:07
- JSONから配列に変換 2026-05-18 07:42:07
- 処理を一定時間待つ 2026-05-18 07:42:07
- A non well formed numeric value encountered 2026-05-18 07:42:07
コメントを削除してもよろしいでしょうか?