为什么80%的码农都做不了架构师?>>>   

非侵入式框架

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 {//创建一系列的Beanpublic List<Object> createBeans(List<Class<?>> clslist){return clslist.stream().map(cls->{return createBean(cls);}).collect(Collectors.toList());}//创建一个BeanObject createBean(Class<?> cls){//添加到容器return new BeanWrapper(cls.newInstance());}
}//包装代理
class BeanWrapper {private Object bean;public BeanWrapper(Object bean) {this.bean = bean;}@Overridepublic 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";}@beforepublic void init() {System.out.println("Before Init:" + this.toString());}
}//预设的Bean2
class MyBean2 {public String toString() {return "MyBean2 Ins";}@afterpublic 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

转载于:https://my.oschina.net/chkui/blog/1840824

Spring核心——IOC处理器扩展相关推荐

  1. spring源码分析第三天------spring核心IOC容器和依赖注入原理

    基于XML的依赖注入 1.依赖注入发生的时间 当 Spring IOC 容器完成了 Bean 定义资源的定位.载入和解析注册以后,IOC 容器中已经管理类 Bean 定义的相关数据,但是此时 IOC ...

  2. 最详细的Spring核心IOC的源码分析

    https://blog.csdn.net/nuomizhende45/article/details/81158383 详细,清晰,收藏下便于后续查看

  3. 05. 手写Spring核心框架

    目录 05 手写Spring核心框架 Pt1 手写IoC/DI Pt1.1 流程设计 Pt1.2 基础配置 application.properties pom.xml web.xml Pt1.3 注 ...

  4. 一步一步手绘Spring IOC运行时序图一(Spring 核心容器 IOC初始化过程)

    相关内容: 架构师系列内容:架构师学习笔记(持续更新) 一步一步手绘Spring IOC运行时序图一(Spring 核心容器 IOC初始化过程) 一步一步手绘Spring IOC运行时序图二(基于XM ...

  5. Spring 核心方法 refresh 刷新流程简要概述及相关源码扩展实现(一)

    前言 Spring 启动流程解析 Refresh 内部方法全解析 prepareRefresh obtainFreshBeanFactory AbstractApplicationContext 类以 ...

  6. 转 Spring源码剖析——核心IOC容器原理

    Spring源码剖析--核心IOC容器原理 2016年08月05日 15:06:16 阅读数:8312 标签: spring 源码 ioc 编程 bean 更多 个人分类: Java https:// ...

  7. 使用 spring 的 IOC 解决程序耦合——获取spring的Ioc核心容器,并根据id获取对象、核心容器的两个接口(ApplicationContext、BeanFactory)引发出的问题

    IOC概念和spring中的IOC 明确 ioc 的作用: 削减计算机程序的耦合(解除我们代码中的依赖关系). 使用 spring 的 IOC 解决程序耦合 获取spring的Ioc核心容器,并根据i ...

  8. Spring核心之对 IOC的理解

    Spring核心之对 IOC的理解 Spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器框架. 一 .IOC IOC : Inversion of Control,中文译为" ...

  9. Spring核心机制IoC与AoP梳理

    Spring核心机制IoC与AoP梳理 文章目录 Spring核心机制IoC与AoP梳理 IoC介绍 IoC案例介绍 pom文件中IoC环境引入 自己new对象方法举例(正转) IoC创建对象 基于X ...

最新文章

  1. 01-.Net编程机制
  2. 被辞后恶意报复,程序员清除125台设备数据,被判21个月监禁
  3. winscp连接虚拟机Linux被拒绝的问题解决方案
  4. 用缓动函数模拟物理动画
  5. linux 安装ios jenkins 打包机器签名证书问题
  6. python动态规划算法最大k乘积_C语言使用DP动态规划思想解最大K乘积与乘积最大问题...
  7. linux 查看显卡信号_Ubuntu 16.04安装nvidia显卡驱动以及各种坑(包含解决方案)
  8. 旅游中用稳定器和相机拍视频是怎样的体验?
  9. 自动化测试 - 封装WebDriver的getDriver
  10. 图书管理分类统计c语言,C语言实现图书管理系统
  11. 网站如何集成支付宝!原来要给钱的
  12. java 限制文件大小_java上传文件大小限制
  13. 三菱PLC与触摸屏的连接不通解决方法
  14. MYSQL监控工具--mytop
  15. cordova通过指纹插件进行指纹验证
  16. 30. Python------(面向对象练习)搬家具
  17. RTL8192EU调试信息
  18. HE4484E芯片资料
  19. WAITED TOO LONG FOR A ROW CACHE ENQUEUE LOCK!
  20. 模板 2018-01-27 分解因数 分解质因数

热门文章

  1. html缓存特效代码,HTML特效代码
  2. html5 酒店入住插件,jQuery酒店类入住日期时间范围选择器插件
  3. 敲代码就是一把梭_2020必看!开发五年的大佬日常工作中所使用的java代码技巧...
  4. 卡迪夫大数据专业排名_大数据揭秘!英国名校哪些专业录取率低?申请人最多呢?...
  5. python设计拼图小游戏_教你用Python自制拼图小游戏,轻松搞定熊孩子
  6. python里help和dir的区别_Python中dir()与help()的使用
  7. Linux查询系统运行的时间
  8. 小派(PiMax)vr Artisan固件升级失败,没反应了
  9. git本地ben远程分支_git 本地分支与远程分支
  10. linux csr蓝牙驱动,csr4.0蓝牙适配器驱动下载