自定义Spring动态代理类型

最近需要需要统计每一个节点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-classfalse。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

自定义Spring动态代理类型的相关文章

Spring动态数据源实现读写分离

一.创建基于ThreadLocal的动态数据源容器,保证数据源的线程安全性 package com.bounter.mybatis.extension; /** * 基于ThreadLocal实现的动态数据源容器,保证DynamicDataSource的线程安全性 * @author simon * */ public class DynamicDataSourceHolder { private static final ThreadLocal<String> dataSourceHolde

自定义spring参数注解 - 打破@RequestBody单体限制

本文主要描述怎样自定义类似@RequestBody这样的参数注解来打破@RequestBody的单体限制. 目录1 @RequestBody的单体限制2 自定义spring的参数注解3 编写spring的参数注解解析器4 将自定义参数注解解析器设置到spring的参数解析器集合中5 指定参数解析器的优先级 一.@RequestBody的单体限制@RequestBody的作用:将请求体中的整体数据转化为对象. 1 @RequestMapping(value = "/body", meth

Spring动态配置多数据源

Spring动态配置多数据源,即在大型应用中对数据进行切分,并且采用多个数据库实例进行管理,这样可以有效提高系统的水平伸缩性.而这样的方案就会不同于常见的单一数据实例的方案,这就要程序在运行时根据当时的请求及系统状态来动态的决定将数据存储在哪个数据库实例中,以及从哪个数据库提取数据. 基本信息 1.Spring配置多数据源的方式和具体使用过程. 2.Spring对于多数据源,以数据库表为参照,大体上可以分成两大类情况: 一是,表级上的跨数据库.即,对于不同的数据库却有相同的表(表名和表结构完全相

VS2017 Linux C++引用自定义的动态库

前一篇博客讲了用系统库libpthread.so的例子,只需要在项目属性页的[C++->命令行参数]和[链接器->命令行参数]中加上对应参数(比如-pthread)即可,然后我试着引用自己的库(libTLPI.so,放在/lib目录下),然后在命令行参数后面加上-lTLPI,发现无法引用自定义的动态库,undefined reference错误. 在网上找了很久,都没找到解决方案,最后还是官方周到,在VS首页的教程中就有 因为之前配置过opengl,所以立刻看出来GL.GLU.glut代表的是

自定义spring schema简化与canal集成

canal是阿里巴巴团队基于数据库日志增量订阅&消费的框架,项目中我们经常使用Spring来集成管理其它框架,本文讲述自定义spring xsd schema的方式配置集成canal到Spring容器. 项目地址:http://git.oschina.net/damivip/spring-xsd-canal 使用canal可以清楚的知道数据库记录的IUD的具体内容. 新增:可解析出新增数据的字段和内容 删除:可解析出删除数据的行所有内容 更新:可解析出更新记录前后的字段内容,和更新的字段是哪些

快速学会Spring动态代理原理

本文主要是讲述快速学会Spring动态代理原理,更多Java技术知识,请登陆疯狂软件教育官网. 一.为什么要使用动态代理 当一个对象或多个对象实现了N中方法的时候,由于业务需求需要把这个对象和多个对象的N个方法加入一个共同的方法,比如把所有对象的所有方法加入事务这个时候有三种方法: 方法一:一个一个对象一个一个方法去加,很显然这个方法是一个比较笨的方法. 方法二:加一个静态代理对象将这个静态代理对象实现要加事务对象的接口.然后在静态代理对象里面每个方法里面加上事务. 方法三:使用动态代理对象,进

Quartz 2.x与Spring 动态整合

一.Quartz简介 Quartz是一个由James House创立的开源项目,是一个功能强大的作业调度工具,可以计划的执行任务,定时.循环或在某一个时间来执行我们需要做的事,这可以给我们工作上带来很大的帮助.例如,你的程序中需要每个月的一号导出报表.定时发送邮件或程序需要每隔一段执行某一任务--等等,都可以用Quartz来解决. Quartz大致可分为三个主要的核心: 1.调度器Scheduler:是一个计划调度器容器,容器里面可以盛放众多的JobDetail和Trigger,当容器启动后,里

Spring动态切换多数据源解决方案

spring动态配置多数据源,即在大型应用中对数据进行切分,并且采用多个数据库实例进行管理,这样可以有效提高系统的水平伸缩性.而这样的方案就会不同于常见的单一数据实例的方案,这就要程序在运行时根据当时的请求及系统状态来动态的决定将数据存储在哪个数据库实例中,以及从哪个数据库提取数据. Spring2.x以后的版本中采用Proxy模式,就是我们在方案中实现一个虚拟的数据源,并且用它来封装数据源选择逻辑,这样就可以有效地将数据源选择逻辑从Client中分离出来.Client提供选择所需的上下文(因为

Spring动态多数据源实例Demo

最近由于咨询Spring如何配置多数据源的人很多,一一回答又比较麻烦,而且以前的博文中的配置也是有问题,因此特此重新发布一个Demo给大家. Demo中共有两个数据源,即Mysql和Oracle,并已经进行简单测试,动态切换数据源是没有问题的,希望借此Demo能帮助到大家. Demo下载地址: Spring动态切换多数据源Demo:http://download.csdn.net/download/wangpeng047/8419953 另外我给些说明,阐述下多数据源配置时的重点: 1. 注意事