SpringBoot reads all properties as 0 - spring

Using SpringBoot, I'm having simple class
#Component
#PropertySource("server.properties")
public class CoreFacade {
private static final Logger log = Logger.getLogger(CoreFacade.class);
#Value("${chat.server.port}")
private int port;
private Server server;
public CoreFacade() {
log.info(String.valueOf(port));
server = new Server(port);
}
}
and under src/main/resources I got server.properties with
chat.server.port = 9999
Yet logging this ends up with
INFO 2018-08-20 21:48:02,878 [main]
com.example.chatserver.core.CoreFacade [] [] - 0
instead of 9999
Any ideas what could possibly go wrong here?

You annotated your class as a Spring component.
It prints 0 as the constructor is invoked by Spring before any dependency injection done for the bean.
What you need is to declaring the class as a configuration class such as :
#Configuration
#PropertySource("server.properties")
public class CoreFacade {
...
}
If it makes sense you could make Server a bean defined in the class such as :
#Value("${chat.server.port}")
private int port;
#Bean
public Server server() {
return new Server(port);
}

An alternative solution as the one proposed by davidxxx, is to mark your constructor as #Autowired :
#Component
#PropertySource("server.properties")
public class CoreFacade {
private static final Logger log = Logger.getLogger(CoreFacade.class);
private Server server;
#Autowired
public CoreFacade(
#Value("${chat.server.port}")
int port
) {
log.info(String.valueOf(port));
server = new Server(port);
}
}
You could also use #PostConstruct, which is called after #Value resolution :
#Component
#PropertySource("server.properties")
public class CoreFacade {
private static final Logger log = Logger.getLogger(CoreFacade.class);
#Value("${chat.server.port}")
private int port;
private Server server;
#PostConstruct
public void postConstruct() {
log.info(String.valueOf(port));
server = new Server(port);
}
}

Related

What is idea of bindings in spring boot rabbitmq?

I need to bind several exchanges with several routing keys to one single queue and be able to send messages by exchange and routing key and receive it by listening to queue by queue-name.
my code:
#Configuration
#RequiredArgsConstructor
#EnableConfigurationProperties(ExchangeConfig.class)
public class RabbitConfig {
private final ExchangeConfig exchangeConfig;
#Bean
public List<Binding> bindings() {
List<Binding> bindings = new ArrayList<>();
exchangeConfig.getExchangesWithKeys()
.forEach(exchangeWithKeys -> exchangeWithKeys.getRoutingKeys()
.forEach(key -> {
Exchange exchange = ExchangeBuilder.directExchange(exchangeWithKeys.getExchange()).build();
Queue queue = QueueBuilder.durable(exchangeConfig.getLogsQueue()).build();
Binding binding = BindingBuilder.bind(queue).to(exchange)
.with(key).noargs();
bindings.add(binding);
}));
return bindings;
}
}
config:
spring:
rabbitmq:
host: localhost
port: 5672
rabbitmq:
exchanges-with-keys:
- exchange: exchange1
routing-keys: exchange1.live, exchange1.after
- exchange: exchange2
routing-keys: exchange2.live, exchange2.after
- exchange: exchange3
routing-keys: exchange3.live, exchange3.after
logs-queue: log-messages_q
props:
#Data
#Component
#ConfigurationProperties(prefix = "rabbitmq")
public class ExchangeConfig {
private String logsQueue;
private List<ExchangeWithKeys> exchangesWithKeys;
#Data
public static class ExchangeWithKeys {
private String exchange;
private List<String> routingKeys;
}
}
listener:
#Component
#Slf4j
#RequiredArgsConstructor
public class LogsListener {
private final LogMessageEventProcessor logMessageEventProcessor;
#RabbitListener(queues = "${rabbitmq.logs-queue}")
public void onLiveEvent(LogMessageEvent event) {
log.info("Received log event message [{}]", event.getBody());
logMessageEventProcessor.processLogMessageEvent(event);
}
}
test:
#SpringBootTest
#ContextConfiguration(initializers = LogsListenerTest.Initializer.class)
class LogsListenerTest {
#Autowired
private RabbitTemplate template;
#ClassRule
private static final RabbitMQContainer container = new RabbitMQContainer("rabbitmq:3.7.25-management-alpine")
.withExposedPorts(5672, 15672).withQueue("log-messages_q");
#BeforeAll
private static void startRabbit() {container.start();}
#AfterAll
private static void stopRabbit() {
container.stop();
}
#Test
public void test() {
template.convertAndSend("exchange1", "exchange1.live", new LogMessageEvent());
template.receiveAndConvert("log-messages_q");
}
public static class Initializer implements
ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(#NotNull ConfigurableApplicationContext configurableApplicationContext) {
val values = TestPropertyValues.of(
"spring.rabbitmq.host=" + container.getContainerIpAddress(),
"spring.rabbitmq.port=" + container.getMappedPort(5672)
);
values.applyTo(configurableApplicationContext);
}
}
}
Everything above does not working.
So where should i put these bindings to make it work? Thanks.
What version are you using? The use of List<Binding> has been replaced by Declarables.
See https://docs.spring.io/spring-amqp/docs/current/reference/html/#collection-declaration
The documentation is a bit out of date, the admin declareCollections property was removed in 2.2.

