Generic Autowiring Not Working with #Transactional - java

Spring appears fully capable of autowiring the correct type based on generic parameters without the need for #Qualifiers. However, as soon as I tack on a #Transactional annotation, it can no longer autowire based on generic parameters. Consider this example, invented only for purposes of illustrating the issue:
interface Product {}
interface Book extends Product {}
interface Toy extends Product {}
interface Store<P extends Product> {}
#Component
class BookStore implements Store<Book> {}
#Component
class ToyStore implements Store<Toy> {}
#Component
class BookDealer {
#Autowired
BookDealer(Store<Book> store) {
...
}
void inventoryBooks() {
... doesn't really matter what this does ...
}
}
Note that the above code wires up fine. The BookStore class is autowired into the BookDealer constructor without any issue. I can call inventoryBooks() and it works fine.
However, if I add a #Transactional annotation to a method upstream from the call to inventoryBooks(), e.g. on the client method that calls it, the BookDealer will no longer autowire, and I must resort to either injecting concrete types, or using a #Qualifier. The error is that there are two matching beans for the constructor argument of BookDealer, meaning both the BookStore and the ToyStore and Spring can't decide which one is needed. That tells me that Spring can no longer detect the generic types now that some upstream method has been proxied for the #Transactional. Something like that anyway...
I would like to stick to interfaces and not use #Qualifiers. Is there a way to do this with #Transactional, or is it a known limitation of generics and autowiring and things like #Transactional?

The Spring uses the JDK Proxy to generate the proxy used in AOP by default. The JDK proxy is based on the interface. So it maybe the problem, when you need to autowire the concrete class. So use the cglib instead(add the denpendency) and set the "" in the spring config file to "" and have a try. Hope it helps.
Using #Transaction annotation with #Autowired - Spring

Related

Why don't I experience any exception when I lookup bean wrapped by JDK dynamic proxy by class(instead of interface)?

