I'm new in Spring, and want to figure out how to inject value from properties file.
I have component with Value annotation:
#Component
public class ConfigProp {
private final String login;
public ConfigProp(#Value("${login}") String login) {
this.login = login;
}
#Override
public String toString() {
return "ConfigProp{" +
"login='" + login + '\'' +
'}';
}
}
then, I have main class:
public class App {
public static void main(String[] args) {
var context = new AnnotationConfigApplicationContext(ConfigProp.class);
context.addBeanFactoryPostProcessor(new PropertySourcesPlaceholderConfigurer());
var cp = context.getBean(ConfigProp.class);
System.out.println(cp);
}
}
Additionally I have application.properties file
with content:
login=some_login
And when I launching my app I'm getting ConfigProp{login='${login}'}
Yes, I know, I may annotate ConfigProp with #PropertySource(path-to-properties) annotation, but it's not a good solution, because in the Prod I may want to use another properties file,
How can I resolve this issue?
Related
I have a class annotated with #Component which is use to initialze application.yml config properties. Service classe is using configuration property. But sometime my Service class instance created before the Configuration class and I get null property value in service class, Its random not specific pattern.
Configuration Initializer class..
#Component
public class ConfigInitializer implements InitializingBean {
private static final Logger log = LoggerFactory.getLogger(ConfigInitializer.class);
#Autowired
ProxyConfig proxyConfig;
/*#PostConstruct
public void postConstruct(){
setProperties();
}
*/
#Override
public void afterPropertiesSet() {
setProperties();
}
private void setSystemProperties(){
log.debug("Setting properties...");
Properties props = new Properties();
props.put("PROXY_URL", proxyConfig.getProxyUrl());
props.put("PROXY_PORT", proxyConfig.getProxyPort());
System.getProperties().putAll(props);
}
}
#Component
#ConfigurationProperties(prefix = "proxy-config")
public static class ProxyConfig {
private String proxyUrl;
private String proxyPort;
public String getProxyUrl() {
return proxyUrl;
}
public void setProxyUrl(String proxyUrl) {
this.proxyUrl = proxyUrl;
}
public String getProxyPort() {
return proxyPort;
}
public void setProxyPort(String proxyPort) {
this.proxyPort = proxyPort;
}
}
Service Class..
#Service("receiverService")
public class ReceiverService {
private static final Logger logger = LoggerFactory.getLogger(ReceiverService.class);
private ExecutorService executorService = Executors.newSingleThreadExecutor();
#Autowired
public ReceiverService() {
initClient();
}
private void initClient() {
Future future = executorService.submit(new Callable(){
public Object call() throws Exception {
String value = System.getProperty("PROXY_URL"); **//Here I am getting null**
logger.info("Values : " + value);
}
});
System.out.println("future.get() = " + future.get());
}
}
Above Service class get null values String value = System.getProperty("PROXY_URL")
When I use #DependsOn annotation on Service class, it works fine.
In my little knowledge, I know Spring does not have specific order of bean creation.
I want to know If I use #Configuration instead of #Component on ConfigInitializer class like below, Will spring initialize ConfigInitializer
class before other beans ?.
#Configuration
public class ConfigInitializer implements InitializingBean {
//code here
}
I have a custom defined key-value pair in my Spring Boot application.properties file:
jwt.secret=secretkey
I've created a configuration class for this property in the same directory as the runner class:
#Configuration
#ConfigurationProperties(prefix = "jwt")
#PropertySource("classpath:application.properties")
public class JwtProperties {
/**
* Secret key used to sign JSON Web Tokens
*/
private String secret = "";
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
}
As expected, I can access this value in my main Spring runner class, ServerApplication.java using the #Value annotation:
#SpringBootApplication
#EnableConfigurationProperties(JwtProperties.class)
public class ServerApplication implements CommandLineRunner {
#Value("${jwt.secret}")
String test;
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
// correctly prints "test is: secretkey"
System.out.println("test is " + test);
}
}
I have a class /security/JwtClient.java where I'd like to be able to use this jwt.secret property, but I can't get the #Value annotation to work (it always results in a null String):
#Component
public class JwtClient {
#Value("${jwt.secret}")
private String secretKey;
public String buildJWT(Customer customer) {
// incorrectly prints "secret key: null"
System.out.println("secret key: " + secretKey);
// attempts to build JSON Web Token here but secret key is missing
}
}
I've read many StackOverflow questions on this topic, but almost all of them seem to assume that the #Value annotation will work properly on a #Component-annotated class. What am I missing here?
Spring's dependency injection mechanism looks convenient because I don't have to create classes manually from root class and pass them to deeper classes. But what if I have 3 separate threads which do the same job and have some thread-specific data which should be spread through the class hierarchy?
Consider the following App:
#SpringBootApplication
public class App {
public static void main(String[] args) throws IOException {
Properties props = new Properties();
props.load(App.class.getResourceAsStream("/application.properties"));
String[] domains = props.getProperty("domains").split(",");
ConfigurableApplicationContext context = SpringApplication.run(App.class);
ConfigurableListableBeanFactory factory = context.getBeanFactory();
for(String domain : domains) {
DomainListener listener = factory.getBean(DomainListener.class);
listener.setDomain(domain);
listener.start();
}
}
}
#Service
#Scope("prototype")
public class DomainListener extends Thread {
#Autowired
EventPublisher publisher;
String domain;
public void setDomain(String domain) {
this.domain = domain;
}
#Override
public void run() {
System.out.println("starting '" + domain + "' domain thread...");
publisher.setDomain(domain); // <-- is this the best way to initialize bean?
// some processing
publisher.publish("Some event");
}
}
#Component
#Scope("prototype")
public class EventPublisher {
String domain;
public void setDomain(String domain) {
this.domain = domain;
}
public void publish(String msg) {
System.out.println("publishing '" + msg + "' to '" + domain + "' domain");
}
}
application.properties file content
domains = domain1.example.com,domain2.example.com,domain3.example.com
DomainListener and EventPublisher classes can't be initialized with domain value automatically by Spring, thus I have to call setDomain method and pass the domain value manually, first during DomainListener thread construction, then in DomainListener.run() to initialize EventPublisher with domain value. Would I need to pass the value even deeper, for example from EventPublisher to some other class, say MessageBuilder (let's assume it requires domain value), then I would have to call MessageBuilder.setDomain() instance method from EventPublisher constructor (as it is the same domain thread).
Can this be improved somehow?
I can't make method level validation right. Or I don't understand how it works.
My application class is below. Very simple. It contains MethodValidationPostProcessor bean definition. It also runs Greeter service.
#SpringBootApplication
public class App implements CommandLineRunner {
private final Greeter greeter;
public App(Greeter greeter) {
this.greeter = greeter;
}
public static void main(String[] args) {
new SpringApplicationBuilder().main(App.class).sources(App.class).web(false).run(args).close();
}
#Bean
public org.springframework.validation.beanvalidation.MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
#Override
public void run(String... args) throws Exception {
final Input input = new Input();
input.setName("j");
final String messageFromInput = greeter.getMessageFromInput(input);
final String messageFromString = greeter.getMessageFromString("j");
}
}
Greeter service below. Here I do expect to validate input and output.
#Service
#Validated
public class Greeter {
String getMessageFromInput(#Valid #NotNull Input name) {
return "[From Input] Greetings! Oh mighty " + name + "!";
}
String getMessageFromString(#Size(min = 4) String name) {
return "[From String] Greetings! Oh mighty " + name + "!";
}
}
Input DTO is very simple as well.
public class Input {
#NotEmpty
#Size(min = 3)
private String name;
// Getters, setters and toString ommited.
}
Since the name in both cases, direct String and DTO, is only one letter I expect this setup to throw exception. Unfortunately, nothing happens and application completes successfully. It works with controller's methods. But I would like it to work with any bean's methods.
You are injecting your Greeter bean as a constructor argument into the class annotated with #SpringBootApplication which is a #Configuration class. To satisfy that dependency the Greeter is created very early on in the startup process of the ApplicationContext and as such will remove it as a candidate for proxy creation.
Instead of injecting it as a constructor argument move your CommandLineRunner logic to a #Bean annotated method and simply inject the Greeter as a dependency. This will delay the creation of the bean and as such make it available for proxying.
#Bean
public CommandLineRunner runner(Greeter greeter) {
return new CommandLineRunner() {
#Override
public void run(String... args) throws Exception {
final Input input = new Input();
input.setName("j");
final String messageFromInput = greeter.getMessageFromInput(input);
final String messageFromString = greeter.getMessageFromString("j");
}
};
}
Another thing is that your methods of the Greeter should be, due the the nature of proxies, public.
I have Spring service, which is actually actor, it is received info, but I cant pass it to another Spring service, because injection fails.
#Service("mailContainer")
#Scope("prototype")
#Component
public class MailContainer extends UntypedActor {
private final LoggingAdapter LOG = Logging.getLogger(getContext().system(), this);
private Mail value;
private List<Mail> mailList = new ArrayList<Mail>();
private Integer size;
#Autowired
#Qualifier("springService")
private SpringService springService;
//#Autowired
public void setSpringService(SpringService springService) {
this.springService = springService;
}
public MailContainer(Mail value) {
this.value = value;
}
#Override
public void onReceive(Object message) throws Exception {
// LOG.debug("+ MailContainer message: {} ", message);
if (message instanceof Mail) {
value = (Mail) message;
System.out.println("MailContainer get message with id " + value.getId());
System.out.println("With time " + value.getDateSend());
//getSender().tell(value, getSelf()); //heta uxarkum
//this.saveIt(value);
springService.add(value);
}
}
and second service
#Service("springService")
//#Component
#Scope("session")
public class SpringService {
private List<Mail> mailList = new ArrayList<Mail>();
public void add(Mail mail) {
System.out.println("Saving mail from Spring " +mail.getId());
mailList.add(mail);
}
public List<Mail> getMailList() {
return mailList;
}
}
Spring config, this is from akka spring example
#Configuration
//#EnableScheduling
//EnableAsync
#ComponentScan(basePackages = {"com"}, excludeFilters = {
#ComponentScan.Filter(Configuration.class)})
//#ImportResource("classpath:META-INF/spring/spring-data-context.xml")
//#EnableTransactionManagement
//#EnableMBeanExport
//#EnableWebMvc
public class CommonCoreConfig {
// the application context is needed to initialize the Akka Spring Extension
#Autowired
private ApplicationContext applicationContext;
/**
* Actor system singleton for this application.
*/
#Bean
public ActorSystem actorSystem() {
ActorSystem system = ActorSystem.create("AkkaJavaSpring");
// initialize the application context in the Akka Spring Extension
SpringExtProvider.get(system).initialize(applicationContext);
return system;
}
}
So, how I can inject just another Spring service?????????
Based on our discussions, I think it is due to the way you create the MailContainer actor. You aren't using the SpringExtProvider and instead are using Props.create directly. This means that Spring doesn't get the opportunity to perform dependency injection on your new actor.
Try changing this code:
#Override
public void preStart() throws Exception {
System.out.println("Mail collector preStart: {} ");
getContext().actorOf(Props.create(MailContainer.class, result), "one");
}
to use the the SpringExtProvider like this:
#Override
public void preStart() throws Exception {
System.out.println("Mail collector preStart: {} ");
getContext().actorOf(SpringExtProvider.get(getContext().system()).props("mailContainer"), "one");
}
This way you are asking the Spring extension to create the new actor and inject any required dependecnies.