Java – why can’t classes that use @ transactional methods be assembled automatically?
I am using spring security with a waffle filter that authenticates users against active directory servers
This combination is valid until I add the @ transactional annotation method to the service The current service cannot automatically connect to the filter
This is a service class:
@Service public class UserService implements UserDetailsService { @Autowired private UserRepository userRepository; @Autowired private LdapUserDao ldapUserDao; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return getUserByUsername(username); } public User getUserByUsername(final String username) { final User databaseUser = userRepository.findByUsername(username); final User ldapUser = ldapUserDao.findByUsername(username); if (null == databaseUser || null == ldapUser) { return null; } final User user = mergeUsers(databaseUser,ldapUser); return user; } public List<User> getAllUsers() { return userRepository.findAll(); } @Transactional public void storeUser(final User user) { userRepository.save(user); } private User mergeUsers(final User database,final User ldap) { final User mergedUser = new User(); mergedUser.setId(database.getId()); mergedUser.setUsername(database.getUsername()); mergedUser.setEnabled(database.isEnabled()); mergedUser.setRoles(database.getRoles()); mergedUser.setEmail(ldap.getEmail()); mergedUser.setFirstName(ldap.getFirstName()); mergedUser.setLastName(ldap.getLastName()); mergedUser.setLocale(ldap.getLocale()); return mergedUser; } }
This is the filter:
public class WaffleAuthenticationWrapperFilter extends GenericFilterBean { @Autowired private UserService userService; @Override public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain) throws IOException,ServletException { final SecurityContext securityContext = SecurityContextHolder.getContext(); final Authentication authentication = securityContext.getAuthentication(); if (null == authentication || !(authentication instanceof WindowsAuthenticationToken)) { chain.doFilter(request,response); return; } final WindowsAuthenticationToken waffleAuthentication = (WindowsAuthenticationToken) authentication; final String username = waffleAuthentication.getName().replaceAll("^.*\\\\",""); final User user = userService.getUserByUsername(username); if (null == user) { securityContext.setAuthentication(null); return; } final WaffleAuthenticationWrapper authenticationWrapper = new WaffleAuthenticationWrapper(waffleAuthentication,user); securityContext.setAuthentication(authenticationWrapper); chain.doFilter(request,response); } }
Once I delete @ transactional, the project will compile again Why can't you annotate a method as @ transactional?
This is a stack trace:
Exception in thread "main" org.springframework.context.ApplicationContextException: Unable to start embedded container; nested exception is org.springframework.boot.context.embedded.EmbeddedServletContainerException: Unable to start embedded Tomcat at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRefresh(EmbeddedWebApplicationContext.java:133) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:474) at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118) at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:686) at org.springframework.boot.SpringApplication.run(SpringApplication.java:320) at org.springframework.boot.SpringApplication.run(SpringApplication.java:957) at org.springframework.boot.SpringApplication.run(SpringApplication.java:946) at my.project.App.main(App.java:13) Caused by: org.springframework.boot.context.embedded.EmbeddedServletContainerException: Unable to start embedded Tomcat at org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer.initialize(TomcatEmbeddedServletContainer.java:98) at org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer.<init>(TomcatEmbeddedServletContainer.java:75) at org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory.getTomcatEmbeddedServletContainer(TomcatEmbeddedServletContainerFactory.java:378) at org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory.getEmbeddedServletContainer(TomcatEmbeddedServletContainerFactory.java:155) at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.createEmbeddedServletContainer(EmbeddedWebApplicationContext.java:157) at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRefresh(EmbeddedWebApplicationContext.java:130) ... 7 more Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration': Injection of autowired dependencies Failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration.setFilterChainProxySecurityConfigurer(org.springframework.security.config.annotation.ObjectPostProcessor,java.util.List) throws java.lang.Exception; nested exception is org.springframework.beans.factory.BeanExpressionException: Expression parsing Failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'webSecurityConfig': Injection of autowired dependencies Failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private my.project.auth.WaffleAuthenticationWrapperFilter my.project.config.WebSecurityConfig.waffleAuthenticationWrapperFilter; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'waffleAuthenticationWrapperFilter': Injection of autowired dependencies Failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private my.project.services.UserService my.project.auth.WaffleAuthenticationWrapperFilter.userService; nested exception is java.lang.IllegalArgumentException: Can not set my.project.services.UserService field my.project.auth.WaffleAuthenticationWrapperFilter.userService to com.sun.proxy.$Proxy84 at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334) at org.springframework.beans.factory.support.AbstractAutowireCapablebeanfactory.populateBean(AbstractAutowireCapablebeanfactory.java:1210) at org.springframework.beans.factory.support.AbstractAutowireCapablebeanfactory.doCreateBean(AbstractAutowireCapablebeanfactory.java:537) at org.springframework.beans.factory.support.AbstractAutowireCapablebeanfactory.createBean(AbstractAutowireCapablebeanfactory.java:476) at org.springframework.beans.factory.support.Abstractbeanfactory$1.getObject(Abstractbeanfactory.java:303) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) at org.springframework.beans.factory.support.Abstractbeanfactory.doGetBean(Abstractbeanfactory.java:299) at org.springframework.beans.factory.support.Abstractbeanfactory.getBean(Abstractbeanfactory.java:194) at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:368) at org.springframework.beans.factory.support.AbstractAutowireCapablebeanfactory.instantiateUsingFactoryMethod(AbstractAutowireCapablebeanfactory.java:1119) at org.springframework.beans.factory.support.AbstractAutowireCapablebeanfactory.createBeanInstance(AbstractAutowireCapablebeanfactory.java:1014) at org.springframework.beans.factory.support.AbstractAutowireCapablebeanfactory.doCreateBean(AbstractAutowireCapablebeanfactory.java:504) at org.springframework.beans.factory.support.AbstractAutowireCapablebeanfactory.createBean(AbstractAutowireCapablebeanfactory.java:476) at org.springframework.beans.factory.support.Abstractbeanfactory$1.getObject(Abstractbeanfactory.java:303) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) at org.springframework.beans.factory.support.Abstractbeanfactory.doGetBean(Abstractbeanfactory.java:299) at org.springframework.beans.factory.support.Abstractbeanfactory.getBean(Abstractbeanfactory.java:199) at org.springframework.boot.context.embedded.ServletContextInitializerBeans.getOrderedBeansOfType(ServletContextInitializerBeans.java:209) at org.springframework.boot.context.embedded.ServletContextInitializerBeans.addAsRegistrationBean(ServletContextInitializerBeans.java:165) at org.springframework.boot.context.embedded.ServletContextInitializerBeans.addAsRegistrationBean(ServletContextInitializerBeans.java:160) at org.springframework.boot.context.embedded.ServletContextInitializerBeans.addAdaptableBeans(ServletContextInitializerBeans.java:143) at org.springframework.boot.context.embedded.ServletContextInitializerBeans.<init>(ServletContextInitializerBeans.java:74) at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.getServletContextInitializerBeans(EmbeddedWebApplicationContext.java:234) at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.selfInitialize(EmbeddedWebApplicationContext.java:221) at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.access$000(EmbeddedWebApplicationContext.java:84) at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext$1.onStartup(EmbeddedWebApplicationContext.java:206) at org.springframework.boot.context.embedded.tomcat.TomcatStarter.onStartup(TomcatStarter.java:54) at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5151) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150) at org.apache.catalina.core.ContainerBase$startChild.call(ContainerBase.java:1409) at org.apache.catalina.core.ContainerBase$startChild.call(ContainerBase.java:1399) at java.util.concurrent.FutureTask$Sync.innerRun(UnkNown Source) at java.util.concurrent.FutureTask.run(UnkNown Source) at java.util.concurrent.ThreadPoolExecutor.runWorker(UnkNown Source) at java.util.concurrent.ThreadPoolExecutor$Worker.run(UnkNown Source) at java.lang.Thread.run(UnkNown Source) Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration.setFilterChainProxySecurityConfigurer(org.springframework.security.config.annotation.ObjectPostProcessor,java.util.List) throws java.lang.Exception; nested exception is org.springframework.beans.factory.BeanExpressionException: Expression parsing Failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'webSecurityConfig': Injection of autowired dependencies Failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private my.project.auth.WaffleAuthenticationWrapperFilter my.project.config.WebSecurityConfig.waffleAuthenticationWrapperFilter; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'waffleAuthenticationWrapperFilter': Injection of autowired dependencies Failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private my.project.services.UserService my.project.auth.WaffleAuthenticationWrapperFilter.userService; nested exception is java.lang.IllegalArgumentException: Can not set my.project.services.UserService field my.project.auth.WaffleAuthenticationWrapperFilter.userService to com.sun.proxy.$Proxy84 at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:649) at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331) ... 35 more Caused by: org.springframework.beans.factory.BeanExpressionException: Expression parsing Failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'webSecurityConfig': Injection of autowired dependencies Failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private my.project.auth.WaffleAuthenticationWrapperFilter my.project.config.WebSecurityConfig.waffleAuthenticationWrapperFilter; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'waffleAuthenticationWrapperFilter': Injection of autowired dependencies Failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private my.project.services.UserService my.project.auth.WaffleAuthenticationWrapperFilter.userService; nested exception is java.lang.IllegalArgumentException: Can not set my.project.services.UserService field my.project.auth.WaffleAuthenticationWrapperFilter.userService to com.sun.proxy.$Proxy84 at org.springframework.context.expression.StandardBeanExpressionResolver.evaluate(StandardBeanExpressionResolver.java:164) at org.springframework.beans.factory.support.Abstractbeanfactory.evaluateBeanDeFinitionString(Abstractbeanfactory.java:1365) at org.springframework.beans.factory.support.DefaultListablebeanfactory.doResolveDependency(DefaultListablebeanfactory.java:957) at org.springframework.beans.factory.support.DefaultListablebeanfactory.resolveDependency(DefaultListablebeanfactory.java:942) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:606) ... 37 more Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'webSecurityConfig': Injection of autowired dependencies Failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private my.project.auth.WaffleAuthenticationWrapperFilter my.project.config.WebSecurityConfig.waffleAuthenticationWrapperFilter; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'waffleAuthenticationWrapperFilter': Injection of autowired dependencies Failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private my.project.services.UserService my.project.auth.WaffleAuthenticationWrapperFilter.userService; nested exception is java.lang.IllegalArgumentException: Can not set my.project.services.UserService field my.project.auth.WaffleAuthenticationWrapperFilter.userService to com.sun.proxy.$Proxy84 at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334) at org.springframework.beans.factory.support.AbstractAutowireCapablebeanfactory.populateBean(AbstractAutowireCapablebeanfactory.java:1210) at org.springframework.beans.factory.support.AbstractAutowireCapablebeanfactory.doCreateBean(AbstractAutowireCapablebeanfactory.java:537) at org.springframework.beans.factory.support.AbstractAutowireCapablebeanfactory.createBean(AbstractAutowireCapablebeanfactory.java:476) at org.springframework.beans.factory.support.Abstractbeanfactory$1.getObject(Abstractbeanfactory.java:303) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) at org.springframework.beans.factory.support.Abstractbeanfactory.doGetBean(Abstractbeanfactory.java:299) at org.springframework.beans.factory.support.Abstractbeanfactory.getBean(Abstractbeanfactory.java:199) at org.springframework.beans.factory.support.DefaultListablebeanfactory.getBeansOfType(DefaultListablebeanfactory.java:523) at org.springframework.beans.factory.support.DefaultListablebeanfactory.getBeansOfType(DefaultListablebeanfactory.java:512) at org.springframework.security.config.annotation.web.configuration.AutowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers(AutowiredWebSecurityConfigurersIgnoreParents.java:52) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(UnkNown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(UnkNown Source) at java.lang.reflect.Method.invoke(UnkNown Source) at org.springframework.expression.spel.support.ReflectiveMethodExecutor.execute(ReflectiveMethodExecutor.java:112) at org.springframework.expression.spel.ast.MethodReference.getValueInternal(MethodReference.java:129) at org.springframework.expression.spel.ast.MethodReference.access$000(MethodReference.java:49) at org.springframework.expression.spel.ast.MethodReference$MethodValueRef.getValue(MethodReference.java:342) at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:88) at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:120) at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:242) at org.springframework.context.expression.StandardBeanExpressionResolver.evaluate(StandardBeanExpressionResolver.java:161) ... 41 more Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private my.project.auth.WaffleAuthenticationWrapperFilter my.project.config.WebSecurityConfig.waffleAuthenticationWrapperFilter; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'waffleAuthenticationWrapperFilter': Injection of autowired dependencies Failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private my.project.services.UserService my.project.auth.WaffleAuthenticationWrapperFilter.userService; nested exception is java.lang.IllegalArgumentException: Can not set my.project.services.UserService field my.project.auth.WaffleAuthenticationWrapperFilter.userService to com.sun.proxy.$Proxy84 at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:561) at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331) ... 63 more Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'waffleAuthenticationWrapperFilter': Injection of autowired dependencies Failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private my.project.services.UserService my.project.auth.WaffleAuthenticationWrapperFilter.userService; nested exception is java.lang.IllegalArgumentException: Can not set my.project.services.UserService field my.project.auth.WaffleAuthenticationWrapperFilter.userService to com.sun.proxy.$Proxy84 at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334) at org.springframework.beans.factory.support.AbstractAutowireCapablebeanfactory.populateBean(AbstractAutowireCapablebeanfactory.java:1210) at org.springframework.beans.factory.support.AbstractAutowireCapablebeanfactory.doCreateBean(AbstractAutowireCapablebeanfactory.java:537) at org.springframework.beans.factory.support.AbstractAutowireCapablebeanfactory.createBean(AbstractAutowireCapablebeanfactory.java:476) at org.springframework.beans.factory.support.Abstractbeanfactory$1.getObject(Abstractbeanfactory.java:303) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) at org.springframework.beans.factory.support.Abstractbeanfactory.doGetBean(Abstractbeanfactory.java:299) at org.springframework.beans.factory.support.Abstractbeanfactory.getBean(Abstractbeanfactory.java:194) at org.springframework.beans.factory.support.DefaultListablebeanfactory.findAutowireCandidates(DefaultListablebeanfactory.java:1127) at org.springframework.beans.factory.support.DefaultListablebeanfactory.doResolveDependency(DefaultListablebeanfactory.java:1044) at org.springframework.beans.factory.support.DefaultListablebeanfactory.resolveDependency(DefaultListablebeanfactory.java:942) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:533) ... 65 more Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private my.project.services.UserService my.project.auth.WaffleAuthenticationWrapperFilter.userService; nested exception is java.lang.IllegalArgumentException: Can not set my.project.services.UserService field my.project.auth.WaffleAuthenticationWrapperFilter.userService to com.sun.proxy.$Proxy84 at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:561) at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331) ... 76 more Caused by: java.lang.IllegalArgumentException: Can not set my.project.services.UserService field my.project.auth.WaffleAuthenticationWrapperFilter.userService to com.sun.proxy.$Proxy84 at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnkNown Source) at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnkNown Source) at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnkNown Source) at java.lang.reflect.Field.set(UnkNown Source) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:557) ... 78 more
Solution
To apply AOP, spring uses agents. There are two versions of JDK dynamic proxies (interface based) and cglib based agents (class based) By default, spring uses JDK dynamic proxies for classes that implement one or more interfaces
At run time, you will get a dynamically created object (proxy) that implements all interfaces (userdetailsservice) of the class it wraps (userservice) Therefore, the dynamically created object is userdetailsservice instead of userservice
As a result, the agent failed to cast an error to userservice
You have (at least) two ways to solve this problem
>Program to interface > enable class - based proxy
Program to interface
When calling getuserbyusername, you need to create a new interface and implement it and userdetailsservice
public interface UserService { User getUserByUsername(String username); void storeUser(User user); List<User> findAllUsers(); } @Service("userService") public class UserServiceImpl implements UserDetailsService,UserService { ... }
If you do, you can keep your filter unchanged because userservice is now just an interface If you specify a different name for the interface, you must also change the userservice field in the filter to this type
Enable class based proxy
When you use spring boot, enable class based proxy as in application Adding properties to the properties file is just as simple
spring.aop.proxy-target-class=true
This creates a class - based proxy so you can keep the filter code unchanged