ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Spring开发时Logger自动注入

2022-07-29 11:03:59  阅读:250  来源: 互联网

标签:Spring class bean Passed Logger 注入 logger 属性


起因是在每一个类中我都得写上下面的代码:

private Logger logger = LoggerFactory(this.getClass().getSimpleName());

虽然不是很长,但是我就是不想写,每次写这个我都会很烦。开发中有很多属性都是这样。

然后,我就想到,如何将这一过程自动化?我在看黑马程序员的网课时,那个老师是通过继承一个基类来实现的,但是这个操作很昂贵啊,占用了这个类唯一一次的继承机会。

接口实现

第一个想到的办法就是使用接口,一个类可以实现多个接口,在接口中提供默认方法来返回Logger

public interface LogOwnerComponent {
    default Logger logger() {
        return logger(this.getClass().getName());
    }
    default Logger logger(String name) {
        return LoggerFactory.getLogger(name);
    }
}

上面的接口给了两个方法,无参的返回以当前类名为名字的Logger,有参的指定一个名字。

接口实现的问题:

  1. 调用者总得实现LogOwnerComponent接口,并且在使用到logger时要调用方法,很难看:
    @Controller
    public class MyController implements LogOwnerComponent{
        @RequestMapping("/")
        public String test() {
            logger().info("xxx");
        }
    }
    
  2. 每次都调用LoggerFactory.getLogger创建一个新的Logger,我不知道该方法是否对于名字相同的Logger有缓存,但是这种写法总归不太讲究。

InstantiationAwareBeanPostProcessor

第二个想到的办法就是实现一个BeanPostProcessor,然后在某些规则下自动给类注入一个Logger

// 该BeanPostProcessor在Bean对象实例化后检测Bean中是否有一个
// 标注了`@Passed`注解的未赋值的`org.slf4j.Logger`类型的属性
// 如果有,就为这个属性注入值
@Component
public class LoggerInjectBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
    // 
    private Logger logger = LoggerFactory.getLogger(LoggerInjectBeanPostProcessor.class);

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        // 获取该Bean的所有属性
        Arrays.stream(bean.getClass().getDeclaredFields())
                // 检测该属性是否能设置成Logger.class的实例
                .filter(f -> f.getType().isAssignableFrom(Logger.class))
                // 检测该属性上是否具有@Passed注解
                .filter(f -> f.getAnnotation(Passed.class) != null)
                // 检测该属性是否尚未被设置
                .filter(f -> {
                    f.setAccessible(true);
                    try {
                        System.out.println("already set check "+f);
                        return f.get(bean) == null;
                    } catch (IllegalAccessException e) {
                        return false;
                    }
                })
                // 尝试设置属性
                .forEach(f -> {
                    logger.debug("Found there's a Logger field annotated with `@Passed`. Bean [" + beanName + "] field name: [" + f.getName() + "].");
                    // 以Passed的value作为logger的name,如果没有就用类全限定名
                    Passed passed = f.getAnnotation(Passed.class);
                    String loggerName = passed.value();
                    if (null == loggerName || loggerName.equals("")) {
                        loggerName = bean.getClass().getName();
                    }

                    try {
                        f.set(bean, LoggerFactory.getLogger(loggerName));
                    } catch (IllegalAccessException e) {
                        logger.warn("Inject logger field! In bean [" + beanName + "], field is [" + f.getName() + "]");
                    }
                });
        return true;
    }

}

@Passed注解是自己提供的一个注解:

/**
 * 在本项目中,一些BeanPostProcessor会在合适的时机检测Bean中是否具有某些属性,并且如果上面有`@Passed`注解,代表该Bean期望这个属性由外界初始化并传入
 *
 * 一个典型的例子就是`LoggerInjectBeanPostProcessor`会检测bean中是否有一个标注了`@Passed`注解的`org.slf4j.Logger`的属性,如果有,就自动帮它初始化
 *
 * 就像上面的例子,项目中总有些东西的初始化很繁琐,充满模式化,但又不得不做,`@Passed`注解就是为了解决这个问题
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Passed {
    /**
     * 对于不同类型的属性,value的作用可能不一样,具体请看对应的BeanPostProcessor上的注释
     * @return
     */
    String value() default "";
}

然后,对于项目中的所有被Spring的ApplicationContext所管理的组件,只需要这样,即可在它实例化后获得一个Logger:

@Passed
private Logger log;

你还可以给Logger指定名字:

@Passed("customLoggerName")
private Logger log;

该方法的问题:

  1. 这应该不算个问题:Logger的注入在实例化后阶段,有些基础ApplicationContext会在初始化阶段对所有未满足简单属性进行自动赋值,此时我们的Logger已经在实例化阶段被满足,所以如果有的用户期待在初始化阶段它还会被赋值,这个行为不会发生。不过我们的方法中必须得有@Passed注解,而且用户一般不会混用这两种行为,而且在常用的高级ApplicationContext中,这个未满足简单属性自动赋值的行为都被关闭了,所以不太会造成问题

标签:Spring,class,bean,Passed,Logger,注入,logger,属性
来源: https://www.cnblogs.com/lilpig/p/16531525.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有