2011-06-14 93 views
24

我使用spring with aspect-j註釋支持以允許@Loggable註釋。這允許根據配置自動記錄類。使用java註釋注入記錄器依賴關係

我想知道如果我能以某種方式使用此批註的SLF4J Logger變量暴露到類直接使用,所以我沒有做一些事來的效果:

Logger logger = LoggerFactory.getLogger(MyClass.class); 

它如果上述內容由於註釋而隱含可用,那麼會很好,我可以在沒有聲明的情況下去做logger.debug("...");。我不確定這是否可能。

+0

你說的是實際添加一個記錄器領域的一個bean /類,沒有一個? – sourcedelica 2011-06-16 04:38:42

回答

21

您可以使用BeanPostProcessor界面,該界面由ApplicationContext爲所有創建的bean調用,因此您有機會填充相應的屬性。

我創建了一個簡單的實現,它這是否:

import java.lang.reflect.Field; 
import java.util.List; 

import net.vidageek.mirror.dsl.Mirror; 

import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
import org.springframework.beans.BeansException; 
import org.springframework.beans.factory.config.BeanPostProcessor; 
import org.springframework.stereotype.Component; 

@Component 
public class LoggerPostProcessor implements BeanPostProcessor { 

    @Override 
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 
     List<Field> fields = new Mirror().on(bean.getClass()).reflectAll().fields(); 
     for (Field field : fields) { 
      if (Logger.class.isAssignableFrom(field.getType()) && new Mirror().on(field).reflect().annotation(InjectLogger.class) != null) { 
       new Mirror().on(bean).set().field(field).withValue(LoggerFactory.getLogger(bean.getClass())); 
      } 
     } 
     return bean; 
    } 

    @Override 
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 
     return bean; 
    } 
} 

你不必做任何複雜的註冊步驟,因爲ApplicationContext能夠識別BeanPostProcessor實例,並自動註冊。

@InjectLogger註釋:

import java.lang.annotation.Retention; 
import java.lang.annotation.RetentionPolicy; 

@Retention(RetentionPolicy.RUNTIME) 
public @interface InjectLogger { 
} 

然後你就可以輕鬆地使用註釋:

public static @InjectLogger Logger LOGGER; 

... 

LOGGER.info("Testing message"); 

我用Mirror庫中查找註釋字段,但很明顯,你可以進行手動查找以避免這種額外的依賴性。

實際上,避免重複代碼以及從其他類複製並粘貼Logger定義的小問題(比如忘記更改參數class導致錯誤日誌)是一個不錯的主意。

+0

非常酷的解決方案。如果您想通過包或一組類來對日誌進行分組,則可以使用此解決方案以XML格式移動該配置。此外,一個非常好的完整答案和清晰的例子。 – Pace 2011-06-15 13:41:46

+0

這是糟糕的屁股解決方案。 – 2014-07-31 13:20:07

+2

想做點小事。您可以使用'standard''Autowired'註釋,'required ='false',並且不僅通過註釋搜索目標字段,而且還通過'BeanPostProcessor'中的'Logger'類型搜索目標字段,而不是引入自定義的'Log'註釋。 – nndru 2015-03-01 21:27:33

7

你無法做到這一點,但可以幫助你,在我看來,優雅的方式。請參閱@Log註釋。

+0

哇 - 龍目島很酷。基本上與Groovy AST轉換相同。它現在看起來有點出色,但也許隨着OpenJDK的發展,這將成爲標準。 – sourcedelica 2011-06-16 13:16:13

+1

我同意,龍目島應該是標準的java(+1) – surfealokesea 2015-10-25 08:55:51

+0

龍目島是非常好的,即使是創建getter,setter,構造函數或構建器......它的規則! – Henrique 2016-03-06 16:19:52

5

我覺得從@Redder解決的辦法是這樣做的很好的方式。但是,我不想包含鏡像庫,因此我編寫了一個使用Java反射庫的LoggerPostProcessor的實現。那就是:

package com.example.spring.postProcessor; 

import com.example.annotation.InjectLogger; 

import java.lang.reflect.Field; 
import java.lang.reflect.Modifier; 
import java.util.Arrays; 
import java.util.List; 

import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
import org.springframework.beans.BeansException; 
import org.springframework.beans.factory.config.BeanPostProcessor; 
import org.springframework.stereotype.Component; 

@Component 
public class LoggerPostProcessor implements BeanPostProcessor { 

    private static Logger logger = LoggerFactory.getLogger(LoggerPostProcessor.class); 

    /* (non-Javadoc) 
    * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object, java.lang.String) 
    */ 
    @Override 
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 

     List<Field> fields = Arrays.asList(bean.getClass().getDeclaredFields()); 

