I have following class:
#Configuration
public class EndpointStatus {
private static final Logger serverLogger = LogManager.getLogger(EndpointStatus.class);
private Long id;
private volatile Status status;
#OneToOne
private volatile CurrentJob currentJob;
public enum Status {
AVAILABLE,
BUSY
}
#Bean
#Primary
public EndpointStatus getEndpointStatus() {
serverLogger.info("STATUS CREATED");
return new EndpointStatus();
}
public EndpointStatus() {
}
public CurrentJob getCurrentJob() {
return currentJob;
}
public void setCurrentJob(CurrentJob currentJob) {
this.currentJob = currentJob;
}
public Status getStatus() {
return status;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public void setStatus(Status status) {
this.status = status;
}
public boolean isBusy() {
return getStatus() == Status.BUSY;
}
Bean is used in endpoint which is annotated with #Component
and then i try to get the bean in endpoint like
ApplicationContext ctx = new AnnotationConfigApplicationContext(EndpointStatus.class);
EndpointStatus sc = ctx.getBean(EndpointStatus.class);
EndpointStatus is not used anywhere else.
To my knowledge, there should be no reason to create a second bean...
However at startup I always get
INFO 6169 [main] c.e.k.d.r.m.i.EndpointStatus : STATUS CREATED
INFO 6169 [main] c.e.k.d.r.m.i.EndpointStatus : STATUS CREATED
What am I doing wrong here?
EDIT:
Have tried every answer given to no avail whatsoever..
my class now looks like this
#Configuration
public class EndpointStatusConfig {
private static final Logger serverLogger = LogManager.getLogger(JavaXRest.class);
private Long id;
private volatile Status status = EndpointStatusConfig.Status.AVAILABLE;
#OneToOne
private volatile CurrentJob currentJob;
public enum Status {
AVAILABLE,
BUSY
}
#Bean
#Primary
public EndpointStatusConfig getEndpointStatus() {
serverLogger.info("STATUS CREATED");
return new EndpointStatusConfig();
}
public CurrentJob getCurrentJob() {
return currentJob;
}
public void setCurrentJob(CurrentJob currentJob) {
this.currentJob = currentJob;
}
public Status getStatus() {
return status;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public void setStatus(Status status) {
this.status = status;
}
public boolean isBusy() {
return getStatus() == Status.BUSY;
}
}
no matter #Component or #Configuration, making call to sc in endpoint will result in hundreds of beans created crashing the app...
EDIT2:
this is just getting worse and worse...
now even call to
if ( sc.isBusy() ) { return Response.ok( sc.getCurrentJob() ).type(MediaType.APPLICATION_JSON).build(); }
will jump to #Bean and create as many EndpointStatus objects as it can before the application crashes.... #Component creates one at startup, then thousands. #Configuration will create 2 at startup and then thousands also...
Just a guesswork but defining the configuration class both as a configuration and a factory bean return type is probably the issue.
EndpointStatus is a configuration class as the class is declared with #Configuration and a configuration class produces a bean in Spring and it is also an explicit bean as you annotated the bean factory method getEndpointStatus() with #Bean.
It is a little like if you had defined twice the bean.
Simply change the name of your Configuration class from EndpointStatus to EndpointStatusConfig and this will then only create a single bean with EndpointStatus class.
As you are annotating EndpointStatus both as #Configuration & #Bean it creates 2 Beans.
I think your problem is using #Configuration instead of #Component
The #Configuration will try add to spring context any #Autowired or #Bean inside the class with #Configuration but won't add it self to the spring context.
If you want to add that class as a bean to the spring context you should use #Component
Edit:
Did you try to inject EndpointStatus class with the #Configuration on it self?
In case you don't and don't know what is injection in spring try this:
#Autowired
EndpointStatus status;
void yourMethod(){
//Change this
//ApplicationContext ctx = new AnnotationConfigApplicationContext(EndpointStatus.class);
//EndpointStatus sc = ctx.getBean(EndpointStatus.class);
//Use instead the status variable declared before
}
Related
I have a class with two final fields of the same type, and I need to make second field injected with null if property props.enabled in application.yml is false. However, if it's false Spring Boot injects both fields with the same bean instance.
How to forbid Spring Boot injecting both fields of the same type with the same bean instance?
#AllArgsConstructor
public class MySettings {
private int val;
}
My configuration class
#Configuration
public class MySpringConfig {
#Bean
public MySettings settingsA() {
return new MySettings(1);
}
#Bean
#ConditionalOnProperty(prefix = "props", name = "enabled")
public MySettings settingsB() {
return new MySettings(2);
}
}
And this is my class
#Component
#RequiredArgsConstructor
public class MyClass {
private final MySettings settingsA; // MySettings(1)
private final MySettings settingsB; // also MySettings(1) but must be null if props.enabled=false
#Value("${props.enabled}")
private boolean enabled;
...
}
This is part of the real project, so I have very little space to deviate from
UPDATE
I came up with solution of constructor injection but the code starts to look ugly
#Component
public class MyClass {
private final MySettings settingsA;
private final MySettings settingsB;
private boolean enabled;
public MyClass(MySettings settingsA,
#Nullable #Qualifier("settingsB") MySettings settingsB,
#Value("${props.enabled}") boolean enabled) {
this.settingsA = settingsA;
this.settingsB = settingsB;
this.enabled = enabled;
}
...
You need a qualifier to tell Spring which bean to inject:
#Configuration
public class MySpringConfig {
#Bean(name = "SettingsA")
public MySettings settingsA() {
return new MySettings(1);
}
#Bean(name = "SettingsB")
#ConditionalOnProperty(prefix = "props", name = "enabled")
public MySettings settingsB() {
return new MySettings(2);
}
}
And now in MyClass:
#Component
public class MyClass {
private final MySettings settingsA;
private final MySettings settingsB;
#Value("${props.enabled}")
private boolean enabled;
public MyClass(#Qualifier("SettingsA") MySettings settingsA, #Qualifier("SettingsB") MySettings settingsB) {
this.settingsA = settingsA;
this.settingsB = settingsB;
}
...
}
However, since one of those Beans might not be available I believe you need to not include it in the constructor injection otherwise you will get an error. In that case you need to do the following:
#Component
public class MyClass {
#Qualifier("SettingsA")
#Autowired
private MySettings settingsA;
#Qualifier("SettingsB")
#Autowired(required = false)
private MySettings settingsB;
#Value("${props.enabled}")
private boolean enabled;
...
}
On a spring boot 2.4.3 application with Java:
I am using a DTO to construct the JSON response from the domain model of the application. The DTO is just a plain java object.
I am trying to property inject a new class that I created for data transformation using the #Autowired but I get a nullPointerException on the runtime.
#Data
#NoArgsConstructor
public class FetchSupplierDTO {
private long id;
private String name;
private String description;
private String info;
private List<String> tags;
#Autowired
private TagTranslation tagTranslation;
public FetchSupplierDTO(SupplierEntity supplier) {
this.id = supplier.getId();
this.name = supplier.getDisplayName();
this.description = supplier.getGivenDescription();
this.info = supplier.getInfo();
if (supplier.getTags() != null) {
this.tags = tagTranslation.extractTagsFromEntity(supplier.getTags());
}
}
}
#Service
public class TagTranslation {
public List<String> extractTagsFromEntity(List<TagEntity> tagEntityList) {
List<String> tagStringList = new ArrayList<>();
tagEntityList.forEach(productTag -> { tagStringList.add(productTag.getTag()); });
return tagStringList;
}
}
First of all, looking at the current code I would design it so that the caller of the constructor is responsible for calling the autowired service. Then the DTO really stays a DTO and is not at the same time responsible for calling a service.
If there is really no way around calling a Spring component from inside a DTO then you will have to get the component manually. And call SpringBeanLocator.getBean(TagTranslation.class) to get that component and insert it in your field.
Spring holds a single instance of each component, on initialization it scans for autowired annotations within annotated classes (#Component, #Service) and initializes those fields. Once you call the constructor of such a class separately, it will not return the instance that is maintained by Spring, it will construct a new instance. Therefore it's autowired fields will be null.
public class SpringBeanLocator implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public final void setApplicationContext(ApplicationContext context) {
validate(getInternalContext());
setContext(context);
}
public static <T> T getBean(Class<T> type) {
context.getBean(type);
}
}
We've been using Guice for DI in AWS Lambdas, but now are moving to Spring Boot and long running services.
We've got feature toggles working as dynamic proxies in Guice, but need to implement in Spring.
Say we have a SomeFeature interface and two implementations DisabledImplementation and EnabledImplementation.
I can get really close by tagging DisabledImplementation with #Component("some.feature.disabled") and EnabledImplementation with #Component("some.feature.enabled") and then writing an implementation like this:
#Primary
#Component
public class FlippingFeature implements SomeFeature {
private final SomeFeature enabled;
private final SomeFeature disabled;
private final FeatureFlip featureFlip;
#Inject
public FlippingFeature(#Named("some.feature.enabled") SomeFeature enabled,
#Named("some.feature.disabled") SomeFeature disabled,
FeatureFlip featureFlip) {
this.enabled = enabled;
this.disabled = disabled;
this.featureFlip = featureFlip;
}
#Override
public String foo() {
return featureFlip.isEnabled("some.feature") ? enabled.foo() : disabled.foo();
}
}
But I'd prefer to not write the FlippingFeature class at all and do it w/ a dynamic proxy hidden away. Can I do this with a custom BeanFactoryPostProcessor or something else?
I've got a pretty decent solution now.
#Qualifier
#Retention(RUNTIME)
// tag the disabled feature implementation w/ this annotation
public #interface Disabled {}
#Qualifier
#Retention(RUNTIME)
// tag the enabled feature implementation w/ this annotation
public #interface Enabled {}
#Target(TYPE)
#Retention(RUNTIME)
// tag the feature interface w/ this annotation
public #interface Feature {
String value();
}
// create a concrete implementation of this class for each feature interface and annotate w/ #Primary
// note the use of #Enabled and #Disabled injection qualifiers
public abstract class FeatureProxyFactoryBean<T> implements FactoryBean<T> {
private final Class<T> type;
private FeatureFlag featureFlag;
protected T enabled;
protected T disabled;
protected FeatureProxyFactoryBean(Class<T> type) {
this.type = type;
}
#Autowired
public void setFeatureFlag(FeatureFlag featureFlag) {
this.featureFlag = featureFlag;
}
#Autowired
public void setEnabled(#Enabled T enabled) {
this.enabled = enabled;
}
#Autowired
public void setDisabled(#Disabled T disabled) {
this.disabled = disabled;
}
#Override
public T getObject() {
Feature feature = type.getAnnotation(Feature.class);
if (feature == null) {
throw new IllegalArgumentException(type.getName() + " must be annotated with #Feature");
}
String key = feature.value();
ClassLoader classLoader = FeatureProxyFactoryBean.class.getClassLoader();
Class<?>[] interfaces = {type};
return (T) Proxy.newProxyInstance(classLoader, interfaces,
(proxy1, method, args) -> featureFlag.isEnabled(key) ?
method.invoke(enabled, args) :
method.invoke(disabled, args));
}
#Override
public Class<T> getObjectType() {
return type;
}
}
// test classes
#Feature("test_key")
public interface SomeFeature {
String foo();
}
#Disabled
#Component
public class DisabledFeature implements SomeFeature {
#Override
public String foo() {
return "disabled";
}
}
#Enabled
#Component
public class EnabledFeature implements SomeFeature {
#Override
public String foo() {
return "enabled";
}
}
#Primary
#Component
public class SomeFeatureProxyFactoryBean extends FeatureProxyFactoryBean<SomeFeature> {
public SomeFeatureProxyFactoryBean() {
super(SomeFeature.class);
}
}
Then inject #Inject SomeFeature someFeature where needed and it will get the proxy instance due to the #Primary annotation.
Now we can toggle the feature on and off in Launchdarkly and it (nearly) instantly gets reflected in all running instances without a restart or re-initializing the Spring context.
I am learning Spring Boot and I am trying to make a very simple app that fetches data from Mongo DB by using Dynamic Queries. I am using Intellij as my IDE.
FILE: application.properties (inside resource folder)
spring.mongo.host=127.0.0.1
spring.mongo.port=27017
spring.mongo.databaseName=spring
FILE: person.java
#Document (collection = "person")
public class Person {
#Id
String id;
int age;
String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
FILE: MyRepo.java
#Repository
public interface MyRepo extends PagingAndSortingRepository<Person, String> {
public List<Person> findAllByName(String name);
}
FILE: Config.java
#Configuration
#EnableMongoRepositories(basePackages = {"mongo.customQueries"})
public class Config {
}
FILE: Main.java
public class Main {
#Autowired
public static MyRepo myRepo;
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
MyRepo myRepo = context.getBean(MyRepo.class);
System.out.println(myRepo.findAllByName("Avishek"));
}
}
When I run the project, I get an error
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [mongo.customQueries.MyRepo] is defined
What is it that I am missing here? Why is my MyRepo bean not created as most of the examples in net are doing so.
The problem is you want to annotation the MyRepo in the Main class, please remove it as below:
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
MyRepo myRepo = context.getBean(MyRepo.class);
System.out.println(myRepo.findAllByName("Avishek"));
}
}
If someone could just give me a simple example to run Dynamic Queries
in Spring boot with mongo. Some examples similar to that of above. Or
how can I make the above example correct.
You can see working example here. And find explanations here.
I have a simple project, based on this guide. I created a simple REST interface and I want it to use my database. I added Hibernate to the dependencies and created the DAO class. I'm using Spring Tool-Suite for IDE. As far as I understand I should add some beans to tell the classes what to use but I don't understand how. Here are my classes.
Application.java
package com.learnspring.projectfirst;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Marker.java
package com.learnspring.projectfirst;
#Entity
public class Marker {
#Id
#Column
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
#Column
private double longitude;
#Column
private double latitude;
#Column
private String address;
public Marker() {
// Empty constructor
}
public Marker(long id, double longitude, double latitude, String address) {
this.id = id;
this.longitude = longitude;
this.latitude = latitude;
this.address = address;
}
//Getters and Setters
}
MarkerController.java
package com.learnspring.projectfirst.controller;
#Controller
public class MarkerController {
private Logger logger = Logger.getLogger(MarkerController.class.getName());
#Autowired
private MarkerServiceImplementation markerService;
#RequestMapping(value="/markers", method=RequestMethod.GET)
public #ResponseBody List<Marker> getMarkers(#RequestParam(value="city", defaultValue="") String city) {
return this.markerService.getAllMarkers();
}
#RequestMapping(value="/markers/new", method=RequestMethod.POST)
public #ResponseBody Marker addMarker(#RequestBody Marker marker) {
this.markerService.addMarker(marker);
return marker;
}
}
MarkerDaoImplementation.java
package com.learnspring.projectfirst.dao;
#Repository
public class MarkerDaoImplementation implements MarkerDaoInterface {
#Autowired
private SessionFactory sessionFactory;
#Override
public void addMarker(Marker marker) {
this.sessionFactory.getCurrentSession().save(marker);
}
#Override
public void deleteMarker(int markerId) {
this.sessionFactory.getCurrentSession().delete(this.getMarker(markerId));
}
#Override
public Marker getMarker(int markerId) {
return (Marker) this.sessionFactory.getCurrentSession().get(Marker.class, markerId);
}
#Override
public List<Marker> getAllMarkers() {
return this.sessionFactory.getCurrentSession().createQuery("from Marker").list();
}
}
MarkerServiceImplementation.java
package com.learnspring.projectfirst.service;
#Service
public class MarkerServiceImplementation implements MarkerServiceInterface {
#Autowired
private MarkerDaoImplementation markerDao;
#Transactional
public void addMarker(Marker marker) {
this.markerDao.addMarker(marker);
}
#Transactional
public void deleteMarker(int markerId) {
this.markerDao.deleteMarker(markerId);
}
#Transactional
public Marker getMarker(int markerId) {
return this.markerDao.getMarker(markerId);
}
#Transactional
public List<Marker> getAllMarkers() {
return this.markerDao.getAllMarkers();
}
}
And here is the file structure:
I understand that I should tell my program the database name and the columns using beans but I don't understand how. How can I link the java code to the beans? Sorry I pasted so much code, I just wanted to make sure you have everything needed. Thank you in advance!
This is the one you need: Spring Boot with MySQL
Refer this example : Spring MVC with JdbcTemplate Example
The annotations in your "Marker" class determine the MySQL table and column names (based on the class and class variable names). The tablename will be "marker", with the columns "id", "longitude", "latitude", "address".
You forgot the most important part in your code: your spring configuration. it determines how the SessionFactory instance will be initialized before being injected into your DAO class. Here you have to set an appropriate connection to the MySQL Server (e.g. via an JNDI Resource)