Skip to content

Commit

Permalink
#15 Cached by proxy (cglib) in spring
Browse files Browse the repository at this point in the history
  • Loading branch information
vlad-iv committed May 20, 2019
1 parent 6c7668d commit 456ea0c
Show file tree
Hide file tree
Showing 17 changed files with 124 additions and 49 deletions.
10 changes: 10 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@
<artifactId>lombok</artifactId>
<version>1.18.6</version>
</dependency>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jool-java-8</artifactId>
<version>0.9.14</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
Expand Down Expand Up @@ -118,6 +123,10 @@
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jool-java-8</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
Expand Down Expand Up @@ -202,6 +211,7 @@
<output>file</output>
<excludes>
<exclude>app/prime/log/**/*</exclude>
<exclude>app/prime/cache/**/*</exclude>
</excludes>
</configuration>
<executions>
Expand Down
3 changes: 0 additions & 3 deletions rest/src/main/java/app/prime/controller/PrimeController.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package app.prime.controller;

import app.prime.Logged;
import app.prime.WithCache;
import app.prime.model.PrimeList;
import app.prime.model.PrimeResult;
import app.prime.service.PrimeFactorService;
Expand All @@ -22,10 +21,8 @@
@RequestMapping("/prime")
public class PrimeController {
@Autowired
@WithCache
PrimeService primeService;
@Autowired
@WithCache
PrimeFactorService primeFactorService;

@Logged
Expand Down
6 changes: 5 additions & 1 deletion rest/src/main/java/app/prime/log/LoggingAspect.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@
public class LoggingAspect {
@Around("execution(* *(..)) && @annotation(app.prime.Logged)")
public Object logAroundLoggedMethod(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("Call: {}.{}({})", joinPoint.getTarget().getClass().getSimpleName(), joinPoint.getSignature().getName(), Arrays.toString(joinPoint.getArgs()));
Class<?> target = joinPoint.getTarget().getClass();
if (target.getName().contains("$$")) {
target = target.getSuperclass();
}
log.info("Call: {}.{}({})", target.getSimpleName(), joinPoint.getSignature().getName(), Arrays.toString(joinPoint.getArgs()));
return joinPoint.proceed();
}
}
8 changes: 7 additions & 1 deletion rest/src/main/resources/prime-servlet.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
<aop:aspectj-autoproxy/>
<context:annotation-config/>

<cache:annotation-driven/>
<bean id="cacheManager" class="org.springframework.cache.concurrent.ConcurrentMapCacheManager"/>

<import resource="service-context.xml"/>
<context:component-scan base-package="app.prime.controller;app.prime.log"/>
<mvc:annotation-driven/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import static org.junit.Assert.assertThat;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "/test-prime-context.xml")
@ContextConfiguration(classes = TestConfig.class)
public class PrimeControllerTest {
@Autowired
PrimeController primeController;
Expand Down
14 changes: 14 additions & 0 deletions rest/src/test/java/app/prime/controller/TestConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package app.prime.controller;

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

/**
* Test configuration with lazy bean init.
*
* @author Vladimir Ivanov ([email protected])
*/
@Configuration
@ComponentScan(lazyInit = true, value = {"app.prime.controller", "app.prime.service", "app.prime.cache", "app.prime.log"})
public class TestConfig {
}
10 changes: 0 additions & 10 deletions rest/src/test/resources/test-prime-context.xml

This file was deleted.

65 changes: 65 additions & 0 deletions service/src/main/java/app/prime/cache/CacheBeanPostProcessor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package app.prime.cache;

import lombok.extern.log4j.Log4j2;
import org.jooq.lambda.Unchecked;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
* Add cacheable proxy to bean annotated with cache.
*
* @author Vladimir Ivanov ([email protected])
*/
@Log4j2
@Component
public class CacheBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean.getClass().isAnnotationPresent(WithCache.class)) {
log.debug("Wrapping with cache " + bean);
return proxy(bean);
}
return bean;
}

