Invoke static method from spring config - java

Is it possible to invoke static method in Spring configuration file?
public MyClass {
public static void staticMethod() {
//do something
}
}
<bean id="myBean" class="MyClass">
<!-- invoke here -->
</bean>

When the static method creates an instance of MyClass you an do it like this
config
<bean id="myBean" class="MyClass" factory-method="staticMethod">
<!-- invoke here -->
</bean>
code
public static MyClass staticMethod() {
//create and Configure a new Instance
}
If you want the method only to be called on bean instantiation spring can't do it this way.
config
<bean id="myBean" class="MyClass" init-method="init">
<!-- invoke here -->
</bean>
code
public static void staticMethod() {
//create and Configure a new Instance
}
public void init() {
staticMethod();
}

try this
<bean id="b1" class="org.springframework.beans.factory.config.MethodInvokingBean">
<property name="staticMethod" value="MyClass.staticMethod" />
</bean>
see http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/config/MethodInvokingBean.html

Try something like this:
<!-- call static method -->
<bean id="test" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="MyClass" />
<property name="targetMethod" value="staticMethod" />
<property name="arguments">
<list>
<value>anArgument</value>
</list>
</property>
</bean>
Remove arguments as you might not need them.
Taken from https://gist.github.com/bulain/1139874
I was needing to call a static method. The above code worked fine.
This might be useful as well: How to make spring inject value into a static field.

If you are using annotations for spring configuration you can add the following method into your #Configuration class:
#Bean
public MethodInvokingFactoryBean methodInvokingFactoryBean() {
MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
methodInvokingFactoryBean.setStaticMethod("MyClass.staticMethod");
return methodInvokingFactoryBean;
}

Related

Use properties of other property placeholder in own property placeholder configurer

I try to implement an own PropertyPlaceholderConfigurer which uses properties of an other PropertyPlaceholderConfigurer in the constructor. I tried doing it like this.
<!-- load properties which are used in second configurer -->
<context:property-placeholder
location="classpath:config.properties" ignore-unresolvable="true" order="1" />
<bean class="com.example.MyPropertyPlaceholderConfigurer">
<!-- use property of other file -->
<constructor-arg value="${password}" />
<property name="location">
<value>file:config.properties</value>
</property>
<property name="ignoreUnresolvablePlaceholders" value="true" />
</bean>
config.properties
password=1234
But the property ${password} in the constructor of MyPropertyPlaceholderConfigurer is not resolved.
What is my mistake?
I post hereby my solution I came up with to overcome this problem.
I implemented a single PropertyPlaceholderConfigurer which loads all properties and adds special functionality to some properties. This way inside the PropertyPlaceholderConfigurer you are able to use properties which are defined in the loaded property files.
config.properties
password=1234
special.properties
user={SPECIAL}name
spring config:
<bean
class="com.example.MyPropertyPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="locations">
<list>
<value>classpath:config.properties</value>
<value>classpath:special.properties</value>
</list>
</property>
</bean>
PropertyPlaceholderConfigurer:
public class MyPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
private final static String PROPERTY_PREFIX = "{SPECIAL}";
protected Properties properties;
#Override
protected void convertProperties(Properties props) {
this.properties = props;
super.convertProperties(props);
}
#Override
public String convertPropertyValue(String originalValue) {
if (originalValue.startsWith(PROPERTY_PREFIX)) {
return convert(originalValue.substring(PROPERTY_PREFIX.length()));
}
return originalValue;
}
protected String convert(String value){
// access properties from config.properties
String pw = this.properties.getProperty("password");
// use properties and do what you need to do
return value + pw;
}
}
Maybe it helps somebody.

Spring annotation based SAP connector

