前两天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方法实现的完美一点,可以让我们直接使用。
所有代码在附件中,谢谢。
分享到:
相关推荐
标题"Spring_0300_JDKProxy"暗示我们将讨论Spring如何使用JDK的Proxy类来实现动态代理。在Spring中,`org.springframework.aop.framework.ProxyFactoryBean`或`org.springframework.aop.framework.ProxyFactory`可以...
然而,如果需要更精细的控制,可以通过`@EnableAspectJAutoProxy`注解开启基于AspectJ的自动代理支持,或者通过`proxyTargetClass`属性来强制使用CGLIB代理。 总结一下,Spring的AOP机制通过JDK动态代理和CGLIB动态...
在Spring Boot中,我们可以利用`ProxyServlet`实现反向代理,将请求转发到不同的微服务,同时保持对外接口的一致性。 实现步骤如下: 1. 添加依赖:在`pom.xml`文件中引入Spring Boot的`spring-boot-starter-web`...
public class Client { public static void main(String[] args) { // UserService userService=new UserServiceImpl(); // ProxyInovationHandler pih =new ProxyInovationHandler(); // pih.setTarget...
在本案例中,我们将探讨如何实现一个动态代理接口并将其注册到Spring容器,以便于通过@Autowired注解或其他方式在Spring应用中使用。 首先,我们需要定义一个接口,例如`MyService`,这个接口包含了我们需要代理的...
Spring支持两种主要的动态代理方式:Java Proxy和cglib。这两种方法都可以用来增强或拦截目标对象的方法调用,实现如AOP(面向切面编程)的功能。 首先,我们来看Java Proxy。Java Proxy是Java内置的动态代理机制,...
proxyTargetClass参数指示是否使用CGLIB代理机制(对于没有实现接口的类)或者是JDK动态代理机制(对于实现接口的类)。exposeProxy参数则决定是否将代理对象暴露给ThreadLocal,以便可以在Bean内部获取到。 在...
TargetClass proxyInstance = (TargetClass) enhancer.create(); // 调用代理对象的方法 proxyInstance.targetMethod(); } } class TargetClass { public void targetMethod() { System.out.println("Target ...
@EnableAspectJAutoProxy(proxyTargetClass = false) public class AppConfig { // 配置Bean等 } ``` 3. **选择正确的代理模式**: - 如果需要使用CGLIB代理,可以在XML配置中加入以下语句: ```xml ...
虽然没有具体的源码提供,但`Proxy.newProxyInstance()`方法的实现涉及到JVM层面的字节码生成。它会生成一个实现了指定接口的新类,这个类包含了调用`InvocationHandler.invoke()`的逻辑。这个过程涉及到反射API和...
在Java开发领域,Spring框架是不可或缺的一部分,尤其在面向切面编程(AOP)的应用上。Spring AOP允许我们通过代理来实现横切关注点,如日志、事务管理等,而JDK动态代理则是Spring AOP实现的一种方式。本文将深入...
1. **定义目标类(Target Class)**:首先,我们需要一个业务逻辑类,例如`MyService`,它包含我们想要增强的方法。 ```java public class MyService { public void doSomething() { // 业务逻辑 } } ``` 2. **...
如果没有接口,或者配置了`proxyTargetClass=true`,则会使用CGLIB代理,它通过字节码生成技术实现。 2. **配置属性**: - `target`:这是必须设置的属性,表示要代理的目标对象。 - `interceptors`或`advisors`...
在Spring框架中,动态代理是一种常见且强大的技术,它允许我们在运行时创建对象的代理,以便在调用方法时添加额外的功能,如日志、事务管理、缓存等。本篇将深入探讨如何通过动态代理将对象注册到Spring容器,并实现...
<aop:aspectj-autoproxy proxy-target-class="true"> xml中要有这句,可以解决子类类报错的问题。 或者去掉这句话,同时去掉public class AccountServiceImpl implements AccountService {继承关系,也可以解决
Class<?> proxyClass = Proxy.getProxyClass(realObject.getClass().getClassLoader(), realObject.getClass().getInterfaces()); Foo foo = (Foo) proxyClass.getConstructor(InvocationHandler.class).newInstance...
TargetClass proxyInstance = (TargetClass) enhancer.create(); proxyInstance.targetMethod(); ``` 在上述代码中,我们使用`Enhancer`创建了目标类的代理对象,并通过`setCallback`设置了一个`MethodInterceptor`...
<security:form-login login-page="/login" default-target-url="/home" /> <!-- 用户服务实现 --> <bean id="userDetailsService" class=...
可以通过配置`proxyTargetClass="true"`来强制使用CGLIB。 4. **代理对象的获取**:通常,Spring容器会自动创建并管理代理对象。在需要调用目标方法的地方,使用Spring注入的bean,实际上就是在操作代理对象。 综...
这个新类在运行时被编译并加载到JVM中,因此称为动态代理。 下面详细讲解一下JDK动态代理的实现过程: 1. **定义接口**:首先,我们需要定义一个或多个业务接口,这些接口定义了目标对象需要实现的方法。 ```java...