Guice and general application configuration - java

For a monitoring software written in Java I consider using Google Guice as DI provider. The project needs to load its configuration from an external resource (file or database). The application is designed to run in standalone mode or in a servlet container.
At the moment the configuration does not contain bindings or parameters for dependency injection, only some global application settings (JDBC connection definitions and associated database management/monitoring objects).
I see two options:
to use another library, for example Apache Commons Configuration, which supports file and JDBC configuration sources (and many other)
or
to use a file based addon for Guice like guice-xml-config to store the application options (this would allow to configure the DI part later if it becomes necessary).
Would you recommend to use Guice for both tasks, or keep the general application configuration separate from the dependency injection? Which advantages and disadvantages would you consider the most important ones?

It's straightforward to slurp a property file in a Guice module:
public class MyModule extends AbstractModule {
#Override
protected void configure() {
try {
Properties properties = new Properties();
properties.load(new FileReader("my.properties"));
Names.bindProperties(binder(), properties);
} catch (IOException ex) {
//...
}
}
}
Later it's easy to switch from Properties to other config sources.
[Edit]
BTW, you can get the injected properties by annotating it with #Named("myKey").

Check the governator library:
https://github.com/Netflix/governator/wiki/Configuration-Mapping
You will get a #Configuration annotation and several configuration providers. In code it helps to see where is You configuration parameters used:
#Configuration("configs.qty.things")
private int numberOfThings = 10;
Also, You will get a nice configuration report on startup:
https://github.com/Netflix/governator/wiki/Configuration-Mapping#configuration-documentation

Try Guice configuration available on maven central, it's support Properties, HOCON and JSON format.
You can inject properties from file application.conf to your service as :
#BindConfig(value = "application")
public class Service {
#InjectConfig
private int port;
#InjectConfig
private String url;
#InjectConfig
private Optional<Integer> timeout;
#InjectConfig("services")
private ServiceConfiguration services;
}
You must install the modules ConfigurationModule as
public class GuiceModule extends AbstractModule {
#Override
protected void configure() {
install(ConfigurationModule.create());
requestInjection(Service.class);
}
}

I ran into the same problem in my own project. We had already chosen Guice as DI-framework and to keep things simple wanted to use it also with configuration.
We ended up reading the configuration from properties file using Apache Commons Configuration and binding them to Guice injector like suggested in Guice FAQ How do I inject configuration parameters?.
#Override public void configure() {
bindConstant().annotatedWith(ConfigurationAnnotation.class)
.to(configuration.getString("configurationValue"));
}
Reloading of configuration supported by Commons Configuration is also quite easy implement into Guice injection.
#Override public void configure() {
bind(String.class).annotatedWith(ConfigurationAnnotation.class)
.toProvider(new Provider<String>() {
public String get() {
return configuration.getString("configurationValue");
}
});
}

Related

Spring Boot 2 - Do something before the beans are initialized

