Defining additional placeholder/property only in beans.xml - java

I have a list of strings, that i would like to define in beans.xml.
<util:list id="myFractions" value-type="java.lang.String">
<value>#{ T(com.myapp.longname.verylong.WelcomeController).RED_FRACTION }</value>
<value>#{ T(com.myapp.longname.verylong.WelcomeController).BLUE_FRACTION }</value>
<value>#{ T(${my.prefix}).GREEN_FRACTION }</value>
</util:list>
It works fine, but each time I need to write the full qualified constant's name com.myapp.longname.verylong.WelcomeController. I would like to write it only once. One solution I have found is to replace it with a property like my.prefix so I can write only my short prefix instead of the real full path. But then I will need to pollute the global "namespace" with property that is only needed once. I would like to define a placeholder only for this list or at least only for this beans.xml file. I have already tried to define a property directly in beans.xml with PropertyPlaceholderConfigurer and it works, but then all my inital properties are not available anymore.
So how can I avoid to writing com.myapp.longname.verylong.WelcomeController each time in a list as a prefix and only define it once? Ideally something like
<util:list id="myFractions" value-type="java.lang.String">
<define-local-placeholder name="my.prefix" value="com.myapp.longname.verylong.WelcomeController" />
<value>#{ T(${my.prefix}).RED_FRACTION }</value>
<value>#{ T(${my.prefix}).BLUE_FRACTION }</value>
<value>#{ T(${my.prefix}).GREEN_FRACTION }</value>
</util:list>

Please give a try on this
<context:property-placeholder properties-ref="shorthandHelperConstants"/>
<util:properties id="shorthandHelperConstants">
<prop key="my.prefix">com.myapp.longname.verylong.WelcomeController</prop>
</util:properties>
<util:list id="myFractions" value-type="java.lang.String">
<value>#{ T(${shorthandHelperConstants['my.prefix']}).RED_FRACTION }</value>
<value>#{ T(${shorthandHelperConstants['my.prefix']}).BLUE_FRACTION }</value>
<value>#{ T(${shorthandHelperConstants['my.prefix']}).GREEN_FRACTION }</value>
</util:list>

Try defining your prefix in properties file and use it in your beans.xml as shown here:
Best ways to deal with properties values in XML file in Spring, Maven and Eclipses
and here
Using Variable Substitution from Configuration Files in Spring
Another solution is using SpEL
<property name="userCountry" value="#{'India'}" />
Spring Expression Language (SpEL) Example

Checkout out Constants. Which allows you to extract constants and values from classes, this way you only need to define your class onetime instead for each property.
<bean id="someConstants" class="org.springframework.core.Constants">
<constructor-arg value="your-class-name=here" />
</bean>
<bean id="myFractions" factory-bean="someConstants" factory-method="getValuesForSuffix">
<constructor-arg value="FRACTION" />
</bean>
This will expose all of your constants which end with FRACTION as a Set in your context. Without you needing to define each and every constant. You could also place this into a FactoryBean making it a little easier
public class FractionExporter extends AbstractFactoryBean<List<String>> {
#Override
public Class<List> getObjectType() {
return List.class;
}
protected List<String> createInstance() {
Constants constants = new Constants(YourClass.class);
return new ArrayList(constants.getValuesForSuffix("FRACTION"));
}
}
You could now define this FactoryBean in XML
<bean id="myFractions" class="FractionExporter" />

A solution is to implement a FactoryBean,
by extending the AbstractFactoryBean class:
package test;
import java.util.*;
import org.springframework.beans.factory.config.AbstractFactoryBean;
public class ConstantListFactoryBean extends AbstractFactoryBean<List<Object>>
{
private Class<?> targetClass;
private List<String> constantNames;
public void setTargetClass(Class<?> targetClass)
{
this.targetClass = targetClass;
}
public void setConstantNames(List<String> constantNames)
{
this.constantNames = constantNames;
}
#Override
public Class<List> getObjectType()
{
return List.class;
}
#Override
protected List<Object> createInstance() throws Exception
{
ArrayList<Object> list = new ArrayList<Object>();
for(String name : constantNames)
list.add(targetClass.getField(name).get(null));
return list;
}
}
Here is a sample beans.xml that uses the ConstantListFactoryBean:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="colors" class="test.ConstantListFactoryBean">
<property name="targetClass" value="test.Colors"/>
<property name="constantNames">
<list>
<value>RED</value>
<value>BLUE</value>
<value>GREEN</value>
</list>
</property>
</bean>
</beans>
And the sample class that holds the constants:
package test;
public class Colors
{
public static final String RED = "red";
public static final String BLUE = "blue";
public static final String GREEN = "green";
}
And finally, some code that shows that it works:
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test
{
public static void main(String[] args)
{
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
List colors = (List)context.getBean("colors");
System.out.println(colors);
}
}
Output:
[red, blue, green]

