如何确定一个类是否是Spring代理?

huangapple go评论86阅读模式
英文:

How do you figure out whether a CLASS is a spring proxy?

问题

简而言之

AopUtils中,我们有

	/**
	 * 检查给定对象是否为JDK动态代理或CGLIB代理。
	 * <p>此方法还检查给定对象是否为{@link SpringProxy}的实例。
	 * @param object 要检查的对象
	 * @see #isJdkDynamicProxy
	 * @see #isCglibProxy
	 */
	public static boolean isAopProxy(@Nullable Object object) {
		return (object instanceof SpringProxy && (Proxy.isProxyClass(object.getClass()) ||
				object.getClass().getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)));
	}

现在,我想要在BeanFactoryPostProcessor中,仅通过其类(而不是实例化该bean)来检查一个bean类是否被代理。

我以为可以将上述方法"翻译"如下:

    private fun <T> isAopProxyClass(candidate: Class<T>): Boolean {
        return SpringProxy::class.java.isAssignableFrom(candidate)
            && (
            Proxy.isProxyClass(candidate)
                || candidate.name.contains(CGLIB_CLASS_SEPARATOR)
            )
    }

但是这并不能检测到代理,因为SpringProxy::class.java.isAssignableFrom(candidate)对于显然被代理的类也是false

我该如何让这个方法工作起来?

完整情况

我在一个BeanFactoryPostProcessor中,需要获取未被代理的bean类,以便通过反射访问某些带注释的方法。

访问发生在一个lambda函数中,该函数首先会使用ApplicationContext检索类的bean。在这个BeanFactoryPostProcessor中,不能强制实例化bean(实际上如果这样做会抛出异常,因为某些bean是会话范围的)。

如何确定一个类是否是Spring代理?

英文:

In a nutshell

In the AopUtils, we have

	/**
	 * Check whether the given object is a JDK dynamic proxy or a CGLIB proxy.
	 * &lt;p&gt;This method additionally checks if the given object is an instance
	 * of {@link SpringProxy}.
	 * @param object the object to check
	 * @see #isJdkDynamicProxy
	 * @see #isCglibProxy
	 */
	public static boolean isAopProxy(@Nullable Object object) {
		return (object instanceof SpringProxy &amp;&amp; (Proxy.isProxyClass(object.getClass()) ||
				object.getClass().getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)));
	}

In now want to check whether a bean class is proxied without instantiating the bean (i.e. just with its class) in a BeanFactoryPostProcessor.

I thought I could just "translate" above method:

    private fun &lt;T&gt; isAopProxyClass(candidate: Class&lt;T&gt;): Boolean {
        return SpringProxy::class.java.isAssignableFrom(candidate)
            &amp;&amp; (
            Proxy.isProxyClass(candidate)
                || candidate.name.contains(CGLIB_CLASS_SEPARATOR)
            )
    }

But this does not detect proxies because SpringProxy::class.java.isAssignableFrom(candidate) is false even for obviously proxied classes.

How do I make this work?

Full picture

I'm in a BeanFactoryPostProcessor and I need the un-proxied bean classes to access certain annotated methods by reflection.

Access happens in a lambda function that will first use the ApplicationContext to retrieve the bean for the class. The bean must not be forcibly instantiated in this BeanFactoryPostProcessor (and in fact should throw an exception if it does because some beans are session-scoped).

如何确定一个类是否是Spring代理?

答案1

得分: 1

以下是翻译好的部分:

有趣的问题。 😀

在你的截图中突出显示的三个类是CGLIB代理,但不是AOP代理。看看它们的类名:它们都是Spring配置类。但这并不意味着它们是普通的Spring代理,尤其不是AOP代理。关于@Component@Configuration之间的区别,特别是关于代理和自调用行为,请阅读我的回答

因此,Spring的@Configuration类也不像普通的Spring代理一样实现了SpringProxy