Spring Boot #Component doesn't create Beans

Since according to the docs #Component registers beans for the Spring container I'm trying to create a simple example of dependency injection using the following code:
package pl.playground;
//...
#SpringBootApplication
public class PlaygroundApplication {
#Autowired
private static Building building;
public static void main(String[] args) {
building.setBuildingSize(12L);
System.out.println(building.monthlyHeatingCost());
}
}
package pl.playground.facade;
//...
#Component
public class Building {
private HeatingService service;
private Long buildingSize;
#Autowired
public Building(HeatingService service) {
this.service = service;
}
public Double monthlyHeatingCost() {
return service.getMonthlyHeatingCost(buildingSize);
}
// getters & setters...
}
package pl.playground.service;
public interface HeatingService {
Double getMonthlyHeatingCost(Long size);
}
package pl.playground.service;
//...
#Component
public class HeatingServiceImpl implements HeatingService {
private final Double CUBIC_PRICE = 2.3;
public HeatingServiceImpl() {}
#Override
public Double getMonthlyHeatingCost(Long size) {
return size * CUBIC_PRICE;
}
}
It builds and runs, but there is a NullPointerException at building.setBuildingSize(12L);. However the one below works without any issues:
//PlaygroundApplication.java
package pl.playground;
//...
#SpringBootApplication
public class PlaygroundApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
Building building = context.getBean(Building.class);
building.setBuildingSize(12L);
System.out.println(building.monthlyHeatingCost());
}
}
package pl.playground.config;
//...
#Configuration
public class Config {
#Bean
public Building building(HeatingService service) {
return new Building(service);
}
#Bean
public HeatingServiceImpl heatingServiceImpl() {
return new HeatingServiceImpl();
}
}
The rest is the same as before.
Why is #Component not creating Beans?
It is working the way I think it should when used inside a #Controller of a web app, does that make a difference? How does exactly #Bean and #Component differ?
What am I failing to understand?
EDIT
Consider the following scenario:
package pl.playground;
//...
#SpringBootApplication
public class ExampleApplication {
public static void main(String[] args) {
SpringApplication.run(ExampleApplication.class, args);
}
}
package pl.playground.controller;
//...
#Controller
public class Controller {
private Facade facade;
#Autowired
public Controller(Facade facade) {
this.facade = facade;
}
#GetMapping("/")
public String getIndexPage(Model model) {
return "index";
}
}
package pl.playground.facade;
//...
#Component
public class Facade {
private PostsService postService;
private UserService userService;
private TagService tagService;
#Autowired
public Facade(PostsService retrieve, UserService user, TagService tag) {
this.postService = retrieve;
this.userService = user;
this.tagService = tag;
}
//...
}
I don't need #Configuration here for it to work. That's my concern.
The problem with your code is that you are trying to #Autowire on a static field. You simply cannot do that. Look here: Can you use #Autowired with static fields?
It fails to work because the PlaygroundApplication class is not being created and managed by spring. The injection works only inside instances managed by spring. You can treat class annotated with #SpringBootApplication as configuration classes. Spring creates instances of those classes and injection works inside them but only on instance fields.
The second example shows the correct way to access spring beans from main method of the application.
Well. I used your original question and is working without any issues. #cezary-butler pointed out in the comments you can autowire into PlaygroundApplication but you can get hold of it easily in the static main method using context.getBean(Building.class)
#SpringBootApplication
public class PlaygroundApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context =
SpringApplication.run(PlaygroundApplication.class);
Building building = context.getBean(Building.class);
building.setBuildingSize(12L);
System.out.println(building.monthlyHeatingCost());
}
}
Here is the sample repo https://github.com/kavi-kanap/stackoverflow-63072236
TLDR;
A Spring context needs to be created before any bean can be injected. In the first scenario, just the fact of having a #SpringBootApplication decorator does not ensure a context in the scope of the class it decorates.
SpringApplication.run(ExampleApplication.class, args); instantiates a context (and e.g. a web server among other things)
var context = new AnnotationConfigApplicationContext(Config.class); instantiates a scoped context
Thus the first example had null inside of Building as there was no context with the bean to inject.

