Запись идентификатора компонента в файл журнала log4j без интерфейса BeanNameAware - PullRequest
4 голосов
/ 06 сентября 2011

Учитывая набор классов, связанных весной.Есть несколько классов, которые используются с различной конфигурацией в нескольких экземплярах в среде.Конечно, у них разные beanid.

Проблемы:

  • Когда они делают записи в журнале, мы не знаем точно, какой бин сделал журнал, так как log4j отображает только имя класса
  • Я знаю, что мог бы использовать регистратор, созданный с помощью методов интерфейса Spring InitializationBean + BeanNameAware, но я не хочу этого делать, поскольку я не хочу реализовывать их во всех классах

решение может быть следующим:

  • Имеет некоторый эффект на фабрику бинов для сохранения идентификатора бинов в карте со ссылкой на бин (ключ - ссылка, имя - значение)
  • Создание аспекта, который будет применяться к каждому методу, который установит запись MDC «BeanName» в Log4j перед вызовом и восстановит его до предыдущего значения после вызова.Между тем предыдущие bean-имена могут быть сохранены в локальном потоке в стеке.

Вопросы:

  • Как мне изменить / настроить фабрику bean-компонентов, чтобы выполнить этот трюк для меня?Есть ли какая-либо точка настройки, которую я мог бы использовать для этой цели?
  • Как я могу избежать утечек памяти на карте в реестре beanid?Может быть, реестр вообще не нужен, если каким-то образом Spring может поискать идентификатор для ссылки.
  • У вас есть идея получше, что не приведет к изменению десяти тысяч классов?

Заранее спасибо.

ОБНОВЛЕНИЕ: - У кого-нибудь есть решение для прототипов бобов?

1 Ответ

5 голосов
/ 16 сентября 2011

Мне удалось что-то взломать, основываясь на этом примере Spring AOP .

Я еще не освоил Spring 3, поэтому я реализовал это с помощью Spring 2.5 - осмелюсь сказать, что есть более элегантные способы достижения того, чего вы хотите. Я реализовал это, используя System.out для простоты, но их можно легко преобразовать в вызовы log4j.

Сначала я создаю карту между именами bean-компонентов Spring и строковым представлением объекта ( InitBean ). Эта карта используется внутри MethodInterceptor - я попытался сделать MethodInterceptor InitializingBean , но MethodInterceptor по какой-то причине перестал работать.

Выполнение равных между компонентом, переданным через MethodInterceptor , и другими компонентами в контексте приложения не сработало. например используя что-то вроде " ctx.getBeansOfType (GoBean.class) " внутри MethodInterceptor . Я предполагаю, что это потому, что объект, переданный через MethodInvocation , был GoBean , тогда как объекты, полученные из контекста приложения в этой точке, являются прокси (например, что-то вроде пример .GoBean $$ EnhancerByCGLIB $$ bd27d40e ).

Вот почему мне пришлось прибегнуть к сравнению строковых представлений объекта (что не идеально). Также я специально не хочу активировать логику MethodInterceptor при вызове метода " toString " для объекта (так как использование toString в другом месте приводит к бесконечные циклы и StackOverflow).

Надеюсь, это полезно,

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