I have been trying to get a very basic example of a custom PropertySource running in a Spring Application.
This is my PropertySource:
public class RemotePropertySource extends PropertySource{
public RemotePropertySource(String name, Object source) {
super(name, source);
}
public RemotePropertySource(String name) {
super(name);
}
public Object getProperty(String s) {
return "foo"+s;
}
}
It gets added to the ApplicationContext via an ApplicationContextInitializer:
public class RemotePropertyApplicationContextInitializer implements ApplicationContextInitializer<GenericApplicationContext> {
public void initialize(GenericApplicationContext ctx) {
RemotePropertySource remotePropertySource = new RemotePropertySource("remote");
ctx.getEnvironment().getPropertySources().addFirst(remotePropertySource);
System.out.println("Initializer registered PropertySource");
}
}
Now I created a simple Unit-Test to see if the PropertySource is used correctly:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = RemotePropertySourceTest.ContextConfig.class, initializers = RemotePropertyApplicationContextInitializer.class)
public class RemotePropertySourceTest {
#Autowired
private UnderTest underTest;
#Autowired
Environment env;
#Test
public void testContext() {
assertEquals(env.getProperty("bar"),"foobar");
assertEquals(underTest.getFoo(),"footest");
}
#Component
protected static class UnderTest {
private String foo;
#Autowired
public void setFoo(#Value("test")String value){
foo=value;
}
public String getFoo(){
return foo;
}
}
#Configuration
#ComponentScan(basePackages = {"test.property"})
protected static class ContextConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigurer() {
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
return configurer;
}
}
}
Accessing the value via the Environment gives me the correct result ("foobar"), but using the #Value-Annotation fails. As far as I have read in the documentation the PropertySourcesPlaceholderConfigurer in my Configuration should automatically pick up my PropertySource from the environment but apparently it does not. Is there something I am missing?
I know that accessing properties explicitly via the environment is preferrable but the existing application uses #Value-Annotations a lot.
Any help is greatly appreciated. Thanks!
To get value from property source with #Value you have to use ${} syntax:
#Autowired
public void setFoo(#Value("${test}")String value){
foo=value;
}
Take a look at official documentation.
Related
I am working within an environment that changes credentials every several minutes. In order for beans that implement clients who depend on these credentials to work, the beans need to be refreshed. I decided that a good approach for that would be implementing a custom scope for it.
After looking around a bit on the documentation I found that the main method for a scope to be implemented is the get method:
public class CyberArkScope implements Scope {
private Map<String, Pair<LocalDateTime, Object>> scopedObjects = new ConcurrentHashMap<>();
private Map<String, Runnable> destructionCallbacks = new ConcurrentHashMap<>();
private Integer scopeRefresh;
public CyberArkScope(Integer scopeRefresh) {
this.scopeRefresh = scopeRefresh;
}
#Override
public Object get(String name, ObjectFactory<?> objectFactory) {
if (!scopedObjects.containsKey(name) || scopedObjects.get(name).getKey()
.isBefore(LocalDateTime.now().minusMinutes(scopeRefresh))) {
scopedObjects.put(name, Pair.of(LocalDateTime.now(), objectFactory.getObject()));
}
return scopedObjects.get(name).getValue();
}
#Override
public Object remove(String name) {
destructionCallbacks.remove(name);
return scopedObjects.remove(name);
}
#Override
public void registerDestructionCallback(String name, Runnable runnable) {
destructionCallbacks.put(name, runnable);
}
#Override
public Object resolveContextualObject(String name) {
return null;
}
#Override
public String getConversationId() {
return "CyberArk";
}
}
#Configuration
#Import(CyberArkScopeConfig.class)
public class TestConfig {
#Bean
#Scope(scopeName = "CyberArk")
public String dateString(){
return LocalDateTime.now().toString();
}
}
#RestController
public class HelloWorld {
#Autowired
private String dateString;
#RequestMapping("/")
public String index() {
return dateString;
}
}
When I debug this implemetation with a simple String scope autowired in a controller I see that the get method is only called once in the startup and never again. So this means that the bean is never again refreshed. Is there something wrong in this behaviour or is that how the get method is supposed to work?
It seems you need to also define the proxyMode which injects an AOP proxy instead of a static reference to a string. Note that the bean class cant be final. This solved it:
#Configuration
#Import(CyberArkScopeConfig.class)
public class TestConfig {
#Bean
#Scope(scopeName = "CyberArk", proxyMode=ScopedProxyMode.TARGET_CLASS)
public NonFinalString dateString(){
return new NonFinalString(LocalDateTime.now());
}
}
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 config and it's #Import-ed by an annotation. I want the values on the annotation to be accessible by the config. Is this possible?
Config:
#Configuration
public class MyConfig
{
#Bean
public CacheManager cacheManager(net.sf.ehcache.CacheManager cacheManager)
{
//Get the values in here
return new EhCacheCacheManager(cacheManager);
}
#Bean
public EhCacheManagerFactoryBean ehcache() {
EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean();
ehCacheManagerFactoryBean.setShared(true);
return ehCacheManagerFactoryBean;
}
}
The annotation
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#Import(MyConfig.class)
public #interface EnableMyCaches
{
String value() default "";
String cacheName() default "my-cache";
}
How would I get the value passed below in my config?
#SpringBootApplication
#EnableMyCaches(cacheName = "the-cache")
public class MyServiceApplication {
public static void main(String[] args) {
SpringApplication.run(MyServiceApplication.class, args);
}
}
Use simple Java reflection:
Class c = MyServiceApplication.getClass();
EnableMyCaches enableMyCaches = c.getAnnotation(EnableMyCaches.class);
String value = enableMyCaches.value();
Consider how things like #EnableConfigurationProperties are implemented.
The annotation has #Import(EnableConfigurationPropertiesImportSelector.class) which then imports ImportBeanDefinitionRegistrars. These registrars are passed annotation metadata:
public interface ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}
You can then get annotation attributes from annotation metadata:
MultiValueMap<String, Object> attributes = metadata
.getAllAnnotationAttributes(
EnableMyCaches.class.getName(), false);
attributes.get("cacheName");
I'm trying to get some properties from the command line when I start my app. I really try a lot of solution but nothings is working. I'll only use #Value as a last solution
java -jar myjar.jar --test.property=something
Here's some classes
Main class
#SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplicationBuilder builder = new SpringApplicationBuilder(TestApplication.class);
builder.run(args);
}
}
Config class
#Configuration
#EnableConfigurationProperties(StringProperties.class)
public class StringConfiguration {
#Autowired
private StringProperties stringProperties;
#Bean(name = "customBeanName")
public List<String> properties() {
List<String> properties = new ArrayList<>();
properties.add(stringProperties.getString());
return properties;
}
}
Properties class
#Component
#ConfigurationProperties("test")
public class StringProperties {
private String property;
public String getString() {
return property;
}
}
Ops.....I have tested your code.It is setProperty that missed in StringProperties.
public void setProperty(String property) {
this.property = property;
}
I would like to implement Factory pattern in my project..i have gone through online resources and I came to know that spring ServiceLocatorFactoryBean should be implemented instead of normal java factory pattern....
i have followed this link but it is explained in xml based....can any one tell me how to do it using annotations based Factory pattern??
Spring Java Configuration ref guide #Configuration
Interface Parser.class
public interface Parser {
void parse(String str);
}
Implementation for above interface.
JsonParser.java
public class JsonParser implements Parser {
#Override
public void parse(String str) {
System.out.println("JsonParser.parse::" + str);
}
}
XMLParser.java
public class XMLParser implements Parser{
#Override
public void parse(String str) {
System.out.println("XMLParser.parse :: " + str);
}
}
ParserFactory.java actual Factory interface.
public interface ParserFactory {
public Parser getParser(ParserType parserType);
}
ParseType.java enum to specify parsing types(avoid typos and safe)
public enum ParserType {
JSON("jsonParser"), XML("xmlParser");
private final String value;
ParserType(String input) {
this.value = input;
}
public String getValue() {
return this.value;
}
#Override
public String toString() {
return this.value;
}
}
ParseService.java , where Business logic implemeted.
#Service
public class ParserService {
#Autowired
private ParserFactory parserFactory;
public void doParse(String parseString, ParserType parseType) {
Parser parser = parserFactory.getParser(parseType);
System.out.println("ParserService.doParse.." + parser);
parser.parse(parseString);
}
}
Finally AppConfig.java Spring java configuration class, where all of my beans registered as container managed beans.
#Configuration
#ComponentScan(basePackages = {"<Your Package Name>"})
public class AppConfig {
#Bean
public FactoryBean serviceLocatorFactoryBean() {
ServiceLocatorFactoryBean factoryBean = new ServiceLocatorFactoryBean();
factoryBean.setServiceLocatorInterface(ParserFactory.class);
return factoryBean;
}
#Bean(name = "jsonParser")
#Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public JsonParser jsonParser() {
return new JsonParser();
}
#Bean(name = "xmlParser")
#Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public XMLParser xmlParser() {
return new XMLParser();
}
}
Now autowire ParserService bean in either controller or test classs, and invoke parese(String, ParseType) method to test.
Here is my test.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = AppConfig.class)
public class ServiceLocatorFactoryExample {
#Autowired
private ParserService parserService;
#Test
public void testParserFactory() {
parserService.doParse("Srilekha", ParserType.JSON);
parserService.doParse("Srilekha", ParserType.XML);
}
}
Look this complete example: Serevice Locator factory
It helps me to understand how it works using spring boot.