Spring Boot testing with Junit

Using Spring Boot 2.0.3.RELEASE
The goal of the test is have a service call a controller in the same app.
Here is the simplified setup I am trying
The app class
#SpringBootApplication
public class StartApp {
public static void main(String[] args) {
SpringApplication.run(StartApp.class, args);
}
}
The controller class
#RestController
public class EmpCtrl {
private static final Logger logger = LoggerFactory.getLogger(EmpCtrl.class);
#Autowired
private EmpDao empDao;
#RequestMapping(value = "/emp01", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
public List<Emp> findAllEmp01() {
logger.trace("running my.demo.controller.findAllEmp01");
List<Emp> emps = new ArrayList<>();
Iterable<Emp> results = this.empDao.findAll();
results.forEach(emp -> {emps.add(emp);});
return emps;
}
}
The service class
#Service
public class GetEmpSrv {
private static final Logger logger = LoggerFactory.getLogger(GetEmpSrv.class);
public void getEmps01(){
final String uri = "http://localhost:8080/emp01";
RestTemplate restTemplate = new RestTemplate();
String result = restTemplate.getForObject(uri, String.class);
logger.debug(result);
}
}
and the Junit class
#RunWith(SpringRunner.class)
#SpringBootTest(classes = StartApp.class)
public class GetEmpSrvTest01 {
#Test
public void doCall() {
GetEmpSrv getEmpSrv = new GetEmpSrv();
getEmpSrv.getEmps01();
}
}
This is being run inside Eclipse Oxygen.3a (4.7.3a)
in the console it appears Spring Boot is running .. ic the load of h2 db and /emp01 is being mapped however in the Failure Trace of Junit ic
I/O error on GET request for "http://localhost:8080/emp01": Connection refused: connect; nested exception is java.net.ConnectException: Connection refused: connect
This makes me think the embedded Tomcat is not running. When I start Spring normally /emp01 returns a JSON as expected.
My question is: Is this type of testing possible with Junit? If so what do I need to do to make it work?
In your test, please autowire TestRestTemplate. The reason is that your spring test will run on another port and the call to http://localhost:8080 will fail.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = StartApp.class)
public class GetEmpSrvTest01 {
#Autowired
TestRestTemplate testRestTemplate;
#Test
public void doCall() {
// without http://localhost:8080, testRestTemplate it will handle it for you
testRestTemplate.getForObject("/emp01", String.class);
}
}
Also you can expect a list of objects in your test:
List<Emp> emps = testRestTemplate.getForObject("/emp01", List.class);

How to load springboot properties?

