文档章节

Spring核心——IOC处理器扩展

随风溜达的向日葵
 随风溜达的向日葵
发布于 07/05 15:58
字数 2194
阅读 1676
收藏 42
点赞 1
评论 3

非侵入式框架

Spring一直标注自己是一个非侵入式框架。非侵入式设计的概念并不新鲜,目标就是降低使用者和框架代码的耦合,毕竟框架的开发者和使用者几乎肯定不是同一个团队。Spring最早的非侵入式实现就是他的一系列XML配置,理想状态下Spring框架的所有的功能都应该是通过配置实现的。元编程在Java中的使用现给非侵入式的设计提供了更好的解决方案,在Java中通过注解(Annotation)即可标记某个类、方法、域的附加功能,而无需通过继承的方式来扩展原始框架没有的功能。下面通过3段代码的例子来说明侵入式与非侵入式的区别。

文章中的代码仅仅用于说明原理,已经删除了一些无关代码,无法执行。可执行代码在:https://github.com/chkui/spring-core-example,如有需要请自行clone,仅支持gradle依赖。

一个基本的容器

下面的代码是大致模仿的IoC容器创建Bean的过程。BeanFactory::createBeans方法传入Bean的类型列表,而迭代器遍历列表完成每一个类的实例创建:

/**框架代码*/
package chkui.springcore.example.xml.beanpostprocessor.nopluging;

//创建Bean的工厂类,由框架开发者开发
class BeanFactory {
	//创建一系列的Bean
	public List<Object> createBeans(List<Class<?>> clslist){
		return clslist.stream().map(cls->{
			return createBean(cls);
		}).collect(Collectors.toList());
	}
	//创建一个Bean
	Object createBean(Class<?> cls){
		//添加到容器
		return new BeanWrapper(cls.newInstance());
	}
}

//包装代理
class BeanWrapper {
	private Object bean;
	public BeanWrapper(Object bean) {
		this.bean = bean;
	}
	@Override
	public String toString() {
		return "Wrapper(" + this.bean.toString() + ")";
	}
}

下面的代码是框架使用者的代码——将Bean1和Bean2交给BeanFactory来完成初始化:

/**使用端代码*/
package chkui.springcore.example.xml.beanpostprocessor.nopluging;

//import ...

public class IocExtensionSampleNoPluging {
    public static void main(String[] args) {
    	List<Class<?>> classes = Arrays.asList(new Class<?>[]{MyBean1.class, MyBean2.class});
    	List<Object> ins = new BeanFactory().createBeans(classes);
    	System.out.println("Result:" + ins.toString());
    }
}

//Bean1,由使用者编码
class MyBean1 {
	public String toString() {
		return "MyBean1 Ins";
	}
}

//Bean2,使用者编码
class MyBean2 {
	public String toString() {
		return "MyBean2 Ins";
	}
}

classpath:chkui.springcore.example.xml.beanpostprocessor.nopluging.IocExtensionSample。源码地址

某个时刻,框架的使用者有个新需求是在要在每个Bean创建的前后进行一些处理。我们可以通过继承的方式来实现功能。下面我们修改使用端代码实现这个功能。

继承实现功能扩展

通过继承类BeanFactory,并修改createBean方法可以实现我们的需求:

package chkui.springcore.example.xml.beanpostprocessor.extend;

//执行
public class IocExtensionSampleNoPluging {
    public static void main(String[] args) {
    	List<Class<?>> classes = Arrays.asList(new Class<?>[]{MyBean1.class, MyBean2.class});
    	List<Object> ins = new ModifyBeanFactory().createBeans(classes);
    	System.out.println("Result:" + ins.toString());
    }
}

//新建一个BeanFactory的派生类,并修改createBean的实现,添加使用者的处理逻辑
class ModifyBeanFactory extends BeanFactory {
	Object createBean(Class<?> cls){
		Object ins = cls.newInstance();
		//添加容器之前的处理
		BeanWrapper wrapper = new BeanWrapper(ins);
		//添加容器之后的处理
		return wrapper;
	}
}

classpath:chkui.springcore.example.xml.beanpostprocessor.extend.IocExtensionSample。源码地址