I'm trying to move from a xml based config to java annotations
I need your help getting this to work:
Obviously I can't set the RemoteJco interface to my SapConnector but what can I do to get this xml-config working?
#Bean
public RmiProxyFactoryBean jcoPool(){
RmiProxyFactoryBean jcoPool = new RmiProxyFactoryBean();
jcoPool.setServiceUrl("rmi://localhost/CH");
jcoPool.setServiceInterface(RemoteJco.class);
jcoPool.setRefreshStubOnConnectFailure(true);
return jcoPool;
}
#Bean
public SapConnector SapConnector(){
SapConnector sapConnector = new SapConnector();
sapConnector.setJcoPool(jcoPool());
return sapConnector;
}
this in the XML-Config works just fine:
<!-- JCO-Pool RMI Service -->
<bean id="jcoPool" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
<property name="serviceUrl" value="rmi://localhost/CH"/>
<property name="serviceInterface" value="com.itensis.jco.common.RemoteJco"/>
<property name="refreshStubOnConnectFailure" value="true" />
</bean>
<bean id="SapConnector" class="com.itensis.core.SapConnector">
<property name="jcoPool">
<ref bean="jcoPool" />
</property>
</bean>
this is my SAP-Connector
#Service
public class SapConnector {
#Autowired private RemoteJco jcoPool;
public RemoteJco getJcoPool() {
return jcoPool;
}
public void setJcoPool(RemoteJco jcoPool) {
this.jcoPool = jcoPool;
}
}
You have to make some changes on the jcoPool bean:
#Bean
public RemoteJco jcoPool(){
RmiProxyFactoryBean jcoPool = new RmiProxyFactoryBean();
jcoPool.setServiceUrl("rmi://localhost/CH");
jcoPool.setServiceInterface(RemoteJco.class);
jcoPool.setRefreshStubOnConnectFailure(true);
jcoPool.afterPropertiesSet();
return (RemoteJco) jcoPool.getObject();
}
Make sure that you return value has the same class as you used as service interface. And you have to call afterPropertiesSet() before calling getObject on the RmiProxyFacotoryBean instance.

Instantiating an object from bean using constructor args and refs

I've a class which takes in 2 Object Injections. 1 of them is to be injected through other bean ref whereas the other injected based on bean call. I want to instantiate an object using spring. How can I do this ?
I tried doing this:
MyBean Class:
class MyBean{
Injection1 ijn1;
MyBean(Injection1 ijn1,Injection2 ijn2){
this.ijn1=ijn1;
this.ijn2=ijn2;
}
}
Beans.xml
<bean name="myBean" class="MyBean" scope="prototype">
<constructor-arg>
<null />
</constructor-arg>
<constructor-arg>
<ref bean="injection2" />
</constructor-arg>
</bean>
<bean name="injection2" class="Injection2">
</bean>
Application Code:
MyBean getMyBean(Injection ijn1) {
return (MyBean)context.getBean("myBean", new Object[] { ijn1 })
}
But this doesn't works.
Any tips ?
You code doesn't work because spring looks for a MyBean's constructor like MyBean(Injection1 ijn1); you have to pass injection2 in this way.
MyBean getMyBean(Injection ijn1) {
return (MyBean)context.getBean("myBean", new Object[] { ijn1, context.getBean("injection2") })
}
If you want to use your code another way is to have partial inject in this way:
class MyBean{
Injection1 ijn1;
Injection2 ijn2;
MyBean(Injection1 ijn1){
this.ijn1=ijn1;
}
public void setIjn2(Injection2 ijn2I ) {
this.ijn2 = ijn2;
}
}
and in xml
<bean name="myBean" class="MyBean" scope="prototype">
<property name="inj2" ref="injection2" />
</bean>
<bean name="injection2" class="Injection2">
</bean>

NPE when injection class in spring

