I have a Controller which calls a Service which has #Transactional annotation.
But when I declare a bean MethodValidationPostProcessor, no transaction is created (could not initialize proxy - no Session).
#EnableWebMvc
#ComponentScan(basePackages = {"my"})
public class Application extends WebMvcConfigurerAdapter {
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
}
Controller bean:
#RestController
#RequestMapping(path = "/my", produces = APPLICATION_JSON_VALUE)
public class MyController {
#Autowired
private TransactionalService transactionalService;
#RequestMapping(method = POST)
public void post(#SafeHtml #RequestBody String hey) {
transactionalService.doStuff(hey);
}
}
Service bean:
#Service
public class TransactionalService {
#PersistenceContext
private EntityManager entityManager;
#Transactional
public void doStuff(String hey) {
Item h = entityManager.find(Item.class, hey);
h.getParent(); // could not initialize proxy - no Session
}
}
I'd like to understand why #Transactional doesn't work when I declare MethodValidationPostProcessor. Thanks !
Note: If I add #Transactional on my Controller, it works. But it's not what I want to do.
Thanks to #Kakawait, I got a work-around: declaring my bean MethodValidationPostProcessor. Needs to be static so that #Transactional still work properly.
/**
* This bean must be static, to be instantiated before the other MethodValidationPostProcessors.
* Otherwise, some are not instantiated.
*/
#Bean
public static MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
Related
How to override #Configuation which is present under src/main/java with #TestConfiguration during unit tests?
#Configuration
public class AppConfig {
#Bean
public EmployeeService employeeService(){
return new EmployeeService();
}
}
#Component
public class ServerStartSetup implements CommandLineRunner {
#Autowired
private EmployeeService employeeService;
public void run(String... args) {
// do something with employee service
}
}
I would like to override the above bean with some below custom bean for testing purposes.
#TestConfiguration
public class TestAppConfig {
#Bean
public EmployeeService employeeService(){
return new FakeEmployeeService();
}
}
#SpringBootTest
#Import(TestAppConfig.class)
public class UnitTest {
}
However AppConfig does not seem to be skipped. That is , it throws an error saying that there is a bean with same name employeeService. If I rename bean method name in the TestAppConfig, it injects the bean created via AppConfig.
How to fix this.?
Note: One possible solution is using #Profile. I am looking for anything other than using Profiles.
I tested locally and found that changing the method name or #Bean to #Bean("fakeEmployeeService") and adding the #Primary annotation works.
#SpringBootTest
class DemoApplicationTests {
#Autowired
private EmployeeService employeeService;
#TestConfiguration
static class TestConfig {
//#Bean("fakeEmployeeService")
#Bean
#Primary
public EmployeeService employeeServiceTest() {
return new EmployeeService() {
#Override
public void doSomething() {
System.out.println("Do something from test...");
}
};
}
}
...
}
If we want to override a bean definition in #TestConfiguration, we need:
To use the same name as the overridden bean. (Otherwise it would be an "additional" bean and we could get conflict/'d have to qualify/primary)
Since spring-boot:2.1: spring.main.allow-bean-definition-overriding=true (set this in tests ONLY!plz!)
#ref
Then, with:
#TestConfiguration
public class TestAppConfig {
#Bean // when same name, no #Primary needed
public EmployeeService employeeService(){ // same name as main bean!
return new FakeEmployeeService();
}
}
We can do that:
#Import(TestAppConfig.class)
#SpringBootTest(properties = "spring.main.allow-bean-definition-overriding=true")
public class UnitTest {
... // EmployeeService will be "fake", the rest is from "main config"
You can mock the AppConfig bean in your test like this:
#MockBean
private AppConfig config;
Or, like you said, just use profiles.
Using SpringBoot, I have a Component bean that is defined as #Scope("protoype"). It also injects another prototype bean
The class is defined as
#Component
#Scope("prototype")
public class MyClass{
#Autowired
public BeanFactory beanFactory
private InjectedBean injectedBean
public MyClass(DataObj data) {
this.injectedBean = beanFactory.getBean(InjectedBean.class, data)
}
}
However, IntelliJ complains about the data field on the constructor: Could not autowire. No beans of 'DataObj' type found.. But DataObj is a POJO. I pass it in at runtime in order to create the bean. Am I defining the constructor incorrectly?
Update
Had the same problem doing it this way. It still wants to treat DataObj as a bean on the factory constructor class. Doesn't matter if I annotate the class with #Component or #Configuration
#Component
public class MyClass{
#Autowired
public BeanFactory beanFactory
private InjectedBean injectedBean
public MyClass(InjectedBean injectedBean) {
this.injectedBean = injectedBean;
}
#Bean
#Scope("prototype")
public MyClass myClass(DataObj data) {
InjectedBean injectedBean = beanFactory.getBean(InjectedBean.class, data)
return new MyClass(injectedBean);
}
}
Also tried this example from that same link:
#Configuration
public class ServiceConfig {
#Bean
public Function<DataObj, MyClass> thingFactory() {
return data-> myClass(data); //
}
#Bean
#Scope(value = "prototype")
public MyClass myClass(DataObj data) {
return new MyClass(data);
}
}
Update
I think I resolved this with some information in Spring Java Config: how do you create a prototype-scoped #Bean with runtime arguments?. Part of my problem is that I tried to put the factory bean in the Component itself, which doesn't work
In other words
#Component
public class MyClass{
#Autowired
public BeanFactory beanFactory
private InjectedBean injectedBean
public MyClass(InjectedBean injectedBean) {
this.injectedBean = injectedBean;
}
#Bean
#Scope("prototype")
public MyClass myClass(DataObj data) {
InjectedBean injectedBean = beanFactory.getBean(InjectedBean.class, data)
return new MyClass(injectedBean);
}
}
In this cass, Spring tries to create a MyClass bean because of the #Component annotation, but another MyClass bean due to the #Bean annotation.
So I moved the #Bean to another class
#Configuration
public class ServiceConfig {
#Bean
public Function<DataObj, MyClass> thingFactory() {
return data-> myClass(data); //
}
#Bean
#Scope(value = "prototype")
public MyClass myClass(DataObj data) {
return new MyClass(data);
}
}
This appears to work, but IntelliJ still complains about DataObj. This might be an Intellij issue
Autowiring works everywhere in application except inside this custom validation annotation class where it is null when called from inside isValid() method.
javax.validation:validation-api: 2.0.1.Final
org.hibernate:hibernate-validator: 5.0.1.Final
spring: 5.1.4.RELEASE
#Component
public class ValidatorUniqueUsername implements ConstraintValidator<UniqueUsername, String> {
#Autowired
AccountService jpaAccountService;
#Override
public void initialize(UniqueUsername constraintAnnotation) { }
#Override
public boolean isValid(String username, ConstraintValidatorContext context) {
return username != null && jpaAccountService.findByUsername(username) == null;
}
}
#Entity
...
public class Account extends BaseEntity<Long> implements Serializable{
#NotEmpty
#UniqueUsername
private String username;
}
#Configuration
public class AppConfig implements AsyncConfigurer {
#Bean
public Validator validatorFactory() {
return new LocalValidatorFactoryBean();
}
#Bean
public static LocalValidatorFactoryBean validatorFactory() {
return new LocalValidatorFactoryBean();
}
Your custom annotation #UniqueUsername instantiates and calls your ValidatorUniqueUsername but it does not inject it even it is annotated with #Component.
And because of this none of the resources to be autowired in your ValidatorUniqueUsername will be injected.
You could try to add this to your #Configuration:
#Bean
public Validator validatorFactory() {
return new LocalValidatorFactoryBean();
}
See more here (excerpt below):
In spring if we register LocalValidatorFactoryBean to bootstrap javax.validation.ValidatorFactory then custom ConstraintValidator classes are loaded as Spring Bean. That means we can have benefit of Spring's dependency injection in validator classes.
I have three #RestController classes, for two of them the #Autowired is injecting the bean, but for one it is not. I don't know what the issue is, as few hours ago the same code was working fine.
package com.learn.service;
package com.learn.service;
#Service
#Transactional
public class RoleService {
#Autowired
private RoleJpaRepository roleJpaRepository;
public List<Role> findAll(){
return roleJpaRepository.findAll();
}
}
the controller for Role
package com.learn.controller;
#RestController
#RequestMapping("/roles")
public class RoleController {
#Autowired
private RoleService roleService;
#RequestMapping(method = RequestMethod.GET)
private List<Role> findAll() {
System.out.println(roleService); // roleService is null here and NullPointerException is thrown from below method call.
return roleService.findAll();
}
}
Configuration class for Service
package com.learn.springConfig;
#Configuration
#ComponentScan("com.learn.service")
public class ServiceConfig {
public ServiceConfig() {
super();
}
}
the runner
#SpringBootApplication
#Import({
ContextConfig.class,
PersistenceJpaConfig.class,
ServiceConfig.class,
WebConfig.class,
SecurityConfig.class
})
public class WebservicesLearningApplication {
public static void main(String[] args) {
SpringApplication.run(WebservicesLearningApplication.class, args);
}
}
For the same configurations, the controller for User is working fine whose Service layer exists in the same package as that of Role.
package com.learn.controller;
#RestController
#RequestMapping("/users")
public class UserController {
#Autowired
private UserService userService;
#RequestMapping(method = RequestMethod.GET)
public List<User> findAll() {
System.out.println(userService);
List<User> users = userService.findAll();
return users;
}
Service layer
package com.learn.service;
#Service
#Transactional
public class UserService {
#Autowired
private UserJpaRepository userJpaRepository;
public List<User> findAll(){
return userJpaRepository.findAll();
}
}
Accessing the localhost:8080/api/users is successful but localhost:8080/api/roles gives NullPointerException
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause
java.lang.NullPointerException: null
at com.learn.controller.RoleController.findAll(RoleController.java:30) ~[classes/:na]............
Update1:
Web configuration class
#Configuration
#ComponentScan(basePackages = {"com.learn.controller"})
#EnableWebMvc
public class WebConfig implements WebMvcConfigurer{
public WebConfig() {
super();
}
#Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
Optional<HttpMessageConverter<?>> convertFound = converters.stream().filter(c -> c instanceof AbstractJackson2HttpMessageConverter).findFirst();
if(convertFound.isPresent()) {
final AbstractJackson2HttpMessageConverter converter = (AbstractJackson2HttpMessageConverter) convertFound.get();
converter.getObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
converter.getObjectMapper().enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
}
}
}
screenshot of project structure
Update2 : I tried using the same UserService using #Autorired in a jUnit test case, and everything is working there. No nullpointer exception.
#ContextConfiguration(classes = {PersistenceJpaConfig.class, ContextConfig.class, ServiceConfig.class})
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class RoleTest {
#Autowired
private RoleService roleService ;
#Test
public void checkIfAllRolesCanBeRetrieved() {
List<Role> roles = roleService.findAll();
Assert.assertNotNull(roles);
}
}
This happened to me! in my case I had a controller using service,The service used a method of a class that did not have a #service,#controller or another annotation and I inject in service then when I use parent service error null occurred.
I hope that it will be used
I have designed a packing structure.
Controller
Delegates (which is helper class) - this class do all the business and return the value to Controllers.
Service
Service Implementation
DAO
DAO Implementation.
I want to implement autowired (Annotation) concept and would like to avoid xml configuration such as service and DAO configuration on spring-bean.xml.
This code is not working if I want to avoid xml configuration.
I have done those changes
bean id :loginDelegate, userService, userDao
added the #Service & #Repository annotation to the corresponding service & DAO implementation.
#Controller("loginController")
public class LoginController {
#Autowired
private LoginDelegate loginDelegate;
public LoginDelegate getLoginDelegate() {
return this.loginDelegate;
}
public void setLoginDelegate(LoginDelegate tLoginDelegate) {
this.loginDelegate = tLoginDelegate;
}
#RequestMapping(value="/login.do",method=RequestMethod.GET)
public ModelAndView displayLogin(HttpServletRequest request, HttpServletResponse response) {
log.info("<---displayLogin()--->");
ModelAndView model = new ModelAndView("login");
LoginBean loginBean = new LoginBean();
model.addObject("loginBean", loginBean);
return model;
}
}
public class LoginDelegate {
#Autowired
private IUserService userService;
public IUserService getUserService() {
return this.userService;
}
public void setUserService(IUserService userService) {
this.userService = userService;
}
public boolean isValidUser(String username, String password) throws Exception {
return userService.isValidUser(username, password);
}
}
public interface IUserService {
public boolean isValidUser(UserBean userObj);
public int addUsers(UserBean userObj);
}
public class UserServiceImpl implements IUserService {
#Autowired
private IUserDao userDao;
public IUserDao getUserDao() {
return this.userDao;
}
public void setUserDao(IUserDao userDao) {
this.userDao = userDao;
}
public boolean isValidUser(UserBean userObj) {
return userDao.isExistUser(userObj);
}
#Override
public int addUser(final UserBean userObj) {
return userDao.saveUserDetails(userObj);
}
}
public interface IUserDao {
public boolean isExistUser(UserBean userObj);
public int saveUserDetails(UserBean userObj);
}
public class UserDaoImpl implements IUserDao {
#Autowired
UserBean userObj;
#Autowired
DataSource dataSource ;
public DataSource getDataSource(){
return this.dataSource;
}
public void setDataSource(DataSource dataSource){
this.dataSource = dataSource;
}
Use Java-based configuration if you want to completely get rid of XML-based configuration
#Configuration
#ComponentScan(basePackages = "com.acme")
public class AppConfig {
...
}
The above normal Java class when annotated with #Configuration, makes it a 'Spring Configuration class' (analogous to XML-based configuration).
#ComponentScan annotation scans for classes annotated with #Component, #Controller, #Service, #Repository classes from the package defined during start-up time to get them registered as Spring beans. This can be done in XML also with <context:component-scan base-package="com.acme" />
Refer:http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/beans.html#beans-java-instantiating-container-scan