     for (Field field : fields) { 
      if (Logger.class.isAssignableFrom(field.getType()) && field.getAnnotation(InjectLogger.class) != null) { 

       logger.debug("Attempting to inject a SLF4J logger on bean: " + bean.getClass()); 

       if (field != null && (field.getModifiers() & Modifier.STATIC) == 0) { 
        field.setAccessible(true); 
        try { 
         field.set(bean, LoggerFactory.getLogger(bean.getClass())); 
         logger.debug("Successfully injected a SLF4J logger on bean: " + bean.getClass()); 
        } catch (IllegalArgumentException e) { 
         logger.warn("Could not inject logger for class: " + bean.getClass(), e); 
        } catch (IllegalAccessException e) { 
         logger.warn("Could not inject logger for class: " + bean.getClass(), e); 
        } 
       } 
      } 
     } 

     return bean; 
    } 

    /* (non-Javadoc) 
    * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization(java.lang.Object, java.lang.String) 
    */ 
    @Override 
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 
     return bean; 
    } 

} 
+0

這個工作,但只在頂級具體類。如果您已經擴展了包含日誌變量的Abstract類,那麼它似乎無法達到它。我不得不在具體類中聲明我的日誌變量,並在具體類中覆蓋我的getLog()方法。 – lincolnadym 2016-10-16 14:12:57

1

因爲我試圖做同樣的事情,當得到這個作爲第一個結果CDI(JSR 299:上下文和依賴注入)this link shows the straightforward way to do this using CDI(並且還使用Spring替代):

基本上,你只需要注入:

class MyClass { 
    @Inject private Log log; 

而且有一個記錄器工廠,像這樣:

@Singleton 
public class LoggerFactory implements Serializable { 
    private static final long serialVersionUID = 1L; 

    static final Log log = LogFactory.getLog(LoggerFactory.class); 

    @Produces Log createLogger(InjectionPoint injectionPoint) { 
    String name = injectionPoint.getMember().getDeclaringClass().getName(); 
    log.debug("creating Log instance for injecting into " + name); 
    return LogFactory.getLog(name); 
    } 
} 

我發現我需要添加transient到注入log,使我沒有得到我的會話鈍化範圍的例外範圍的Bean:

@Named() 
@SessionScoped() 
public class MyBean implements Serializable { 
    private static final long serialVersionUID = 1L; 

    @Inject 
    private transient Log log; 
0

先驅提供了一個非常簡單的BeanPostProcessor這確實爲你所有的魔法。您可以使用@Log註釋來註釋Spring bean的任何字段,以讓Herald在此字段中注入合適的記錄器。

支持日誌框架:

  • 的JavaTM 2平臺核心日誌框架
  • 阿帕奇百科全書登錄
  • 簡單的日誌門面的Java(SLF4J)
  • SLF4J擴展記錄
  • 的logback
  • Apache Log4j
  • 阿帕奇的Log4j 2個
  • JBoss的測井
  • Syslog4j
  • Syslog4j叉從Graylog
  • 流利記錄器的Java

另外,也可以加入其它日誌框架。

Github上回購:https://github.com/vbauer/herald

+0

我在我的彈簧啓動應用程序中試過這個庫。 我在pom.xml中添加了依賴關係 我在字段上使用了註釋。但是當我嘗試使用時,該字段爲空。 – 2017-01-04 08:07:12

+0

@HarishReddy您能否聯繫我或在Github上創建問題?這看起來很奇怪,我在不同的項目中使用過這個庫,一切都很好。另外,Hareld項目中的測試檢查與Spring Boot的集成 – 2017-01-04 19:10:48

+0

Sure.I將創建一個github倉庫,只需要重新生成代碼,我將在github上提出一個問題以及該倉庫的url。 – 2017-01-05 07:35:01

5

我想對@比較紅的解決方案的一些改進。

  • 首先 - 我們可以省略引進新的註釋@Log的,而不是我們 可以使用Spring的@Autowired註釋設置爲 「假」「需要」標誌,使彈簧不檢查豆注入或不 (因爲我們稍後會注入它)。
  • Second - use Spring's ReflectionUtils API提供了所有需要的字段發現和操作方法,因此我們不需要額外的外部依賴關係。

這裏是一個例子(在Java 8中,但可以在Java 7/6/etc中重寫)。,也被用來slf4j門面,但它可以與任何其他記錄器所取代):

@Component 
public class LoggerPostProcessor implements BeanPostProcessor { 

    @Override 
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 

     Logger logger = getLogger(bean.getClass()); 
     doWithFields(bean.getClass(), field -> { 

      makeAccessible(field); 
      setField(field, bean, logger); 

     }, field -> field.isAnnotationPresent(Autowired.class) && Logger.class.equals(field.getType())); 

     return bean; 
    } 
    ... 
} 
... 
//logger injection candidate 
@Autowired(required = false) 
private Logger log;