18

Springを使用してSLF4Jロガーを次のようなクラスに注入しようとしています。

@Component
public class Example {

  private final Logger logger;

  @Autowired
  public Example(final Logger logger) {
    this.logger = logger;
  }
}

FactoryBean実装したクラスを見つけました。しかし、問題は、注入ターゲットに関する情報を取得できないことです。

public class LoggingFactoryBean implements FactoryBean<Logger> {

    @Override
    public Class<?> getObjectType() {
        return Logger.class;
    }  

    @Override
    public boolean isSingleton() {  
        return false;
    }

    @Override
    public Logger getObject() throws Exception {
        return LoggerFactory.getLogger(/* how do I get a hold of the target class (Example.class) here? */);
    }
}   

FactoryBeanは正しい方法でさえありますか?ピココンテナのファクトリインジェクションを使用するTypeと、ターゲットのが渡されます。推測では、少し注意が必要です。しかし、春にこれをどのように達成しますか?

4

8 に答える 8

20

これがあなたの解決策の代替案です。BeanFactoryPostProcessorの実装で目標を達成できます。

ロギング付きのクラスが必要だとしましょう。ここにあります:

  package log;
  import org.apache.log4j.Logger;

  @Loggable
  public class MyBean {

     private Logger logger;
  }

ご覧のとおり、このクラスは何も実行せず、単純化のためにロガーコンテナとして作成されています。ここで注目すべきことは、@Loggableアノテーションだけです。ここにそのソースコードがあります:

package log;

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Loggable {
}

この注釈は、さらに処理するためのマーカーにすぎません。そして、ここに最も興味深い部分があります:

package log;

import org.apache.log4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

import java.lang.reflect.Field;

