Spring一直标注本身是一个非侵入式框架。非侵入式设计的概念并不新鲜,目标就是下降使用者和框架代码的耦合,毕竟框架的开发者和使用者几乎确定不是同一个团队。Spring最先的非侵入式实现就是他的一系列XML配置,理想状态下Spring框架的全部的功能都应该是经过配置实现的。元编程在Java中的使用现给非侵入式的设计提供了更好的解决方案,在Java中经过注解(Annotation)便可标记某个类、方法、域的附加功能,而无需经过继承的方式来扩展原始框架没有的功能。下面经过3段代码的例子来讲明侵入式与非侵入式的区别。java
文章中的代码仅仅用于说明原理,已经删除了一些无关代码,没法执行。可执行代码在:https://github.com/chkui/spring-core-example,若有须要请自行clone,仅支持gradle依赖。git
下面的代码是大体模仿的IoC容器建立Bean的过程。BeanFactory::createBeans方法传入Bean的类型列表,而迭代器遍历列表完成每个类的实例建立:github
/**框架代码*/ 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来完成初始化:spring
/**使用端代码*/ 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方法能够实现咱们的需求:app
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点问题:ide
出现这些问题就叫作“侵入式”——框架代码侵入到使用者的工程代码,致使2者严重耦合,对将来的升级、扩展、二次开发都有深远的影响。post
实际上注解和在XML进行配置都是同样的思路,只是注解讲关系写在了源码上,而使用XML是将关系经过XML来描述。这里实现的功能就相似于在 Bean的定义与控制 一文中介绍的Bean的生命周期方法。
使用注解最大的价值就是非侵入式。非侵入式的好处显而易见:
非侵入式也有一个问题,那就是接入的功能仍是须要框架预设,而不可能像继承那样为所欲为。
咱们将前面的代码进行一些修改,支持经过注解来指定扩展的功能:
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