《玩转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 河北廊坊  多云              

《玩转Spring》第二章 BeanPostProcessor扩展

时间: 2024-11-05 10:54:53

《玩转Spring》第二章 BeanPostProcessor扩展的相关文章

淘宝技术这十年 第一章 第二章

作者:子柳先生,本名赵超,2004年加入淘宝网,取花名子柳.历任开发工程师.项目经理.产品经理.测试经理,2009年随着淘宝系统的大规模重构和人才的迅速扩张,创办了"淘宝技术大学",培养内外部工程师众多,人称"校长". ? ? 这本书,主要想了解淘宝的技术/业务发展过程,遇到了哪些问题,怎么解决的.在阅读的过程中有很多不懂的地方,纯属拓展视野,的确如书中所说,不同阶段的读者会有不一样的收货,我也会不止一次的读下去的. ? ? 第一章 ? ? 淘宝的第一版源代码直接从

Kali Linux Web 渗透测试秘籍 第二章 侦查

第二章 侦查 作者:Gilberto Najera-Gutierrez 译者:飞龙 协议:CC BY-NC-SA 4.0 简介 在每个渗透测试中,无论对于网络还是 Web 应用,都有一套流程.其中需要完成一些步骤,来增加我们发现和利用每个影响我们目标的可能的漏洞的机会.例如: 侦查 枚举 利用 维持访问 清理踪迹 在 Web测试场景中,侦查是一个层面,其中测试者必须识别网络.防火墙和入侵检测系统中所有可能组件.它们也会收集关于公司.网络和雇员的最大信息.在我们的例子中,对于 Web 应用渗透测试

Machine Learning In Action 第二章学习笔记: kNN算法

本文主要记录<Machine Learning In Action>中第二章的内容.书中以两个具体实例来介绍kNN(k nearest neighbors),分别是: 约会对象预测 手写数字识别 通过“约会对象”功能,基本能够了解到kNN算法的工作原理.“手写数字识别”与“约会对象预测”使用完全一样的算法代码,仅仅是数据集有变化. 约会对象预测 1 约会对象预测功能需求 主人公“张三”喜欢结交新朋友.“系统A”上面注册了很多类似于“张三”的用户,大家都想结交心朋友.“张三”最开始通过自己筛选的

深入浅出Zabbix 3.0 -- 第二章 Zabbix Web操作与定义

第二章  Zabbix Web操作与定义 本章介绍Zabbix 中一些基本概念的定义和web前端页面的操作,包括Zabbix中使用的一些术语的定义,Web页面中用户管理.主机和主机组的管理,以及监控项.模板.触发器.告警的管理和操作,还有Graphs.Screens.Maps及Reports等.通过本章的学习掌握一些基本概念并能够通过Web页面的操作完成对Zabbix的管理. 2.1 定义 hosts(主机) Zabbix中需要监控的服务器.交换机及其他设备我们都统一称作host,这些设备与Za

构建之法第二章个人技术和流程

如果说第一章我还有点概念,那么第二章我可以说一窍不通,什么是VSTS,什么是单元测试,什么是回归测试,还是什么是效能分析--好多的新名称,第一次看见,是那么的没有头绪.要不是百度了,仅仅看书,我完全不知VSTS--Visual Studio Team System,是由微软开发的一套具有高生产力.高集成性.可扩展的生命周期开发工具. 2.1.1节图文并茂的告诉我,单元测试重要作用,可能是自己的能力不足,相关知识了解的太少了,一边看还一边想,单元测试怎么写?写在哪里?什么时候写?就算是按照书本上的

大道至简第二章读后感

 读了大道至简第一章的老愚公的故事,我们知道了勤劳的人总会能够完成所有的困难,最终完成自己的任务,完成自己的目标,愚公移山,看似不能完成,但是与共凭借着子又生孙,孙又生子,活生生的完成了这一个不可能完成的任务,但是在旁人眼里看来,又有一些古板,耗时,毕竟动用了不知道多少代子孙的时间,反而观之第二章的李冰,修建都江堰,也需要“移山”,而且山上又全是石头,要是按照愚公的办法,那得修到什么时候才能完工?但是他发现了最终的方法,用火烧石头,然后浇水,石头就会变得酥脆容易挖走,这就是一种智慧. 从某种情况

linux程序设计——shell程序设计(第二章)

第二章    shell程序设计 本章介绍以下内容: 什么是shell 基本思路 微妙的语法:变量,条件判断和程序控制 命令列表 函数 命令和命令的执行 here文档 调试 grep命令和正则表达式 find命令 shell执行shell程序,这些程序通常称为脚本,它们是在运行时解释执行的.这使得调试工作比较容易进行,因为可以逐行地执行指令,而且节省了重新编译的时间.然而,这也使得shell不适合用来完成时间紧迫型和处理器忙碌型的任务. UNIX架构非常依赖于代码的高度可重用性,如果你编写了一个

第二章 【面向对象设计原则】

(一)如何衡量软件设计的质量 内聚度: 表示一个应用程序的单个单元所负责的任务数量和多样性.内聚与单个类或者单个方法单元相关.(好的软件设计应该做到高内聚.) 耦合度: 耦合度表示类之间关系的紧密程度.低耦合是指尽量使用抽象耦合,少用具体耦合. 设计原则名称 设计原则简介 重要性 单一职责原则 的职责要单一,不能将太多的职责放在一个类中. ★★★★☆ 开闭原则 软件实体对扩展是开放的,但对修改是关闭的,即在不修改一个软件实体的基础上去扩展其功能.  ★★★★★ 历史替换原则 在软件系统中,一个可

《无码的青春》 第二章 明羽的过去式

回到住的地方,登陆vpn,远程重启了一下服务器,查看了一下服务器日志,发现没有什么异常,给jerry发了一封email就ok了.刚打开微信,就看到沈思雨发来的消息,她回宿舍了.其实沈思雨是我大学的同班同学,毕业后,她在这个城市读研究生,我也在这个城市工作,而刚到这个城市的沈思雨并没有朋友,我很快成了她无话不谈的男闺蜜.打电话给那哥们,他正在lol,问我要不要一起玩,我说累了,不想玩. 程序员的日子总是过得那么快,不知不觉,一周就过去了.周六上午,当然是睡到自然醒的节奏,打开手机,看到微信上有一条