Problem Statement
I want to load properties from a properties file in a classpath or at an external location before the beans are initialized. These properties are also a part of Bean initialization. I cannot autowire the properties from Spring's standard application.properties or its customization because the same properties file must be accessible by multiple deployables.
What I Tried
I'm aware about Spring Application Events; in fact, I'm already hooking
ContextRefreshedEvent to perform some tasks after the Spring Context is initialized (Beans are also initialized at this stage).
For my problem statement, from the description of Spring Docs ApplicationEnvironmentPreparedEvent looked promising, but the hook did not work.
#SpringBootApplication
public class App {
public static void main(String[] args) throws IOException {
SpringApplication.run(App.class, args);
}
#EventListener
public void onStartUp(ContextRefreshedEvent event) {
System.out.println("ContextRefreshedEvent"); // WORKS
}
#EventListener
public void onShutDown(ContextClosedEvent event) {
System.out.println("ContextClosedEvent"); // WORKS
}
#EventListener
public void onEvent6(ApplicationStartedEvent event) {
System.out.println("ApplicationStartedEvent"); // WORKS BUT AFTER ContextRefreshedEvent
}
#EventListener
public void onEvent3(ApplicationReadyEvent event) {
System.out.println("ApplicationReadyEvent"); // WORKS WORKS BUT AFTER ContextRefreshedEvent
}
public void onEvent1(ApplicationEnvironmentPreparedEvent event) {
System.out.println("ApplicationEnvironmentPreparedEvent"); // DOESN'T WORK
}
#EventListener
public void onEvent2(ApplicationContextInitializedEvent event) {
System.out.println("ApplicationContextInitializedEvent"); // DOESN'T WORK
}
#EventListener
public void onEvent4(ApplicationContextInitializedEvent event) {
System.out.println("ApplicationContextInitializedEvent");
}
#EventListener
public void onEvent5(ContextStartedEvent event) {
System.out.println("ContextStartedEvent");
}
}
Update
As suggested by M.Deinum in the comments, I tried adding an application context initializer like below. It doesn't seem to be working either.
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(App.class)
.initializers(applicationContext -> {
System.out.println("INSIDE CUSTOM APPLICATION INITIALIZER");
})
.run(args);
}
Update #2
While my problem statement is regarding loading properties, my question/curiosity is really about how to run some code before the classes are initialized as beans and put into Spring IoC container. Now, these beans require some property values during initialization and I can't/don't want to Autowire them because of the following reason:
As stated in comments and answers, the same can be done using Spring Boot's externalized configuration and profiles. However, I need to maintain application properties and domain-related properties separately. A base domain properties should have at least 100 properties, and the number grows over time. Both application properties and domain-related properties have a property file for different environments (dev, SIT, UAT, Production). Property files override one or more of the base properties. That's 8 property files. Now, the same app needs to be deployed into multiple geographies. That makes it 8 * n property files where n is the number of geographies. I want all the property files stored in a common module so that they can be accessed by different deployables. Environment and geography would be known in run-time as system properties.
While these might be achieved by using Spring profiles and precedence order, I want to have a programmatic control over it (I also would maintain my own property repository). Eg. I would write a convenience utility called MyPropUtil and access them like:
public class MyPropUtil {
private static Map<String, Properties> repository;
public static initialize(..) {
....
}
public static String getDomainProperty(String key) {
return repository.get("domain").getProperty(key);
}
public static String getAppProperty(String key) {
return repository.get("app").getProperty(key);
}
public static String getAndAddBasePathToAppPropertyValue(String key) {
...
}
}
#Configuration
public class MyComponent {
#Bean
public SomeClass getSomeClassBean() {
SomeClass obj = new SomeClass();
obj.someProp1(MyPropUtil.getDomainProperty('domainkey1'));
obj.someProp2(MyPropUtil.getAppProperty('appkey1'));
// For some properties
obj.someProp2(MyPropUtil.getAndAddBasePathToAppPropertyValue('some.relative.path.value'));
....
return obj;
}
}
From the docs, it seems like ApplicationEvents and ApplicationInitializers fit my need, but I am not able to get them to work for my problem statement.
Bit late to the party but hopefully I can offer a solution to your updated problem statement.
This will focus on problem of how to run some code before the classes are initialized as beans and put into Spring IoC container
One issue I notice is that you're defining your application events via the #EventListener annotation.
These are only called once all beans are initiated since these annotations are processed by EventListenerMethodProcessor which is only triggered when the context is ready (see SmartInitializingSingleton#afterSingletonsInstantiated)
As such, some of the events that occur before the context is ready. e.g. ContextStartedEvent, ApplicationContextInitializedEvent won't make it to your listener.
Instead, what you can do is extend the interface for these events directly.
#Slf4j
public class AllEvent implements ApplicationListener<ApplicationEvent> {
#Override
public void onApplicationEvent(final ApplicationEvent event) {
log.info("I am a {}", event.getClass().getSimpleName());
}
Note the missing #Component. Even bean instantiation can occur after some of these events. If you use #Component, then you'll get the following logs
I am a DataSourceSchemaCreatedEvent
I am a ContextRefreshedEvent
I am a ServletWebServerInitializedEvent
I am a ApplicationStartedEvent
I am a ApplicationReadyEvent
Still better and more instant than the annotative listeners but will still not receive the initialization events. For that, what you need to do is follow the instructions found here
To summarize,
Create directory resources/META-INF
Create file spring.factories
org.springframework.context.ApplicationListener=full.path.to.my.class.AllEvent
The result:-
I am a ApplicationContextInitializedEvent
I am a ApplicationPreparedEvent
I am a DataSourceSchemaCreatedEvent
I am a ContextRefreshedEvent
I am a ServletWebServerInitializedEvent
I am a ApplicationStartedEvent
I am a ApplicationReadyEvent
In particular, ApplicationContextInitializedEvent should allow you to perform whatever per-instantiation tasks you need.
I think Spring Cloud Config is a perfect solution for your problem statement. Detailed documentation Here
Spring Cloud Config provides server-side and client-side support for externalized configuration in a distributed system.
So you can easily manage the configurations outside of the app, as well as all the instances will use same configurations.
Create a bean that will be a properties repository and inject it in other beans requiring properties.
In your example, instead of having static methods in MyPropUtil, make the class a bean itself with instance methods. Initialize Map<String, Properties> repository in the initialize method annotated with #PostConstruct.
#Component
public class MyPropUtil {
private static final String DOMAIN_KEY = "domain";
private static final String APP_KEY = "app";
private Map<String, Properties> repository;
#PostConstruct
public void init() {
Properties domainProps = new Properties();
//domainProps.load();
repository.put(DOMAIN_KEY, domainProps);
Properties appProps = new Properties();
//appProps.load();
repository.put(APP_KEY, appProps);
}
public String getDomainProperty(String key) {
return repository.get(DOMAIN_KEY).getProperty(key);
}
public String getAppProperty(String key) {
return repository.get(APP_KEY).getProperty(key);
}
public String getAndAddBasePathToAppPropertyValue(String key) {
//...
}
}
and
#Configuration
public class MyComponent {
#Autowired
private MyPropUtil myPropUtil;
#Bean
public SomeClass getSomeClassBean() {
SomeClass obj = new SomeClass();
obj.someProp1(myPropUtil.getDomainProperty("domainkey1"));
obj.someProp2(myPropUtil.getAppProperty("appkey1"));
// For some properties
obj.someProp2(myPropUtil.getAndAddBasePathToAppPropertyValue("some.relative.path.value"));
//...
return obj;
}
}
Or you can inject MyPropUtil directly to the SomeClass:
#Component
public class SomeClass {
private final String someProp1;
private final String someProp2;
#Autowired
public SomeClass(MyPropUtil myPropUtil) {
this.someProp1 = myPropUtil.getDomainProperty("domainkey1");
this.someProp2 = myPropUtil.getAppProperty("appkey1");
}
//...
}
I feel like your main issue is that you need to maintain application properties and domain-related properties separately.
From spring's perspective, it doesn't really matter since all properties files are kinda merged together after they have been loaded in memory.
So for example, you have two files that contain some properties:
application.related=property1 # this is in application.properties
domain.related=property2 # this is in domain-specific.properties
After they have been loaded, you will get one big thing that contains all properties, if I am not mistaken, it is a org.springframework.core.env.ConfigurableEnvironment instance.
Then what you need to do is just inject the property you need using something like #Value.
For the main issue, to separate properties into different files, you just need to specify spring's spring.config.name property (via environment variable, command line or programmatically). Following the above example, it should be spring.config.name=application,domain-specific.
Furthermore, if you really want to have programmatic control, you can add a custom EnvironmentPostProcessor which exposes the ConfigurableEnvironment instance.
As explaned in this post you can add external property files like this;
public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer properties = new PropertySourcesPlaceholderConfigurer();
properties.setLocation(new FileSystemResource("/Users/home/conf.properties"));
properties.setIgnoreResourceNotFound(false);
return properties;
}
If you don't want to use this, just read the property file with jackson and set the properties to System.setProperty("key","value") in the main method before spring starts.
If you don't want to use this too, take a look at the BeanPostProcessor#postProcessBeforeInitialization method. It runs before bean properties initialized by spring.
I might be missing what exactly do you mean by "Beans initialization", probably an example of such a bean in a question could be beneficial.
I think you should differentiate between properties reading part and bean initialization.
By the time of bean initialization, properties are already read and available. Thats a part of spring magic, if you wish.
That's why the following code works for example:
#Component
public class MySampleBean {
public MySampleBean(#Value("${some.prop}" String someProp) {...}
}
It doesn't matter from where do these property come (spring boot defines many different ways of these places with precedence between them), it will happen before the initialization of beans happens.
Now, lets get back to your original question:
I want to load properties from a properties file in a classpath or at external location (before the beans are initialized - irrelevant).
In spring / spring-boot there is a concept of profiles that basically allows to create a file application-foo.properties (or yaml) and when you load with --spring.profiles.active=foo it will automatically load properties defined in this application-foo.properties in addition to the regular application.properties
So you can place the stuff that you want to "load from classpath" into application-local.properties (the word local is for the sake of example only) and start the application with --spring.profiles.active=local (in the deployment script, docker file or whatever)
If you want to run the property from external location (outside the classpath) you can use: --spring.config.location=<Full-path-file>
Note that even if you put some properties into a regular application.properties and still use --spring.config.location with the same key-value pairs they will take precedence over the properties in the classpath.
Alternatively you can use only --sring.profiles.active=local or remote and do not use config locations at all.
You can configure external location directly in the command line:
java -jar app.jar --spring.config.location=file:///Users/home/config/external.properties
You can use WebApplicationInitializer to execute code before classes are initialized as beans
public class MyWebInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
var ctx = new AnnotationConfigWebApplicationContext();
ctx.register(WebConfig.class);
ctx.setServletContext(servletContext);
We create an AnnotationConfigWebApplicationContext and register a web configuration file with register().
You can check if PropertySource may help you.
Example:
#PropertySource({"classpath:persistence/persistence.properties"})
You can use this annotation on every #Configuration or #SpringBootApplication bean
It sounds like you want to take some ownership of a part of the bean initialization. Typically people think of Spring completing the bean configuration, but in your case it might be easier to consider Spring as starting it.
So, your bean has some properties you want to configure, and some that you want Spring to configure. Just annotate the ones you want Spring to configure (with #Autowire or #Inject, or whatever flavour you prefer), and then take over the control from there, using #PostConstruct or InitializingBean.
class MyMultiStageBoosterRocket {
private Foo foo;
private Bar bar;
private Cat cat;
#Autowire
public MyMultiStageBoosterRocket(Foo foo, Bar bar) {
this.foo = foo;
this.bar = bar'
}
// called *after* Spring has done its injection, but *before* the bean
// is registered in the context
#PostConstruct
public void postConstruct() {
// your magic property injection from whatever source you happen to want
ServiceLoader<CatProvider> loader = ServiceLoader.load(CatProvider.class);
// etc...
}
}
Of course your mechanism for property resolution would need to be available statically somehow, but that seems to fit with you MyPropUtil example.
Getting far more involved, you start looking at Bean Post Processors directly (#PostConstruct is a simple variant of sorts).
There's a previous question, with a useful answer, here How exactly does the Spring BeanPostProcessor work?, but for simplicity, you'd do something like
public class CustomBeanPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
// fixme: detect if this bean needs fancy initialization
return bean;
}
}
Clearly #PostProcess, or InitializingBean are simpler, but the custom post processor has a big advantage... it can be injected with other Spring managed beans. That means you can Spring manage your property injection whatever-thing, and still manually manage the actual injection process.
Just try to load everything you need in main before
SpringApplication.run()
call
public static void main(String[] args) {
// before spring initialization
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
SpringApplication.run(CyberRiskApplication.class, args);
}
You can use ApplicationEnvironmentPreparedEvent but it can't be configured using EventListener annotation. Because by this time Bean drfinitions are not loaded. See the below link on how to cofigure this event.
https://www.thetechnojournals.com/2019/10/spring-boot-application-events.html

Dynamically generating Spring Data Repositories

I am working on creating a library built on top of spring-data so that it would be able to dynamically generate Spring Data repositories. I was hoping to create a generic wrapper of this process that operates on the Spring Data abstraction.
This way depending on what dependency is added to the classpath example: spring-data-jpa or spring-data-mongodb I would be able to generate repositories and inject them into my spring context.
I was having trouble finding good documentation on how to go about doing this. Any ideas?
Spring provides a #Conditional annotation for controlling which beans/configurations are picked up depending on classpath ( similar with the spring-boot autoconfiguration ).
You could define two configuration classes which are picked up by spring based on your classpath.
For example:
Configuration for JPA present on classpath could look something like.
#Configuration
#EnableJpaRepositories(basePackages=...)
#Conditional(JpaClassPathCondition.class)
public class JpaAutoConfiguration {
static class JpaClassPathCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return ClassUtils.isPresent("javax.persistence.EntityManager", JpaAutoConfiguration.class.getClassLoader());
}
}
}
Configuration for MongoDB:
#Configuration
#EnableMongoRepositories(...)
#Conditional(MongoClassPathCondition.class)
public class MongoAutoConfiguration {
static class MongoClassPathCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return !ClassUtils.isPresent("javax.persistence.EntityManager", MongoAutoConfiguration.class.getClassLoader()); )//or whatever classpath element fits you best
}
}
}
All you need to do from this point forward is to use both configuration classes in your module and let spring do it's magic