I have problem when injection class.
In my configuration I have one class which setting level of login and then one variable for setting level:
<bean id="config" class="..." init-method="init">
<property name="log4jConfig" ref="log4j" />
<property name="levelLogging" value="9" />
</bean>
and code:
public void setlevelLogging(Integer level) {
if (level == null) {
set0();
} else {
switch (level) {
case 0:
set0();
break;
case 9:
set9();
}
}
this.level = level;
}
private void set0() {
log4jConfig.setLoggerLevel("org.springframework.ws.server.MessageTracing", "OFF");
log4jConfig.setLoggerLevel("org.app", "INFO");
log4jConfig.setLoggerLevel("org.springframework.ws.client.MessageTracing", "OFF");
}
public void setLog4jConfig(Log4jConfigJmx log4jConfig) {
this.log4jConfig = log4jConfig;
}
when I want to run this code I got NPE because log4jConfig is null when is setlevelLogging calling.
How I can solve this exception ?
now I exclude this class from properties and creating new class in configClass:
Log4jConfigJmx log4jConfig = new Log4jConfigJmx()
but I dont think this is good idea
EDIT:
I try example below but I have still some problem:
first I got this exception:
[ERROR] java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given
because I am using transactional and AOP so I add default constructor to the class so I have two of them:
public Config() {
}
public Config(Log4jConfigJmx log4jConfig, Integer level) {
this.log4jConfig = log4jConfig;
setlevelLoggin(level);
}
setlevelLogging ...
<bean id="config" class="..." init-method="init">
<constructor-arg index="0" ref="log4j" />
<constructor-arg index="1" value="9" />
</bean>
but now I have still NPE
pls help
You should place the code from the method setLoggingLevel in the init method.
Only leave this.level = level so it is a plain setter.
The init method is called after all the properties have been set.
----EDIT after comment----
After you comment I suggest you use a constructor:
public Class(Integer level, Log4jConfigJmx log4jConfig){
this.log4jConfig = log4jConfig;
setLevelLogging(level);
}
<bean id="config" class="..." init-method="init">
<constructor-arg index="0" value="9"/>
<constructor-arg index="1" ref="log4j"/>
</bean>
You can use constructor-args like this:
<bean id="config" class="..." init-method="init">
<constructor-arg><ref bean="anotherExampleBean"/></constructor-arg>
<constructor-arg type="int"><value>9</value></constructor-arg>
</bean>
Then your constructor can initialise both variables whilst keeping your setter methods in place.
E.g.
public MyClass(Log4jConfigJmx log4jConfig, Integer level) {
this.log4jConfig = log4jConfig;
this.setLevelLogging(level);
}
You can also try making the log4jConfig autowired.
Change your bean config back to:
<bean id="config" class="..." init-method="init">
<property name="levelLogging" value="9" />
</bean>
And then simply annotate a field in your class to be autowired:
#Autowired
private Log4jConfigJmx log4jConfig;
Add this at the top of your spring context to enable annotations:
<context:annotation-config />

AnnotationConfigApplicationContext and parent context