private static Object proxy(Object bean) {
Callback callback = new MethodInterceptor() {
private final Map<Object, Object> cache = new ConcurrentHashMap<>();

private List<Object> getCachekey(Method method, Object[] args) {
List<Object> list = new ArrayList<>();
list.add(method.getName());
list.addAll(Arrays.asList(args));
return list;
}

@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) {
List<Object> key = getCachekey(method, args);
log.debug("Intercepted: " + method.getName());
return cache.computeIfAbsent(key, Unchecked.function(ignored -> {
log.info("Calculating {}.{}({})", bean.getClass().getSimpleName(), method.getName(), Arrays.toString(args));
long start = System.nanoTime();
Object invoke = method.invoke(bean, args);
long finish = System.nanoTime();
log.info("Calculating {}.{}({}) {} ms", bean.getClass().getSimpleName(), method.getName(), Arrays.toString(args), (finish - start) / 1_000L);
return invoke;
}));
}
};
return Enhancer.create(bean.getClass(), callback);
}

}
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
package app.prime;
package app.prime.cache;

import org.springframework.beans.factory.annotation.Qualifier;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Select cached version.
*
* @author Vladimir Ivanov ([email protected])
*/
@Qualifier
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface WithCache {
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package app.prime;
package app.prime.cache;

import org.springframework.beans.factory.annotation.Qualifier;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package app.prime.service;

import app.prime.WithCache;
import app.prime.WithOutCache;
import app.prime.cache.WithOutCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
Expand All @@ -14,8 +12,6 @@
*
* @author Vladimir Ivanov ([email protected])
*/
@Service
@WithCache
public class CachedPrimeFactorService implements PrimeFactorService {
private final PrimeFactorService primeFactorService;
private final Map<Long, List<Long>> cache;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package app.prime.service;

import app.prime.WithCache;
import app.prime.WithOutCache;
import app.prime.cache.WithOutCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
Expand All @@ -13,8 +11,6 @@
*
* @author Vladimir Ivanov ([email protected])
*/
@Service
@WithCache
public class CachedPrimeService implements PrimeService {
private final PrimeService primeService;
private final Map<Long, Boolean> cache;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package app.prime.service;

import app.prime.Logged;
import app.prime.WithCache;
import app.prime.WithOutCache;
import app.prime.cache.WithCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

Expand All @@ -15,22 +14,20 @@
* @author Vladimir Ivanov ([email protected])
*/
@Service
@WithOutCache
@WithCache
public class SimplePrimeFactorService implements PrimeFactorService {
final PrimeService primeService;

@Autowired
@WithCache
public SimplePrimeFactorService(PrimeService primeService) {
this.primeService = primeService;
PrimeService primeService;

public SimplePrimeFactorService() {
}

/**
* List prime factors.
*/
@Logged
public List<Long> findPrimeFactors(final long number) {

//SimplePrimeFactorService proxy = (SimplePrimeFactorService) AopContext.currentProxy();
final List<Long> primes = new ArrayList<>();

long divide = number;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package app.prime.service;

import app.prime.Logged;
import app.prime.WithOutCache;
import app.prime.cache.WithCache;
import org.springframework.stereotype.Service;

/**
Expand All @@ -10,7 +10,7 @@
* @author Vladimir Ivanov ([email protected])
*/
@Service
@WithOutCache
@WithCache
public class SimplePrimeService implements PrimeService {
/**
* Check a number is prime.
Expand Down
2 changes: 1 addition & 1 deletion service/src/main/resources/service-context.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="app.prime.service"/>
<context:component-scan base-package="app.prime.service;app.prime.cache"/>
</beans>
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,9 @@ public class CachedPrimeFactorServiceTest {
@Before
public void setUp() {
cache = new ConcurrentHashMap<>();
primeFactorService = new CachedPrimeFactorService(
new SimplePrimeFactorService(
new SimplePrimeService()
),
cache
);
SimplePrimeFactorService primeFactorService = new SimplePrimeFactorService();
primeFactorService.primeService = new SimplePrimeService();
this.primeFactorService = new CachedPrimeFactorService(primeFactorService, cache);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
import static org.junit.Assert.assertThat;

public class SimplePrimeFactorServiceTest {
PrimeFactorService primeFactorService;
SimplePrimeFactorService primeFactorService;

@Before
public void setUp() {
primeFactorService = new SimplePrimeFactorService(new SimplePrimeService());

primeFactorService = new SimplePrimeFactorService();
primeFactorService.primeService = new SimplePrimeService();
}

@Test
Expand Down

0 comments on commit 456ea0c

Please sign in to comment.