After I added #Retryable annotation to my spring-data-cassandra repository interface, now the application fails to start with following exception:
APPLICATION FAILED TO START
The bean 'airingDao' could not be injected as a 'my.dao.AiringDao' because it is a JDK dynamic proxy that implements:
org.springframework.data.cassandra.repository.CassandraRepository
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.
Added proxyTargetClass=true to #EnableAsync and #EnableCaching, even to #EnableRetry(proxyTargetClass = true)
#EnableAspectJAutoProxy(proxyTargetClass = true)
But still doesn't work.
After I remove the #Retryable, every thing works fine.
Checked the code, without #Retryable, in org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(String, Class<T>, Object[], boolean)
bean.getClass().getInterfaces()
(java.lang.Class<T>[]) [interface my.dao.AiringDao, interface
org.springframework.data.repository.Repository, interface
org.springframework.transaction.interceptor.TransactionalProxy, interface
org.springframework.aop.framework.Advised, interface
org.springframework.core.DecoratingProxy]
So requiredType.isAssignableFrom(bean.getClass()) is true
But after added #Retryable:
bean.getClass().getInterfaces()
(java.lang.Class<T>[]) [interface
org.springframework.retry.interceptor.Retryable, interface
org.springframework.aop.SpringProxy, interface
org.springframework.aop.framework.Advised, interface
org.springframework.core.DecoratingProxy]
So now requiredType.isAssignableFrom(bean.getClass())) is false and getTypeConverter().convertIfNecessary(bean, requiredType) throws the exception.
Could anyone please help or provide some clue how to troubleshoot it?
Thanks for your help.
Don't know Spring Retry, and haven't checked in detail, but I suspect, that there is no integration between Spring Data and Spring Retry yet. If this is true, you have two approaches available to you:
Open an issue on Spring Data or Spring Retry to get the integration implemented.
Introduce a separate layer for you #Retry which you then let delegate to your Spring Data repositories.
Of course those approaches aren't mutual exclusive.
Here is a workaround provided. I raised an issue for this in Spring-Retry GitHub.
Spring-Retry Github Issue
Related
In a spring boot app, using annotations only, i want to implement security
I have added an #EnableGlobalMethodSecurity(jsr360Enabled=true) to a configuration class. the class also has an #EnableSecurity anntation
Now, when i add a #RolesAllowed to any #RestController class, be it on method level or level, the startup logs don't list the class at all. instead , there is line:
'Rejected bean name (rest controller class): no URL paths identified'.
Does anyone have an idea what might be causing this?
After #M.Deinum set me on the path, i did some reading and have an explanation. If anyone can elaborate more or correct me on the more technical details, feel very welcome.
Spring proxies classes to wire beans together.
Classes that don't implement an interface are proxied by using CGLib to make a subclass with additional functionality
classes that do implement an interface, spring uses dynamic proxy to access the functionality of the class, but doing so it can only proxy interface methods.
in my case, my controller implements an interface with non-endpoint related methods, so spring ignored all handling methods and the RequestMappingHandlerMapping did not find any methods to bind, because if only 'saw' the interface methods on the class
using #EnableGlobalMethodSecurity(proxyTargetClass=true) forces the use of CGLib, so the full methods are recognised by our mapper.
What I understand so far is that when you use the #Transactional annotation any failure will cause the operation to rollback to its previous state.I have this method which adds Items to a quotation using spring PagingAndSortingRepository Interface.
#Override
public Quotation createNewQuotation(PurchaseRequest purchaseRequest, Supplier supplier) {
Quotation quotation = new Quotation();
Date now = new Date(Calendar.getInstance().getTimeInMillis());
quotation.setQuotationDate(now);
quotation.setPurchaseRequest(purchaseRequest);
quotation.setSupplier(supplier);
quotationRepository.save(quotation);
return quotation;
}
When I put a #Transactional annotation on the whole class and manually testing if the operation will rollback (by not selecting any supplier), other properties like quotationDate, PurchaseRequest is still stored in the database.
I get the feeling that I'm not using the #Transactional annotation. What might be the problem and what can I do to fix it?
Thanks for the replies.
I made this work by adding a #EnableTransactionManagement on my main class.
I solved the
The bean 'purchaseRequestServiceImpl' could not be injected as a
'com.eprocurement.service.PurchaseRequestServiceImpl' because it is a
JDK dynamic proxy that implements:
com.eprocurement.service.PurchaseRequestService 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.
by placing the #Transactional annotation on my service interfaces and #autowiring it instead of #autowiring my implementation classes.
So, I have an application running on WildFly10, which uses JSF, Spring (DI), JPA, Spring Data;
Right now we're trying to move it to CDI and remove Spring(DI). For now we'll keep Spring Data.
So, I set up CDI and made an EntityManager producer.
#Produces
#Dependent
#PersistenceContext
public EntityManager entityManager;
So, I'm able to inject repositories with CDI and all.
However on my original environment we had a custom repository factory,that was defined in my SpringConfiguration like this:
#EnableJpaRepositories(basePackages = {"com.foo.repository" }, repositoryFactoryBeanClass=CustomJpaRepositoryFactoryBean.class)
So, the question is, how can I define this repositoryFactoryBeanClass=CustomJpaRepositoryFactoryBean.class on a CDI environment?
The current implementation doesn't allow the configuration of the JpaRepositoryFactoryBean as one can see from the code where it gets instantiated.
So I guess you have the following options:
reimplement the instantiation process.
open a feature request.
do 2. and 1. in a reusable way and submit the result as a PR for the issue.
When trying to solve this problem I found that the custom impl was not being picked up. The solution suggested in this question helped me however: https://stackoverflow.com/a/38541669/188624
Basically is uses Java8 default interface method to provide the additional functionality. I had to use the "CDI.current().select" method to get hold of the entity manager though as property injection of course won't work.
I tested with Spring Data JPA 2.0.0
Why am I posting this?
After several hours of search / analysis / getting paranoid I would like to share my fault with you - I nailed down my cause of the problem.
When did it happen?
within the application server (in my case embedded tomcat) - tests had no problem
on two specific #Services on which the methods got properly annotated with #Transactional
What was not the problem?
Missing #EnableTransactionManagement in the #Configuration
Some weired AOP issue, eg having no default constructor in a Service not implementing an interface (AFAIK this problem does not exist anymore since Spring 4.0)
What was the problem?
I am using a CustomPermisionEvaluator for Security eg for securing methods like this:
#RequestMapping(value = "/{name}", method = RequestMethod.GET)
#PreAuthorize("hasPermission(null, 'SERVICE.ASSOCIATION.FIND_BY_NAME')")
public ResponseEntity<AssociationResult> findByName(#PathVariable String name) {
//do stuff
}
For doing this I have to extend the GlobalMethodSecurityConfiguration to get my CustomPermissionEvaluator into business. In there I used #Autowiring to get access to the beans needed for the permission evaluation. And guess what - this were those 2 #Services which the TransactionInterceptor ignored.
tl;dr
Do not autowire beans into GlobalMethodSecurityConfiguration => they will not get intercepted properly afterwards.
I can autowire two QueueChannel instances using #Autowired and #Qualifier in a test class just fine, until I add JMX export namespace handlers.
I've not got the exact config handy (it was a problem at work that's now bugging me out of hours!), but I'm using these elements:
<int-jmx:mbean-export default-domain="com.business" server="mbeanServer" />
<context:mbean-export />
<context:mbean-server />
When I've got these three things defined, the autowiring process fails throwing a NoSuchBeanException. However, I can see that there are beans with the IDs of my queues, as I've got a post processor that's iterating over all beans in the context.
Is this something to do with proxying obfuscating the declared type of the QueueChannels, preventing autowiring-by-type working?
You need to autowire using the interface instead of the concrete class because the JMX export wraps the channel in a proxy. Use PollableChannel for QueueChannel, or SubscribableChannel for DirectChannel.
It's always good practice to code using interfaces rather than concrete classes, for exactly this reason.