I'm facing an issue trying to define a context hierarchy using AnnotationConfigApplicationContext.
The problem is when defining a module context inside beanRefContext.xml and setting the 'parent' property with another context (XML/Annotated based).
Example:
beanRefContext.xml in module A
<bean id="moduleA_ApplicationContext"
class="org.springframework.context.support.ClassPathXmlApplicationContext">
<property name="configLocations">
<list>
<value>classpath:db-context.xml</value>
</list>
</property>
</bean>
db-context.xml
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
p:driverClassName="org.h2.Driver"
p:url="jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;MODE=MySQL;TRACE_LEVEL_SYSTEM_OUT=2"/>
<!-- Hibernate Session Factory -->
<bean name="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="useTransactionAwareDataSource" value="true"/>
<property name="packagesToScan">
<list>
<value>com.example.model</value>
</list>
</property>
<property name="hibernateProperties">
<!-- hibernate props -->
</property>
</bean>
beanRefContext.xml in module B
<bean id="moduleB_ApplicationContext"
class="org.springframework.context.annotation.AnnotationConfigApplicationContext" >
<property name="parent" ref="moduleA_ApplicationContext"/>
<constructor-arg>
<list>
<value>com.example.dao</value>
</list>
</constructor-arg>
</bean>
FooHibernateDao
class FooHibernateDao implements FooDao {
#Autowired
#Qualifier("sessionFactory")
private SessionFactory sessionsFactory;
// CRUD methods
}
Module B application context fails to find bean defined in module A application context.
From looking at the code of AnnotationConfigApplicationContext it seems that the scanning process doesn't use the parent as a reference to resolve beans.
Is there something I'm doing wrong or my attempt to create a hierarchy is impossible with annotation configuration?
The problem stems from the fact that the constructor of the AnnotationConfigApplicationContext does the scan. Thus the parent is not set at this stage, it is only set after the scan is done as the parent is set by a property - thus the reason why it does not find your bean.
The default AnnotationConfigApplicationContext bean does not have a constructor that takes a parent factory - not sure why.
You can either use the normal xml based application context and configure your annotation scanning in there or you can create a custom fatory bean that will do create the annotation application context. This would specify the parent reference and then do the scan.
Take a look at the source...
The factory would look like this:
public class AnnotationContextFactory implements FactoryBean<ApplicationContext> {
private String[] packages;
private ApplicationContext parent;
#Override
public ApplicationContext getObject() throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.setParent(parent);
context.scan(packages);
context.refresh();
return context;
}
#Override
public Class<ApplicationContext> getObjectType() {
return ApplicationContext.class;
}
#Override
public boolean isSingleton() {
return true;
}
public void setPackages(String... args) {
this.packages = args;
}
public void setParent(ApplicationContext parent) {
this.parent = parent;
}
}
And your bean definition:
<bean id="moduleB_ApplicationContext" class="za.co.test2.AnnotationContextFactory">
<property name="parent" ref="moduleA_ApplicationContext" />
<property name="packages">
<list>
<value>za.co.test2</value>
</list>
</property>
</bean>
Don't use XML for the child context.
Use ctx.setParent then ctx.register. Like this:
public class ParentForAnnotationContextExample {
public static void main(String[] args) {
ApplicationContext parentContext = new AnnotationConfigApplicationContext(ParentContext.class);
AnnotationConfigApplicationContext childContext = new AnnotationConfigApplicationContext();
childContext.setParent(parentContext);
childContext.register(ChildContext.class); //don't add in the constructor, otherwise the #Inject won't work
childContext.refresh();
System.out.println(childContext.getBean(ParentBean.class));
System.out.println(childContext.getBean(ChildBean.class));
childContext.close();
}
#Configuration
public static class ParentContext {
#Bean ParentBean someParentBean() {
return new ParentBean();
}
}
#Configuration
public static class ChildContext {
#Bean ChildBean someChildBean() {
return new ChildBean();
}
}
public static class ParentBean {}
public static class ChildBean {
//this #Inject won't work if you use ChildContext.class in the child AnnotationConfigApplicationContext constructor
#Inject private ParentBean injectedFromParentCtx;
}
}
I run into the same problem,
Another possibility is to extend AnnotationConfigApplicationContext and add just the required constructor or build the context programmatically, if you are instantiating the AnnotationConfigApplicationContext from java.
What I did was the following:
BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance("classpath:beanRefContext.xml");
BeanFactoryReference parentContextRef = locator.useBeanFactory("ear.context");
ApplicationContext parentContext = (ApplicationContext) parentContextRef.getFactory();
childContext.setParent(parentContext);
And guess what, it worked :)
PS: If anyone knows how to replace the classpath:beanRefContext.xml with an #Configuration class, please let us all know.
I also run into a similar problem and after some research I found the following approach using a constructor from AnnotationConfigApplicationContext that allows to set the a hierarchy between contexts
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory(parentContext);
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(lbf);
context.register(annotatedClass1.class, annotatedClass2.class);
context.refresh();

Categories