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 methodhashCode()
, whether or not the given object's
class overrideshashCode()
. 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
theequals(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
Java Type Generic as Argument for Gson
Reverse Hashmap Keys and Values in Java
Java Lambda Returning a Lambda
Is There Something Like Instanceof(Class<> C) in Java
Where Are Generic Types Stored in Java Class Files
How to Specify the Default Jvm Arguments for Programs I Run from Eclipse
Java 8 Localdatetime Is Parsing Invalid Date
Drag and Drop Custom Object from Jlist into Jlabel
Jaxb Mapping Cyclic References to Xml
Is Executorservice (Specifically Threadpoolexecutor) Thread Safe
Remove Duplicates from Arraylists
Eventlisteners Using Hibernate 4.0 with Spring 3.1.0.Release