I have the following bean:
package com.test;
#Component
public class Sample{
String modified = null;
#Value("${url}")
private String url;
public Sample(){
System.out.println(url );
if(baseUrl.equals(""){
throw new RuntimeException("missing");
}
else{
modified = "test"+url;
}
}
}
I have added:
<context:annotation-config />
<context:property-placeholder location="classpath:test.properties"/> & <context:component-scan base-package="com.test"/>
and trying to access above "modified" field as below
<bean id="url" class="java.lang.String">
<constructor-arg value="#{sample.modified}" />
</bean>
in my application context. But I keep getting the following error:
Field or property 'sample' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext'
Not sure why i get this error?
When Spring creates the object it uses the default constructor. It can't set the property until after it constructs it. Instead of what you have, try this to see if the value is being set.
#PostConstruct
public void init(){
System.out.println(url );
if(baseUrl.equals(""){
throw new RuntimeException("missing");
}
}
JustinKSU's answer is right. You have another option: inject value via constructor using #Autowired:
#Component
public class Sample {
#Autowired
public Sample(#Value("${url}") String url) {
System.out.println(url);
if(url.equals("") {
throw new RuntimeException("missing");
}
}
}
Related
I am currently using spring. I created a restClient that uses a service based on another module. I am trying to instantiate this restClient but I always receive a null pointer exception. After investigation, it appears that the encapsulated attribute (objects) are not instantiating, their nested encapsulated neither.
This is the member variables, my class, the constructor and a method I am trying to call.
private OAuth2RestTemplate oAuth2RestTemplate;
private ConsumedDestinationModel consumedDestinationModel;
#Autowired
private DestinationService<ConsumedDestinationModel> destinationService ;
public DefaultSacRestClient()
{
consumedDestinationModel = getSacConsumedDestinationModel(getDestinationService().getAllConsumedDestinations());
oAuth2RestTemplate = configureOAuth2Credentials(getConsumedDestinationModel());
}
#Override
public List<Story> fetchStories() {
List<Story> stories = new ArrayList<>();
String endpoint = getConsumedDestinationModel().getUrl() + STORIES_URL;
stories.add(oAuth2RestTemplate.getForObject(endpoint, Story.class));
return stories;
}
in my constructor, both methods getSacConsumedDestinationModel() &
configureOAuth2Credentials() returns an object.
but the method getSacConsumedDestinationModel() uses the destinationService which is not instantiated. I dont understand why the destinationService member variable is not instantiated.
Here is the application context which corresponds only to the module I am working on. This application context is appended to the global application context of the application
<bean id="defaultSacRestClient" class="de.hybris.platform.sacintegrationbackoffice.client.impl.DefaultSacRestClient">
<property name="destinationService" ref="destinationService"/>
</bean>
<bean id="destinationService" class="de.hybris.platform.apiregistryservices.services.impl.DefaultDestinationService">
<property name="destinationDao" ref="destinationDao"/>
</bean>
<bean id="destinationDao" class="de.hybris.platform.apiregistryservices.dao.impl.DefaultDestinationDao">
<property name="flexibleSearchService" ref="defaultFlexibleSearchService"/>
<property name="modelService" ref="defaultModelService"/>
</bean>
Is it a bean configuration problem? Am I missing something? There is definitely something I do wrong.
Two possible problems:
Is the class annotated with #Component or #Service?
Some objects are instantiated with the constructor and some aren't.
Try
#Service
public class DefaultSacRestClient {
private final OAuth2RestTemplate oAuth2RestTemplate;
private final ConsumedDestinationModel consumedDestinationModel;
private final DestinationService<ConsumedDestinationModel> destinationService ;
public DefaultSacRestClient(DestinationService<ConsumedDestinationModel> destinationService) {
this.destinationService = destinationService;
this.consumedDestinationModel = getSacConsumedDestinationModel(destinationService.getAllConsumedDestinations());
this.oAuth2RestTemplate = configureOAuth2Credentials(getConsumedDestinationModel());
}
...
}
I have the next simple application(class Message has only one method which prints incoming message and has no interest for the question):
package messager.spring;
public class User {
private Messenger misiger;
private String name;
public User(String name) {
this.name = name;
}
public void setMessenger(Messenger messinger) {
this.misiger = messinger;
}
public void send(String mess) {
String message = name + " sent message " + "'" + mess + "'";
misiger.send(message);
}
// public String getname() {
// return name;
// }
}
Main class:
package messager.spring;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) context.getBean("user");
user.send("testing3...");
}
}
Spring configuration file:
<?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
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="user" class="messager.spring.User" autowire="byName">
<constructor-arg type="java.lang.String" value="Vova2"/>
</bean>
<bean id="messenger" class="messager.spring.MobileMessenger"/>
</beans>
I used autowiring for class User autowiring Messanger class. According to documentation :
When ByName is used, it then tries to match and wire its properties with the beans defined by the same names in the configuration file. If matches are found, it will inject those beans otherwise, it will throw exceptions.
This configuration works but I dont understand why((( I dont have property with name messenger inside User class((( I changed it on purpose to misiger. And it still works. It seems that bean id directly depends not on property name, but on setter name!!! Is it so?
YES, you are right. As described here :
Spring will lowercase the first letter after “set” in the method name and use the rest of the method name as-is for deducing the property name.
So not the member variable but the setter defines the property name.
That's how JavaBeans work, by looking at naming conventions.
The underlying reference isn't necessarily relevant.
The name of your property is messenger because there's a getter called that.
i want pass parameter to #autowired ref like
public CoreDao {
private String taskId;
private final String sql = "select ....."+getTaskId()+".....";
public CoreDao(String taskId){
if(taskId.length != 0){
this.taskId = taskId;
}else{
this.taskId = "0";
}
public getTaskId(){
return this.taskId;
}
}
xml is:
<bean id="coreDao" class="Coredao" scope="prototype">
<constructor-arg type="java.lang.String" value=""/>
</bean>
and the CoreService is
#service
CoreService implement ICoreService{
#Autowired
pirvate CoreDao;
}
and xml is
<bean id="coreService" class="CoreService" scope="prototype">
<property name="coreDao" ref="coreDao"/>
</bean>
and i want use getBean("coreService","123") to get the bean with dynamic reference of coreDao.
However,when i use getBean("coreService","123"),the exception is:
error creating bean with name "coreService" defined in file ....xml,could not resolve matching constructor (hint:specify index/type/name arguments for simple parameter to avoid ambiguities.
how could do that?thanks your help.
getBean(String, Object ...) is applicable to bean's constructors or factory methods.
Your CoreService should have CoreService(String s) constructor in order to use this method.
If you want to create many CoreService instances with different parameters, you can create a factory bean which creates all instances for you and puts them together, like
#Component
public class CoreServiceFactoryBean {
#Autowired ApplicationContext ctx;
public CoreService getBean(String param) {
CoreService coreService = ctx.getBean("coreService");
CoreDao coreDao = ctx.getBean("coreDao", parameter);
coreService.setCoreDao(coreDao);
return coreService;
}
}
This way, the logic of creating bean and using it remains separate. Using factories is pretty common to configure prototype scoped beans.
OK, I haven't used Spring in a while, so I'm a little rusty. Not sure if I missed something in all of this or not. My appContext.xml for Spring states that 'no setter is found for property testBean in class com.ztp.spring.injection.TestBean.
Here is the appContext.xml file:
<bean id="myTestBean" class="com.ztp.spring.injection.TestBean" />
<bean id="myTestClass" class="com.ztp.spring.injection.TestClass">
<property name="testBean" ref="myTestBean" />
</bean>
and here is the TestClass.java file in its entirety:
public class TestClass {
TestBean testBean;
public void setTestClass(TestBean testBean) {
this.testBean = testBean;
}
public void fillBean() {
testBean.setId(5);
testBean.setTestAnimal("sheltie");
}
}
I have another program that I worked on months ago, and its the same logic-wise, and it works. So I'm not sure what I'm missing.
If its already been answer or you need more info, just say so, I'd like to figure this out.
Thank you in advance.
Typo in the method name. What you meant is this:
public void setTestBean(TestBean testBean) {
this.testBean = testBean;
}
You had setTestClass. This would violate the JavaBean conventions.
The method name should match the property name for the bean:
public void setTestBean(TestBean testBean) {
Given a set of classes wired together by spring. There are several classes that are used with different configuration in multiple instances in the environment. They have different beanid of course.
The problems:
When they make log entries, we dont know exactly which bean made the log, since the log4j displays the classname only
I know that I could use logger instantiated by spring InitializationBean+BeanNameAware interface methods, but I do not want to do it, since I do not want to implement them in all classes
The solution could be:
Having some effect on bean factory to store the id of the beans in a map with the bean reference (key is the ref, name is the value)
Creating an aspect to be applied on every method, that would set an "BeanName" MDC entry in Log4j before the call, and would restore it to the previous value after the call. Meanwhile the previous beannames could be stored in a threadlocal in a stack.
The questions:
How can I change/configure the bean factory to do this trick for me? Is there any customization point I could use to this aim?
How can I avoid memory leaks in the map in the beanid registry? Maybe the registry is not needed at all, if somehow spring can look up the id for a reference.
Do you have any better idea, that would not result in changing ten thousand classes?
Thanks in advance.
UPDATE:
- Does anyone have solution for the prototype beans?
I have managed to hack something together based on this Spring AOP Example.
I am not yet up to speed with Spring 3 so I have implemented this using Spring 2.5 - I dare say there are more elegant ways of achieving what you want. I have implemented this using System.out's for simplicity but these could easily be converted to log4j calls.
Initially I create a map between the Spring's bean names and the string representation of the object (InitBean). This map is used inside the MethodInterceptor - I did try making the MethodInterceptor an InitializingBean but the MethodInterceptor stopped working for some reason.
Performing an equals between the bean passed in via the MethodInterceptor and the other beans in the application context did not work. e.g. by using something like "ctx.getBeansOfType(GoBean.class)" inside the MethodInterceptor. I presume this is because the object passed in via the MethodInvocation was a GoBean whereas objects obtained from the application context at this point are proxied (e.g. something like example.GoBean$$EnhancerByCGLIB$$bd27d40e).
This is why I had to resort to a comparison of object string representations (which is not ideal). Also I specifically do not want to activate the MethodInterceptor logic when calling the "toString" method on an object (as since I'm using toString elsewhere leads to infinite loops and StackOverflow).
I hope this is useful,
applicationContext.xml
<beans>
<bean name="initBean" class="example.InitBean"/>
<bean name="methodLoggingInterceptor" class="example.MethodLoggingInterceptor">
<property name="initBean" ref="initBean"/>
</bean>
<bean name="proxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<value>go*</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>methodLoggingInterceptor</value>
</list>
</property>
</bean>
<bean name="goBean1" class="example.GoBean" />
<bean name="goBean2" class="example.GoBean" />
<bean name="goBean3" class="example.GoBean" />
</beans>
GoBean.java
public class GoBean {
public void execute(){
System.out.println(new Date());
}
}
SimpleTestClass.java
public static void main( String[] args ){
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
ArrayList<GoBean> goBeans = new ArrayList<GoBean>();
goBeans.add((GoBean) ctx.getBean("goBean1"));
goBeans.add((GoBean) ctx.getBean("goBean2"));
goBeans.add((GoBean) ctx.getBean("goBean3"));
for(GoBean g: goBeans){
g.execute();
}
}
InitBean.java
public class InitBean implements ApplicationContextAware, InitializingBean {
private ApplicationContext ctx;
private Map<String, String> beanMap = new HashMap<String,String>();
public void setApplicationContext(ApplicationContext ac) throws BeansException {
ctx = ac;
}
public void afterPropertiesSet() throws Exception {
for(String beanName: ctx.getBeanNamesForType(GoBean.class)){
beanMap.put(ctx.getBean(beanName).toString(), beanName);
}
}
public Map<String,String> getBeanMap(){
return beanMap;
}
}
MethodLoggingInterceptor.java
public class MethodLoggingInterceptor implements MethodInterceptor{
private InitBean initBean;
public Object invoke(MethodInvocation method) throws Throwable {
if (!"toString".equals(method.getMethod().getName())) {
StringBuilder sb = new StringBuilder();
Object obj = method.getThis();
if (obj instanceof GoBean) {
Map<String,String> beanMap = initBean.getBeanMap();
String objToString = obj.toString();
if (beanMap.containsKey(objToString)) {
System.out.println(beanMap.get(objToString));
sb.append("bean: ");
sb.append(beanMap.get(objToString));
sb.append(" : ");
}
}
sb.append(method.getMethod().getDeclaringClass());
sb.append('.');
sb.append(method.getMethod().getName());
System.out.println(sb.toString() + " starts");
Object result = method.proceed();
System.out.println(sb.toString() + " finished");
return result;
} else {
return method.proceed();
}
}
public void setInitBean(InitBean ib) {
this.initBean = ib;
}
}