上次在参加支付宝架构培训的时候, 看到他们框架中有一个不错的对logger的注解来简化定义, 具体用法如下:
@Logger
private static Log log;
当时觉得不错, 也没问他们怎么实现的, 后来自己做了一个, 基本原理如下:
通过自定义一个BeanPostProcessor, 在对所有bean初始化之前, 对每一个bean的field进行检查, 是否适用了Logger注解, 如果有, 则调用LogFactory创建一个logger实例.
做法如下:
1.定义一个Logger注解:
@Retention(RetentionPolicy.RUNTIME)
@Target( {
ElementType.FIELD
})
public @interface Logger {
}
2.创建一个BeanPostProcessor(这个是重点)
public class LogBeanPostProcessor implements BeanPostProcessor {
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
List<Class<?>> clazzes = getAllClasses(bean);
for (Class<?> clazz : clazzes) {
initializeLog(bean, clazz);
}
return bean;
}
/**
* 取得指定bean的class以及所有父类的列表, 该列表排列顺序为从父类到当前类
* @param bean
* @return
*/
private List<Class<?>> getAllClasses(Object bean) {
Class<? extends Object> clazz = bean.getClass();
List<Class<?>> clazzes = new ArrayList<Class<?>>();
while (clazz != null) {
clazzes.add(clazz);
clazz = clazz.getSuperclass();
}
Collections.reverse(clazzes);
return clazzes;
}
/**
* 对logger变量进行初始化
*
* @param bean
* @param clazz
*/
private void initializeLog(Object bean, Class<? extends Object> clazz) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.getAnnotation(Logger.class) == null) {
continue;
}
if (!field.getType().isAssignableFrom(Log.class)) {
continue;
}
field.setAccessible(true);
try {
field.set(bean, LogFactory.getLog(clazz));
} catch (Exception e) {
throw new BeanInitializationException(String
.format("初始化logger失败!bean=%s;field=%s", bean, field));
}
}
}
}
3.在spring beans配置文件中加入如下配置:
<bean id="logBeanPocessor" class="com.mysoft.log.LogBeanPostProcessor" lazy-init="false" />
不过这种做法也有一个缺点, 就是这些log的类必须是受spring管理的bean, 否则log将无法被初始化.