`
Norther
  • 浏览: 26062 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

拿到spring proxy的target class

阅读更多
前两天ahuaxuan同学的帖子aop cache再讨论,讲述了利用AOP来实现method cache,写的很好,但是有一个遗憾,就是那个代码不能对代理对象实现cache,下面是ahuaxuan的代码
if (invocation.getThis().getClass().getCanonicalName().contains("$Proxy")) {  
	if (logger.isWarnEnabled()) {  
	      logger.warn("----- The object has been proxyed and method " +  
                  "cache interceptor can't get the target, " +  
                  "so the method result can't be cached which name is ------" + methodName);  
        }  
               
return invocation.proceed();  

上述代码中,如果invocation.getThis()得到的对象是一个spring利用JDK dynamic proxy创建的代理,那么其getClass().getName()方法返回的是一种类似$Proxy21之类的名字,就无法利用其class name配置相应的cache了,所以ahuaxuan才写了上面的代码,碰到是dynamic proxy,则跳过cache处理,造成了遗憾,我们就试着弥补一下ahuaxuan的遗憾,经过观察spring代码,找到了类org.springframework.aop.support.AopUtils的方法getTargetClass,其java doc也说明了该方法可以根据proxy获得其target class,代码如下,
public static Class getTargetClass(Object candidate) {
	Assert.notNull(candidate, "Candidate object must not be null");
	if (candidate instanceof TargetClassAware) {
		return ((TargetClassAware) candidate).getTargetClass();
	}
	if (isCglibProxyClass(candidate.getClass())) {
		return candidate.getClass().getSuperclass();
	}
	return candidate.getClass();
}

短短几行代码,我们发现,这个方法对dynamic proxy根本不起作用,它只适用于实现了TargetClassAware接口的对象,或者由cglib产生的代理对象,但是jdk提供的dynamic proxy是我们最常用的创建代理的方式,是spring的默认行为,难道就不行了吗?当然不是了!(好老套的句型。。。),我们知道在spring中,创建proxy主要有两种方式,一种是cglib,其核心类为org.springframework.aop.framework.Cglib2AopProxy,另一种是最常用的jdk提供的dynamic proxy机制,核心类是org.springframework.aop.framework.JdkDynamicAopProxy,而这个JdkDynamicAopProxy类也是创建dynamic proxy的核心,既它实现了java.lang.reflect.InvocationHandler接口,而通过java.lang.reflect.Proxy的静态方法getInvocationHandler可以从一个proxy得到其对应的InvocationHandler,这就是问题关键了,拿到了InvocationHandler的实例,也就是JdkDynamicAopProxy,就可以通过反射拿到它携带的AdvisedSupport对象,从而拿到target class!

JdkDynamicAopProxy的主要代码如下:
class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
	/** Config used to configure this proxy */
	private final AdvisedSupport advised;


而我们实现的代码如下
private static final String ADVISED_FIELD_NAME = "advised";

private static final String 
CLASS_JDK_DYNAMIC_AOP_PROXY = "org.springframework.aop.framework.JdkDynamicAopProxy";

public static Class getTargetClass(Object candidate) {
	if (!org.springframework.aop.support.AopUtils.isJdkDynamicProxy(candidate)) {
		return org.springframework.aop.support.AopUtils.getTargetClass(candidate);
	}

	return getTargetClassFromJdkDynamicAopProxy(candidate);
}

private static Class getTargetClassFromJdkDynamicAopProxy(Object candidate) {
	try {
		InvocationHandler invocationHandler = Proxy.getInvocationHandler(candidate);
		if (!invocationHandler.getClass().getName().equals(CLASS_JDK_DYNAMIC_AOP_PROXY)) {
			//在目前的spring版本,这处永远不会执行,除非以后spring的dynamic proxy实现变掉
			log.warn("the invocationHandler of JdkDynamicProxy isn`t the instance of "
 + CLASS_JDK_DYNAMIC_AOP_PROXY);
			return candidate.getClass();
		}
		AdvisedSupport advised = (AdvisedSupport) new DirectFieldAccessor(invocationHandler).getPropertyValue(ADVISED_FIELD_NAME);
		Class targetClass = advised.getTargetClass();
		if (Proxy.isProxyClass(targetClass)) {
			// 目标类还是代理,递归
			Object target = advised.getTargetSource().getTarget();
			return getTargetClassFromJdkDynamicAopProxy(target);
		}
		return targetClass;
	} catch (Exception e) {
		log.error("get target class from " + CLASS_JDK_DYNAMIC_AOP_PROXY + " error", e);
		return candidate.getClass();
	}
}