这里在使用者的代码里新增了一个ModifyBeanFactory类,并重写了createBean方法。在重写的方法中实现我们需要的功能逻辑。但是这样开发会出现以下2点问题:

  1. 导致使用者的代码与框架代码产生了极强的耦合性。如果某天框架进行了调整,例如将方法名改为buildBean、或者增加了更多的代理模式会出现一些意想不到的问题。更麻烦的是可能会遇到一些到运行期才出现的问题。
  2. 我们需要先理解框架的源码才能植入我们的功能,这和很多设计模式的原则是背道而驰的。也会大大影响我们的开发效率。

出现这些问题就叫做“侵入式”——框架代码侵入到使用者的工程代码,导致2者严重耦合,对未来的升级、扩展、二次开发都有深远的影响。

通过注解(Annotation)扩展功能

实际上注解和在XML进行配置都是一样的思路,只是注解讲关系写在了源码上,而使用XML是将关系通过XML来描述。这里实现的功能就类似于在 Bean的定义与控制 一文中介绍的Bean的生命周期方法。

使用注解最大的价值就是非侵入式。非侵入式的好处显而易见:

  1. 无需和框架代码耦合,更新升级框架风险和成本都很小。
  2. 任何时候我们需要需要更换框架,只需修改配置或注解,而无需再去调整我们自己的功能代码。

非侵入式也有一个问题,那就是接入的功能还是需要框架预设,而不可能像继承那样随心所欲。

我们将前面的代码进行一些修改,支持通过注解来指定扩展的功能:

package chkui.springcore.example.xml.beanpostprocessor.annotation;

class BeanFactory {
	public List<Object> createBeans(List<Class<?>> clslist){
		//同前文...
	}
	Object createBean(Class<?> cls){
		BeanWrapper wrapper = null;
		Object ins = cls.newInstance();
        /**这里增加了一个Handle对象。
           Handle会对注解进行处理,确定添加容器前后的执行方法。*/
		Handle handle = processBeforeAndAfterHandle(ins);
		handle.exeBefore();
		wrapper = new BeanWrapper(ins);
		handle.exeAfter();
		return wrapper;
	}
	
    // 通过反射来确定Bean被添加到容器前后的执行方法。
	private Handle processBeforeAndAfterHandle(Object obj) {
		Method[] methods = obj.getClass().getDeclaredMethods();
		Handle handle = new Handle(obj);
		for(Method method : methods) {
			Annotation bef = method.getAnnotation(before.class);
			Annotation aft = method.getAnnotation(after.class);
			if(null != bef) handle.setBefore(method);
			if(null != aft) handle.setBefore(method);
		}
		return handle;
	}
}

下面是Handle处理器和对应的注解的代码:

class Handle{
	Object instance;
	Method before;
	Method after;
	Handle(Object ins){
		this.instance = ins;
	}
	void setBefore(Method method) {
		this.before = method;
	}
	void setAfter(Method method) {
		this.after = method;
	}
	void exeBefore(){
		if(null != this.before) {
			this.before.invoke(this.instance, null);
		}
	}
	void exeAfter(){
		if(null != this.after) {
			this.after.invoke(this.instance, null);
		}
	}
}

//注解----------------------------------------
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface before {}

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface after{}

使用者的代码,我们将注解添加到Bean的对应的方法上:

public class IocExtensionSampleNoPluging {
    public static void main(String[] args) {
    	List<Class<?>> classes = Arrays.asList(new Class<?>[]{MyBean1.class, MyBean2.class});
    	List<Object> ins = new BeanFactory().createBeans(classes);
    	System.out.println("Result:" + ins.toString());
    }
}

//预设的Bean1
class MyBean1 {
	public String toString() {
		return "MyBean1 Ins";
	}
	
	@before
	public void init() {
    	System.out.println("Before Init:" + this.toString());
	}
}

//预设的Bean2
class MyBean2 {
	public String toString() {
		return "MyBean2 Ins";
	}
	
	@after
	public void post() {
    	System.out.println("After Init:" + this.toString());
	}
}

我们为MyBean1和MyBean2分别添加了init、post方法和对应的@before、@after注解。执行之后输出一下内容:

Before Init:MyBean1 Ins
After Init:MyBean2 Ins
Result:[Wrapper(MyBean1 Ins), Wrapper(MyBean2 Ins)]