Another simpler way is to create an object that wrap a class instance (e.g call ClassReflector). Then define a method on it to return the value of the constant field.
For example :
import org.springframework.util.ReflectionUtils;
public class ClassReflector {
private Class<?> clazz;
public ClassReflector(String className) throws Exception {
this.clazz = Class.forName(className);
}
public String getFieldVal(String fieldName) throws Exception {
return (String) ReflectionUtils.getField(clazz.getField(fieldName), null);
}
}
Then in the XML, use this method to get the value of different constant fields.
<bean id="cf" class="com.myapp.longname.verylong.ClassReflector">
<constructor-arg value="com.myapp.longname.verylong.WelcomeController" />
</bean>
<util:list id="myFractions" value-type="java.lang.String">
<value>#{cf.getFieldVal('RED_FRACTION')}</value>
<value>#{cf.getFieldVal('BLUE_FRACTION')}</value>
<value>#{cf.getFieldVal('GREEN_FRACTION')}</value>
</util:list>

As per comments & question, as your requirement is quite minimal , I would define one general purpose map & put key-value there & then access it inside your list:
<bean id="map" class="java.util.HashMap">
<constructor-arg>
<map key-type="java.lang.String" value-type="java.lang.String">
<entry key="my.prefix" value="com.myapp.longname.verylong.WelcomeController" />
</map>
</constructor-arg>
</bean>
<util:list id="myFractions" value-type="java.lang.String">
<value>#{ T(${map['my.prefix']}).RED_FRACTION }</value>
<value>#{ T(${map['my.prefix']}).BLUE_FRACTION }</value>
<value>#{ T(${map['my.prefix']}).GREEN_FRACTION }</value>
</util:list>
Benefit I can see here is you can put as many key-values in this map if you have multiple prefixes / properties of this kind & you want to access it in different beans without defining any global scoped beans.
Please let me know if this is of any help.

Related

Queries bean is null, how do I get it to populate?

so I'm trying to run a sql query within this java app. I think I have the DAO set up correctly but it can't find the XML file which contains my queries. The code in question for my DAO implementation is:
private Properties queries;
public void setQueries(Properties queries) {
this.queries = queries;
}
public Boolean checkAssigned(String Id) {
String sql = queries.getProperty("CHECK_IF_ASSIGNED");
Map<String,Object> params = new HashMap<>();
List<String> assignedList;
params.put(":Id",Id);
LOG.info("Checking to see if already assigned \n" + "sql=" + sql
+ "\n" + "params=" + params);
assignedList = getNamedParameterJdbcTemplate().query(sql,params,
new assignedMapper());
if (assignedList == null || assignedList.size() == 0) {
ScreenVo.setSwitch(false);
}
else {
ScreenVo.setSwitch(true);
}
return ScreenVo.getSwitch();
}
My DAO is just:
public interface ScreenDao {
Boolean checkAssigned(String Id);
}
My queries.xml file looks like:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<util:properties id="queries">
<prop key="CHECK_IF_ASSIGNED">
<![CDATA[
--Long query
]]>
</prop>
</util:properties>
</beans>
The bean for the dao in the applicationContext.xml is:
<bean id="screenDaoImpl" class="com.corp.apps.actionator.dao.ScreenDaoImpl">
<property name="dataSource" ref="datasource"/>
<property name="queries" ref="queries"/>
</bean>
And my declaration of the queries file in the applicationContext is:
<import resource="classpath:queries.xml"/>
It's declared in my web.xml in a similar fashion.
I tried to include everything that could possibly be relevant. I've tried autowiring the bean in ScreenDaoImpl.java but that didn't work. I'm really not sure where to go from here, or what I might have done wrong.
EDIT:
The exception I'm getting is:
javax.faces.event.MethodExpressionActionListener.processAction java.lang.NullPointerException
And my screenDaoImpl is declared before use as:
private static ScreenDao screenDao = new ScreenDaoImpl();
Spring-Bean screenDaoImpl must be created through Spring context, in this case Spring can inject required properties (dataSource and queries) in created bean.
I don't know your architecture of application. But I can offer you a couple of ways.
1 - If you want use screenDaoImpl in spring-bean which declared in spring-xml then you can do it like this:
<bean id="screenServiceImpl" class="com.corp.apps.actionator.service.ScreenServiceImpl">
<property name="screenDao" ref="screenDaoImpl"/>
</bean>
The better way is make all your application in Spring. And create (and inject) beans by spring-context xml. Do not create bean-objects by new. Spring can not inject properties in these objects.
If it is difficult then try to find examples of applications on the Spring site. Maybe try spring-boot (without xml).
2 - If you want use screenDaoImpl in non-spring object you can get screenDaoImpl from spring-context by "bridge". Create class:
package com.corp.apps.actionator.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class AppSpringBridge implements ApplicationContextAware {
private static ApplicationContext context;
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.context = context;
}
public static ApplicationContext getApplicationContext() {
return context;
}
}
Define bean in application-context.xml:
<bean id="springBridge" class="com.corp.apps.actionator.util.AppSpringBridge />
Spring create this bean, but method getApplicationContext() (and context property) of this bean is static. And we can use getApplicationContext() in any methods:
ScreenDao screenDao = (ScreenDao)AppSpringBridge.getApplicationContext().getBean("screenDaoImpl");
I fixed it, and for posterity's sake I'll post my solution here:
First I autowired my screenDao bean in the invoking class, and then I created a static method to set screenDao.
#Autowired
private static ScreenDao screenDao;
#PostConstruct
public static void setScreenDao(ScreenDao newScreenDao) {
screenDao = newScreenDao;
}
#PostConstruct
public ScreenDao getScreenDao() {
return screenDao;
}
I'm not really sure if getScreenDao does anything but I added it as well.
Then in my application context I created a bean I called initialize to invoke the static method.
<bean id="initialize" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="com.corp.apps.consolidator.backing.ScreenBean"/>
<property name="targetMethod" value="setScreenDao"/>
<property name="arguments">
<list>
<ref bean="screenDao"/>
</list>
</property>
</bean>
These two changes resolved my issue.