Lets consider following bean:
#Service
#Scope(value = "prototype", proxyMode = ScopedProxyMode.INTERFACES)
public class MyBeanB implements MyBeanBInterface {
private static final AtomicLong COUNTER = new AtomicLong(0);
private Long index;
public MyBeanB() {
index = COUNTER.getAndIncrement();
System.out.println("constructor invocation:" + index);
}
#Transactional
#Override
public long getCounter() {
return index;
}
}
and consider 2 different usages:
USAGE 1:
#Service
public class MyBeanA {
#Autowired
private MyBeanB myBeanB;
....
}
At this case application can't be started and prints:
***************************
APPLICATION FAILED TO START
***************************
Description:
The bean 'myBeanB' could not be injected as a 'my.pack.MyBeanB' because it is a JDK dynamic proxy that implements:
my.pack.MyBeanBInterface
Action:
Consider injecting the bean as one of its interfaces or forcing the use of CGLib-based proxies by setting proxyTargetClass=true on #EnableAsync and/or #EnableCaching.
I expected to see it because I asked spring to create JDK dynamic proxy for bean MyBeanB and that proxy is not a subtype of MyBeanB. We can easily fix it like this:
#Service
public class MyBeanA {
#Autowired
private MyBeanBInterface myBeanB;
....
}
USAGE 2:
MyBeanB beanB = context.getBean(MyBeanB.class);
System.out.println(beanB.getCounter());
Surprisingly for me it works wihtout any Runtime Exceptions but I expected to see NoSuchBeanDefinitionException at this case because int case 1 application can't start
Thanks for guy from comments - I checked the class of beanB and it is my.pack.MyBeanB$$EnhancerBySpringCGLIB$$b1346261 so Spring used CGLIB to create proxy but it contradicts the bean definition(#Scope(value = "prototype", proxyMode = ScopedProxyMode.INTERFACES) and looks like a bug.
)
Could you explain why it is working for case 2 not working for case 1 ?
As I explained to you in my comments to the other question, Spring AOP can use both CGLIB and JDK proxies depending on the situation. The default are JDK proxies for classes implementing interfaces, but you can enforce CGLIB usage for them too. For classes not implementing interfaces only CGLIB remains because JDK proxies can only create dynamic proxies based on interfaces.
So looking at your case 1, you explicitly say you want interface proxies, i.e. JDK proxies:
#Scope(value = "prototype", proxyMode = ScopedProxyMode.INTERFACES)
But MyBeanA does not implement any interfaces. Consequently you get the error message you see in this case.
In case 2 however you use ApplicationContext.getBean(..) in order to create a proxy. Here you are relying on Spring to determine which proxy type to choose, you are not trying to enforce anything. Thus, proxying via CGLIB succeeds.
No surprises here.
If you want to avoid the error message in case 1, maybe you ought to use ScopedProxyMode.TARGET_CLASS.
Update: Sorry, I was irritated by your similar and nondescript class names MyBeanA and MyBeanB. It would make sense to use more descriptive, clean-code-like class names next time, ideally ones describing the roles ob the classes in your scenario like MyService, MyInterface, MyScopedBean.
Anyway, I read your question and the error message again. The error message says that according to your annotation an interface-based proxy is being generated but you are trying to inject it into a class type. You can fix that by declaring it like this:
#Autowired
private MyBeanBInterface myBeanB;
In case/usage 2 you are again explicitly declaring a class and not an interface type for your bean. So as I said, Spring tries to satisfy your requirement by the only way possible, i.e. creating a CGLIB proxy for the class. You can fix this by declaring an interface type and you will get the expected JDK proxy:
MyBeanBInterface myBeanBInterface = appContext.getBean(MyBeanBInterface.class);
System.out.println(myBeanBInterface.getCounter());
System.out.println(myBeanBInterface.getClass());
Update 2: Something I think you still do not understand according to your comments is this basic fact of OOP: If you have
class Base and class Sub extends Base or
interface Base and class Sub implements Base
you can declare Base b = new Sub() but of course not Sub s = new Base() because a Sub is also a Base, but not every Base is a Sub. For example, if you also have OtherSub extends Base, when trying to assign a Base object to a Sub variable it could be an OtherSub instance. This is why this does dot even compile without using Sub s = (Sub) myBaseObject.
So far, so good. Now look at your code again:
In usage 1 you have #Autowired private MyBeanB myBeanB; but configured MyBeanB to produce a JDK proxy, i.e. a new proxy class with parent class Proxy directly implementing MyBeanBInterface will be created. I.e. you have two different classes, both directly implementing the same interface. Those classes are assignment-incompatible to each other for the reason I explained above. With regard to the interface we have the class hierarchy
MyBeanBInterface
MyBeanB
MyBeanB_JDKProxy
Thus you cannot inject MyBeanB_JDKProxy into a MyBeanB field because a proxy object is not an instance of MyBeanB. Don't you understand? The problem sits in front of the computer, there is no mysterious Spring bug. You configured it to fail.
This is why I told you to change the code to #Autowired private MyBeanBInterface myBeanB; because then of course it works because the proxy implements the interface and everything is fine. I also told you that alternatively you can keep #Autowired private MyBeanB myBeanB; if you use proxyMode = ScopedProxyMode.TARGET_CLASS for your scope declaration.
In usage 2 the problem is the same: You are saying getBean(ClassB.class), i.e. you are explicitly instructing Spring to create a proxy for that class. But for a class you cannot create a JDK proxy, only a CGLIB proxy, which is what Spring does. Again, I gave you the solution by instructing you to use getBean(MyBeanBInterface.class) instead. Then you get the expected JDK proxy.
Spring is smart enough to both
make the JDK proxy in usage 1 find the scoped service bean MyClassB and delegate method calls to it (note: delegation, not inheritance!) and
make the CGLIB proxy extend MyClassB (note: inheritance here, no delegation necessary).

How to call Spring Framework repositories methods