I'm trying to load properties once in my springboot application.
Actually, I have created a class to do that :
#Configuration
#PropertySource(value = { "classpath:parameters.properties", "classpath:iot.properties" })
public class PropertiesHelper {
#Autowired
protected Environment env;
private static Environment properties;
#PostConstruct
public void init() {
properties = env;
}
public static String getProperty(final String propertyName) {
return properties.getProperty(propertyName);
}
}
This class work fine but this is not clean code (sonar hate my static variable).
So, how can I load in my springboot application all my properties correctly and only once ?
#PropertySource("classpath:parameters.properties")
#PropertySource("classpath:iot.properties")
public class PropertiesHelper {
#Value( "${Value.you.need}" )
private String valueYouNeed;
}
parameters.properties
Value.you.need=12345
Make it something like this, it should be work in your scenario.
An elegant solution
If you just want to eliminate the sonar alarm(sonar hate my static variable)
// application.properties
spring.profiles.active=test1010
// IOC config bean container
#Configuration
#Data
public class PropertiesConfig{
#Value("${spring.profiles.active:preline}")
private String profiles;
}
//Use
#Autowired
private PropertiesConfig propertiesConfig;
#GetMapping("/getPropertiesConfig")
public String getPropertiesConfig(){
return propertiesConfig.getProfiles();
}
I think the above scheme is a more elegant way~,Do you have a better solution?

Injecting Configuration Properties is not working in my test

I'm stuck! If I skip tests and deploy to tomcat auto wiring the configuration properties file works. In my test, it fails! I'm not sure what I'm missing.
Here is my setup:
Spring Boot v 1.2.5.RELEASE
Application.yml
git:
localRepo: './powershell-status-scripts/'
remoteRepo: 'https://github.com/...'
RepositoryProperties this class has getters and setters for the properties
#Configuration
#ConfigurationProperties(locations = "classpath:application.yml", prefix = "git", ignoreUnknownFields = false)
public class RepositoryProperties {
private String localRepo;
private String remoteRepo;
public RepositoryProperties() {
}
public String getLocalRepo() {
return localRepo;
}
public void setLocalRepo(String localRepo) {
this.localRepo = localRepo;
}
public String getRemoteRepo() {
return remoteRepo;
}
public void setRemoteRepo(String remoteRepo) {
this.remoteRepo = remoteRepo;
}
}
Application.java
#EnableAutoConfiguration
#EnableConfigurationProperties
#ComponentScan(basePackages = "com.sendash.admin")
#EnableJpaRepositories("com.sendash.admin.dao.jpa")
#EnableSwagger
public class Application extends SpringBootServletInitializer {
private static final Class<Application> applicationClass = Application.class;
private static final Logger log = LoggerFactory.getLogger(applicationClass);
public static void main(String[] args) {
SpringApplication.run(applicationClass, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(applicationClass);
}
}
GitService - Autowiring the properties works on tomcat!
#Service
#EnableConfigurationProperties
public class GitService {
#Autowired
private RepositoryProperties repositoryProperties;
public void updateLocalRepository() {
...
}
GitServiceTest this class fails on init because of a NPE. Properties is null.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = Application.class)
#Profile("test")
#TestExecutionListeners({ DependencyInjectionTestExecutionListener.class })
public class GitServiceTest {
#Autowired
private static GitService manager;
#Autowired
private static RepositoryProperties properties;
private static final String localRepoLocation = properties.getLocalRepo();
I do realize after pasting this that #EnableConfigurationProperties is on both the Application.java and the GitService.java class. Stopping the duplication does not fix the problem.
If you want to use Spring Boot in your tests, you should configure the tests accordingly. To do that, remove the ContextConfiguration and add the following:
#SpringApplicationConfiguration(classes = Application.class, initializers = ConfigFileApplicationContextInitializer.class)
This should enable injecting the configuration properties.
I did change my ContextConfiguration as suggested, but my main problem was trying to autowire a static field. It was static for #BeforeClass test setup logic so I needed to move things around a bit, but I got it working. Thanks for the suggestion.

Resources