Ensar Basri Kahveci

overly distributed

Spring Bean'lerine otomatik olarak Logger enjekte etmek

Posted at — Feb 8, 2011

Öncelikle başlıkta bir kısmına şahit olduğunuz ve birazdan fazlasına maruz kalacağınız kötü Türkçe için sizden özür diliyorum.

Bir projede, log tutmak için SLF4J, Dependency Injection için de Spring kullanıyorken, bean’lerin SLF4J logger’larına statik olarak bağımlı olması yerine logger’ların bean’lere Spring ile enjekte etmenin daha güzel, aradaki bağımlılığı azaltıcı yönde bir yaklaşım olacağını düşündüm (enjekte edilecek loggerların SLF4J logger’ı olması gerekmiyor). Bunu nasıl yaparım diye düşünürken Spring’in tam da düşündüğüm gibi bir güzellik sağladığını, insanların da benden çok çok evvel o güzellikten bir çözüm elde ettiğini araştırırken buldum.

Spring’in BeanPostProcessor arayüzü (interface), kendimize has ilklendirme (instantiation), bağımlılık çözümleme (dependency-resolution) işlemleri yapabilmemiz için bize metodlar sağlıyor. Spring’in container’ı ilklendirme işlemini yaptıktan sonra biz de devreye girip bir takım işler yapmak istiyorsak, bu arayüzün bir gerçekleştirimini yapıp gerçekleştirimimizi Spring’e sunarak amacımıza ulaşıyoruz. Bizim senaryomuz ise şöyle. Spring container’i yarattığı bean’lerin bağımlılıklarını sağladıktan sonra, kontrolü bize verecek ve biz de bean’e logger enjekte edeceğiz.

Bunun için öncelikle bir annotation tanımlayalım.

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target(FIELD)
@Documented
public @interface Logger {
}

Bean’imizde, logger tanımımızı bu annotation ile notlandıracağız. Spring container’ı bean’i yaratıp bağımlılıklarını sağladıktan sonra, BeanPostProcessor gerçekleştirimimizi devreye sokacak ve biz de reflection ile logger annotation’una sahip niteliklere (field, attribute), SLF4J logger’larını enjekte edeceğiz.

import java.lang.reflect.Field;

import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.FieldCallback;

public class LoggerInjector implements BeanPostProcessor {

	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
		return bean;
	}

	public Object postProcessAfterInitialization(final Object bean,
			String beanName) throws BeansException {

		ReflectionUtils.doWithFields(bean.getClass(), new FieldCallback() {
			public void doWith(Field field) throws IllegalArgumentException,
					IllegalAccessException {
				// make the field accessible if defined private
				ReflectionUtils.makeAccessible(field);
				if (field.getAnnotation(Logger.class) != null) {
					org.slf4j.Logger logger = LoggerFactory.getLogger(bean
							.getClass());
					field.set(bean, logger);
				}
			}
		});

		return bean;
	}
}

Kod Stackoverflow’dan alıntı. Koda kısaca bir göz atalım. postProcessBeforeInitialization() metodunda, sınıf içindeki her nitelik üzerinde çağrılacak bir FieldCallback gerçekleştirimi sunuyoruz. ReflectionUtils.doWithFields() metodu sınıfın nitelikleri üzerinde bizim sağladığımız FieldCallback gerçekleştirimlerini çağırıyor. doWith() metodunda yaptığımız işlem ise, önce niteliği erişilebilir yapmak, daha sonra da nitelikte Logger annotation’ı varsa, o niteliğe SLF4J logger’ını atamak.

@Logger
private org.slf4j.Logger logger;

Şimdi logging yapacağımız bean’de logger’ı tanımlıyoruz. Son olarak BeanPostProcessor gerçekleştirimimizi, application context’te tanımlıyoruz. Artık Spring, bizim için logger’ları bean’lerimize enjekte ediyor.

http://jgeeks.blogspot.com/2008/10/auto-injection-of-logger-into-spring.html

comments powered by Disqus