#Value Annotation and a Database PropertyPlaceHolderConfigurer

I've got two PropertyPlaceHolderConfigurer in my Spring XML. The first one obtains application properties from a file. The second one obtains user properties from database and looks like this:
<myConfiguration>
<bean id="databaseProperties"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="properties">
<bean class="org.apache.commons.configuration.ConfigurationConverter"
factory-method="getProperties">
<constructor-arg>
<ref bean="propertiesSource" />
</constructor-arg>
</bean>
</property>
</bean>
<bean id="propertiesSource" class="org.apache.commons.configuration.DatabaseConfiguration">
<constructor-arg type="javax.sql.DataSource" ref="tomcatDataSource" />
<constructor-arg value="application_properties" />
<constructor-arg value="PROPERTY_KEY" />
<constructor-arg value="PROPERTY_VALUE" />
</bean>
<bean id="propertiesService" class="com.xxx.PropertiesServiceImpl">
<property name="propertiesSource" ref="propertiesSource"></property>
</bean>
<myConfiguration>
It works properly and I can access to these properties injecting 'propertiesService' like:
#Autowired
private PropertiesService propertiesService;
Which is:
public class PropertiesServiceImpl implements PropertiesService {
#Autowired
private DatabaseConfiguration propertiesSource;
private Properties properties;
#Override
public String getProperty(String key) {
if (properties == null) {
properties = ConfigurationConverter.getProperties(propertiesSource);
}
return properties.getProperty(key);
}
#Override
public Properties getProperties() {
if (properties == null) {
properties = ConfigurationConverter.getProperties(propertiesSource);
}
return properties;
}
The problem is that I like to use #Value annotation but it does not work.
I've tried:
private #Value("#{propertiesService.helloWorld}") String helloWorld;
And, of course, the property exists and is reachable through 'propertiesService' but it results in the next error:
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 18): Property or field 'helloWorld' cannot be found on object of type 'com.infraportal.model.properties.PropertiesServiceImpl' - maybe not public?
at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:224)
at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:94)
at org.springframework.expression.spel.ast.PropertyOrFieldReference.access$000(PropertyOrFieldReference.java:46)
at org.springframework.expression.spel.ast.PropertyOrFieldReference$AccessorLValue.getValue(PropertyOrFieldReference.java:374)
at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:88)
at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:120)
at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:242)
at org.springframework.context.expression.StandardBeanExpressionResolver.evaluate(StandardBeanExpressionResolver.java:161)
Which makes me think that Spring is looking for a 'getHelloWorld()' method instead using 'getProperty(String key)'
Any suggestion?
You need to explicitly specify which method is to be invoked, on bean referred in EL, and supply the argument value as below
#Value("#{propertiesService.getProperty('helloWorld')}")
private String helloWorld;
#Value: It is used for expression-driven dependency injection.
If you want to use with below example.
sample code
#Configuration
#PropertySource("classpath:jdbc.properties")
public class AppConfig {
#Value("${jdbc.driverClassName}")
private String driverClassName;
#Value("${jdbc.url}")
private String jdbcURL;
#Value("${jdbc.username}")
private String username;
#Value("${jdbc.password}")
private String password;
.....
..
}

Invoke static method from spring config

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;
}

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 />

Categories