[英]Using java annotation to inject logger dependency
I am using spring with aspect-j annotation support to allow for an @Loggable
annotation. 我使用带有aspect-j注释支持的@Loggable
来允许@Loggable
注释。 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: 我想知道我是否可以以某种方式使用此注释将slf4j Logger
变量暴露给类以供直接使用,这样我就不必对以下内容执行某些操作:
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("...");
如果上面因为注释而隐式可用,那将是很好的,我可以去做logger.debug("...");
without the declaration. 没有声明。 I'm not sure if this is even possible. 我不确定这是否可行。
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. 您可以使用BeanPostProcessor
接口,该接口由ApplicationContext
为所有创建的bean调用,因此您有机会填充相应的属性。
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. 您不必执行任何复杂的注册步骤,因为ApplicationContext
能够识别BeanPostProcessor
实例并自动注册它们。
The @InjectLogger
annotation is: @InjectLogger
注释是:
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. 实际上,避免重复代码,甚至是从其他类复制和粘贴Logger
定义所产生的小问题也是一个好主意,比如当我们忘记更改class
参数时,这会导致错误的日志。
I think the solution from @Redder is a great way of doing this. 我认为@Redder的解决方案是一种很好的方法。 However, I didn't want to include the Mirror library so I wrote an implementation of LoggerPostProcessor that uses the Java reflect library instead. 但是,我不想包含镜像库,因此我编写了一个使用Java反射库的LoggerPostProcessor实现。 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;
}
}
I want to make some improvements to @Redder's solution. 我想对@Redder的解决方案做一些改进。
@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). 首先 - 我们可以省略新注释@Log
引入,而是我们可以使用Spring的@Autowired
注释,并将'required'标志设置为'false',以使Spring不检查是否注入了bean(因为,我们稍后会注入它) )。 ReflectionUtils
API that provides all needed methods for field discovering and manipulation, so we don't need additional external dependencies. 第二 - 使用Spring的ReflectionUtils
API,它提供了字段发现和操作所需的所有方法,因此我们不需要额外的外部依赖。 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): 这里有一个例子(在Java 8中,但可以在Java slf4j
/等中重写,也使用了slf4j
facade,但它可以替换为任何其他记录器):
@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;
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
): 因为我在尝试在CDI
(JSR 299:上下文和依赖注入)中做同样的事情时得到了这个结果, 这个链接显示了使用CDI (以及使用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: 我发现我需要在注入的log
添加transient
,以便在我的会话范围bean中没有得到钝化范围异常:
@Named()
@SessionScoped()
public class MyBean implements Serializable {
private static final long serialVersionUID = 1L;
@Inject
private transient Log log;
Herald provides a very simple BeanPostProcessor which does all the magic for you. Herald提供了一个非常简单的BeanPostProcessor,它为您提供了所有的魔力。 You can annotate any field of Spring bean with a @Log annotation to let Herald inject suitable logger in this field. 您可以使用@Log注释来注释Spring bean的任何字段,以使Herald在此字段中注入合适的记录器。
Supported logging frameworks: 支持的日志框架:
It is also possible to add other logging frameworks. 还可以添加其他日志框架。
Github repo: https://github.com/vbauer/herald Github回购: https : //github.com/vbauer/herald
这是一篇博客文章,其中包含一个包含所有代码的完整示例: 使用Spring注入记录器
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.