这样就OK了,扩展了AopUtils.getTargetClass方法,从而支持获得dynamic proxy的target class,上述代码里的JdkDynamicAopProxy不是public的,是我们通过反射来强行拿到其里面的代理信息,这种方式属于很猥琐的方式,如果以后spring升级了,那个JdkDynamicAopProxy内部的代码改了,这段代码就行不通了,呵呵,这个原理很简单,雕虫小技而已,个人认为spring完全可以把那个getTargetClass方法实现的完美一点,可以让我们直接使用。

所有代码在附件中,谢谢。
分享到:
评论
4 楼 redhat 2014-08-27  
stupidly wrong! java proxy has many targeted interfaces, it has no concrete implementation, which means it may have no single target!
3 楼 sorphi 2008-11-19  
哦了
spring 2.5加入aspectj的支持后,ProceedingJoinPoint更方便拿到target了
2 楼 Norther 2008-11-19  
sorphi 写道
根本在于,武断的将classname、methodname、parametervalue来做cache的key要素,是很“猥琐”的方案,以至于又出现这些“猥琐”的补丁。

那帖我也建议了,完全可以在method上指定@Cache(key="pattern{0}{1}")这样的方式,可惜没人听啊


你说的有道理,cache那个地方可以有很多种处理方式,但这个不仅限于method cache的补丁,在不少的情况下,需要拿到proxy的target class,都可以用这样的方式,在这里只是从method cache那篇文章引出来而已。
1 楼 sorphi 2008-11-19  
根本在于,武断的将classname、methodname、parametervalue来做cache的key要素,是很“猥琐”的方案,以至于又出现这些“猥琐”的补丁。

那帖我也建议了,完全可以在method上指定@Cache(key="pattern{0}{1}")这样的方式,可惜没人听啊

