アドバイスの作成2 - @Around

Spring AOP により、どのようなことができるのか、少しずつ掴めてきたかと思います。既に説明済みではありますが、@Around については、挙動にくせがあるので、ここで実際に確認しておきます。

SQL の結果を、ヒープメモリー上のマップにキャッシュするためのコンフィグレーション、ポイントカット、およびアドバイスを、それぞれ以下を参考に作成します。

// src/main/java/io/github/yo1000/sss/config/CacheConfiguration.java
package io.github.yo1000.sss.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.ConcurrentHashMap;

@Configuration
public class CacheConfiguration {
    public static class CacheStoreMap<K, V> extends ConcurrentHashMap<K, V> {}

    @Bean
    public CacheStoreMap<String, Object> cacheStoreMap() {
        return new CacheStoreMap<>();
    }
}
// src/main/java/io/github/yo1000/sss/aspect/RepositoryPointcut.java
package io.github.yo1000.sss.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class RepositoryPointcut {
    @Pointcut("execution(* io.github.yo1000.sss.repository..MemoRepository+.save(..))")
    public void save() {}

    @Pointcut("execution(* io.github.yo1000.sss.repository..MemoRepository+.findByAuthor(..)) && args(author)")
    public void findByAuthor(String author) {}
}
// src/main/java/io/github/yo1000/sss/aspect/CacheAdvice.java
package io.github.yo1000.sss.aspect;

import io.github.yo1000.sss.config.CacheConfiguration;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class CacheAdvice {
    private static final Logger LOGGER = LoggerFactory.getLogger(CacheAdvice.class);

    private CacheConfiguration.CacheStoreMap<String, Object> cacheStoreMap;

    @Autowired
    public CacheAdvice(CacheConfiguration.CacheStoreMap<String, Object> cacheStoreMap) {
        this.cacheStoreMap = cacheStoreMap;
    }

    @Around(value = "io.github.yo1000.sss.aspect.RepositoryPointcut.findByAuthor(author)")
    public Object cacheFind(ProceedingJoinPoint proceedingJoinPoint, String author) throws Throwable {
        LOGGER.info("getSignature().toLongString() " + proceedingJoinPoint.getSignature().toLongString());

        if (getCacheStoreMap().containsKey(author)) {
            LOGGER.info("Cache hit");
            return getCacheStoreMap().get(author);
        }

        LOGGER.info("Cache miss");
        Object returnValue = proceedingJoinPoint.proceed();
        getCacheStoreMap().put(author, returnValue);

        return returnValue;
    }

    public CacheConfiguration.CacheStoreMap<String, Object> getCacheStoreMap() {
        return cacheStoreMap;
    }
}

アプリケーションを再起動して、改めて、http://localhost:8080/memo/金次郎 へアクセスします。画面が表示されたら、ページをリロードして、ログを確認します。

2016-05-24 20:23:48.278  INFO 8091 --- [nio-8080-exec-1] io.github.yo1000.sss.aspect.CacheAdvice  : getSignature().toLongString() public abstract java.util.List io.github.yo1000.sss.repository.MemoRepository.findByAuthor(java.lang.String)
2016-05-24 20:23:48.279  INFO 8091 --- [nio-8080-exec-1] io.github.yo1000.sss.aspect.CacheAdvice  : Cache miss
2016-05-24 20:23:50.499  INFO 8091 --- [nio-8080-exec-2] io.github.yo1000.sss.aspect.CacheAdvice  : getSignature().toLongString() public abstract java.util.List io.github.yo1000.sss.repository.MemoRepository.findByAuthor(java.lang.String)
2016-05-24 20:23:50.500  INFO 8091 --- [nio-8080-exec-2] io.github.yo1000.sss.aspect.CacheAdvice  : Cache hit

初回アクセスでは、Cache miss が出力され、リロード後は、Cache hit がログに出力されます。キャッシュ上に目的のオブジェクトが見つかった場合、#proceed() を呼び出しておらず、DB へはアクセスされないことが確認できます。

このように、@Around を使用すると、目的の処理前後にアドバイスが作用し、本来の処理の流れそのものを変更できてしまいます。非常に協力に開発を助ける一方、処理の流れを変えることができてしまうため、ポイントカット記述次第では、予期せぬ処理の変更が紛れ込んでしまうこともあり、十分に注意して使用する必要があります。

サンプル

ここまでのコードサンプルは、以下より入手できます。

$ git clone -b chapter/9 https://github.com/yo1000/self-study-spring.git

results matching ""

    No results matching ""