classpath:chkui.springcore.example.xml.beanpostprocessor.annotation.IocExtensionSample。源码地址

注解对应的方法都顺利执行。

通过注解,我们实现了扩展功能,任何时候只需要通过添加或修改注解即可向容器扩展功能。在Spring核心功能里,Bean的生命周期管理都是通过这种思路实现的,除了注解之外还有XML支持。

在使用spring的过程中,我想各位码友多多少少都通过继承Spring某些类来实现了一些需要扩展的功能。而且我发现网上很多使用spring某些功能的例子也是通过继承实现的。建议尽量不要去采用这种加深耦合的方式实现扩展,Spring提供了多种多样的容器扩展机制,后面的文章会一一介绍。

后置处理器

后置处理器——BeanPostProcessor是Spring核心框架容器扩展功能之一,作用和Bean的生命周期方法类似,也是在Bean完成初始化前后被调用。但是和生命周期方法不同的是,他无需在每一个Bean上去实现代码,而是通过一个独立的Bean来处理全局的初始化过程。

BeanPostProcessor与Bean生命周期方法体现出的差异是:我们无论任何时候都可以加入处理器来实现扩展功能,这样做的好处是无需调整之前的Bean的任何代码也可以植入功能

这种实现方式与切面(AOP)有一些相似的地方,但是实现的方式是完全不一样的,而且处理器会对所有Bean进行处理。

BeanPostProcessor的实现非常简单,只添加一个Bean实现BeanPostProcessor接口即可:

package chkui.springcore.example.xml.beanpostprocessor;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class Processor implements BeanPostProcessor {
    //初始化之前
	public Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean;
    }
	//初始化之后
	public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("Bean '" + beanName + "' created : " + bean.toString());
        return bean;
    }
}

BeanPostProcessor的使用案例请查看实例代码中 chkui.springcore.example.xml.beanpostprocessor 包中的代码,包含:

一个实体类:chkui.springcore.example.xml.entity.User

一个服务接口和服务类:chkui.springcore.example.xml.service.UserService

处理器:chkui.springcore.example.xml.beanpostprocessor.Processor

Main入口:chkui.springcore.example.xml.beanpostprocessor.BeanPostProcessor

配置文件:/src/main/resources/xml/config.xml

更多的后置处理器说明

见:https://www.chkui.com/article/spring/spring_core_post_processor_of_official

 

© 著作权归作者所有

共有 人打赏支持
随风溜达的向日葵
粉丝 192
博文 61
码字总数 134014
作品 0
广州
其他
加载中

评论(3)

Mike111
Mike111
vdjdj
随风溜达的向日葵
随风溜达的向日葵

引用来自“红薯”的评论

代码用码云呗
薯爷要求,不得不从啊。
红薯
红薯
代码用码云呗
Spring核心——IOC功能扩展点

上一篇文章介绍了非侵入式的框架的概念以及IOC的功能扩展点之一——BeanPostProcessor,我们接下来的内容继续说明IoC更多的扩展方法。 BeanFactoryPostProcessor BeanFactoryPostProcessor是...

随风溜达的向日葵
07/11
0
0
Spring核心——上下文与IoC

前面3篇分别介绍了IoC容器与Bean的关系、Bean与Bean之间的关系以及Bean自身的控制和管理。在了解Spinrg核心模式时,一定要谨记他的基本工作元素就是IoC容器和Bean,所有的功能是围绕着这2者展...

随风溜达的向日葵
07/02
0
0
Spring(一)——总体介绍

工作闲着,快速回顾一下spring框架。Spring框架,是进行对象管理,对象关联,解耦的一个中间层框架。SSH(Struts+Spring+Hibernate)三大Spring在中间就起着一个承上启下的作用。好,首先我们...

凡尘里的一根葱
2015/11/10
0
0
BeanPostProcessor —— 连接Spring IOC和AOP的桥梁

之前都是从大Boss的视角,来介绍Spring,比如IOC、AOP。 今天换个视角,从一个小喽啰出发,来加深对Spring的理解。 这个小喽啰就是,BeanPostProcessor(下面简称BBP)。 讲解思路: BBP怎么...

SexyCode
06/19
0
0
Spring核心——FactoryBean

