복붙노트

[SPRING] 자바 주석을 사용하여 로거 의존성 삽입

SPRING

자바 주석을 사용하여 로거 의존성 삽입

@Loggable 어노테이션을 허용하기 위해 aspect-j 어노테이션 지원과 함께 spring을 사용하고있다. 이렇게하면 구성에 따라 클래스에 자동 로깅이 가능합니다.

어떻게 든이 주석을 사용하여 slf4j 로거 변수를 직접 사용하기 위해 클래스에 노출시킬 수 있는지 궁금해서 그 결과를 위해 뭔가 할 필요가 없습니다.

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

주석이 주석으로 인해 위와 같이 암시 적으로 사용 가능하면 logger.debug ( "...")를 수행하면 좋을 것입니다. 선언없이. 이것이 가능한지 확실하지 않습니다.

해결법

  1. ==============================

    1.BeanPostProcessor 인터페이스를 사용할 수 있습니다.이 인터페이스는 작성된 모든 bean에 대해 ApplicationContext에서 호출되므로 적절한 특성을 채울 수 있습니다.

    BeanPostProcessor 인터페이스를 사용할 수 있습니다.이 인터페이스는 작성된 모든 bean에 대해 ApplicationContext에서 호출되므로 적절한 특성을 채울 수 있습니다.

    나는 다음과 같은 간단한 구현을 만들었다.

    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");
    

    미러 라이브러리를 사용하여 주석 필드를 찾았지만이 추가 종속성을 피하기 위해 수동 조회를 수행 할 수도 있습니다.

    실제로 반복되는 코드는 피하는 것이 좋습니다. 클래스 매개 변수를 변경하는 것을 잊어 버린 경우와 같이 잘못된 로그로 연결되는 경우와 같이 다른 클래스의 로거 정의를 복사하여 붙여 넣는 작은 문제도 있습니다.

  2. ==============================

    2.당신은 어떤면에서 그것을 할 수는 없지만, 롬 보크는 제 생각에 우아한 방식으로 당신을 도울 수 있습니다. @Log 주석을 참조하십시오.

    당신은 어떤면에서 그것을 할 수는 없지만, 롬 보크는 제 생각에 우아한 방식으로 당신을 도울 수 있습니다. @Log 주석을 참조하십시오.

  3. ==============================

    3.@Redder의 해결책은 이것을하는 좋은 방법이라고 생각합니다. 그러나 미러 라이브러리를 포함하고 싶지 않았으므로 대신 Java 반영 라이브러리를 사용하는 LoggerPostProcessor 구현을 작성했습니다. 여기있어:

    @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;
        }
    
    }
    
  4. ==============================

    4.@ Redder의 솔루션을 약간 개선하고 싶습니다.

    @ Redder의 솔루션을 약간 개선하고 싶습니다.

    여기 예제 (Java 8에서는 Java 7 / 6 / etc로 다시 작성할 수 있지만 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;
    
  5. ==============================

    5.CDI (JSR 299 : Context and Dependency Injection)에서 똑같은 일을 시도 할 때이 결과를 얻었으므로,이 링크는 CDI (그리고 Spring을 사용하는 대안)를 사용하여이를 수행하는 간단한 방법을 보여줍니다.

    CDI (JSR 299 : Context and Dependency Injection)에서 똑같은 일을 시도 할 때이 결과를 얻었으므로,이 링크는 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)을 추가 할 필요가 있다는 것을 발견했다. 그래서 세션 범위가 제한된 bean에서 비활성화 영역 예외를 얻지 못했다.

    @Named()
    @SessionScoped()
    public class MyBean implements Serializable {
        private static final long serialVersionUID = 1L;
    
        @Inject
        private transient Log log;
    
  6. ==============================

    6.다음은 모든 코드를 포함하는 완전한 예제가 포함 된 블로그 게시물입니다. Spring을 사용하여 로거 삽입

    다음은 모든 코드를 포함하는 완전한 예제가 포함 된 블로그 게시물입니다. Spring을 사용하여 로거 삽입

  7. ==============================

    7.Herald는 모든 마술을 처리하는 매우 간단한 BeanPostProcessor를 제공합니다. Herald가이 필드에서 적절한 로거를 삽입 할 수 있도록 @Log 주석으로 봄 빈의 모든 필드에 주석을 달 수 있습니다.

    Herald는 모든 마술을 처리하는 매우 간단한 BeanPostProcessor를 제공합니다. Herald가이 필드에서 적절한 로거를 삽입 할 수 있도록 @Log 주석으로 봄 빈의 모든 필드에 주석을 달 수 있습니다.

    지원되는 로깅 프레임 워크 :

    다른 로깅 프레임 워크를 추가하는 것도 가능합니다.

    Github 레포 : https://github.com/vbauer/herald

  8. from https://stackoverflow.com/questions/6351082/using-java-annotation-to-inject-logger-dependency by cc-by-sa and MIT license