I understand the difference between a BeanFactory and an ApplicationContext.
I am also aware that a BeanFactory instance can be created from xml files, which reside in the classpath or anywhere else in the file system. So, an XMLBeanFactory instance is created in such cases.
At the same time, I was digging into BeanFactory documentation and stumbled upon this.
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/BeanFactory.html
Normally a BeanFactory will load bean definitions stored in a configuration source (such as an XML document), and use the org.springframework.beans package to configure the beans. However, an implementation could simply return Java objects it creates as necessary directly in Java code. There are no constraints on how the definitions could be stored: LDAP, RDBMS, XML, properties file, etc. Implementations are encouraged to support references amongst beans (Dependency Injection).
So, does this mean that the bean definition can be in non-XML format as well? viz, LDAP, RDBMS, properties file etc? If yes, please provide an snippet of it. I am exclusively looking for BeanFactory only and not any ApplicationContext implementations.
How to load an ApplicationContext which is a BeanFactory on steriods is explained in this blog.
public class SpringContextsApplication {
public static void main(String[] args) throws Exception {
GenericApplicationContext contextFromProperties =
new GenericApplicationContext();
BeanDefinitionReader reader =
new PropertiesBeanDefinitionReader(contextFromProperties);
reader.loadBeanDefinitions("classpath:application-context.properties");
contextFromProperties.refresh();
doGreeting(contextFromProperties);
contextFromProperties.stop();
}
private static void doGreeting(ApplicationContext ctx) {
Greeter greeter = ctx.getBean(Greeter.class);
Person person = ctx.getBean(Person.class);
greeter.greet(person);
}
}
Where there is a GenericApplicationContext one could also use a DefaultListableBeanFactory instead and achieve the same result.
public class SpringContextsApplication {
public static void main(String[] args) throws Exception {
GenericApplicationContext contextFromProperties =
new DefaultListableBeanFactory();
BeanDefinitionReader reader =
new PropertiesBeanDefinitionReader(contextFromProperties);
reader.loadBeanDefinitions("classpath:application-context.properties");
doGreeting(contextFromProperties);
contextFromProperties.stop();
}
private static void doGreeting(BeanFactory ctx) {
Greeter greeter = ctx.getBean(Greeter.class);
Person person = ctx.getBean(Person.class);
greeter.greet(person);
}
}
To load bean definitions one needs a BeanDefinitionReader for that specific implementation you want to use. Here that is property files but you could write a BeanDefinitionReader for LDAP, JDBC, YAML etc. as well.
Spring supports out-of-the-box
XML
Properties (Deprecated as of Spring 5.3, propably removed in Spring 6)
Groovy
Java Classes
JDBC (Purely as an exampled, deprecated in 5.3 and removed in Spring 6)
However you can create whatever implementation you like if you need.
Related
I have a situation where I want to look for interfaces annotated with a given annotation, then check if a matching implementation is available. If not, I want to make a bean available that is actually a JDK proxy of the interface, essentially exactly what:
#ConditionalOnMissingBean
would do, except without writing a factory method for each of those.
I have code that is working "sometimes" - it seems extremely sensitive to the classloader structure, specifically, wether classes are loaded from a springboot fat jar (works) or wether some part is loaded from a separate classpath entry (doesnt work, mostly).
here is what I am doing:
#Service
#Order(value = Ordered.LOWEST_PRECEDENCE)
public class RemotingImportService implements BeanFactoryPostProcessor {
private static Log log = LogFactory.getLog(RemotingExportService.class);
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// scan classpath with classgraph, then:
for (ClassInfo classInfo : scanResult.getClassesWithAnnotation(MyAnnotation.class.getCanonicalName())) {
Class c = Class.forName(classInfo.getName());
if(beanFactory.getBeanNamesForType(c).length > 0) {
implFound = true;
log.info(c.getName()+" already has an implementation ... skipping");
continue;
}
// create proxy, then:
GenericBeanDefinition bdService = new GenericBeanDefinition();
bdService.setBeanClassName(classInfo.getName());
bdService.setInstanceSupplier(new ProxySupplier(proxy));
bdService.setLazyInit(true);
((DefaultListableBeanFactory) beanFactory).registerBeanDefinition(classInfo.getName(), bdService);
log.info(c.getName()+" has NO implementation ... proxy registerd");
}
in some cases, it seems that the beanfactory isn't finished, and
beanFactory.getBeanNamesForType()
returns an empty list, although it does find beans for that type later. i am aware that messing with this is probably not ideal - but it would be nice to find a solution that plays nice with spring boot.
any suggestions on how to solve this? a way to mark a bean definition as "ConditionalOnMissingBean" would also be great.
You should use BeanPostProcessor instead of BeanFactoryPostProcessor
BeanPostProcessor is operating by assembled beans, while BeanFactoryPostProcessor uses raw bean definitions.
Read more here
public class ConditionalOnMissingProcessor implements BeanPostProcessor, Ordered, ApplicationContextAware
{
private static final Logger LOG = Logger.getLogger(ConditionalOnMissingProcessor .class);
private ApplicationContext applicationContext;
// Ordering to last in chain.
#Override
public int getOrder()
{
return Ordered.LOWEST_PRECEDENCE;
}
#Override
public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException
{
this.applicationContext = applicationContext;
}
/**
* Process each bean and inject proxy objects in fields marked with: {#ConditionalOnMissingBean}
*/
#Override
public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException
{
LOG.debug("Processing bean: " + beanName);
final Class clazz = bean.getClass();
ReflectionUtils.doWithFields(clazz, new ReflectionUtils.FieldCallback()
{
#Override
public void doWith(final Field field)
{
try
{
if(field.isAnnotationPresent(ConditionalOnMissingBean.class))
{
ReflectionUtils.makeAccessible(field);
final String beanFieldName = field.getName();
...
// Find proper implementation in application context
// Or create a proxy.
// Inject proxy or impl into a bean
field.set(bean, <value>));
}
}
catch(IllegalAccessException e)
{
LOG.error("Cannot set " + field.getName() + " in " + beanName, e);
}
}
});
return bean;
}
UPD:
First of all, I have to say about disadvantages of your impl (IMHO):
- you are trying to scan classpath to find all interfaces with #RemoteEndpoint annotation, and, if current application context doesn't contain a bean that implemented this interface - create a proxy bean.
But what if I say, that not all interfaces marked with #RemoteEndpoint should be taken into account? Developer should explicitly mark those interfaces, and then you can create all needed beans (for example, developer makes a common-services library).
- probably you are specifying redudnant information, when marking impl class with #RemotingEndpoint(value=RandomService.class) annotation. You are already mentioned that when you implemented an interface.
There are multiple ways for implementing your idea
Using custom annotation on bean fields instead of #Autowired.
pros:
simply check all bean fields for presence of your custom annotation, and inject dependency (proxy or impl), using BeanPostProcessor.
cons:
developer should mark all bean dependencies with custom annotation;
it won't work if developer would have to obtain new dependencies at runtime.
Using regular #Autowired and #Value annotations for injecting remote service proxy
In this case you should use BeanFactoryPostProcessor (as you already tried). You'll have to iterate over all bean definitions, collect a map of field names and interfaces of remote service proxies you'll have to register (dependencies meta info). And next step is creating and registering beans using dependencies meta info (creating new ones only if there is no implementation bean in context). Spring will autowire those fields later. But, those dependencies should be singleton beans.
pros:
no mess with custom annotations;
works at runtime
cons:
proxies are singletons (you need prototypes as diagram displayed).
Still using regular #Autowired and #Value annotations and BeanFactoryPostProcessor, but instead of registering new beans, you should register a FactoryBean for each interface #RemoteEndpoint.
pros:
no mess with custom annotations.
works at runtime
prototype-scoped proxies
My applications requirements mean that I need to create application beans manually at runtime and then add them to the application context.
These beans belong to third party libraries so I cannot modify them e.g. a TibjmsConnectionFactory
So my factory class that creates these beans needs to be provided with a Properties object that will set username, password, connectionTimeouts etc
Ideally I'd like to use Spring's property support so I do not need to convert Strings to Integers etc
Also, the Properties provided to my factory class will not be the same Properties used for by the PropertyPlaceholderConfigurer in my overall ApplicationContext
How do I achieve this, or is it even possible?
public class MyCustomFactoryStrategy {
#Override
public TibjmsConnectionFactory create(Properties properties) {
TibjmsConnectionFactory connectionFactory = new TibjmsConnectionFactory();
connectionFactory.setServerUrl(properties.getProperty("emsServerUrl")); // this is a string
connectionFactory.setConnAttemptCount(new Integer(properties.getProperty("connAttemptCount"))); // this is an integer
...
return connectionFactory;
}
Have a look in this post. I think it might be what you need.
[PropertyPlaceholderConfigurer not loading programmatically
[1]:
Hi i want to know what are the different ways to get ApplicationContext Object in Spring?
I know only one way that is,
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
is there any other ways? if it is please let me know.
Thanks.
You can also use annotation based configuration
#Configuration
public class Config {
#Bean
public Bean1 bean1() {
return new Bean1();
}
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
}
}
You can implement the interface ApplicationContextAware, like this :
public class MyClass implements ApplicationContextAware {
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
}
If you are using annotation, you could also autowire it
#Autowired
private ApplicationContext applicationContext;
Also, the code you wrote does not get an existing application context, it creates one.
Well, there are a lot of ways out there, I wonder whom would know them all...
But first, we need to make a difference between instanting a new context, or getting a running and existing application-context.
By new ***ApplicationContext a new context will be created. Therefore all Subclasses of org.springframework.context.ApplicationContext can be used to create a new ApplicationContext. You can find all implementing classes here. The new way to instantiate a spring-context is through AnnotationConfigApplicationContext.
Also, you can add a displatcher-servlet or an servlet-listener in your web.xml. Or use a framework like gemini-blueprint in an osgi-environment which starts all xml-files in meta-inf/spring. (e.g. eclipse virgo)
On the other hand, you can get an existing context (which means not a new one) through different ways:
ApplicationContextAware
Implement the ApplicationContextAware interface and you will get the context via setApplicationContext(ApplicationContext applicationContext) method.
Just add #Autowired private ApplicationContext applicationContext; to your spring bean. But make sure it is a spring bean.
In your web-application, you can get the context of your listener-context via ApplicationContextUtils.getWebApplicationContext( servletcontext)
There would a lot of more ways, but these are those which popped up in my mind quickly.
If you are referring to the possible way you can create an ApplicationContext and not to the ways such an instance can be passed through your code then I suggest taking a look at the Spring javadoc for ApplicationContext. So based on this the concrete implementations of this interface are:
org.springframework.context.annotation.AnnotationConfigApplicationContext
Standalone application context, accepting annotated classes as input - in particular #Configuration-annotated classes, but also plain #Component types and JSR-330 compliant classes using javax.inject annotations. Allows for registering classes one by one using register(Class...) as well as for classpath scanning using scan(String...).
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
This is essentially the equivalent of AnnotationConfigApplicationContext for a web environment.
org.springframework.context.support.ClassPathXmlApplicationContext
Standalone XML application context, taking the context definition files from the class path, interpreting plain paths as class path resource names that include the package path (e.g. "mypackage/myresource.txt"). Useful for test harnesses as well as for application contexts embedded within JARs.
org.springframework.context.support.FileSystemXmlApplicationContext
Standalone XML application context, taking the context definition files from the file system or from URLs, interpreting plain paths as relative file system locations (e.g. "mydir/myfile.txt"). Useful for test harnesses as well as for standalone environments.
org.springframework.context.support.GenericApplicationContext
Generic ApplicationContext implementation that [...] does not assume a specific bean definition format
org.springframework.context.support.GenericXmlApplicationContext
Convenient application context with built-in XML support. This is a flexible alternative to ClassPathXmlApplicationContext and FileSystemXmlApplicationContext, to be configured via setters, with an eventual AbstractApplicationContext.refresh() call activating the context.
org.springframework.context.support.GenericGroovyApplicationContext
An ApplicationContext implementation that extends GenericApplicationContext. [...] Consider this as the equivalent of GenericXmlApplicationContext for Groovy bean definitions, or even an upgrade thereof since it seamlessly understands XML bean definition files as well.
org.springframework.web.context.support.GenericWebApplicationContext
Subclass of GenericApplicationContext, suitable for web environments.
org.springframework.web.context.support.GroovyWebApplicationContext
WebApplicationContext implementation which takes its configuration from Groovy bean definition scripts and/or XML files, as understood by an GroovyBeanDefinitionReader. This is essentially the equivalent of GenericGroovyApplicationContext for a web environment.
org.springframework.jca.context.ResourceAdapterApplicationContext
ApplicationContext implementation for a JCA ResourceAdapter. Needs to be initialized with the JCA BootstrapContext, passing it on to Spring-managed beans that implement BootstrapContextAware.
org.springframework.context.support.StaticApplicationContext
ApplicationContext implementation which supports programmatic registration of beans and messages, rather than reading bean definitions from external configuration sources. Mainly useful for testing.
org.springframework.web.portlet.context.StaticPortletApplicationContext
Static Portlet-based ApplicationContext implementation for testing. Not intended for use in production applications.
org.springframework.web.context.support.StaticWebApplicationContext
Static WebApplicationContext implementation for testing. Not intended for use in production applications.
org.springframework.web.portlet.context.XmlPortletApplicationContext
Portlet-based WebApplicationContext implementation which takes its configuration from XML documents, understood by an XmlBeanDefinitionReader.
org.springframework.web.context.support.XmlWebApplicationContext
WebApplicationContext implementation which takes its configuration from XML documents, understood by an XmlBeanDefinitionReader. This is essentially the equivalent of GenericXmlApplicationContext for a web environment.
I keep on learning Spring and it is very difficult to figure out which implementation of ApplicationContext is intended for. I've standalone J2EE application and I don't interested in Web* or Portlet* implementations.
Can you provide me the brief list of possibilities (if isn't clear, see P.S. section of my question) and purposes of each implementation below:
ResourceAdapterApplicationContext
StaticApplicationContext
ClassPathXmlApplicationContext
FileSystemApplicationContext
P.S.
A don't ask you to provide me reference to the docs. For example:
ClassPathXmlApplicationContext Standalone XML application context,
taking the context definition files from the class path, interpreting
plain paths as class path resource names that include the package path
But from that definition its not clear that ClassPathXmlApplicationContext also implements AbstractRefreshableApplicationContext and can be used to change beans definition without stopping server.
I'm sorry you don't want references to the docs, but that's where all the information is.
StaticApplicationContext states
org.springframework.context.ApplicationContext implementation which
supports programmatic registration of beans and messages, rather than
reading bean definitions from external onfiguration sources. Mainly
useful for testing.
So you use it to register bean definitions directly
StaticApplicationContext context = new StaticApplicationContext();
context.registerBeanDefinition(beanName, beanDefinition);
This can be used in cases where your ApplicationContext needs to be dynamically changed. Note that you can pass a parent ApplicationContext to the StaticApplicationContext if you need both behaviors, ie. reading from XML/Java config and dynamically registering.
ClassPathXmlApplicationContext is one of the more common ApplicationContext implementations in my opinion. You simply point it to an XML (bean definition) resource on the classpath and it loads it up. The javadoc states
Useful for test harnesses as well as for application contexts embedded
within JARs.
You could therefore simply point to a resource on the classpath coming from a JAR and load that. It's simply enough to setup tests environments this way.
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("some-context.xml");
// boom you're ready to go
Note that Spring's JUnit support classes offer other (better) ways to setup testing environment.
But from that definition its not clear that
ClassPathXmlApplicationContext also implements
AbstractRefreshableApplicationContext and can be used to change beans
definition without stopping server.
That's what the javadoc is for.
FileSystemXmlApplicationContext is similar to the ClasspathXmlApplicationContext above, but it takes the configuration files from the file system instead of reading resources from the classpath.
ResourceAdapterApplicationContext states
org.springframework.context.ApplicationContext implementation for a
JCA ResourceAdapter. Needs to be initialized with the JCA
javax.resource.spi.BootstrapContext, passing it on to Spring-managed
beans that implement BootstrapContextAware.
I haven't worked with this one at all and I don't know where Resource Adapters are useful, but here are some more docs.
Just to add couple things to #Solitirios answer:
You forgot to mention several more context:
GenericApplicationContext
GenericXmlApplicationContext
AnnotationConfigApplicationContext
GenericWebApplicationContext
StaticWebApplicationContext
And many others.
In general, GenericApplicationContext is almost the same as StaticApplicationContext, the only difference between them in MessageSource support in StaticApplicationContext. Purpose for both of these classes is for small tests with tiny application context with couple beans.
GenericWebApplicationContext and StaticWebApplicationContext are also quite similar to each other, and typically they are used for emulation of Servlet container, e.g. tests or non-Servlet environment.
F.e. you can use something like this in your code (f.e. tests):
//create parent context
ApplicationContext xmlContext = new GenericXmlApplicationContext("classpath:/spring-*.xml");
//create mock servlet context
MockServletContext mockServletContext = new MockServletContext();
//create web context
GenericWebApplicationContext webContext = new GenericWebApplicationContext(mockServletContext);
//set attribute
mockServletContext.setAttribute(GenericWebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, webContext);
//set parent context
webContext.setParent(xmlContext);
//refresh context
webContext.refresh();
But there are couple contexts classes, which are worthy of attention. And considering your pre-requisites, I would choose one of them.
GenericXmlApplicationContext is very good alternative of ClassPathXmlApplicationContext and FileSystemXmlApplicationContext. Consider this example:
ApplicationContext context = new GenericXmlApplicationContext("classpath:some-context.xml");
is equivalent to
ApplicationContext context = new ClassPathXmlApplicationContext("some-context.xml");
or
ApplicationContext context = new GenericXmlApplicationContext("some-context.xml");
is equivalent to
ApplicationContext context = new FileSystemXmlApplicationContext("some-context.xml");
So GenericXmlApplicationContext looks more flexible.
AnnotationConfigApplicationContext is a context holder, if you don't want to keep your beans in XML-file.
//context creation
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
//context class
#Configuration
#ComponentScan("com.examples.services")
public class AppConfig {
#Bean
public DataSources dataSource() {
DataSource ds = new BasicDataSource();
//... init ds
return ds;
}
}
More information you can find here.
I want to write a standalone application with IOC, how do I use springs dependency injection in there? I'm using JIdea. There is spring 2.5 support but I want to use spring 3.0 here is the way I tried!
I experience in using Spring MVC we can inject dependencies there in a WebApplicationContext but how do I inject dependencies in a standalone application
I tried this
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"com\\ttg\\xmlfile.xml"});
but I cannot see that the dependencies are injected by the beans defined there (in the XML file)
I put the above code in the main method and two bean definitions for two Objects,in one Java class's constructor I used the other class's object - which was injected to this object - and called a method on that which will print some thing but it didn't worked I thought that the above code creates all the dependencies and injects them but it doesn't seem like that
How do I properly use Springs IOC, dependency injection in my stand alone app which does not contain a WebApplicationContext?
Please mention steps.
suppose you have:
class Bean1 {
Bean2 bean2;
}
class Bean2 {
String data;
}
the context.xml file
<bean id="bean1" class="Bean1">
<property name="bean2" ref="bean2" />
</bean>
<bean id="bean2" class="Bean2" />
then this should be true
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"context.xml"});
Bean1 bean1 = (Bean1) context.getBean("bean1");
// bean1.bean2 should not be null here.
you can use autowiring support provided by spring, in order to inject dependencies (and possibly apply post processors) to an object that is not part of the application context.
In your case, this object is your standalone application.
Here is the way to achieve this. In this example, I use #Autowired (for b1), traditional DI (for b2) and initialization hook for b3. The autowiring support with annotations assumes you have defined the appropriate spring post-processor in your application context (e.g. by declaring <context:annotation-config/>).
public class StandaloneApp implements InitializingBean {
#Autowired private Bean1 b1;
private Bean2 b2;
private Bean3 b3;
public void afterPropertiesSet() throws Exception {
this.b3 = new Bean3(b1, b2);
}
public void setB2(Bean2 b2) {
this.b2 = b2;
}
public static void main(String... args) {
String[] locations = // your files relative to the classpath
ApplicationContext ac = new ClasspathXmlApplicationContext(locations);
// or use FileSystemXmlApplicationContext if the files are not in the classpath
StandaloneApp app = new StandaloneApp();
AutowireCapableBeanFactory acbf = ac.getAutowireCapableBeanFactory();
acbf.autowireBeanProperties(app, AUTOWIRE_BY_NAME, false);
acbf.initializeBean(app, "standaloneApp"); // any name will work
}
}
In this example, all b1, b2 and b3 should be non-null (assuming b1 and b2 beans exist in your application context).
I haven't tested it (might not even compile due to some typo), but the idea is in the last 3 lines. See the javadocs for AutowireCapableBeanFactory and mentionned methods to see exactly what happens.
If you prefer (mostly) pure java, you can use java config:
#Configuration
pubic class MyConfig {
#Bean
public Bean1 bean1() { return new Bean1(); }
#Bean
public Bean2 bean2() { return new Bean2(bean1()); }
}
And then to instantiate:
ApplicationContext ctx =
new AnnotationConfigApplicationContext(MyConfig.class);
Keep in mind there's some things that pure annotation driven configuration doesn't yet support (such as things like tx:annotation-driven), which you might need some xml glue code for.
<beans>
<tx:annotation-driven />
<context:annotation-config/>
<bean class="MyConfig"/>
</beans>
And then use a standard xml based way of creating the ApplicationContext (like ClasspathXmlApplicationContext, or the spring web context loader, etc...).
How did you confirm that your beans aren't being wired correctly? One common issue is the xml config file not being in the right place. Can you give us some more information like your project layout and the code you use to obtain the beans from the container?
If you add log4j logging to your app, you should see a cascade of messages coming out that will tell you a lot about what Spring is and is not doing. If you don't have that feedback, you're in the dark. You might be able to ferret out the answer just by getting more information out of Spring from log4j.
Are you calling context.getBean("beanName") to get a reference to the bean or are you doing a new SomeClass()? If you do it through getBean() then the injected properties should be set.
Also, be sure to use the same bean factory (ClassPathXmlApplicationContext instance) for retrieving all your beans. This should most likely be static final and used by the entire application.
you can use the command #Autowired