本文继续之前的2篇文章(BeanPostProcessor和BeanFactoryPostProcessor)介绍Ioc容器的功能扩展。 FactoryBean是用来构造Bean的接口。常规情况下向容器添加一个Bean只需要像下面这样通过XML...

随风溜达的向日葵
07/12
0
0
第二章 Spring MVC入门

2.1、Spring Web MVC是什么 Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱动指的就是...

亮liang
2015/03/20
0
0
第二章:springmvc入门

2.1、Spring Web MVC是什么 Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱动指的就是...

xiejunbo
2014/12/16
0
0
SpringMvc基础知识

1.SpringMvc是什么 Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱动指的就是使用请求-...

王念博客
2016/03/27
78
0
Spring基础篇——Spring容器和应用上下文理解

上文说到,有了Spring之后,通过依赖注入的方式,我们的业务代码不用自己管理关联对象的生命周期。业务代码只需要按照业务本身的流程,走啊走啊,走到哪里,需要另外的对象来协助了,就给Spr...

圆梦巨人
05/03
0
0
Spring MVC入门 —— 学SpringMVC

2.1、Spring Web MVC是什么 Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱动指的就是...

qq58edf1d989a2d
2017/04/13
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

shell中的函数、shell中的数组、告警系统需求分析

shell中的函数 格式: 格式: function f_name() { command } 函数必须要放在最前面 示例1(用来打印参数) 示例2(用于定义加法) 示例3(用于显示IP) shell中的数组 shell中的数组1 定义数...

Zhouliang6
今天
2
0
用 Scikit-Learn 和 Pandas 学习线性回归

      对于想深入了解线性回归的童鞋,这里给出一个完整的例子,详细学完这个例子,对用scikit-learn来运行线性回归,评估模型不会有什么问题了。 1. 获取数据,定义问题     没有...

wangxuwei
今天
1
0
MAC安装MAVEN

一:下载maven压缩包(Zip或tar可选),解压压缩包 二:打开终端输入:vim ~/.bash_profile(如果找不到该文件新建一个:touch ./bash_profile) 三:输入i 四:输入maven环境变量配置 MAVEN_HO...

WALK_MAN
今天
0
0
33.iptables备份与恢复 firewalld的9个zone以及操作 service的操作

10.19 iptables规则备份和恢复 10.20 firewalld的9个zone 10.21 firewalld关于zone的操作 10.22 firewalld关于service的操作 10.19 iptables规则备份和恢复: ~1. 保存和备份iptables规则 ~2...

王鑫linux
今天
2
0
大数据教程(2.11):keeperalived+nginx高可用集群搭建教程

上一章节博主为大家介绍了目前大型互联网项目的系统架构体系,相信大家应该注意到其中很重要的一块知识nginx技术,在本节博主将为大家分享nginx的相关技术以及配置过程。 一、nginx相关概念 ...

em_aaron
今天
1
0
Apache Directory Studio连接Weblogic内置LDAP

OBIEE默认使用Weblogic内置LDAP管理用户及组。 要整理已存在的用户及组,此前办法是导出安全数据,文本编辑器打开认证文件,使用正则表达式获取用户及组的信息。 后来想到直接用Apache Dire...

wffger
今天
2
0
HFS

FS,它是一种上传文件的软件。 专为个人用户所设计的 HTTP 档案系统 - Http File Server,如果您觉得架设 FTP Server 太麻烦,那么这个软件可以提供您更方便的档案传输系统,下载后无须安装,...

garkey
今天
1
0
Java IO类库之BufferedInputStream

一、BufferedInputStream介绍 /** * A <code>BufferedInputStream</code> adds * functionality to another input stream-namely, * the ability to buffer the input and to * sup......

老韭菜
今天
0
0
STM 32 窗口看门狗

http://bbs.elecfans.com/jishu_805708_1_1.html https://blog.csdn.net/a1985831055/article/details/77404131...

whoisliang
昨天
1
0
Dubbo解析(六)-服务调用

当dubbo消费方和提供方都发布和引用完成后,第四步就是消费方调用提供方。 还是以dubbo的DemoService举例 -- 提供方<dubbo:application name="demo-provider"/><dubbo:registry address="z...

青离
昨天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部