相关推荐

    Spring_0300_JDKProxy

    标题"Spring_0300_JDKProxy"暗示我们将讨论Spring如何使用JDK的Proxy类来实现动态代理。在Spring中,`org.springframework.aop.framework.ProxyFactoryBean`或`org.springframework.aop.framework.ProxyFactory`可以...

    spring之AOP(动态代理)

    然而,如果需要更精细的控制,可以通过`@EnableAspectJAutoProxy`注解开启基于AspectJ的自动代理支持,或者通过`proxyTargetClass`属性来强制使用CGLIB代理。 总结一下,Spring的AOP机制通过JDK动态代理和CGLIB动态...

    Spring Boot 使用 ProxyServlet 代理并统一响应

    在Spring Boot中,我们可以利用`ProxyServlet`实现反向代理,将请求转发到不同的微服务,同时保持对外接口的一致性。 实现步骤如下: 1. 添加依赖:在`pom.xml`文件中引入Spring Boot的`spring-boot-starter-web`...

    08spring4_dynamicproxy.rar

    public class Client { public static void main(String[] args) { // UserService userService=new UserServiceImpl(); // ProxyInovationHandler pih =new ProxyInovationHandler(); // pih.setTarget...

    动态代理接口并注册到spring容器

    在本案例中,我们将探讨如何实现一个动态代理接口并将其注册到Spring容器,以便于通过@Autowired注解或其他方式在Spring应用中使用。 首先,我们需要定义一个接口,例如`MyService`,这个接口包含了我们需要代理的...

    Spring如何基于Proxy及cglib实现动态代理

    Spring支持两种主要的动态代理方式:Java Proxy和cglib。这两种方法都可以用来增强或拦截目标对象的方法调用,实现如AOP(面向切面编程)的功能。 首先,我们来看Java Proxy。Java Proxy是Java内置的动态代理机制,...

    SpringAop的简单理解.pdf

    proxyTargetClass参数指示是否使用CGLIB代理机制(对于没有实现接口的类)或者是JDK动态代理机制(对于实现接口的类)。exposeProxy参数则决定是否将代理对象暴露给ThreadLocal,以便可以在Bean内部获取到。 在...

    简单模拟spring cglib代理

    TargetClass proxyInstance = (TargetClass) enhancer.create(); // 调用代理对象的方法 proxyInstance.targetMethod(); } } class TargetClass { public void targetMethod() { System.out.println("Target ...

    Spring中的AOP不生效

    @EnableAspectJAutoProxy(proxyTargetClass = false) public class AppConfig { // 配置Bean等 } ``` 3. **选择正确的代理模式**: - 如果需要使用CGLIB代理,可以在XML配置中加入以下语句: ```xml ...

    java Proxy 动态代理

    虽然没有具体的源码提供,但`Proxy.newProxyInstance()`方法的实现涉及到JVM层面的字节码生成。它会生成一个实现了指定接口的新类,这个类包含了调用`InvocationHandler.invoke()`的逻辑。这个过程涉及到反射API和...

    spring jdk动态代理

    在Java开发领域,Spring框架是不可或缺的一部分,尤其在面向切面编程(AOP)的应用上。Spring AOP允许我们通过代理来实现横切关注点,如日志、事务管理等,而JDK动态代理则是Spring AOP实现的一种方式。本文将深入...

    proxy增加日志例子

    1. **定义目标类(Target Class)**:首先,我们需要一个业务逻辑类,例如`MyService`,它包含我们想要增强的方法。 ```java public class MyService { public void doSomething() { // 业务逻辑 } } ``` 2. **...

    spring-aop-ProxyFactoryBean 源码分析

    如果没有接口,或者配置了`proxyTargetClass=true`,则会使用CGLIB代理,它通过字节码生成技术实现。 2. **配置属性**: - `target`:这是必须设置的属性,表示要代理的目标对象。 - `interceptors`或`advisors`...

    动态代理的方式注册到spring容器

    在Spring框架中,动态代理是一种常见且强大的技术,它允许我们在运行时创建对象的代理,以便在调用方法时添加额外的功能,如日志、事务管理、缓存等。本篇将深入探讨如何通过动态代理将对象注册到Spring容器,并实现...

    spring框架手动提交事务,jdbctample

    <aop:aspectj-autoproxy proxy-target-class="true"> xml中要有这句,可以解决子类类报错的问题。 或者去掉这句话,同时去掉public class AccountServiceImpl implements AccountService {继承关系,也可以解决

    spring 的动态代理详解

    Class<?> proxyClass = Proxy.getProxyClass(realObject.getClass().getClassLoader(), realObject.getClass().getInterfaces()); Foo foo = (Foo) proxyClass.getConstructor(InvocationHandler.class).newInstance...

    使用CGLIB模拟spring的拦截器

    TargetClass proxyInstance = (TargetClass) enhancer.create(); proxyInstance.targetMethod(); ``` 在上述代码中,我们使用`Enhancer`创建了目标类的代理对象,并通过`setCallback`设置了一个`MethodInterceptor`...

    初识 Spring Security - v1.1.pdf

    <security:form-login login-page="/login" default-target-url="/home" /> <!-- 用户服务实现 --> <bean id="userDetailsService" class=...

    springAOP配置动态代理实现

    可以通过配置`proxyTargetClass="true"`来强制使用CGLIB。 4. **代理对象的获取**:通常,Spring容器会自动创建并管理代理对象。在需要调用目标方法的地方,使用Spring注入的bean,实际上就是在操作代理对象。 综...

    spring_aop之JDK的动态代理

    这个新类在运行时被编译并加载到JVM中,因此称为动态代理。 下面详细讲解一下JDK动态代理的实现过程: 1. **定义接口**:首先,我们需要定义一个或多个业务接口,这些接口定义了目标对象需要实现的方法。 ```java...

Global site tag (gtag.js) - Google Analytics