Inject application properties without Spring

I would like a simple, preferably annotation-based way to inject external properties into a java program, without using the spring framework (org.springframework.beans.factory.annotation.Value;)
SomeClass.java
#Value("${some.property.name}")
private String somePropertyName;
application.yml
some:
property:
name: someValue
Is there a recommended way to do this in the standard library?
I ended up using apache commons configuration:
pom.xml:
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>1.6</version>
</dependency>
src/.../PropertiesLoader.java
PropertiesConfiguration config = new PropertiesConfiguration();
config.load(PROPERTIES_FILENAME);
config.getInt("someKey");
/src/main/resources/application.properties
someKey: 2
I did not want to turn my library into a Spring application (I wanted #Value annotations, but no application context + #Component, extra beans, extra Spring ecosystem/baggage which doesn't make sense in my project).
Define application properties here /src/main/resources/application.properties
Define PropertiesLoader class
public class PropertiesLoader {
public static Properties loadProperties() throws IOException {
Properties configuration = new Properties();
InputStream inputStream = PropertiesLoader.class
.getClassLoader()
.getResourceAsStream("application.properties");
configuration.load(inputStream);
inputStream.close();
return configuration;
}
}
Inject property value in the required class like below,
Properties conf = PropertiesLoader.loadProperties();
String property = conf.getProperty(key);
I stumbled upon this answer on google, yet i'm not satisfied.
In the end I created Environment class that uses both application properties from #Stone answer and hardcoded env variables. But Im still looking for better options, since I cannot modify ENV_VARIABLE name inside application.propeties, which is what I originally wanted.
public class EnvironmentPlainJava {
public int httpsPort() {
return Integer.parseInt(PropertiesLoader.loadProperties().getProperty("https.port"));
}
public String keyStorePath() {
return System.getenv("KEY_STORE_PATH");
}
}
For reference, here is an idiomatic Kotlin version of the #Stone code (using ClassPathResource Spring utility class - doesn't require running Spring!):
fun loadProperties(properties: String = "application.properties"): Properties =
ClassPathResource(properties).inputStream.use { inputStream ->
Properties().also {
it.load(inputStream)
}
}

JerseyTest unsatisfied dependency (Dependency Injection)

I need to test my RESTful service and I thought the most appropriate way to do it would be using the Jersey Test Framework since I am already using Jersey for my service.
However, when running a simple test I am getting a UnsatisfiedDependencyException because the implementation of the DAO I am using in the service is not found (it's being set via DI). This error used to happen to me everytime I modified my pom.xml Maven configuration file, mostly when adding or removing dependencies. Supposedly, the IDE I'm using, IntelliJ IDEA is supposed to regenerate the artifacts automatically, but for me to avoid this dependency failure I needed to remove the old artifact from the Tomcat server deployment options as well as from the project structure, and add it again.
The problem here is that the test framework is using a separate server, as the documentation said. I am using the following one:
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>2.23.1</version>
</dependency>
I don't know how to solve the dependency issue if I am using a different server.
The top error message in the exception stack says:
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no
object available for injection at SystemInjecteeImpl
Any ideas?
Edit. I managed to patch it with a dirty solution.
First, let me show you how I was injecting the DAO:
// Binder.java
public class Binder extends AbstractBinder {
#Override
protected void configure() {
bind(TournamentDao.class).to(ITournamentDao.class).in(Singleton.class);
}
}
// DependencyInjectionFeature.java
#Provider
public class DependencyInjectionFeature implements Feature {
#Override
public boolean configure(FeatureContext context) {
context.register(new Binder());
return true;
}
}
// EventSchedulerService.java
#Path("eventscheduler")
public class EventSchedulerService {
private ITournamentDao dao;
#Inject
public EventSchedulerService(ITournamentDao tournamentDao) {
dao = tournamentDao;
}
}
The web configuration file web.xml scans the package where these classes are placed (the package has the same name than the one where the test exists) and perform the dependency injection since it finds the #Provider annotation.
To patch my issue I did the exact same thing in the test class:
public class EventSchedulerServiceTest extends JerseyTest {
protected Application configure() {
ResourceConfig config = new ResourceConfig(EventSchedulerService.class);
config.register(new AbstractBinder() {
#Override
protected void configure() {
bind(TournamentDao.class).to(ITournamentDao.class).in(Singleton.class);
}
});
return config;
}
}
Obviously this is a bad idea because now I have duplicate code; but it did the trick. I'd like to know how to make the test server to use the service configuration correctly.
I remember from a previous discussion in the comments here, you had the problem of not using a ResourceConfig to configure your main application. You we instead using web.xml.
The reason you are using a Feature annotated with #Provider is that you we're using package scanning in your web.xml
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>your.packages.to.scan</param-value>
</init-param>
The package scan picked up the #Provider on the Feature, and automatically registers it. This provider is what is used to register the Binder, which is what configured the dependency injection.
As mentioned, a Jersey application can be configured in a ResourceConfig or in a web.xml. Anything jersey configuration you can do in the web.xml, you can also do in the ResourceConfig. There are a bunch of different ways you can register components with the ResourceConfig
You can just call register
config.register(new DependencyInjectionFeature());
You can also package scan, the same way you are doing in the web.xml, by calling packages
config.packages("your.packages.to.scan");
You should also look at your implementation of the Binder class. It extends AbstractBinder. The look at how you are currently configuring the DI in the test
config.register(new AbstractBinder() {
#Override
protected void configure() {
...
}
});
You are registering an AbstractBinder instance. So you know that calling you can register the DI configuration calling register on the ResourceConfig, so you could instead of registering a different binder, just register an instance of the Binder class
config.register(new Binder());
So you have some options.

How do I configure JSR-330 #Provider and #Inject #Named("foo") String **programmatically** in Spring?

We have decided to use Dependency Injection with JSR-330 annotations for our future modularization efforts, and have been very pleased with the first deliverable based on Guice 2 SVN.
Now we need to ensure and document through unit tests that the constructions we need, also work in Spring when configured programmatically (we want the same refactoring support as with Guice so no XML files). I have problems with #Provider and #Inject #Named("foo") String but I have made plain #Inject work with:
ApplicationContext ctx = new AnnotationConfigApplicationContext(LIBL_Object.class,
CORE_Provider.class);
this.object = ctx.getBean(LIBL_Object.class);
where LIBL_Object is the base class to be injected into, but the CORE_Provider does not register as I hoped within Spring.
The implementation of CORE_Provider is
package qa.jsr330.core;
import javax.inject.Provider;
public class CORE_Provider implements Provider<ProvidedInterface> {
#Override
public ProvidedInterface get() {
return new CORE_Provided();
}
}
and I want it injected into
package qa.jsr330.core;
import javax.inject.Inject;
public class LIBL_Object {
private ProvidedInterface provided;
public ProvidedInterface getProvided() {
return provided;
}
#Inject
public void setProvided(ProvidedInterface provided) {
this.provided = provided;
}
// Other stuff omitted.
}
Also we have found that we can pass configuration values very clearly using the #Named tag. This code looks like:
String hostname;
#Inject
public void setHostname(#Named("as400.hostname") String hostname) {
this.hostname = hostname;
}
where we can then register this string with Guice using
bindConstant().annotatedWith(Names.named("as400.hostname")).to(value);
So the two questions are:
How do I register the #Provider class with Spring 3 programatically?
How do I register a string constant with Spring 3 so that #Named selects it properly?
The short answer is: there is no such thing as programmatic configuration of Spring.
Despite the fact that both Spring and Guice support JSR-330 API and that Spring can be configured without XML now, their ideologies are still very different. Spring relies on static configuration, either in the form of XML files or annotated Java classes. Therefore straightforward attempt to adapt Guice-style configuration to Spring may produce difficulties.
Regarding the problem with Provider - Spring doesn't support javax.inject.Provider in the same way as toProvider() binding in Guice (by the way, this usage of Provider is not specified in JSR-330 docs). Therefore some Spring-specific annotations may be needed, for example
#Configuration
public class CORE_Provider implements Provider<ProvidedInterface> {
#Override #Bean
public ProvidedInterface get() {
return new CORE_Provided();
}
}
Binding value coming from the outside may be difficult due to static nature of Spring configuration. For example, in your case, it can be done like this:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(IBL_Object.class);
ctx.register(CORE_Provider.class);
ctx.registerBeanDefinition("as400.hostname",
BeanDefinitionBuilder.rootBeanDefinition(String.class)
.addConstructorArgValue(value).getBeanDefinition());
ctx.refresh();

Categories

Resources