最近需要需要统计每一个节点memcache的命中、并发、超时情况。
memcache client选择的xmemcached
<dependency>
<groupId>com.googlecode.xmemcached</groupId>
<artifactId>xmemcached</artifactId>
<version>2.0.0</version>
</dependency>
很容易想到@Aspect
来解决。
@Aspect
@Configuration
public class MemcacheClientAspect {
@Around("execution(public * net.rubyeye.xmemcached.MemcachedClient.*(..)) ")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
...
但是加入代码后,Unable to proxy method
...
14:30:29,103 INFO [org.springframework.aop.framework.CglibAopProxy.doValidateClass(CglibAopProxy.java:264)] Unable to proxy method [public final boolean net.rubyeye.xmemcached.XMemcachedClient.isShutdown()] because it is final: All calls to this method via a proxy will NOT be routed to the target instance.
14:30:29,104 INFO [org.springframework.aop.framework.CglibAopProxy.doValidateClass(CglibAopProxy.java:264)] Unable to proxy method [private final void net.rubyeye.xmemcached.XMemcachedClient.registerMBean()] because it is final: All calls to this method via a proxy will NOT be routed to the target instance.
14:30:29,106 INFO [org.springframework.aop.framework.CglibAopProxy.doValidateClass(CglibAopProxy.java:264)] Unable to proxy method [public final java.util.Map net.rubyeye.xmemcached.XMemcachedClient.getStats() throws net.rubyeye.xmemcached.exception.MemcachedException,java.lang.InterruptedException,java.util.concurrent.TimeoutException] because it is final: All calls to this method via a proxy will NOT be routed to the target instance.
14:30:29,107 INFO [org.springframework.aop.framework.CglibAopProxy.doValidateClass(CglibAopProxy.java:264)] Unable to proxy method [public final net.rubyeye.xmemcached.GetsResponse net.rubyeye.xmemcached.XMemcachedClient.gets(java.lang.String,long,net.rubyeye.xmemcached.transcoders.Transcoder) throws java.util.concurrent.TimeoutException,java.lang.InterruptedException,net.rubyeye.xmemcached.exception.MemcachedException] because it is final: All calls to this method via a proxy will NOT be routed to the target instance.
14:30:29,109 INFO [org.springframework.aop.framework.CglibAopProxy.doValidateClass(CglibAopProxy.java:264)] Unable to proxy method [public final net.rubyeye.xmemcached.GetsResponse net.rubyeye.xmemcached.XMemcachedClient.gets(java.lang.String) throws java.util.concurrent.TimeoutException,java.lang.InterruptedException,net.rubyeye.xmemcached.exception.MemcachedException] because it is final: All calls to this method via a proxy will NOT be routed to the target instance.
14:30:29,110 INFO [org.springframework.aop.framework.CglibAopProxy.doValidateClass(CglibAopProxy.java:264)] Unable to proxy method [public final net.rubyeye.xmemcached.GetsResponse net.rubyeye.xmemcached.XMemcachedClient.gets(java.lang.String,long) throws java.util.concurrent.TimeoutException,java.lang.InterruptedException,net.rubyeye.xmemcached.exception.MemcachedException] because it is final: All calls to this method via a proxy will NOT be routed to the target instance.
...
Spring对AOP的支持有几种
- 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
- 如果目标对象没有实现了接口,必须采用CGLIB代理
看下createAopProxy
的源码
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface()) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
...
Spring会自动在JDK动态代理和CGLIB之间转换,但是,但是,
package net.rubyeye.xmemcached;
import ...
/**
* The memcached client‘s interface
*
* @author dennis
*
*/
public interface MemcachedClient {
MemcachedClient
是接口啊,是interface
没错啊,怎么就告诉我
because it is final: All calls to this method via a proxy will NOT be routed to the target instance.
那么这有接口的情况下用的是CglibAopProxy
,为什么呢?因为历史原因,多处配置是这样的。
<aop:aspectj-autoproxy proxy-target-class="true" />
自动转换仅仅在
<aop:aspectj-autoproxy/>
<aop:aspectj-autoproxy proxy-target-class="false" />
历史原因的一些东西不能改的。默认情况下 proxy-target-class
为false
。Spring会自动选择代理方式。想代理XMemcachedClient
,由于其实现的方法几乎都加了final
,cglib的方式是不可行的,只能选择JdkDynamicAopProxy
的方式。
那么就得修改配置,但是,基本没提供什么可供修改的配置。看看代理的建立方式:
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
}
注意看forceAutoProxyCreatorToUseClassProxying
,只要有一个地方配置了 proxyTargetClass
全部强制使用CglibAopProxy
代理方式。
但是XMemcachedClient
只能使用 JdkDynamicAopProxy
,因为实现都是final
。由于ProxyFactory
是单例的,只能在针对XMemcachedClient
在创建一个了。
@Configuration
public class MemcacheClientJDKAspect {
@Bean
public MemcacheInterceptor memcacheInterceptor(){
return new MemcacheInterceptor();
}
public MemcachedClient buildBean(MemcachedClient memcachedClient){
//pointcut-ref
AspectJExpressionPointcut aspectJExpressionPointcut = new AspectJExpressionPointcut();
aspectJExpressionPointcut.setExpression("execution(public * net.rubyeye.xmemcached.MemcachedClient.*(..))");
Advisor advisor = new DefaultPointcutAdvisor(aspectJExpressionPointcut,memcacheInterceptor());
ProxyFactory memcachedClientProxyFactory = new ProxyFactory(memcachedClient);
memcachedClientProxyFactory.addAdvisor(advisor);
memcachedClientProxyFactory.addAdvice(memcacheInterceptor());
memcachedClientProxyFactory.setProxyTargetClass(false);
return (MemcachedClient) memcachedClientProxyFactory.getProxy();
}
}
这样,目的达到了。
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-11-05 18:29:44