2011-06-14 73 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;