I know that there are questions similar to this one, but none of them have helped me. I'm following along this tutorial, and the part I can't wrap my mind around is:
#SpringBootApplication
public class Application {
private static final Logger log =
LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
#Bean
public CommandLineRunner demo(CustomerRepository repository) {
return (args) -> {
// save a couple of customers
...
// more lines, etc...
What I don't understand is where the repository passed into demo comes from. I know that the Autowired annotation can do something like that, but it isn't used at all here.
The more specific reason I ask is because I'm trying to adapt what they do here to an application I'm working on. I have a class, separate from all of the persistence/repository stuff, and I want to call repository methods like save and findAll. The issue is that the repository is an interface, so I can't instantiate an object of it to call the methods. So do I have to make a new class that implements the interface and create an object of that? Or is there an easier way using annotations?
When creating a #Bean, adding the repository in the parameters of the bean is enough to wire the repos in your bean. This works pretty much like adding #Autowired annotation inside a class that is annotated as #Component or something similar.
Spring works mostly with interface, since that is simplier to wire vs wiring concrete classes.
Can you try #Repository before the declaration of class? Worked for me in a Spring MVC structure.
#Repository
public class EntityDAOImpl implements EntityDAO{
...
}
The thing to wrap your head around is a Spring Boot application at startup time aims to resolve its dependancy tree. This means discovering and instantiating Beans that the application defines, and those are classes annotated with #Service, #Repository, etc.
This means the default constructor (or the one marked with #Autowire) of all beans is invoked, and after all beans have been constructed the application starts to run.
Where the #Bean annotation comes into play is if you have a bean which does not know the values of it's constructor parameters at compile time (e.g. if you want to wire in a "started at" timestamp): then you would define a class with an #Configuration annotation on it, and expose an #Bean method in it, which would return your bean and have parameters that are the beans dependencies. In it you would invoke the beans constructor and return the bean.
Now, if you want a certain method of some class to be invoked after the application is resolved, you can implement the CommandLineRunner interface, or you can annotate a method with #PostConstruct.
Some useful links / references:
https://docs.spring.io/spring-javaconfig/docs/1.0.0.m3/reference/html/creating-bean-definitions.html
https://www.baeldung.com/spring-inject-prototype-bean-into-singleton
Running code after Spring Boot starts
Execute method on startup in Spring

How does Java Spring #Autowired work with interface inheriting from several interfaces?

I have a Java Spring Framework project. After a bit of googling I found a way to include custom JPA methods into a JpaRepository. The injection of my repository into my service class using #Autowired works, but I can't understand how Spring handles the injection in this case. Could someone explain how Spring does the injection of CalendarEventRepository into CalendarEventService when the method implementations are in separate classes. It finds the JpaRepository implementation somewhere and my own custom implementation class with my custom method. Howcome their methods are accessible through the same reference variable calendarEventRepository? Bonus question: how does Spring find and instantiate the implementation for JpaRepository?
public interface CalendarEventRepository extends JpaRepository<CalendarEvent, Long>, CalendarEventRepositoryCustom { }
public interface CalendarEventRepositoryCustom {
public List<CalendarEvent> findCalendarEventsBySearchCriteria(CalendarEventSearchCriteria searchCriteria);
}
public class CalendarEventRepositoryImpl implements
CalendarEventRepositoryCustom {
public List<CalendarEvent> findCalendarEventsBySearchCriteria(CalendarEventSearchCriteria searchCriteria) {
}
}
public class CalendarEventService {
#Autowired
CalendarEventRepository calendarEventRepository;
...
calendarEventRepository.delete(calendarEvent);
...
return calendarEventRepository.findCalendarEventsBySearchCriteria(searchCriteria);
...
}
Thanks in advance!
When you are using Spring JPA repository interface (extend JpaRepository class), the important thing is that the implementation of the interface is generated at runtime. Method names are used by Spring to determine what the method should (since you have written the name findCalendarEventsBySearchCriteria correctly, it means that you already know that). In your particular case, CalendarEventRepository extends CalendarEventRepositoryCustom and therefore has a method findCalendarEventsBySearchCriteria(...), and also extends JpaRepository<CalendarEvent, Long>, which means that it should be treated as JPA repository, and the corresponding implementation should be generated.
To enable the generation of the repository implementation, you need to either include <jpa:repositories base-package="..." /> to your XML configuration file, or #Configuration #EnableJpaRepositories(basePackage = "...") When you have these, that's all the information Spring needs to generate (instantiate) repository and add it to application contexts, and the inject it into other beans. In your case, #Autowired CalendarEventRepository calendarEventRepository; specifies where it should be injected. I guess it more answers bonus question than the main one, but seems better to start with it.
I haven't yet touched CalendarEventRepositoryImpl. You should use such class if you want to drop the mentioned generation of repository implementation for particular methods. Spring looks for a class which name equals to repository interface's name + "Impl". If such class exists, Spring merges its methods with generated ones. So, see for yourself whether auto-generated findCalendarEventsBySearchCriteria method fits your needs or you want to implement it yourself. If the generated one fits, you should consider removing CalendarEventRepositoryImpl at all.
Could someone explain how Spring does the injection of
CalendarEventRepository into CalendarEventService when the method
implementations are in separate classes.
Answer: First, and most important - all Spring beans are managed - they "live" inside a container, called "application context".
Regardless of which type of configuration you are usin (Java or xml based) you enable "Component Scanning" this helps Spring determine which resource to inject.
How spring determines which bean to inject:
Matches the names.
Matches the type.
You even use Qualifiers to narrow down the search for spring.
It finds the JpaRepository implementation somewhere and my own
custom implementation class with my custom method. Howcome their
methods are accessible through the same reference variable
calendarEventRepository?
This is more of a java core question of inheritance. Since JpaRepository, CalendarEventRepositoryCustom and CalendarEventRepository are the base classes (implementations) of your CalendarEventRepositoryImpl so any method/field that is public or protected is available to CalendarEventRepositoryImpl class.
Here you are using "Program though interface" your reference variable here is calendarEventRepository which is an interface (parent) and that is why you are able to access the fields/methods.
Bonus question: how does Spring find and instantiate the
implementation for JpaRepository?
In spring configuration (java based) you tell spring to search for JPARepositories as below:
#EnableJpaRepositories(
basePackages = {
"com.package"}
, entityManagerFactoryRef = "EntityManagerFactory", transactionManagerRef = "jpaTransactionManager"
)
This is how spring gets to know which beans to create.
I recommend reading out Spring in Action (2nd to 4th Edition) by Craig Walls.
You can as well go through the https://spring.io/docs
Annotations (Autowired, Inject, your custom) works because of AOP. Read a bit about AOP and you will know how that works.

Rest + Spring AOP + interface doesn't inject

I have a Spring AOP aspect used for logging, where a method can be included for logging by adding an annotation to it, like this:
#AspectLogging("do something")
public void doSomething() {
...
}
I've been using this on Spring beans and it's been working just fine. Now, I wanted to use it on a REST-service, but I ran into some problems. So, I have:
#Path("/path")
#Service
public class MyRestService {
#Inject
private Something something;
#GET
#AspectLogging("get some stuff")
public Response getSomeStuff() {
...
}
}
and this setup works just fine. The Rest-service that I'm trying to add the logging to now has an interface, and somehow that messes stuff up. As soon as I add the #AspectLogging annotation to one of the methods, no dependencies are injected in the bean, and also, the aspect is newer called!
I've tried adding an interface to the REST-service that works, and it gets the same error.
How can having an interface lead to this type of problems? The aspect-logger works on classes with interfaces elsewhere, seems it's only a problem when it's a REST-service..
Ref the below Spring documentation (para 2) -
To enable AspectJ annotation support in the Spring IoC container, you
only have to define an empty XML element aop:aspectj-autoproxy in your
bean configuration file. Then, Spring will automatically create
proxies for any of your beans that are matched by your AspectJ
aspects.
For cases in which interfaces are not available or not used in an
application’s design, it’s possible to create proxies by relying on
CGLIB. To enable CGLIB, you need to set the attribute
proxy-targetclass= true in aop:aspectj-autoproxy.
In case your class implements an interface, a JDK dynamic proxy will be used. However if your class does not implement any interfaces then a CGLIB proxy will be created. You can achieve this #EnableAspectJAutoProxy. Here is the sample
#Configuration
#EnableAspectJAutoProxy
public class AppConfig {
#Bean
public LoggingAspect logingAspect(){
return new LoggingAspect();
}
}
#Component
#Aspect
public class LoggingAspect {
...
...
}
In my opinion what you are actually trying to do is to add spring annotations to a class maintained by jersey. In the result you are receiving a proxy of proxy of proxy of somethng. I do not think so this is a good idea and this will work without any problems. I had a similar issue when I tried to implement bean based validation. For some reasons when there were #PahtParam and #Valid annotations in the same place validation annotations were not visible. My advice is to move your logging to a #Service layer instead of #Controller.

NoSuchBeanDefinitionException when implementing a new interface

I had the following set-up which worked perfectly fine:
#Service
public class MyService {
}
public class Test {
#Autowired
MyService service;
}
I changed MyService to implement a new interface, like this
#Service
public class MyService implements NewInterface {
}
There's nothing special in this interface, it's just a normal Java interface without any annotation and 2 very simple methods.
Now, Spring is not able to autowire this bean anymore, it gives a NoSuchBeanDefinitionException. I also tried to define it explicitly in the XML but it gave the same exception.
In case it's relevant, I'm using Spring 2.5 and build with Maven, the class Test is a unit test class. When I try to run the real application, it's using applicationContext.getBean() to get this service, and it gives the following exception: java.lang.ClassCastException: $Proxy81 cannot be cast to MyService.
What am I missing and what should I do?
When you see a class with a name like $Proxy81, it's telling you that Spring has auto-generated a proxy object for one of your beans, in this case a proxy object for the MyService bean. This uses java.lang.reflect.Proxy to generate the proxy object. This proxy object will implement the same interfaces as the class that's being proxied, but it will not be type-compatible with the target class itself.
Now, if the target class doesn't implement any interfaces, then Spring will instead use CGLIB to generate the proxy. This proxy will be a subclass of the target class, so the proxy object can be safely cast to the original type of the target object.
Now, when using the lower-level proxy-generation stuff in Spring, you can often override this behaviour, and tell it to always use CGLIB proxies, but I'm assuming that since you're using #Service, then you're also using <context:component-scan>, in which case I think you have to stick with the default behaviour.
It's not bad thing, though. This encourages you to not couple your classes together, but instead to program to interfaces. Any interaction with MyService should be expressible via interfaces, although this concept can get a little fuzzy when talking about unit testing.
It looks like you're autowiring by interface instead of autowiring by class name.
I'd simply code my test against the Interface:
public class Test {
#Autowired
NewInterface service;
}
Also, check this bug, it might be relevant to you since it appears like your class is being proxied.

Categories

Resources