首页 > 代码库 > 《玩转Spring》第二章 BeanPostProcessor扩展

《玩转Spring》第二章 BeanPostProcessor扩展

上一章,介绍了如何扩展spring类实现自动读取配置文件。

这一章,我们介绍如何通过实现BeanPostProcessor接口,对容器中的Bean做一层代理,来满足我们的个性化需求。


一、基本原理

我很不想贴代码,显得太没水平。有时候自己的语言又很空洞,不得不贴代码,感觉用代码来说明一件事反而更容易些。


import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class BeanPostPrcessorImpl implements BeanPostProcessor {
 
    // Bean 实例化之前执行该方法
    public Object postProcessBeforeInitialization(Object bean, String beanName)
           throws BeansException {
       System.out.println( beanName + "开始实例化");
       return bean;
    }
 
    // Bean 实例化之后执行该方法
    public Object postProcessAfterInitialization(Object bean, String beanName)
           throws BeansException {
       System.out.println( beanName + "实例化完成");
       return bean;
    }
}


然后将这个BeanPostProcessor接口的实现配置到Spring的配置文件中就可以了:

<bean class="com.jialin.spring.BeanPostPrcessorImpl"/>


注意:

1、BeanPostProcessor的作用域是容器级的,它只和所在容器有关。

2、BeanFactory和ApplicationContext对待bean后置处理器稍有不同。ApplicationContext会自动检测在配置文件中实现了BeanPostProcessor接口的所有bean,并把它们注册为后置处理器,然后在容器创建bean的适当时候调用它。部署一个后置处理器同部署其他的bean并没有什么区别。而使用BeanFactory实现的时候,bean 后置处理器必须通过下面类似的代码显式地去注册:

BeanPostPrcessorImpl beanPostProcessor = new BeanPostPrcessorImpl();
Resource resource = new FileSystemResource("applicationContext.xml");
ConfigurableBeanFactory factory = new XmlBeanFactory(resource);
factory.addBeanPostProcessor(beanPostProcessor);
factory.getBean("beanName");


二、应用

好东西总要用起来

1、利用BeanPostProcessor接口实现Bean的动态代理。

2、自动注入Logging,用于记录日志。


Logger注解

@Retention(RetentionPolicy.RUNTIME)
@Target( {
        ElementType.FIELD
})
public @interface Logger {
}

package com.jialin.framework.annotation;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.config.BeanPostProcessor;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


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

            }
        }


}


在Spring配置文件中,加入

     <!--配置根据注解,自动注入Log对象-->

    <bean id="logBeanPocessor" class="com.jialin.framework.annotation.LogBeanPostProcessor" lazy-init="false" />


//实现代理的BeanPostProcessor的实例,其中注入上文介绍的log:
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.apache.commons.logging.Log;
 
public class ProxyBeanPostProcesser implements BeanPostProcessor {
    private Map map = new ConcurrentHashMap(100);

   //使用logger的注解来简化定义,并自动注入

    @Logger
    private static final Log log;
 
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        MyProxy proxy = new MyProxy();
 
        if (bean.toString().contains("Proxy")) {
            log.info(beanName + "为代理类,不进行再次代理!");
            return bean;
        }


        //……
        //可以加一些其他条件,过滤掉你不想代理的bean
        //……省略部分代码


        if (map.get(beanName) != null) {
            log.info(beanName + "已经代理过,不进行再次代理!");
            return map.get(beanName);
        }
        proxy.setObj(bean);
        proxy.setName(beanName);
        Class[] iterClass = bean.getClass().getInterfaces();
        if (iterClass.length > 0) {
            Object proxyO = Proxy.newProxyInstance(bean.getClass().getClassLoader(), iterClass, proxy);
            map.put(beanName, proxyO);
            return proxyO;
        } else {
            log.info(beanName + "必须实现接口才能被代理。");
            return bean;
        }
    }
 
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
 
}
 



import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import org.apache.commons.logging.Log;
import sun.reflect.Reflection;
 
//代理类Proxy,其中注入上文介绍的log:
public class MyProxy implements InvocationHandler {

   //使用logger的注解来简化定义,并自动注入

    @Logger  
    private static final Log  log;
 
    private Object obj;
 
    private String name;
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public Object getObj() {
        return obj;
    }
 
    public void setObj(Object obj) {
        this.obj = obj;
    }
 
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log.info("-------------------" + "bean 名称为【" + name + "】方法为【" + method.getName() + "】-------------"
                + obj.getClass());
        return method.invoke(obj, args);
    }
 
    public void printDetail(String detail) {
        log.error(detail);
    }
 
}
 


在Spring配置文件中,加入


<!--配置自动为Bean配置代理对象-->

<bean id="proxyBeanPocessor" class="com.jialin.framework.proxy. ProxyBeanPostProcesser " />


从上面的介绍不难看出,实现了BeanPostProcessor接口定制我们自己的Bean处理器可以在Spring容器初始化Bean的时候做我们想做的很多事。Spring首先会使用自己的处理器,然后陆续使用我们的处理器,典型的装饰者模式,我们自定义的处理器就是一个个具体的装饰者。


在这里预告一下,后续会出一个文章,教大家如何《实现一个面向服务的IOC容器,不使用任何框架,纯j2se代码》


这篇到这,下篇继续,敬请关注!谢谢


贾琳   写于 2014-6-25 河北廊坊  多云