所以基本上你的解决方案工作得很好,没有必要担心,就我所看到的情况而言。

P.S.:我是一名Java开发者,而不是Kotlin开发者。因此,我重新在Java中实现了你在截图中的代码,以便我可以调试并复现你的情况。但即使在Kotlin中,我也必须重新输入所有内容。下次请发布可复制的文本代码,而不仅仅是一张图片。


**更新:**如果你检查类似于以下内容的内容:

beanClasses.stream()
  .filter(aClass -&gt; 
    aClass.getName().contains(CGLIB_CLASS_SEPARATOR) &amp;&amp; 
      aClass.getSuperclass().getAnnotation(Configuration.class) == null
  )
  .collect(Collectors.toList())

你应该看到一个空的集合,而

beanClasses.stream()
  .filter(aClass -&gt; 
    aClass.getName().contains(CGLIB_CLASS_SEPARATOR) &amp;&amp; 
      aClass.getSuperclass().getAnnotation(Configuration.class) != null
  )
  .collect(Collectors.toList())

应该产生与以下简单代码相同的类列表:

beanClasses.stream()
  .filter(aClass -&gt; aClass.getName().contains(CGLIB_CLASS_SEPARATOR))
  .collect(Collectors.toList())

也就是说,beanClasses中剩下的所有CGLIB代理实际上都应该是配置类,而不是普通的Spring代理。

英文:

Interesting question. 😀

The three classes highlighted in your screenshot are CGLIB proxies but not AOP proxies. Look at their class names: They are all Spring configuration classes. But that does not make them normal Spring proxies, especially not AOP proxies. For the difference between @Component and @Configuration, also with regard to proxying and self-invocation behaviour, please read my answer here.

Consequently, a Spring @Configuration class also does not implement SpringProxy like normal Spring proxies.

So basically your solution works just fine, no need to worry, as far as I can see.

P.S.: I am a Java guy, not a Kotlin person. So I re-implemented your code from the screenshot in Java, so I could debug into it and reproduce your situation. But even in Kotlin I would have had to re-type everything. Please next time publish the code as copyable text, not just as an image.


Update: If you check something like the content of

beanClasses.stream()
  .filter(aClass -&gt; 
    aClass.getName().contains(CGLIB_CLASS_SEPARATOR) &amp;&amp; 
      aClass.getSuperclass().getAnnotation(Configuration.class) == null
  )
  .collect(Collectors.toList())

you should see an empty collection, whereas

beanClasses.stream()
  .filter(aClass -&gt; 
    aClass.getName().contains(CGLIB_CLASS_SEPARATOR) &amp;&amp; 
      aClass.getSuperclass().getAnnotation(Configuration.class) != null
  )
  .collect(Collectors.toList())

should yield the same list of classes as simply

beanClasses.stream()
  .filter(aClass -&gt; aClass.getName().contains(CGLIB_CLASS_SEPARATOR))
  .collect(Collectors.toList())

I.e. all remaining CGLIB proxies in beanClasses should in fact be configurations, not normal Spring proxies.

答案2

得分: 0

我现在想要检查一个bean类是否被代理,而不需要实例化该bean(即只使用其类),在BeanFactoryPostProcessor中。

这个任务对我来说似乎是不可能的,因为有100500种方法在运行时代理bean(bean后处理器、建议等)。从技术上讲,你可以使用随机数来决定是否代理某个bean。你可以有一个给定类的两个实例(例如,带有不同的限定符),一个被代理,另一个不被代理。

英文:

> In now want to check whether a bean class is proxied without instantiating the bean (i.e. just with its class) in a BeanFactoryPostProcessor.

This task seems impossible to me since there are 100500 ways to proxy bean at runtime (bean post processor, advice, etc). Technically you can use random to decide whether to proxy some bean. You can have two instances of a given class (e.g. with different qualifiers), one proxied, other not.

huangapple
  • 本文由 发表于 2020年9月23日 19:29:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/64026990.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定