版权声明:本文为博主原创文章。未经博主同意不得转载。

https://blog.csdn.net/shan9liang/article/details/34421141

上一章。介绍了怎样扩展spring类实现自己主动读取配置文件。

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

一、基本原理

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

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扩展的更多相关文章

  1. Spring 3.x 实践 第一个例子(Spring 3.x 企业应用开发实战读书笔记第二章)

    前言:工作之后一直在搞android,现在需要更多和后台的人员交涉,技术栈不一样,难免鸡同鸭讲,所以稍稍学习下. 这个例子取自于<Spring 3.x 企业应用开发实战>一书中的第二章,I ...

  2. #Spring实战第二章学习笔记————装配Bean

    Spring实战第二章学习笔记----装配Bean 创建应用对象之间协作关系的行为通常称为装配(wiring).这也是依赖注入(DI)的本质. Spring配置的可选方案 当描述bean如何被装配时, ...

  3. 第26章 FMC—扩展外部SDRAM—零死角玩转STM32-F429系列

    第26章     FMC—扩展外部SDRAM 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/ ...

  4. python 教程 第二十一章、 扩展Python

    第二十一章. 扩展Python /* D:\Python27\Lib\Extest-1.0\Extest2.c */ #include <stdio.h> #include <std ...

  5. Spring学习指南-第二章-Spring框架基础(完)

    第二章 Spring框架基础 面向接口编程的设计方法 ​ 在上一章中,我们看到了一个依赖于其他类的POJO类包含了对其依赖项的具体类的引用.例如,FixedDepositController 类包含 ...

  6. 精通Web Analytics 2.0 (4) 第二章:选择你的网络分析灵魂伴侣的最佳策略

    精通Web Analytics 2.0 : 用户中心科学与在线统计艺术 第二章:选择你的网络分析灵魂伴侣的最佳策略 在Web Analytics 2.0的新世界秩序中,您必须跳出"单一真理来 ...

  7. 玩转Spring Cloud之API网关(zuul)

    最近因为工作原因,一直没有空写文章,所以都是边忙项目,边利用空闲时间,周末时间学习总结,最终在下班回家后加班加点写完本篇文章,若有不足之处,还请谅解,谢谢! 本文内容导航: 一.网关的作用 二.网关与 ...

  8. Spring IoC 容器的扩展

    前言 本篇文章主要介绍 Spring 中 BeanFactory 的扩展 ApplicationContext,我们平时日常开发中也基本上是使用它,不会去直接使用 BeanFactory. 那么在 S ...

  9. 《精通Spring4.x企业应用开发实战》第二章

    昨天联系了一下学长,学长说这个项目因为种种原因代码比较混乱,感觉最坏的打算是从头开始写. 大概询问了一下学长和xianhua学姐的建议,又看了看网上的资料,这个项目开发的技术栈基本就是SpringBo ...

随机推荐

  1. 五步让你玩转CocoaPods

    1 安装和升级 $ sudo gem install cocoapods  $ pod setup 2 更换为taobao的源 $ gem sources -r https://rubygems.or ...

  2. asp.net截屏功能实现截取web页面

    using System.Drawing;  //打开该页面 System.Diagnostics.Process.Start("IEXPLORE.EXE", "http ...

  3. VR资源浏览网站

    https://my.matterport.com 资源 https://my.matterport.com/show/?m=kCeVCzCjQ5s

  4. ssh在本地调用远程主机上的命令,不登录远程主机shell

    需求描述: 在实际shell脚本的编写过程中,需要通过ssh远程执行一个命令,并返回执行的结果 简单来说,就是将命令发送到远程的主机上进行执行,但是并没有实际的登录到远程主机上.即通过 ssh的方式本 ...

  5. schema in oracle

    the conception of schema is different in different db software. here i just refer to oracle schema. ...

  6. easyui-textbox 只读设置取消

    <script> $(function () { $("#txt_beginAmount").attr('readonly', true); $("#txt_ ...

  7. haffman哈夫曼编码的实现

    <span style="font-size:18px;">/* 1.在一棵二叉树中,我们定义从A节点到B节点所经过的分支序列为从A节点到B节点的路径: 定义从A节点到 ...

  8. 查看用户信息:w

    w命令有两个用途: (1) 用于查看当前系统负载(2) 用于查看当前登录用户的行为和信息,执行这个命令可以得知当前登入系统的用户有哪些人,以及他们正在执行哪些程序 [root@localhost ~] ...

  9. HTML和CSS的精华

    今天又是周一喽,我们开始啦又一周的学习啦,想一想,在这里学习已经一个月啦,不知什么时间已经习惯啦这种生活,我应该是一个很难适应环境的人啊,但是现在在这里感觉还可以哦,可能是来到这里有自己的目标吧,所以 ...

  10. 初学hadoop,windows下安装

    先bb一下,woc开始使用Cygwin来模拟linux配置hadoop,然后各种错误,找着找着发现原来2.0+的hadoop可以直接在windows下配置.当时真是1w头神兽飞过. 下载hadoop ...