I use Spring 4.2.8 and I do have the service class below. If this class has the name ScheduleEmailCreateAndSendServiceImpl than everything works fine (method generalEmailMessage is invoked at start time in order to create the Spring Bean)
If I rename this class to EmailCreateAndSendServiceImpl than method generalEmailMessage will not be invoked at start time - does anyone know why?
#Service("emailCreateAndSendService")
public class ScheduleEmailCreateAndSendServiceImpl extends AbstractService
implements EmailService {
protected EmailMessage generalMessage;
#Override
public void createAndSendMessage(String receiver, boolean emailActive, Object[] values) throws BusinessException {
// create and send email
}
#Bean
public EmailMessage generalEmailMessage() {
this.generalMessage = new GeneralEmailInformationMessage();
return generalMessage;
}
}
[EDIT]
with this code it is the same
#Configuration
public #Data class ScheduleGeneralEmailConfiguration {
protected EmailMessage generalMessage;
public ScheduleGeneralEmailConfiguration() {
System.out.println("asdf");
}
#Bean
public EmailMessage generalEmailMessage() {
this.generalMessage = new GeneralEmailInformationMessage();
return generalMessage;
}
}
#Bean annotated methods should be in #Configuration annotated class.
You can also put the #Bean annotated methods in the main class of the Spring Boot application annotated with #SpringBootApplication which encapsulates #Configuration, #EnableAutoConfiguration, and #ComponentScan annotations.
Make sure your #Configuration annotated class is placed in the same package or sub package of the Spring Boot Application class
Related
I've encountered an unexpected behaviour when using dependency injection in a ConstraintValidator which is getting evaluated at class level.
Entity class:
#Entity
#ValidDemoEntity
public class DemoEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}
Validation annotation:
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = {DemoEntityValidator.class})
public #interface ValidDemoEntity {
String message() default "{some.demo.validator.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Validator:
public class DemoEntityValidator implements ConstraintValidator<ValidDemoEntity, DemoEntity> {
private DemoEntityRepository demoEntityRepository;
public DemoEntityValidator(DemoEntityRepository demoEntityRepository) {
this.demoEntityRepository = demoEntityRepository;
}
#Override
public void initialize(ValidDemoEntity constraintAnnotation) {
}
#Override
public boolean isValid(DemoEntity demoEntity, ConstraintValidatorContext constraintValidatorContext) {
return true;
}
}
Test class:
#SpringBootTest
public class ValidatorInstantiationTest {
private Validator validator;
#Before
public void setUp() throws Exception {
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
validator = validatorFactory.getValidator();
}
#Test
public void shouldInitiateAndCallDemoEntityValidator() {
DemoEntity demoEntity = new DemoEntity();
validator.validate(demoEntity);
}
}
Validating the entity leads to:
javax.validation.ValidationException: HV000064: Unable to instantiate ConstraintValidator: com.example.demo.DemoEntityValidator.
and further down the stack trace:
Caused by: java.lang.NoSuchMethodException: com.example.demo.DemoEntityValidator.<init>()
which indicates that Hibernate tried to initiate the the class instead of letting Spring take care of that.
The strange thing about this is that dependency injection works fine for validations applied on field level.
The code is available at GitHub.
The exception says that there is no default constructor because Hibernate Validator tries to instantiate your validator.
You have to use Spring.
1 Make your validator a Spring Bean:
#Component
public class DemoEntityValidator implements ConstraintValidator<ValidDemoEntity, DemoEntity> {
2 Inject the Spring provided validator and use the SpringRunner for executing your tests:
#SpringBootTest
#RunWith(SpringRunner.class)
public class ValidatorInstantiationTest {
#Autowired
private Validator validator;
#Test
public void shouldInitiateAndCallDemoEntityValidator() {
DemoEntity demoEntity = new DemoEntity();
validator.validate(demoEntity);
}
}
1 Make your validator a Spring Bean
This site states:
The Spring framework automatically detects all classes which implement the ConstraintValidator interface. The framework instantiates them and wires all dependencies like the class was a regular Spring bean.
Which clearly works for validations applied on field level.
Nevertheless I've updated the code.
DemoEntityValidator is now a Spring component:
#Component
public class DemoEntityValidator implements ConstraintValidator<ValidDemoEntity, DemoEntity>
I've changed the test to:
#SpringBootTest
#RunWith(SpringRunner.class)
public class ValidatorInstantiationTest {
#Autowired
private DemoEntityRepository demoEntityRepository;
#Test
public void shouldInitiateAndCallDemoEntityValidator() {
DemoEntity demoEntity = new DemoEntity();
demoEntityRepository.save(demoEntity);
}
}
To make the usecase clearer, but the test still leads to the same exception.
Adding an empty constructor to the class DemoEntityValidator disables the error.
I think you answer is here:
https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation-beanvalidation-spring
You need to declare a LocalValidatorFactoryBean in your configuration class and it will just work.
From the documentation:
By default, the LocalValidatorFactoryBean configures a
SpringConstraintValidatorFactory that uses Spring to create
ConstraintValidator instances. This lets your custom
ConstraintValidators benefit from dependency injection like any other
Spring bean.
And an example from the same place:
import javax.validation.ConstraintValidator;
public class MyConstraintValidator implements ConstraintValidator {
#Autowired;
private Foo aDependency;
...
}
And this is how I declared that bean in a #Configuration annotated class:
/**
* Provides auto-wiring capabilities for validators Checkout: https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation-beanvalidation-spring
*/
#Bean
public LocalValidatorFactoryBean validatorFactoryBean() {
LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
bean.setValidationMessageSource(validationMessageSource());
return bean;
}
There's nothing wrong with your validator class. I got this working by making two changes to the test configuration:
1. Run test with Spring
In order to have Spring manage your beans, you need to run your test with a test runner that sets up Spring. You can specify the test runner class using junit's #RunWith-annotation:
#RunWith(SpringRunner.class)
#SpringBootTest
public class ValidatorInstantiationTest { ... }
2. Inject a Spring managed validator bean
Since you're using Spring Boot, you can inject a Spring managed validator – it's already configured. This way, Spring will handle the initiation of your DemoEntityValidator.
#RunWith(SpringRunner.class)
#SpringBootTest
public class ValidatorInstantiationTest {
#Autowired
private Validator validator;
...
}
This is all that is needed. You should not annotate your DemoEntityValidator with #Component or similar.
Note that you need to provide Spring with a data source, since SpringRunner will set up a context based on your Spring Boot setup (I'm guessing it includes spring-boot-starter-data-jpa in your case). The easiest way to get going is just to put an in-memory DB such as h2 on the classpath.
I am using Spring boot. I have some question regarding the spring boot beans.
But I have doubt
I use bean which are default scope that is singleton. So they will have only one instance per application.
#Configuration
public class ...{
#Bean
public void method() {}
}
And
Now i use bean which scope is prototype. So they will have each instance per request.
#Configuration
public class ...{
#Bean
#Scope("prototype")
public void method() {}
}
But
I want single instance per user..? all request use single instance per user.
#Configuration
class Abc {
#Bean
#Scope("session")
public YourBean getYourBean() {
return new YourBean();
}
}
You will need to define one singleton bean with a property using prototype bean:(xml example)
With #bean definition:
#Component
#Scope("singleton")
public class SingletonBean {
// ..
#Autowired
private PrototypeBean prototypeBean;
//..
}
#Component
#Scope("prototype")
public class PrototypeBean {
//.......
}
Example: https://www.baeldung.com/spring-inject-prototype-bean-into-singleton
I'm a beginner at Spring and beginning to understand how beans work. I want to declare a 3rd party class as a Service or a Bean. How do I do this? Should I just extend the class and annotate that?
example:
#Service
public class MyService { public MyService(ThirdPartyClass thirdPartyClass){..}....}
Here I cannot annotate ThirdPartyClass as a Service or otherwise
If your aren't the owner of the class that you would like to use as a bean, you can create the bean declaration in one of application's configuration classes:
#Configuration
public class YourConfig {
#Bean
public ThirdPartyClass thirdPartyClass() {
return new ThirdPartyClass();
}
}
Spring will instantiate an appropriate object based on that description and expose it via container to other beans.
You can add the class which is out of your control as #Bean
#Configuration
public class ApplicationConfig {
#Bean
public ClassName methodName() {
return new ClassName();
}
}
At the time of initializing the application, spring will call this method and register ClassName object to spring context and will be made available where you #Autowired this bean.
I am trying to save data on gemfire using crud repository. I have created one operation class to call save method of repository but at autowired instance I am getting null pointer exception. Below is my code:
public interface GeodeRepository extends CrudRepository<KeyValueBean, String> {
#Override
public KeyValueBean findOne(String name);
#Override
public <S extends KeyValueBean> Iterable<S> save(Iterable<S> entities);
}
#EnableGemfireRepositories(basePackageClasses = GeodeRepository.class)
#EnableAutoConfiguration
#Configuration
public class Operations {
#Autowired
private GeodeRepository repository;
public void saveKeyValueData(KeyValueBean keyValueBean) {
System.out.println("Repository is : " + repository);
repository.save(Arrays.asList(keyValueBean)); // <--- i am getting
// repository as null so
// getting null pointer
// exception
}
}
When we #Autowired any class make sure, you have declared that class as a #Component.
for example:
#Component
public class Operations {
#Autowired
private GeodeRepository repository;
public void saveKeyValueData(KeyValueBean keyValueBean) {
System.out.println("Repository is : " + repository);
repository.save(Arrays.asList(keyValueBean));
}
}
and try using #Autowired to Operation class to your class
in which class your are calling your saveKeyValueData() method.
So, what is not apparent from your example is how you "bootstrap" your application and it's features (e.g. Repositories) into action.
It is not simply enough to add the Spring #Configuration, Spring Boot's #EnableAutoConfiguration and SD GemFire's #EnableGemfireRepositories annotations and expect everything to be auto-configured and wired up successfully. I.e. you need a bootstrapping mechanism, like Spring Boot, especially if you are using the #EnableAutoConfiguration annotation.
For example...
import org.springframework.boot.SpringApplication;
...
class MyApplication {
public static void main(String[] args) {
SpringApplication.run(Operations.class, args);
}
}
Now, you could remove the #EnableAutoConfiguration from your Operations class and add the #SpringBootApplication to the MyApplication class, like so...
#SpringBootApplication
class MyApplication {
...
}
#SpringBootAppliation combines together Spring's #Configuration with Spring Boot's #EnableAutoConfiguration, along with many other useful meta-annotations, like Spring's #ComponentScan, enabling all sorts of magic to happen.
But, if you are not using Spring Boot, you can always bootstrap you application with the AnnotationConfigApplicationContext, like so..
class MyApplication
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(Operations.class);
applicationContext.registerShutdownHook();
}
}
This is essentially what the Spring Boot, SpringApplication class does for you anyway.
If you are developing a Web application, then of course you can specify the type of ApplicationContext created since you are using Java config, for instance. See here for more details.
Hope this helps!
Cheers,
John
I'm trying to implement a custom Converter for an Entity to a Resource object with Spring Data Rest, but the Converter is never invoked.
I'm following this documentation:
If your project needs to have output in a different format, however,
it’s possible to completely replace the default outgoing JSON
representation with your own. If you register your own
ConversionService in the ApplicationContext and register your own
Converter, then you can return a Resource
implementation of your choosing.
That's what I've tried to do.
I have a #Configuration class that extends RepositoryRestMvcConfiguration, with this method:
#Configuration
#EnableWebMvc
#EnableHypermediaSupport(type = HypermediaType.HAL)
public class RepositoryBaseConfiguration extends RepositoryRestMvcConfiguration {
#Override
public DefaultFormattingConversionService defaultConversionService() {
return super.defaultConversionService();
}
}
And I have a Class that extends RepositoryRestConfigurerAdapter, with this implementation:
#Configuration
public class RepositoryBaseConfigurerAdapter extends RepositoryRestConfigurerAdapter {
#Override
public void configureConversionService(ConfigurableConversionService conversionService) {
if(!conversionService.canConvert(Entity.class, Resource.class))
conversionService.addConverter(new EntityConverter());
super.configureConversionService(conversionService);
}
}
Both methods of those two classes are correctly invoked and managed, so it's natural to think that the Converter has been registered in the Application Context...
This is my custom converter EntityConverter:
#Component
public class EntityConverter implements Converter<Entity, Resource> {
#Override
public Resource convert(Entity source) {
System.out.println("method convert of class EntityConverter");
return null;
}
}
The method "convert" is never invoked by Spring Data Rest.
What's wrong/missing ?
Thanks in advance.