使用java注解注入logger依赖

发布于 2024-11-15 08:23:52 字数 285 浏览 0 评论 0原文

我使用带有aspect-j注释支持的spring来允许@Loggable注释。这允许根据配置自动记录类。

我想知道是否可以以某种方式使用此注释将 slf4j Logger 变量公开到类中以供直接使用,这样我就不必执行以下操作:

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

如果由于注释,上面的内容是隐式可用的,我可以在没有声明的情况下执行 logger.debug("..."); 。我不确定这是否可能。

I am using spring with aspect-j annotation support to allow for an @Loggable annotation. This allows automatic logging on a class based on the configuration.

I am wondering if I can somehow use this annotation to expose an slf4j Logger variable into the class for direct use, so that I don't have to do something to the effect of:

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

It would be nice if the above was implicitly available due to the annotation and I could just go about doing logger.debug("..."); without the declaration. I'm not sure if this is even possible.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

如痴如狂 2024-11-22 08:23:52

您可以使用 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 参数时,这会导致错误的日志。

You can use the BeanPostProcessor interface, which is called by the ApplicationContext for all created beans, so you have the chance to fill the appropriate properties.

I created a simple implementation, which does that:

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

You don't have to do any complex registration step, since the ApplicationContext is capable of recognizing BeanPostProcessor instances and automatically register them.

The @InjectLogger annotation is:

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

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

And then you can easily use the annotation:

public static @InjectLogger Logger LOGGER;

...

LOGGER.info("Testing message");

I used the Mirror library to find the annotated fields, but obviously you may perform a manual lookup in order to avoid this additional dependency.

It's actually a nice idea to avoid repeated code, and even small issues that come from copying and paste the Logger definitions from other classes, like when we forget to change the class parameter, which leads to wrong logs.

み青杉依旧 2024-11-22 08:23:52

您无法通过方面来做到这一点,但是 在我看来,可以以一种优雅的方式帮助您。请参阅 @Log 注释。

You can't do it with an aspect, but can help you in a, in my opinion, elegant way. See @Log annotation.

遗弃M 2024-11-22 08:23:52

我认为@Redder 的解决方案是做到这一点的好方法。但是,我不想包含 Mirror 库,因此我编写了一个使用 Java Reflect 库的 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;
    }

}

I think the solution from @Redder is a great way of doing this. However, I didn't want to include the Mirror library so I wrote an implementation of LoggerPostProcessor that uses the Java reflect library instead. Here it is:

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

}
眸中客 2024-11-22 08:23:52

我想对@Redder 的解决方案进行一些改进。

  • 首先 - 我们可以省略新注释 @Log 的引入,而是我们
    可以使用 Spring 的 @Autowired 注释,并将“required”标志设置为
    'false' 使 Spring 不检查 bean 是否被注入
    (因为,我们稍后会注入它)。
  • 其次 - 使用 Spring 的 ReflectionUtils API 提供所有
    字段发现和操作所需的方法,因此我们不需要额外的外部依赖项。

这是一个示例(在 Java 8 中,但可以在 Java 7/6/等中重写,也使用 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;

I want to make some improvements to @Redder's solution.

  • First - we can omit introduction of new annotation @Log, instead we
    can use Spring's @Autowired annotation with 'required' flag set to
    'false' to make Spring not to check that bean was injected or not
    (because, we will inject it later).
  • Second - use Spring's ReflectionUtils API that provides all
    needed methods for field discovering and manipulation, so we don't need additional external dependencies.

Here an example (in Java 8, but can be rewritten in Java 7/6/etc., also slf4j facade is used but it can be replaced with any other logger):

@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;
再可℃爱ぅ一点好了 2024-11-22 08:23:52

由于我在尝试在 CDI 中执行相同操作时得到了第一个结果(JSR 299:上下文和依赖注入),此链接显示了使用 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;

Since I got this as the first result when trying to do the same thing in CDI (JSR 299: Context and Dependency Injection), this link shows the straightforward way to do this using CDI (and also an alternative using Spring):

Basically, you only need to inject:

class MyClass {
   @Inject private Log log;

And have a logger factory like so:

@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);
    }   
}

I found that I needed to add transient to the injected log so that I did not get a passivating scope exception in my session scoped beans:

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

    @Inject
    private transient Log log;
口干舌燥 2024-11-22 08:23:52

Herald 提供了一个非常简单的 BeanPostProcessor,它可以为您完成所有的任务。您可以使用 @Log 注释来注释 Spring bean 的任何字段,以便 Herald 在该字段中注入合适的记录器。

支持的日志框架:

  • JavaTM 2 平台的核心日志框架
  • Apache Commons Logging
  • Simple Logging Facade for Java (SLF4J)
  • SLF4J Extended logger
  • Logback
  • Apache Log4j
  • Apache Log4j 2
  • JBoss Logging
  • Syslog4j
  • Syslog4j 来自 Graylog
  • Fluent Logger for Java 的

分支它是还可以添加其他日志框架。

Github 仓库:https://github.com/vbauer/herald

Herald provides a very simple BeanPostProcessor which does all the magic for you. You can annotate any field of Spring bean with a @Log annotation to let Herald inject suitable logger in this field.

Supported logging frameworks:

  • JavaTM 2 platform's core logging framework
  • Apache Commons Logging
  • Simple Logging Facade for Java (SLF4J)
  • SLF4J Extended logger
  • Logback
  • Apache Log4j
  • Apache Log4j 2
  • JBoss Logging
  • Syslog4j
  • Syslog4j fork from Graylog
  • Fluent Logger for Java

It is also possible to add other logging frameworks.

Github repo: https://github.com/vbauer/herald

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文