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 beannam开发者_如何学Ces 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;
}
}
精彩评论