diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAopProxyFactory.java b/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAopProxyFactory.java index f97455dfc45c..1f0977963f8f 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAopProxyFactory.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAopProxyFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -59,7 +59,7 @@ public class DefaultAopProxyFactory implements AopProxyFactory, Serializable { @Override public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { - if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { + if (config.isOptimize() || config.isProxyTargetClass() || !hasTargetInterfaces(config)) { Class targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: " + @@ -75,14 +75,14 @@ public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException } } - /** - * Determine whether the supplied {@link AdvisedSupport} has only the - * {@link org.springframework.aop.SpringProxy} interface specified - * (or no proxy interfaces specified at all). - */ - private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) { - Class[] ifcs = config.getProxiedInterfaces(); - return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0]))); + private boolean hasTargetInterfaces(AdvisedSupport config) { + Class targetClass = config.getTargetClass(); + for (Class ifc : config.getProxiedInterfaces()) { + if (targetClass != null ? ifc.isAssignableFrom(targetClass) : !SpringProxy.class.isAssignableFrom(ifc)) { + return true; + } + } + return false; } } diff --git a/spring-aop/src/test/java/org/springframework/aop/framework/ProxyFactoryTests.java b/spring-aop/src/test/java/org/springframework/aop/framework/ProxyFactoryTests.java index 1bda9760714a..28e8b7ee6f54 100644 --- a/spring-aop/src/test/java/org/springframework/aop/framework/ProxyFactoryTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/framework/ProxyFactoryTests.java @@ -189,25 +189,22 @@ public int compareTo(Object arg0) { } } TestBeanSubclass raw = new TestBeanSubclass(); - ProxyFactory factory = new ProxyFactory(raw); - //System.out.println("Proxied interfaces are " + StringUtils.arrayToDelimitedString(factory.getProxiedInterfaces(), ",")); - assertThat(factory.getProxiedInterfaces()).as("Found correct number of interfaces").hasSize(5); - ITestBean tb = (ITestBean) factory.getProxy(); + ProxyFactory pf = new ProxyFactory(raw); + assertThat(pf.getProxiedInterfaces()).as("Found correct number of interfaces").hasSize(5); + ITestBean tb = (ITestBean) pf.getProxy(); assertThat(tb).as("Picked up secondary interface").isInstanceOf(IOther.class); raw.setAge(25); assertThat(tb.getAge()).isEqualTo(raw.getAge()); + Class[] oldProxiedInterfaces = pf.getProxiedInterfaces(); long t = 555555L; TimestampIntroductionInterceptor ti = new TimestampIntroductionInterceptor(t); + pf.addAdvisor(0, new DefaultIntroductionAdvisor(ti, TimeStamped.class)); - Class[] oldProxiedInterfaces = factory.getProxiedInterfaces(); - - factory.addAdvisor(0, new DefaultIntroductionAdvisor(ti, TimeStamped.class)); - - Class[] newProxiedInterfaces = factory.getProxiedInterfaces(); + Class[] newProxiedInterfaces = pf.getProxiedInterfaces(); assertThat(newProxiedInterfaces).as("Advisor proxies one more interface after introduction").hasSize(oldProxiedInterfaces.length + 1); - TimeStamped ts = (TimeStamped) factory.getProxy(); + TimeStamped ts = (TimeStamped) pf.getProxy(); assertThat(ts.getTimeStamp()).isEqualTo(t); // Shouldn't fail; ((IOther) ts).absquatulate(); @@ -224,26 +221,26 @@ public Object invoke(MethodInvocation invocation) { NopInterceptor di = new NopInterceptor(); NopInterceptor diUnused = new NopInterceptor(); - ProxyFactory factory = new ProxyFactory(new TestBean()); - factory.addAdvice(0, di); - assertThat(factory.getProxy()).isInstanceOf(ITestBean.class); - assertThat(factory.adviceIncluded(di)).isTrue(); - assertThat(factory.adviceIncluded(diUnused)).isFalse(); - assertThat(factory.countAdvicesOfType(NopInterceptor.class)).isEqualTo(1); - assertThat(factory.countAdvicesOfType(MyInterceptor.class)).isEqualTo(0); - - factory.addAdvice(0, diUnused); - assertThat(factory.adviceIncluded(diUnused)).isTrue(); - assertThat(factory.countAdvicesOfType(NopInterceptor.class)).isEqualTo(2); + ProxyFactory pf = new ProxyFactory(new TestBean()); + pf.addAdvice(0, di); + assertThat(pf.getProxy()).isInstanceOf(ITestBean.class); + assertThat(pf.adviceIncluded(di)).isTrue(); + assertThat(pf.adviceIncluded(diUnused)).isFalse(); + assertThat(pf.countAdvicesOfType(NopInterceptor.class)).isEqualTo(1); + assertThat(pf.countAdvicesOfType(MyInterceptor.class)).isEqualTo(0); + + pf.addAdvice(0, diUnused); + assertThat(pf.adviceIncluded(diUnused)).isTrue(); + assertThat(pf.countAdvicesOfType(NopInterceptor.class)).isEqualTo(2); } @Test void sealedInterfaceExclusion() { // String implements ConstantDesc on JDK 12+, sealed as of JDK 17 - ProxyFactory factory = new ProxyFactory(""); + ProxyFactory pf = new ProxyFactory(""); NopInterceptor di = new NopInterceptor(); - factory.addAdvice(0, di); - Object proxy = factory.getProxy(); + pf.addAdvice(0, di); + Object proxy = pf.getProxy(); assertThat(proxy).isInstanceOf(CharSequence.class); } @@ -330,6 +327,19 @@ void proxyTargetClassWithConcreteClassAsTarget() { assertThat(AopProxyUtils.ultimateTargetClass(proxy2)).isEqualTo(TestBean.class); } + @Test + void proxyTargetClassWithIntroducedInterface() { + ProxyFactory pf = new ProxyFactory(); + pf.setTargetClass(MyDate.class); + TimestampIntroductionInterceptor ti = new TimestampIntroductionInterceptor(0L); + pf.addAdvisor(0, new DefaultIntroductionAdvisor(ti, TimeStamped.class)); + Object proxy = pf.getProxy(); + assertThat(AopUtils.isCglibProxy(proxy)).as("Proxy is a CGLIB proxy").isTrue(); + assertThat(proxy).isInstanceOf(MyDate.class); + assertThat(proxy).isInstanceOf(TimeStamped.class); + assertThat(AopProxyUtils.ultimateTargetClass(proxy)).isEqualTo(MyDate.class); + } + @Test void interfaceProxiesCanBeOrderedThroughAnnotations() { Object proxy1 = new ProxyFactory(new A()).getProxy();