I have a java configuration where I create bean using some properties, defined in application.properties. For one of them I have a default value which is pretty long, so I extracted this value to a public static final String field of this configuration, now I want to make #Value use it as a default value.
So eventually I want something like this:
#Configuration
public class MyConfiguration {
public static final String DEFAULT_PROPERTY_VALUE = "long string...";
#Bean("midPriceDDSEndpoint")
public DistributedDataSpace<Long, MidPriceStrategy> midPriceDDSEndpoint(
#Value("${foo.bar.my-property:DEFAULT_PROPERTY_VALUE}") String myPropertyValue) {
... create and return bean...
}
}
However by spring doesn't my field, so I am curious if I can somehow make it lookup it.
One way to fix this, is to access this static field through the configuration bean: #Value(${foo.bar.my-property:#{myConfigurationBeanName.DEFAULT_PROPERTY_VALUE}}), but using this approach makes constructor unreadable, because Value annotation then takes a lot of space(as property name and configuration bean name is longer then in this example). Is there any other way to make spring use static field as a default value for property?
I would do
#Value("${foo.bar.my-property:" + DEFAULT_PROPERTY_VALUE + "}")
#Vyncent's answer is limited in scope because it only works with publicly accessible static constants, since annotation attributes must be compile-time constants. To invoke a static method, use the following:
#Value("${demo.parallelism:#{T(java.lang.Runtime).getRuntime().availableProcessors()}}")
private int parallelism;
This sets parallelism = demo.parallelism JVM variable or gets the number of processors dynamically.
You may just want to inject Environment, and get the value as default like so:
#Configuration
public class MyConfiguration {
public static final String DEFAULT_PROPERTY_VALUE = "long string...";
#Autowired
private Environment env;
#Bean("midPriceDDSEndpoint")
public DistributedDataSpace<Long, MidPriceStrategy> midPriceDDSEndpoint() {
String myPropertyValue = env.getProperty("foo.bar.my-property", DEFAULT_PROPERTY_VALUE);
}
}
I personally think that's a little bit more readable...
I'm not 100% sure, but I think it's not possible. The real question here is why you would need to do something like that? What is the use case? You can always make a simple workaround like
private String getMyPropertyValue() {
return myPropertyValue.equals("some_explicitly_defined_default_value") ? DEFAULT_PROPERTY_VALUE : myPropertyValue;
}
Related
Spring will automatically bind properties defined in application.properties or application.yml to fields defined in #ConfigurationProperties-annotated classes. For instance, in my application.properties I can have:
fizz.buzz=35
fizz.foo=hello
And in my Java code I can have:
#ConfigurationProperties("fizz")
public class FizzProperties {
private Integer buzz;
private String foo;
// ...
}
And at runtime FizzProperties#buzz will get a value of 35 injected into it and FizzProperties#foo will have a value of "hello" injected into it.
I'm wondering what the naming convention is for camel-cased Java fields, and also for hyphens ("-") and periods (".") used in the properties files. For instance, if I had:
fizz.whistle-feather=true
fizz.baz.boo=always
What would their corresponding Java fields need to look like in order for Spring to map and inject them properly?
public class Baz {
private String boo;
}
#ConfigurationProperties("fizz")
public class FizzProperties {
private Integer whistleFeather; // correct?
private Baz baz; // correct?
// ...
}
Are my assumptions correct here or misled (and if misled, how so)? I can't find this explained in the Spring docs.
As stated in spring-boot docs, it uses "relaxed binding", so both properties "whistle-feather" and "whistleFeather" will be mapped to your private Integer whistleFeather, but it's recommended, when possible, to store properties in lower-case kebab format, such as fizz.whistle-feather=10.
So your first case is correct.
The second case is also correct, because dots are used as delimiters in application.properties, while YAML file uses as delimiters both dots and colon.
You also may define nested properties as nested classes to store them in one place like this:
#ConfigurationProperties("fizz")
public class FizzProperties {
private Integer whistleFeather;
private Baz baz;
// getters, setters
public static class Baz {
private String boo;
// getters, setters
}
}
Take a look here for more info about spring-boot properties binding and examples.
I could do SomeSpringService.class.getSimpleName() and lowercase the first letter to get the default bean name, but I'm wondering if there is a simple Spring function that could find this for me? Simple meaning I'm not getting the application context, or building a factory, or such.
Thank you
Update: current hack
#Service
public class ServiceSelector
{
#Autowired
Map<String, IThing> implementations;
public SomethingInterface getDecisionStrategy(AnEnum type)
{
SomethingInterface something;
switch (type)
{
case ONE:
implementation = findInstance(Impl1.class);
break;
case TWO:
implementation = findInstance(Impl2.class);
break;
default:
implementation = findInstance(Impl2.class);
}
return implementation;
}
private SomethingInterface findInstance(Class clazz)
{
String className = clazz.getSimpleName();
String defaultBeanName = Character.toLowerCase(className.charAt(0)) + className.substring(1);
return implementations.get(defaultBeanName);
}
}
your bean can implement BeanNameAware, so you must add setBeanName(String beanName), in your class. Spring IoC will return your bean name.
For example:
public class Control implements BeanNameAware{
private String beanName;
#Override
public void setBeanName(String s) {
this.beanName = beanName;
}
}
No; you're basically asking to invert a map. Beans can be registered with arbitrary and even multiple names, and the name can come from a variety of places at runtime--the class name, a component annotation, an #Configuration method, XML, and more. The only way to find the name(s) given a specific object would be to enumerate all the beans and find which entries match.
Update: Based on your code sample, it looks like you're trying to go a very long way around two simpler patterns. The more direct implementation of your current approach is to just use the class to ask for the bean directly:
#Autowired ApplicationContext ctx;
...
implementation = ctx.getBean(Impl2.class);
However, this seems like a case for Replace Conditional with Polymorphism: While you've phrased your question in general terms, it's usually the case that it's appropriate either for the enum to know the class (so the switch is replaced by strategy.getImplementingClass()) or for the services to know information directly (see, for example, how Converter instances identify the objects they can operate on to their users) and for you to get a Set<IThing> and iterate over them to query for a suitable match.
org.springframework.beans.factory.ListableBeanFactory#getBeanNamesForType(java.lang.Class<?>)
org.springframework.beans.factory.BeanFactoryUtils#beanNamesForTypeIncludingAncestors(org.springframework.beans.factory.ListableBeanFactory, java.lang.Class<?>)
I see this scattered throughout code base:
#RequestMapping(value = "myValue")
I would prefer to use something like this:
#RequestMapping(value = Constants.myValue)
It seems to break DRY using the actual String value within #RequestMapping instead of constant.
But is this good code practice? Should I use an enum instead?
I may need to use Constants.myValue elsewhere in the code base.
Should I use an enum instead?
You can't. Annotation variables must be compile-time constants. Enums and String literals both are, but you can't create an enum that is a String and #RequestMapping needs a String (and if your enum has a method that returns a String or a String field, that's not a compile-time constant). Since there are multiple rounds of annotation processing it does work when the constant is in another class.
That said: yes, I'd say using a dedicated constants class (perhaps several, for different types of constants) is a good practice that I use whenever I can (and it works with annotations as long as the constant a) is not defined inside the same compilation unit that has the annotation and b) is initialized in the declaration (as opposed to a static initializer block)).
Here's an example:
Controller
#Controller #RequestMapping(value = Mappings.CUSTOMER_PAGE)
public class CustomerPageController{
// methods here
}
Constants class
public static final class Mappings{
private Mappings(){}
public static final String CUSTOMER_PAGE = "path/to/customer/page"
// more constants
}
And here are some versions that won't work:
a)
#Controller #RequestMapping(value = CUSTOMER_PAGE)
public class CustomerPageController{
private static final String CUSTOMER_PAGE = "path/to/customer/page";
}
This won't compile because the annotation references a constant inside the class it annotates. This can't work because during compilation, annotations are processed in a separate round before the rest of the code, while the class needs the annotation to already be processed for compilation (i.e. there's a circular dependency between annotation and constant)
b)
public static final class Mappings{
private Mappings(){}
public static final String CUSTOMER_PAGE;
static{
CUSTOMER_PAGE = "path/to/customer/page"
}
// more constants
}
Although this is a static final field, it is not a compile-time constant, hence it can't be used as an annotation parameter
Suppose, we have 2 classes - Main and MainDependency. The second class is used only by Main, and the purpose of using IoC is constructing an instance of Main class.
MainDependency class has a field of integer type. This field is not required to be set (or, let's assume it should always have a default value if nothing else is specified).
The problem: what's the most correct way to set the integer field? One way I see is creating similar field inside my Module and then using that value inside configure module. But I feel it's a wrong way.
Please, share your experience. Thx in advance.
I think you mainly have two options:
1) Inject it using constant binding. The value of MY_CONSTANT could be passed to Module at instantiation time; could be taken from a system property, or maybe some other way.
class MainDependency{
#Inject
public MainDependency(#Named("myConst") int myConst){
//...
}
}
class Module extends AbstractModule{
public void configure(){
bindConstant().annotatedWith(Names.named("myConst").to(MY_CONSTANT);
}
}
2) Use assisted inject to create a factory which will take your value as a parameter and return an instance of MainDependency:
interface MainDependencyFactory{
MainDependency create(int myConst);
}
class MainDependency{
#Inject
public MainDependency(#Assisted int myConst){
//..
}
}
class Module extends AbstractModule{
public void configure(){
bind(MainDependencyFactory.class).toProvider(
FactoryProvider.newFactory(MainDependencyFactory.class, MainDependency.class));
}
}
//to use the above, instantiate your factory (or inject it somewhere)
MainDependencyFactory f = injector.getInstance(MainDependencyFactory.class);
//Then you can create MainDependency with any value
MainDependency md = f.create(MY_CONSTANT);
Note that with assisted inject you don't need to implement MainDependencyFactory. Guice will create it for you.
We have a situation where we provide an external configuration in form of a Map to our running programs. I have found that JSR-330 Dependency Injection gives a much cleaner way to use that configuration map in the code instead of passing the map around or to use JNDI to get it.
#Inject #Named("server.username") String username;
lets the JSR-330 implementation fill in this field automatically.
With Guice I can set the value with
bindConstant().annotatedWith(Names.named(key)).to(value);
I would like to be able to do the same in Weld (bind "server.username" to e.g. "foobar") and I understand that the mechanism most likely is beans.xml, but I would prefer a simple "feed this map to Weld, please" code alternative. What would be a good way to do this?
EDIT 2013-10-16: After looking into Dagger which works at compile time and not runtime, I found that with us usually having 10-20 per program we could live with having a #Provider method for each configuration string which then looks up in the configuration map. This allows for method specific behavior (including default values), ability to provide javadoc, and ability to put all these methods in the same class. Also it works well with Weld out of the box. I am considering writing a fuller explanation in a blog entry.
I'd like that bounty now please. Figuring this out taught me quite a bit about the innards of WELD, and here's the most interesting lesson: #Named is a qualifier, and must be treated as such if you are going to be able to match against it.
I do have a warning for you: If you are missing any values in your app, it will fail at deploy or load time. This may be desirable for you, but it does specifically mean that "default" values are not possible.
The injection point is specified exactly as you have above, and here's the extension code needed to make it work:
#ApplicationScoped
public class PerformSetup implements Extension {
Map<String, String> configMap;
public PerformSetup() {
configMap = new HashMap<String, String>();
// This is a dummy initialization, do something constructive here
configMap.put("string.value", "This is a test value");
}
// Add the ConfigMap values to the global bean scope
void afterBeanDiscovery(#Observes AfterBeanDiscovery abd, BeanManager bm) {
// Loop through each entry registering the strings.
for (Entry<String, String> configEntry : configMap.entrySet()) {
final String configKey = configEntry.getKey();
final String configValue = configEntry.getValue();
AnnotatedType<String> at = bm.createAnnotatedType(String.class);
final InjectionTarget<String> it = bm.createInjectionTarget(at);
/**
* All of this is necessary so WELD knows where to find the string,
* what it's named, and what scope (singleton) it is.
*/
Bean<String> si = new Bean<String>() {
public Set<Type> getTypes() {
Set<Type> types = new HashSet<Type>();
types.add(String.class);
types.add(Object.class);
return types;
}
public Set<Annotation> getQualifiers() {
Set<Annotation> qualifiers = new HashSet<Annotation>();
qualifiers.add(new NamedAnnotationImpl(configKey));
return qualifiers;
}
public Class<? extends Annotation> getScope() {
return Singleton.class;
}
public String getName() {
return configKey;
}
public Set<Class<? extends Annotation>> getStereotypes() {
return Collections.EMPTY_SET;
}
public Class<?> getBeanClass() {
return String.class;
}
public boolean isAlternative() {
return false;
}
public boolean isNullable() {
return false;
}
public Set<InjectionPoint> getInjectionPoints() {
return it.getInjectionPoints();
}
#Override
public String create(CreationalContext<String> ctx) {
return configValue;
}
#Override
public void destroy(String instance,
CreationalContext<String> ctx) {
// Strings can't be destroyed, so don't do anything
}
};
abd.addBean(si);
}
}
/**
* This is just so we can create a #Named annotation at runtime.
*/
class NamedAnnotationImpl extends AnnotationLiteral<Named> implements Named {
final String nameValue;
NamedAnnotationImpl(String nameValue) {
this.nameValue = nameValue;
}
public String value() {
return nameValue;
}
}
}
I tested that this worked by making a WELD-SE app:
#ApplicationScoped
public class App {
#Inject
#Parameters
List<String> parameters;
#Inject
#Named("string.value")
String stringValue;
public void printHello(#Observes ContainerInitialized event) {
System.out.println("String Value is " + stringValue);
}
}
Lastly, don't forget /META-INF/services/javax.enterprise.inject.spi.Extension, replacing weldtest with the classpath you use:
weldtest.PerformSetup
That should make all of this work. Let me know if you run into any difficulties and I'll send you my test project.
Not all that interested in the bounty, but I'll take it if it's still on the table. This is VERY similar to some code I'm using at $DAYJOB, and so this isn't theory, it's what I use in production code, but modified to protect the guilty. I haven't tried compiling the modified code, so be warned that I may have made some errors in changing names and such, but the principles involved here have all been tested and work.
First, you need a Value Holder Qualifier. Use #Nonbinding to keep WELD from matching ONLY to qualifiers with identical values, since we want all values of this particular qualifier to match a single injection point. By keeping the qualifier and value in the same annotation, you can't just "forget" one of them by accident. (KISS principle)
#Qualifier
#Retention(RUNTIME)
#Target({METHOD, FIELD, PARAMETER, TYPE})
public #interface ConfigValue {
// Excludes this value from being considered for injection point matching
#Nonbinding
// Avoid specifying a default value, since it can encourage programmer error.
// We WANT a value every time.
String value();
}
Next, you need a producer method which knows how to get the Map. You should probably have a Named bean which holds the producer method, so you can either explicitly initialize the value by using getters/setters, or else have the bean initialize it for you.
We must specify a blank value for the qualifier on the producer method to avoid compile time errors, but it's never used in practice.
#Named
public class ConfigProducer {
//#Inject // Initialize this parameter somehow
Map<String,String> configurationMap;
#PostConstructor
public void doInit() {
// TODO: Get the configuration map here if it needs explicit initialization
}
// In general, I would discourage using this method, since it can be difficult to control exactly the order in which beans initialize at runtime.
public void setConfigurationMap(Map<String,String> configurationMap) {
this.configurationMap = configurationMap;
}
#Produces
#ConfigValue("")
#Dependent
public String configValueProducer(InjectionPoint ip) {
// We know this annotation WILL be present as WELD won't call us otherwise, so no null checking is required.
ConfigValue configValue = ip.getAnnotated().getAnnotation(ConfigValue.class);
// This could potentially return a null, so the function is annotated #Dependent to avoid a WELD error.
return configurationMap.get(configValue.value());
}
}
Usage is simple:
#Inject
#ConfigValue("some.map.key.here")
String someConfigValue;
It may be possible to implement this as an #Dependent Producer method that itself injects an #InjectionPoint which would allow you to reflect upon the field you're being injected into -- this would let you peek into a custom annotation (not a qualifier) member on the field to figure out the val you want to return
#Inject #ConfigMapQualifier #Val("user.name") String user;
...
#Produces #ConfigMapQualifier configProducr(...) {
...
#Inject InjectionPoint ip;
// use e.g. ip/getJavaMember() then reflection to figure out the #Val value membr.
Would implementing a custom Weld InjectionServices not be an option here ?
what about
#Resource(name = "server.username", type = java.lang.String.class)
private String injectTo;
Javadoc: http://download.oracle.com/javase/6/docs/api/javax/annotation/Resource.html