Springã§ã®AOPããã£ããã¨ããªããªã¨æãã¾ãã¦ãInterceptorã®æ¸ãæ¹ã軽ãè¦ãã¨ã¨ãã«ãæåã«ã¤ãã¦ææ¡ãã¦ããããã¨æãã¾ãã¦ã
Interceptorã®ãããæ¹ã«ã¤ãã¦ãæ°ã«ãªãã®ã¯
- å¯è¦æ§
- Interceptorãåããã«ã¯ãæ¡å¼µãããã¤ã³ã¹ã¿ã³ã¹ï¼è¦ã¯@Autowiredãªãã§åå¾ãããã®ï¼ã§ããå¿ è¦ãããã®ãï¼
- Interceptorã®é©ç¨é
ã¨ãã£ãã¨ããã§ããã
å®è£ ã¯Spring Bootã§è¡ã£ãã®ã§ãããAOPã使ã£ãããã°ã©ã ãæ¸ãã«ããããåèã«ããã®ã¯ä»¥ä¸ãããã
Aspect Oriented Programming with Spring
@AspectJ cheat sheet | Java and Spring development
ãã®ãã¡å¯è¦æ§ã«ã¤ãã¦ã¯ãSpringã®ããã¥ã¡ã³ãã«ãpublicã¡ã½ããã ãã ãï¼ãã¨æ¸ããã¦ããã®ã§ãOKã¨ãã¾ãããã
Due to the proxy-based nature of Springâs AOP framework, protected methods are by definition not intercepted, neither for JDK proxies (where this isnât applicable) nor for CGLIB proxies (where this is technically possible but not recommendable for AOP purposes). As a consequence, any given pointcut will be matched against public methods only!
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-pointcuts
ãã¨ã¯ãInterceptorãæ¸ãã¤ã¤ãåä½ã確èªãã¦ãã£ã¦ã¿ã¾ãã
æºå
Mavenä¾åé¢ä¿ã¯ã以ä¸ã®ããã«è¨å®ã
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> </dependencies>
ãspring-boot-starter-aopããè¦ããããã§ãã
ã¨ã³ããªãã¤ã³ãã®ã³ã¼ã
ããã°ã©ã ã®ã¨ã³ããªãã¤ã³ãã¯ã以ä¸ã®ããã«å®è£
ã
src/main/java/org/littlewings/springboot/App.java
package org.littlewings.springboot; import org.littlewings.springboot.service.JdkProxyCalcService; import org.littlewings.springboot.service.OuterCalcService; import org.littlewings.springboot.service.SimpleCalcService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.ExitCodeGenerator; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ApplicationContext; @SpringBootApplication public class App implements CommandLineRunner, ExitCodeGenerator { /** @Autowiredã§ããããæ¸ã */ public static void main(String... args) { ApplicationContext context = SpringApplication.run(App.class, args); App app = context.getBean(App.class); int exitCode = SpringApplication.exit(context, app); System.exit(exitCode); } public void run(String... args) throws Exception { // @Autowiredã§ã¤ã³ã¸ã§ã¯ã·ã§ã³ããã¤ã³ã¹ã¿ã³ã¹ãæä½ãã } public int getExitCode() { return 0; } }
ã³ã¡ã³ãã§æ¸ãã¦ããé¨åã¯ãå¾ã§åãã¦ããã¾ãã
Interceptorãæ¸ã
ããã§ã¯ãInterceptorãæ¸ãã¦ã¿ã¾ãã
ä»åã¯ããããªæãã®ãããããã¬ã¼ã¹ç¨Interceptorãç¨æã
src/main/java/org/littlewings/springboot/trace/TraceInterceptor.java
package org.littlewings.springboot.trace; import java.util.Arrays; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Aspect @Component public class TraceInterceptor { @Before("execution(* org.littlewings.springboot.service.*.*(..))") public void invokeBefore(JoinPoint joinPoint) { System.out.printf("[AOP at before] called parameters = %s, by %s#%s%n", Arrays.toString(joinPoint.getArgs()), joinPoint.getTarget().getClass(), joinPoint.getSignature().getName()); // JoinPoint#getThisã®å ´åã¯ãæ¡å¼µããããªãã¸ã§ã¯ããè¿ã } @Around("execution(* org.littlewings.springboot.service.*.*(..))") public Object invoke(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { Object ret = null; try { System.out.printf("[AOP at around] before invoke, parameters = %s, by %s#%s%n", Arrays.toString(proceedingJoinPoint.getArgs()), proceedingJoinPoint.getTarget().getClass(), proceedingJoinPoint.getSignature().getName()); ret = proceedingJoinPoint.proceed(); return ret; } finally { System.out.printf("[AOP at around] after invoke, result = %s, %s#%s%n", ret, proceedingJoinPoint.getTarget().getClass(), proceedingJoinPoint.getSignature().getName()); } } }
@Aspectãä»ãã¦ã@Componentã¨ããã°ããã¨ã
@Aspect @Component public class TraceInterceptor {
Interceptorãå®è¡ããå ´æã§ãããããã¥ã¡ã³ããè¦ã¦ä»åã¯@Beforeã¨@Aroundã¨ãã¾ããã
å
容çã«ã¯ãåå被ã£ã¦ãã¾ããâ¦ã
Interceptorãé©ç¨ãã対象ã¯ãèªä½ã®serviceããã±ã¼ã¸å ã®ä»»æã®ã¯ã©ã¹ï¼ã¡ã½ããã対象ã¨ãã¾ããã
@Before("execution(* org.littlewings.springboot.service.*.*(..))") @Around("execution(* org.littlewings.springboot.service.*.*(..))")
Interceptorãé©ç¨ããã¯ã©ã¹ãæ¸ãã¦ãé©ç¨ç¯å²ã確èªãã
Interceptorãé©ç¨ããã¯ã©ã¹ã¨ãã¦ãã¾ãã¯ããããã®ãç¨æã
src/main/java/org/littlewings/springboot/service/SimpleCalcService.java
package org.littlewings.springboot.service; import org.springframework.stereotype.Service; @Service public class SimpleCalcService { public int add(int a, int b) { System.out.println(SimpleCalcService.class + "#add" + " called."); return a + b; } public int add2(int a, int b) { return add2Internal(a, b); } public int add2Internal(int a, int b) { System.out.println(SimpleCalcService.class + "#add2" + " called."); return a + b; } }
ã²ã¨ã¤ãå®ä½ããªãå¥ã®publicã¡ã½ãããå¼ãã§ããçµäºããã±ã¼ã¹ãç¨æã
ããããå ã»ã©ã®ã¨ã³ããªãã¤ã³ãã®ã¯ã©ã¹ã«@Autowiredãã¦ç¢ºèªãã¦ã¿ã¾ãã
@Autowired private SimpleCalcService simpleCalcService;
ã³ã¼ãã¨çµæãããããè¼ãã¦ããã¾ãã
åç´ã«ã¡ã½ããå¼ã³åºãã
public void run(String... args) throws Exception { System.out.printf("simpleCalcService.add = %d%n", simpleCalcService.add(1, 2)); }
â»ä»¥éãrunã¡ã½ããã®å®£è¨ããã³éãæ¬å¼§ã¯ç«¯æãã¾ãã
çµæã
[AOP at around] before invoke, parameters = [1, 2], by class org.littlewings.springboot.service.SimpleCalcService#add [AOP at before] called parameters = [1, 2], by class org.littlewings.springboot.service.SimpleCalcService#add class org.littlewings.springboot.service.SimpleCalcService#add called. [AOP at around] after invoke, result = 3, class org.littlewings.springboot.service.SimpleCalcService#add simpleCalcService.add = 3
ã¡ããã¨åä½ãã¾ããã¨ã
ç¶ãã¦ãå¥ã®publicã¡ã½ãããå¼ã³åºãã±ã¼ã¹ã
System.out.printf("simpleCalcService.add2 = %d%n", simpleCalcService.add2(1, 2));
çµæã
[AOP at around] before invoke, parameters = [1, 2], by class org.littlewings.springboot.service.SimpleCalcService#add2 [AOP at before] called parameters = [1, 2], by class org.littlewings.springboot.service.SimpleCalcService#add2 class org.littlewings.springboot.service.SimpleCalcService#add2 called. [AOP at around] after invoke, result = 3, class org.littlewings.springboot.service.SimpleCalcService#add2 simpleCalcService.add2 = 3
ãã¡ãã®å ´åã¯ã@Autowiredãããã®ãç´æ¥å¼ãã ã±ã¼ã¹ã®ã¿Interceptorãåä½ãã¦ãã¾ãã
ã¨ãããã¨ã¯ãpublicã¡ã½ããã§ãã£ã¦ãã¯ã©ã¹å ã§å¥ã®ã¡ã½ãããå¼ã³åºãã¦ããã¡ã ã¨ãããã¨ã§ãããããã¾ã§ãã¤ã³ã¸ã§ã¯ã·ã§ã³ããã¤ã³ã¹ã¿ã³ã¹è¶ãã«ã¡ã½ããå¼ã³åºããããã¨ã
å½ç¶ã§ãããå¥ã¡ã½ããã¨ãªã£ããã®ãç´æ¥å¼ã³åºãã°
System.out.printf("simpleCalcService.add2 = %d%n", simpleCalcService.add2Internal(1, 2));
Interceptorãåãã¾ããã¨ã
[AOP at around] before invoke, parameters = [1, 2], by class org.littlewings.springboot.service.SimpleCalcService#add2Internal [AOP at before] called parameters = [1, 2], by class org.littlewings.springboot.service.SimpleCalcService#add2Internal class org.littlewings.springboot.service.SimpleCalcService#add2 called. [AOP at around] after invoke, result = 3, class org.littlewings.springboot.service.SimpleCalcService#add2Internal simpleCalcService.add2 = 3
ç¶ãã¦ãå¥ã®ã¯ã©ã¹ã«å§è²ããã±ã¼ã¹ã
src/main/java/org/littlewings/springboot/service/OuterCalcService.java
package org.littlewings.springboot.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class OuterCalcService { @Autowired private InternalCalcService internalCalcService; public int add(int a, int b) { return internalCalcService.add(a, b); } }
å¤å´ãå
å´ã¨è¨ãã®ãå¤ã§ããã便å®ä¸ã
src/main/java/org/littlewings/springboot/service/InternalCalcService.java
package org.littlewings.springboot.service; import org.springframework.stereotype.Service; @Service public class InternalCalcService { public int add(int a, int b) { return a + b; } }
ããã@Autowiredãã¦
@Autowired private OuterCalcService outerCalcService;
å®è¡ã
System.out.printf("outerCalcService.add = %d%n", outerCalcService.add(1, 2));
çµæã
[AOP at around] before invoke, parameters = [1, 2], by class org.littlewings.springboot.service.OuterCalcService#add [AOP at before] called parameters = [1, 2], by class org.littlewings.springboot.service.OuterCalcService#add [AOP at around] before invoke, parameters = [1, 2], by class org.littlewings.springboot.service.InternalCalcService#add [AOP at before] called parameters = [1, 2], by class org.littlewings.springboot.service.InternalCalcService#add [AOP at around] after invoke, result = 3, class org.littlewings.springboot.service.InternalCalcService#add [AOP at around] after invoke, result = 3, class org.littlewings.springboot.service.OuterCalcService#add outerCalcService.add = 3
両æ¹åä½ãã¾ãããã¨ããããããã§ããâ¦ã
ãã¨ãJDKã®Proxyã使ãçã確èªãã¦ã¿ã¾ããã
ã¤ã³ã¿ã¼ãã§ã¼ã¹ã®å®ç¾©ã
src/main/java/org/littlewings/springboot/service/JdkProxyCalcService.java
package org.littlewings.springboot.service; public interface JdkProxyCalcService { int add(int a, int b); }
å®è£
å´ã
src/main/java/org/littlewings/springboot/service/JdkProxyCalcServiceImpl.java
package org.littlewings.springboot.service; import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.ScopedProxyMode; import org.springframework.stereotype.Service; @Service @Scope(proxyMode = ScopedProxyMode.INTERFACES) public class JdkProxyCalcServiceImpl implements JdkProxyCalcService { @Override public int add(int a, int b) { return a + b; } }
@Autowiredãã¦
@Autowired private JdkProxyCalcService jdkProxyCalcService;
å®è¡ã
System.out.printf("jdkProxyCalcService.add = %d%n", jdkProxyCalcService.add(1, 2));
çµæã
[AOP at around] before invoke, parameters = [1, 2], by class org.littlewings.springboot.service.JdkProxyCalcServiceImpl#add [AOP at before] called parameters = [1, 2], by class org.littlewings.springboot.service.JdkProxyCalcServiceImpl#add [AOP at around] after invoke, result = 3, class org.littlewings.springboot.service.JdkProxyCalcServiceImpl#add jdkProxyCalcService.add = 3
ç¹ã«åé¡ãªãããã§ãã
Interceptorã®é çªãå¶å¾¡ãã
Interceptorã®é©ç¨é ãå¶å¾¡ãããã¨ãããã¨ã§ãã©ãããã°ããã®ããªï¼ã¨æãã¾ãããããããè¦ãã°ããæãã§ãããã
ã¨ããããã2ã¤ãã®Interceptorãç¨æãå
ã®Interceptorã®ã³ãã¼ã§ããã
src/main/java/org/littlewings/springboot/trace/TraceInterceptor2.java
package org.littlewings.springboot.trace; import java.util.Arrays; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; @Aspect @Component public class TraceInterceptor2 { @Before("execution(* org.littlewings.springboot.service.*.*(..))") public void invokeBefore(JoinPoint joinPoint) { System.out.printf("[AOP at before-2] called parameters = %s, by %s#%s%n", Arrays.toString(joinPoint.getArgs()), joinPoint.getTarget().getClass(), joinPoint.getSignature().getName()); // JoinPoint#getThisã®å ´åã¯ãæ¡å¼µããããªãã¸ã§ã¯ããè¿ã } @Around("execution(* org.littlewings.springboot.service.*.*(..))") public Object invoke(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { Object ret = null; try { System.out.printf("[AOP at around-2] before invoke, parameters = %s, by %s#%s%n", Arrays.toString(proceedingJoinPoint.getArgs()), proceedingJoinPoint.getTarget().getClass(), proceedingJoinPoint.getSignature().getName()); ret = proceedingJoinPoint.proceed(); return ret; } finally { System.out.printf("[AOP at around-2] after invoke, result = %s, %s#%s%n", ret, proceedingJoinPoint.getTarget().getClass(), proceedingJoinPoint.getSignature().getName()); } } }
åºåå 容ããã-2ããã¤ãã¦ã¡ããã£ã¨å¤æ´ã
System.out.printf("[AOP at before-2] called parameters = %s, by %s#%s%n",
ãã®ç¶æ ã§ãå®è¡ãã¦ã¿ã¾ãããã
System.out.printf("simpleCalcService.add = %d%n", simpleCalcService.add(1, 2));
ããããçµæã«ãªãã¾ããã
[AOP at around] before invoke, parameters = [1, 2], by class org.littlewings.springboot.service.SimpleCalcService#add [AOP at before] called parameters = [1, 2], by class org.littlewings.springboot.service.SimpleCalcService#add [AOP at around-2] before invoke, parameters = [1, 2], by class org.littlewings.springboot.service.SimpleCalcService#add [AOP at before-2] called parameters = [1, 2], by class org.littlewings.springboot.service.SimpleCalcService#add class org.littlewings.springboot.service.SimpleCalcService#add called. [AOP at around-2] after invoke, result = 3, class org.littlewings.springboot.service.SimpleCalcService#add [AOP at around] after invoke, result = 3, class org.littlewings.springboot.service.SimpleCalcService#add simpleCalcService.add = 3
ãããé転ãããã«ã¯ï¼@Orderã¢ããã¼ã·ã§ã³ãæå®ããã°ããããï¼
import org.springframework.core.annotation.Order;
â»Orderã¤ã³ã¿ã¼ãã§ã¼ã¹ãå®è£ ããã§ãè¯ãããã§ãã
å ã«ä½ã£ãInterceptorã200ã«
@Aspect @Component @Order(200) public class TraceInterceptor {
ãã¨ã§ä½ã£ãInterceptorã100ã«è¨å®ã
@Aspect @Component @Order(100) public class TraceInterceptor2 {
å®è¡çµæã
[AOP at around-2] before invoke, parameters = [1, 2], by class org.littlewings.springboot.service.SimpleCalcService#add [AOP at before-2] called parameters = [1, 2], by class org.littlewings.springboot.service.SimpleCalcService#add [AOP at around] before invoke, parameters = [1, 2], by class org.littlewings.springboot.service.SimpleCalcService#add [AOP at before] called parameters = [1, 2], by class org.littlewings.springboot.service.SimpleCalcService#add class org.littlewings.springboot.service.SimpleCalcService#add called. [AOP at around] after invoke, result = 3, class org.littlewings.springboot.service.SimpleCalcService#add [AOP at around-2] after invoke, result = 3, class org.littlewings.springboot.service.SimpleCalcService#add simpleCalcService.add = 3
çµæãå ¥ãæ¿ããã¾ãããæ°åãå°ããæ¹ããå ã«å®è¡ããããã¨ã
åºç¤çãªä½¿ãæ¹ã¯ããã£ãæãï¼ã§ãããOrderã«æå®ããå¤ã£ã¦ãã©ã®ãããããå§ããã°é©åãªã®ããªã