How to Unproxy a Spring Bean

Is it possible to unproxy a Spring bean?

Try this:

if(AopUtils.isAopProxy(a) && a instanceof Advised) {
Object target = ((Advised)a).getTargetSource().getTarget();
AImpl ai = (AImpl)target;
}

Bonus: in Scala I am using the following equivalent function for the very same purpose:

def unwrapProxy(a: AnyRef) = a match {
case advised: Advised if(AopUtils.isAopProxy(advised)) =>
advised.getTargetSource.getTarget
case notProxy => notProxy
}

How to unproxy child objects from org.springframework.data.repository.CrudRepository descendant

It looks like the transactions only span the immediate call to the repository.

To fix this put a @Transactional annotation on the method that should define your transaction scope.
At least the method that hosts the code you showed us.

That method needs to be public and on a Spring bean.
See https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html for details.

How to get a bean real class instead CGlib proxy?

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/util/ClassUtils.html#getUserClass-java.lang.Object-

You can use ClassUtils.getUserClass(Object bean) to get parent class of the CGLIB-generated subclass.

Autowired spring bean is not a proxy

If you have AspectJ-enabled transaction management (<tx:annotation-driven mode="aspectj" .../>) application of transactions happens in-place in the same class, either during build (compile-time weaving) or on startup (load-time weaving).

No new classes are created (like when using cglib) and no proxies (like with ordinary interface-based AOP in Spring). Instead bytecode of MyServiceImpl was modified directly without you even noticing. Unfortunately the only way to see AOP is to decompile your classes. If you use javap -c MyServiceImpl you'll find plenty of references to Spring transaction layer.

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

Interesting question. /p>

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 ->
aClass.getName().contains(CGLIB_CLASS_SEPARATOR) &&
aClass.getSuperclass().getAnnotation(Configuration.class) == null
)
.collect(Collectors.toList())

you should see an empty collection, whereas

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

should yield the same list of classes as simply

beanClasses.stream()
.filter(aClass -> 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.

How to convert a Hibernate proxy to a real entity object

Here's a method I'm using.

public static <T> T initializeAndUnproxy(T entity) {
if (entity == null) {
throw new
NullPointerException("Entity passed for initialization is null");
}

Hibernate.initialize(entity);
if (entity instanceof HibernateProxy) {
entity = (T) ((HibernateProxy) entity).getHibernateLazyInitializer()
.getImplementation();
}
return entity;
}

How proxyMode and scopeName works under the hood?

A lot has been written about how proxies are implemented in Spring, see my answers in the following posts:

  • https://stackoverflow.com/a/58207312/438154
  • https://stackoverflow.com/a/26893834/438154

In short, with ScopedProxyMode.TARGET_CLASS, Spring will use CGLIB to generate a subclass of RandomNumberGenerator. The class will have a name like RandomNumberGenerator$$EnhancerByCGLIB$$d0daae41.

This class overrides (almost) all the methods of RandomNumberGenerator to act as factories and delegators. Each of these overriden methods will produce a new instance (per request) of the real RandomNumberGenerator type and delegate the method call to it.

Spring will create an instance of this new CGLIB class and inject it into your @Controller class' field

@Autowired
RandomNumberGenerator numberGenerator;

You can call getClass on this object (one of the methods that is not overriden). You'd see something like

RandomNumberGenerator$$EnhancerByCGLIB$$d0daae41

indicating that this is the proxy object.

When you call the (overriden) methods

numberGenerator.getNumber()

That CGLIB class will use Spring's ApplicationContext to generate a new RandomNumberGenerator bean and invoke the getNumber() method on it.


The scopeName controls the scope of the bean. Spring will use RequestScope to handle these beans. If your controller called getNumber() twice, you should get the same value. The proxy (through the RequestScope) would internally cache the new, per-request object

@GetMapping(path = "/number")
public double getNumber(){
System.out.println(numberGenerator.getNumber() == numberGenerator.getNumber()); // true
return 123d;
}

If you had used a scope like "session", Spring would cache the real object across multiple requests. You can even use scope "singleton" and the object would be cached across all requests.


If you really needed to, you can retrieve the actual instance with the techniques described here

  • Is it possible to unproxy a Spring bean?

For example,

Object real = ((Advised)numberGenerator).getTargetSource().getTarget();

As for System.identityHashCode, you're calling it by passing the proxy object to it. There's only one proxy object, so the call will always return the same value.

Note that identityHashCode is not guaranteed to return different values for different objects. Its javadoc states

Returns the same hash code for the given object as would be returned
by the default method hashCode(), whether or not the given object's
class overrides hashCode(). The hash code for the null reference is
zero.

and hashCode()'s javadoc states

It is not required that if two objects are unequal according to
the equals(java.lang.Object) method, then calling the hashCode
method on each of the two objects must produce distinct integer
results.

Debugging issue in Spring proxy

You could also try retrieving the proxied object to see where exactly the NullPointerExeption is thrown.

To get the proxied object:

((Advised)yourProxy).getTargetSource().getTarget();


Related Topics



Leave a reply



Submit