public class LoggerBeanFactoryPostProcessor implements BeanFactoryPostProcessor{

    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        String[] names = beanFactory.getBeanDefinitionNames();
        for(String name : names){
            Object bean = beanFactory.getBean(name);
            if(bean.getClass().isAnnotationPresent(Loggable.class)){
                try {
                    Field field = bean.getClass().getDeclaredField("logger");
                    field.setAccessible(true);
                    field.set(bean, Logger.getLogger(bean.getClass()));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

すべてのBeanを検索し、Beanが@Loggableとしてマークされている場合、そのプライベートフィールドを名前ロガーで初期化します。さらに進んで、@Loggableアノテーションでいくつかのパラメーターを渡すことができます。たとえば、ロガーに対応するフィールドの名前にすることができます。

この例ではLog4jを使用しましたが、slf4jでもまったく同じように機能するはずです。

于 2010-06-15T19:38:44.537 に答える
11

カスタムBeanFactoryで解決しました。誰かがより良い解決策を思いついたら、私はそれを聞いてうれしいです。とにかく、ここに豆工場があります:

import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;

public class CustomBeanFactory extends DefaultListableBeanFactory {

    public CustomBeanFactory() {
    }

    public CustomBeanFactory(DefaultListableBeanFactory delegate) {
        super(delegate);
    }

    @Override
    public Object resolveDependency(DependencyDescriptor descriptor,
            String beanName, Set<String> autowiredBeanNames,
            TypeConverter typeConverter) throws BeansException {
        //Assign Logger parameters if required      
        if (descriptor.isRequired()
                && Logger.class.isAssignableFrom(descriptor
                        .getMethodParameter().getParameterType())) {            
            return LoggerFactory.getLogger(descriptor.getMethodParameter()
                    .getDeclaringClass());
        } else {
            return super.resolveDependency(descriptor, beanName,
                    autowiredBeanNames, typeConverter);
        }
    }
}

XML構成での使用例:

        CustomBeanFactory customBeanFactory = new CustomBeanFactory();      
        GenericApplicationContext ctx = new GenericApplicationContext(customBeanFactory);
        XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(ctx);
        xmlReader.loadBeanDefinitions(new ClassPathResource("beans.xml"));
        ctx.refresh();

編集:

以下に、Arend v。Reinersdorffsの改良版を示します(説明についてはコメントを参照してください)。

import java.lang.reflect.Field;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.core.MethodParameter;

public class CustomBeanFactory extends DefaultListableBeanFactory {

    public CustomBeanFactory() {
    }

    public CustomBeanFactory(DefaultListableBeanFactory delegate) {
        super(delegate);
    }

    @Override
    public Object resolveDependency(DependencyDescriptor descriptor,
            String beanName, Set<String> autowiredBeanNames,
            TypeConverter typeConverter) throws BeansException {
        //Assign Logger parameters if required      
        if (Logger.class == descriptor.getDependencyType()) {            
            return LoggerFactory.getLogger(getDeclaringClass(descriptor));
        } else {
            return super.resolveDependency(descriptor, beanName,
                    autowiredBeanNames, typeConverter);
        }
    }

    private Class<?> getDeclaringClass(DependencyDescriptor descriptor) {
        MethodParameter methodParameter = descriptor.getMethodParameter();
        if (methodParameter != null) {
            return methodParameter.getDeclaringClass();
        }
        Field field = descriptor.getField();
        if (field != null) {
            return field.getDeclaringClass();
        }
        throw new AssertionError("Injection must be into a method parameter or field.");
    }
}
于 2010-06-15T12:05:13.410 に答える
11

コードをよりSpringに対応させるには、を使用しInjectionPointてロガーを定義します。

@Bean
@Scope("prototype")
public Logger logger(InjectionPoint ip) {
    return Logger.getLogger(ip.getMember().getDeclaringClass());
}

@Scope("prototype")ここでは、メソッドが呼び出されるたびに「ロガー」Beanインスタンスを作成するために必要です。

于 2017-01-16T15:07:58.653 に答える
2

次のようなものを試してください:

@Component
public class Example {

  @Autowired
  @Qualifier("exampleLogger")
  private final Logger logger;

}

と:

<bean id="exampleLogger" class="org.slf4j.LoggerFactory" factory-method="getLogger">
  <constructor-arg type="java.lang.Class" value="package.Example"/>        
</bean>
于 2012-09-24T14:34:11.487 に答える
0

ええ、あなたは間違った方向に進んでいます。もし私があなたなら、LoggerFactoryを注入します。それがslf4jであることを隠したい場合は、LoggerFactoryインターフェースを定義し、slf4jLoggerに委任するクラスを挿入します。

public interface LoggerFactory {
    public Logger getLogger(Class<?> clazz);
}
...
import org.slf4j.LoggerFactory;
public class Slf4jLoggerFactory implements LoggerFactory {
    public Logger getLogger(Class<?> clazz) {
        return org.slf4j.LoggerFactory.getLogger(clazz);
    }
}

ただし、そこに行く前に、これはorg.apache.commons.loggingが正しく行っていることとほぼ同じですか? http://commons.apache.org/logging/

ロガーの代わりにログを使用します。

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class CLASS {
    private Log log = LogFactory.getLog(CLASS.class);
    ...

次に、Apacheはクラスパスを調べて、log4jなどがあるかどうかを確認し、見つけた「最良の」クラスパスに委任します。Slf4jはクラスパスのlog4jを置き換えるため、ロードされている場合(およびapache log4jを除外している場合)、commonsloggingが代わりにlog4jに委任します。

于 2010-06-14T23:17:53.707 に答える
0

Spring 4.3.0以降、Bean生成メソッドのパラメーターとしてInjectionPointまたはDependencyDescriptorを使用できます。

@Component
public class LoggingFactoryBean {
    @Bean
    public Logger logger(InjectionPoint injectionPoint) {
        Class<?> targetClass = injectionPoint.getMember().getDeclaringClass();
        return LoggerFactory.getLogger(targetClass);
    }
}
于 2016-06-11T15:06:32.627 に答える
-1
  1. インスタンスごとに新しいロガーを作成するのはなぜですか?典型的なパターンは、クラスごとに1つのロガーを持つことです(プライベート静的メンバーとして)。

  2. あなたが本当にそれをそのようにしたいのなら:たぶんあなたはロガーファクトリクラスを書いてそれを注入することができますか?何かのようなもの:

    @Singleton 
    public class LogFactory { 
        public Logger getLogger(Object o) {  
            return LoggerFactory.getLogger(o.getClass());  
        }  
    }
    
于 2010-06-14T14:58:41.190 に答える
-2

この機能を公式のSLF4JAPIに取り入れようとしています。サポート/投票/貢献してください:https ://issues.jboss.org/browse/JBLOGGING-62

(この機能は、JBoss Logging + Seam Solderによってすでに実装されています。http://docs.jboss.org/seam/3/latest/reference/en-US/html/solder-logging.htmlを参照してください

11.4。ネイティブロガーAPI

(JBoss Logging APIから)「プレーンオールド」ロガーを注入することもできます。

import javax.inject.Inject;
import org.jboss.logging.Logger;

public class LogService {

    @Inject
    private Logger log;

    public void logMessage() {
        log.info("Hey sysadmins!");
    }

}

このロガーから作成されたログメッセージには、Bean実装クラスの完全修飾クラス名と同じカテゴリ(ロガー名)があります。注釈を使用して、カテゴリを明示的に指定できます。

@Inject @Category("billing")
private Logger log;

タイプへの参照を使用してカテゴリを指定することもできます。

@Inject @TypedCategory(BillingService.class)
private Logger log;

関連する回答を提供していないことをお詫び申し上げます。

于 2011-04-18T11:17:34.013 に答える