3

I have a Spring bean (ChildBean extends Parent) which is extending an abstract class (Parent implements Runnable).

public abstract class Parent implements Runnable {
    public final void run() {
        // some code
    }

    public int overridenFunct() {
        // some code
    }
}

Child bean class variant which causes ClassCastException:

@Transactional
@Scope("prototype")    
@Service("beanName")
public class ChildBean extends Parent {
    @Override
    public int overridenFunct() { 
        // some diff code 
    }
}

Everything works fine until I override public non-abstract method from parent class in child bean. After that a ClassCastException is thrown when I'm trying to create an instance of that bean.

Parent p = (Parent) appContext.getBean("beanName");

Bean object returned by getBean() is a ChildBean class instance (checked with debugger). Why does casting ChildBean object to its abstract parent class Parent not work? So, without an overridenFunct() implemented in ChildBean everything works fine. Could someone please tell what is the problem here?

UPDATE:

Changing method overridingFunct() to protected fixes the issue. But what if I need to override a public method? Is that allowed? I'm using Spring 3.2.8

UPDATE2:

Well, I didn't get to the point why overriding public method in abstract parent causes ClassCastException. As the resolution I did the following: created an interface with all public methods with common logic, an abstract class, which implements that interface and all "common" methods. Then all the child beans are extended from that abstract class, implementing its specific logic.

sys463
  • 337
  • 2
  • 5
  • 18
  • Can you show the definition of `Parent`? – flakes Oct 22 '18 at 14:52
  • @flakes, unfortunately, I cannot post a code for that class here for security reasons (plus it has 500+ lines). That class implements runnable and is used to send some periodical requests to another node. It uses some other resources injected (not sure that this is useful...). – sys463 Oct 22 '18 at 14:57
  • You need to post more info for a proper answer. If you can't post code, try reproducing your problem with a minimum viable example, and post that. – BrentR Oct 22 '18 at 15:17
  • I keyed in your sample code in an attempt to reproduce. But, I cannot reproduce your ClassCastException. The change to being protected may solve the problem, but it is not the correct answer. Something else is amiss. – BrentR Oct 22 '18 at 16:51
  • @BrentR, Thanks for your attempt. I'll work on code snap which can reproduce the issue... – sys463 Oct 22 '18 at 18:27
  • The problem is the interface combined with `@Transactional`. This leads to a interface based proxy, so it is an instance of `Runnable` but not the actual classes. In your `@EnableTransactionManagement` set the `proxy-target-class` property to `true` to force class based proxies. – M. Deinum Nov 24 '21 at 19:11

2 Answers2

1

For anyone that may encounter this error, the following may prove to be useful in debugging this. First and foremost, the problem can be caused by the ClassLoader loading two copies of a particular class due to dependency overinclusion.

Supply the following option to your JVM via IDE or via

java -verbose:class {rest of your args / options}

Then, monitor the console output for the particular Parent class. A chance exists that the class has made it into the ClassLoader twice, perhaps by including a particular dependency more than once. Pay particular attention to the time when the bean is retrieved from lookup.

I was able to solve an issue on 4/22/2022 by using the above strategy to track down an issue in our Gradle build script that caused extra files to make their way into a WAR.

0

The Problem with your code is, that appContext.getBean("beanName") does not return an object that inherits from the class Parent.

A common mistake regarding classes with names like Parent is a wrong import. Check if you are importing from the correct package.

If this does not fix the issue, make sure that appContext.getBean("beanName") returns the object you think it does. It might return a Bean Object, that does not inherit from the Parent class.

The context also might not even contain your ChildBean object yet. Make sure it is added to it beforehand.

Max7cd
  • 94
  • 6
  • Class names are only examples here. Yes, the context contains the bean needed. As I already mentioned in a description, the ClassCastException is thrown when trying to cast the object returned by `getBean()` to `Parent`. – sys463 Oct 22 '18 at 15:06
  • Updated my Answer... I guess you are getting a general Bean object back and not one of your ChildBean class... – Max7cd Oct 22 '18 at 15:08
  • updated a description as well. The object that I'm getting from `getBean()` is an instance of `ChildBean`. So my question is why does casting to `Parent` class doesn't work when I have a public non-abstract method overridden in `ChildBean`? – sys463 Oct 22 '18 at 15:37