spring最核心思想--ioc控制反转
前言
鄙人刚开始写博客,定有很多不足的地方,尽量以多种方式进行讲解,如果有那种方式更容易你理解或者有任何意见可以给我留言或者私信,欢迎大家评论。
一IOC
一核心概念
控制反转:将bean的生成交给容器,程序可以从容器中获取指定的bean。
个人理解:此优势也是spring能够流行并成为java主流框架的主要原因,java是帮助java程序员以对象的方式管理 内存,而spring则是一个管理对象的框架。如果使用spring,在开发中基本上不需要考虑内存相关的问题。
接下来从一个设计者的思路去交流下spring的设计思路
二需求
基于以上的需求,我们需要做的核心的需求的是:生成,管理bean,向使用者提供。
三设计
看到这,是不是第一反应就是可以用工厂模式,没错,spring框架中针对此设计也是工厂模式。核心类为Beanactory,核心方法为getBean()。
1 public interface BeanFactory { 2 /** 3 * 获取bean 4 * @param name bean的名字 5 * @return bean 实例 6 * @throws Exception 7 */ 8 Object getBean(String name) throws Exception; 9 }
现在有个工厂,同时我们面对2个问题:
1BeanFactory如何知道生成什么样的bean(bean是由用户指定的)?
2用户如何定义一个bean,便于用户使用也便于beanFactory?
接下来先考虑第二个问题,现在谁也不知道用户将定义怎样的bean,那就来个万能大法,先定义的一个接口专门做这个事,接口为BeanDefinition。
回到第一个问题,我们如何将BeanDefinition告诉给BeanFactory?解决这个问题同时我们还要考虑可扩展性。这个解决方案是注册模式。
public interface BeanDefinitionRegistry {
/**
* 注册bean定义
* @param beanName bean名称(bean的唯一标识)
* @param beanDefinition bean定义
* @throws BeanDefinitionRegistException 注册异常
*/
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionRegistException;
}
每个bean通过BeanDefinitionRegistry接口通知给BeanFactory。
目前只剩下一个问题:BeanDefinition如何定义,通俗点就是BeanDefinition里面都有什么。
Bean生成都有哪些情况,
1是否单例
2bean类名
3生成方式:
3.1指定初始化方法
必须的信息:bean的类名
3.2通过工厂模式
3.2.1静态的
public class PersonFactory{
public static Person getPerson(){
return new Person();
}
}
3.2.2成员方法
public class PersonFactory{ public Person getPerson(){ return new Person(); } }
工厂模式下,必须获取的信息如下:工厂bean的命,工厂方法名
3.3new的方式
4销毁的方法
具体接口如下:
public interface BeanDefinition { String SCOPE_SINGLETION = "singleton"; String SCOPE_PROTOTYPE = "prototype"; /** * 类 */ Class<?> getBeanClass(); /** * Scope */ String getScope(); /** * 是否单例 */ boolean isSingleton(); /** * 是否原型 */ boolean isPrototype(); /** * 工厂bean名 */ String getFactoryBeanName(); /** * 工厂方法名 */ String getFactoryMethodName(); /** * 初始化方法 */ String getInitMethodName(); /** * 销毁方法 */ String getDestroyMethodName(); /** * 校验bean定义的合法性 */ default boolean validate() { // 没定义class,工厂bean或工厂方法没指定,则不合法。 if (this.getBeanClass() == null) { if (StringUtils.isBlank(getFactoryBeanName()) || StringUtils.isBlank(getFactoryMethodName())) { return false; } } // 定义了类,又定义工厂bean,不合法 if (this.getBeanClass() != null && StringUtils.isNotBlank(getFactoryBeanName())) { return false; } return true; } }
ioc容器的主要设计均已设计完成。
简单的实现源代码如下:
package core.ioc.impl; import core.exception.BeanDefinitionRegistException; import core.ioc.BeanDefinition; import core.ioc.BeanDefinitionRegistry; import core.ioc.BeanFactory; import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import javax.xml.ws.WebServiceException; import java.io.Closeable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; public class DefaultBeanFactory implements BeanFactory, BeanDefinitionRegistry, Closeable { private final Log logger = LogFactory.getLog(DefaultBeanFactory.class); //存在beanDefinition的缓存 private Map<String,BeanDefinition> beanDefinitionMap= new ConcurrentHashMap<>(256); //存放单例bean实例的缓存 private Map<String,Object> beanMap = new ConcurrentHashMap<>(256); //获取bean实例 public Object getBean(String name) throws Exception { return this.doGetBean(name); } protected Object doGetBean(String beanName) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { //验证bean不能为空 Objects.requireNonNull(beanName,"beanName不能为空"); //查看是否已经创建,如果已经创建则直接从缓存中取得返回 Object instance = beanMap.get(beanName); if(null!=instance){ return instance; } //如果没有创建,则需要创建, BeanDefinition bd = this.getBeanDefinition(beanName); Objects.requireNonNull(bd,"beanDefinition 不能为空"); Class<?> type=bd.getBeanClass(); if(type!=null){ if(StringUtils.isBlank(bd.getFactoryMethodName())){ //构造方法来构造对象 instance =this.createInstanceByConstructor(bd); }else{ //通过静态工厂方法创建对象 instance=this.createInstanceByStaticFactoryMethod(bd); } }else{ //通过工厂bean方式来构造对象 instance=this.createInstanceByFactoryBean(bd); } //执行初始化方法,比如说给属性赋值等 this.doInit(bd,instance); //如果是单例,则将bean实例放入缓存中 if(bd.isSingleton()){ beanMap.put(beanName,instance); } return instance; } /** * 通过构造方法来构造对象 * @param bd dean定义 * @return bean实例 * @throws IllegalAccessException * @throws InstantiationException */ private Object createInstanceByConstructor(BeanDefinition bd) throws IllegalAccessException, InstantiationException { return bd.getBeanClass().newInstance(); } /** * 通过静态工厂方法创建bean * @param bd bean定义 * @return bean 实例 * @throws NoSuchMethodException * @throws InvocationTargetException * @throws IllegalAccessException */ private Object createInstanceByStaticFactoryMethod(BeanDefinition bd) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { Class<?> type=bd.getBeanClass(); Method m=type.getMethod(bd.getFactoryMethodName(),null); return m.invoke(type,null); } /** * 通过工厂bean 方式来构造对象 * @param bd * @return * @throws InvocationTargetException * @throws IllegalAccessException * @throws NoSuchMethodException */ private Object createInstanceByFactoryBean(BeanDefinition bd) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException { Object factoryBean =this.doGetBean(bd.getFactoryBeanName()); Method m=factoryBean.getClass().getMethod(bd.getFactoryMethodName(),null); return m.invoke(factoryBean,null); } /** * 初始化 * @param bd * @param instance * @throws NoSuchMethodException * @throws InvocationTargetException * @throws IllegalAccessException */ private void doInit(BeanDefinition bd,Object instance) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { if(StringUtils.isNotBlank(bd.getInitMehtodName())){ Method m=instance.getClass().getMethod(bd.getInitMehtodName(),null); m.invoke(instance,null); } } @Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionRegistException { Objects.requireNonNull(beanName,"注册bean需要给入beanName"); Objects.requireNonNull(beanDefinition,"注册bean需要给入beanDefinition"); //检验给如的bean是否合法 if(!beanDefinition.validata()){ throw new BeanDefinitionRegistException(String.format("名字为[%s]的bean的定义不合法:%s",beanName,beanDefinition)); } //验证beanDefinition已经存在 if(this.containBeanDefinition(beanName)){ throw new BeanDefinitionRegistException(String.format("名字为[%s]的bean定义已经存在:%s", beanName,this.getBeanDefinition(beanName))); } this.beanDefinitionMap.put(beanName,beanDefinition); } /** * 获取beanDefinition * @param beanName bean的名称 唯一标识 * @return beanDefinition */ @Override public BeanDefinition getBeanDefinition(String beanName) { return this.beanDefinitionMap.get(beanName); } /** * 验证beanDefinition是否已经存在 * @param beanName bean的名称 唯一标识 * @return true:已存在 false:不存在 */ @Override public boolean containBeanDefinition(String beanName) { return this.beanDefinitionMap.containsKey(beanName); } /** * 执行指定的销毁方法 * @throws WebServiceException */ @Override public void close() throws WebServiceException { //执行单例实例的销毁方法 for(Map.Entry<String,BeanDefinition> e:this.beanDefinitionMap.entrySet()){ String beanName=e.getKey(); BeanDefinition bd=e.getValue(); if(bd.isSingleton() && StringUtils.isNotBlank(bd.getDestroyMethodName())){ Object instance = this.beanMap.get(beanName); try { Method m = instance.getClass().getMethod(bd.getDestroyMethodName()); m.invoke(instance,null); } catch (NoSuchMethodException e1) { logger.error(String.format("执行bean[%s] %s 的 销毁方法异常!",beanName,bd), e1); e1.printStackTrace(); } catch (IllegalAccessException e1) { logger.error(String.format("执行bean[%s] %s 的 销毁方法异常!",beanName,bd), e1); e1.printStackTrace(); } catch (InvocationTargetException e1) { logger.error(String.format("执行bean[%s] %s 的 销毁方法异常!",beanName,bd), e1); e1.printStackTrace(); } } } } }
DefaultBeanFactory
package core.ioc.impl; import core.ioc.BeanDefinition; import org.apache.commons.lang3.StringUtils; public class GenericBeanDefinition implements BeanDefinition { private Class<?> beanClass; //是否为单例 private String scope = BeanDefinition.SCOPE_SINGLETION; //bean工厂的名称 private String factoryBeanName; //bean工厂方法名 private String factoryMethodName; //初始化方法 private String initMethodName; //销毁方法 private String destroyMethodName; /** * 自动生成设置的方法 start */ public void setBeanClass(Class<?> beanClass) { this.beanClass = beanClass; } public void setScope(String scope) { if(StringUtils.isNoneBlank(scope)){ this.scope = scope; } } public void setFactoryBeanName(String factoryBeanName) { this.factoryBeanName = factoryBeanName; } public void setFactoryMethodName(String factoryMethodName) { this.factoryMethodName = factoryMethodName; } public void setInitMethodName(String initMethodName) { this.initMethodName = initMethodName; } public void setDestroyMethodName(String destroyMethodName) { this.destroyMethodName = destroyMethodName; } /** * 自动生成设置的方法 end */ @Override public Class<?> getBeanClass() { return this.beanClass; } @Override public String getScope() { return this.scope; } @Override public boolean isSingleton() { return BeanDefinition.SCOPE_SINGLETION.equals(this.scope); } @Override public boolean isPrototype() { return BeanDefinition.SCOPE_PROTOTYPE.equals(this.scope); } @Override public String getFactoryBeanName() { return this.factoryBeanName; } @Override public String getFactoryMethodName() { return this.factoryMethodName; } @Override public String getInitMehtodName() { return this.initMethodName; } @Override public String getDestroyMethodName() { return this.destroyMethodName; } @Override public String toString() { return String.format("GenericBeanDefinition [beanClass=%s, scope=%s, factoryBeanName=%s, " + "factoryMethodName=%s, initMethodName=%s, destroyMethodName=%s]", beanClass,scope,factoryBeanName,factoryMethodName,initMethodName,destroyMethodName); } /** * 疑问: 为什么要重写equals 方法 * * 重写equals方法需要注意以下几点: * 1自反性:对于任何非空引用x,x.equals(x)应该返回true * 2对称:对于任何引用x,y,如果x.equals(y) 返回true,那么 y.equals(x)也应该返回true。 * 3传递性:对于任何引用x,y和z,如果x=y 为true,那么y=z也一定为true,x=z也一定为true。 * 4一致性:如果x和y引用的对象没有发生变化,那么返回调用x.equals(y),应该返回同样的结果。 * 5非空性:对于任意非空引用x,x.equals(null)应该返回false。 * * 重写equals方法,就必须重写hashCode * 原因是HashMap的需要 */ @Override public boolean equals(Object obj) { if(this==obj){ return true; } if(null==obj){ return false; } if(getClass() !=obj.getClass()){ return false; } GenericBeanDefinition other=(GenericBeanDefinition) obj; //验证每个属性是否相当,只有当每个属性均相等时,才是一个对象 if(beanClass ==null){ if(other.beanClass!=null){ return false; } }else if(!beanClass.equals(other.beanClass)){ return false; } if(destroyMethodName== null){ if(other.destroyMethodName!=null){ return false; } }else if(!destroyMethodName.equals(other.destroyMethodName) ){ return false; } if(factoryBeanName== null){ if(other.factoryBeanName!=null){ return false; } }else if(!factoryBeanName.equals(other.factoryBeanName) ){ return false; } if(factoryMethodName== null){ if(other.factoryMethodName!=null){ return false; } }else if(!factoryMethodName.equals(other.factoryMethodName) ){ return false; } if(initMethodName== null){ if(other.initMethodName!=null){ return false; } }else if(!initMethodName.equals(other.initMethodName) ){ return false; } if(scope== null){ if(other.scope!=null){ return false; } }else if(!scope.equals(other.scope) ){ return false; } return true; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((beanClass == null) ? 0 : beanClass.hashCode()); result = prime * result + ((destroyMethodName == null) ? 0 : destroyMethodName.hashCode()); result = prime * result + ((factoryBeanName == null) ? 0 : factoryBeanName.hashCode()); result = prime * result + ((factoryMethodName == null) ? 0 : factoryMethodName.hashCode()); result = prime * result + ((initMethodName == null) ? 0 : initMethodName.hashCode()); result = prime * result + ((scope == null) ? 0 : scope.hashCode()); return result; } }
二DI 依赖注入
一、需求分析
依赖注入的本质:赋值,给构造参数赋值,给属性赋值
背景:在ioc容器负责生成bean的实例,bean不仅有方法还有属性,我们需要将bean的是属性进行初始化,在这样的背景下,我们将面对第一个问题
问题1:我们需要在什么时候或者在哪里对bean的属性进行初始化?
分析:我们平时写代码时如何创建bean,例如 GirlFriend,存在以下俩种情况:
1GirlFriend gf=new GirlFriend();
2GirlFriend gf=new GirlFriend(“小红”,18);
3GirlFriend gf=GirlFriendFactory.getGirlFriend(“小红”,18);
4 class boy{
GirlFriend gf=new GirlFriend();
}
答:根据举例分析得出分为俩种,1:有参构造参数/工厂方法;2:属性依赖。
解决了第一个问题,我们将面临第二个问题,问题2:构造参数的入参有哪几种情况?·
答:基本类型、String、集合、依赖另一个ioc容器中的bean 等
面对以上的情况,我们将如何设计,详情请看设计
二DI设计
1构造参数依赖
如果我们什么设计,首先要考虑的是先把信息存贮起来,以便后续的使用,那么第一个问题则是:
问题1:参数可以是一个也可以是多个,用什么来存贮呢?
答:答案很明显,是用list,一定要保证存贮多个并且有序,其中有序很重要,因为存在构造函数的重载。
问题2:参数可以是直接只,也可以是bean依赖,如何表示?
答:object
如果用object表示,那我们如何区分是bean依赖?
答:无法判断
问题3:尽然无法判断,我们有没有其他解决方案,不是用object还能使用其他的吗?
答:只需要区分是否为bean依赖即可,使用object是没有问题的。
问题4:如何进行bean依赖的区分
答:基于面向接口的编程的思想,定义个专门标识bean依赖的接口BeanReference,这样就可以解决。
问题5:如果某个参数是数组或者集合等,他们元素中有的是bean依赖,该如何处理
答:元素值还是用BeanReference,同样bean工厂在使用时需要遍历替换
问题6:BeanReference如何定义
public interface BeanReference{ //依赖的bean的名称 private String beanName; //获取依赖bean的名称 String getBeanName(); }
问题7:ioc容器如何获取构造函数
答:在BeanDefinition中添加个方法,即List getConstructorArgumentValues(),通过此方法来获取构造函数
问题8:有参数,如何判断是个构造方法或者工厂方法
分析:存在以下情况
1方法可以重载的
2形参定义时可能是接口或者父类,实参则是具体的子实现
3反射提供的获取的构造方法、方法的API如下:
答:
1先根据参数的类型进行精确匹配查找,如果未找到,则进行第2步
2获取所有的构造方法,扁你了,通过参数数量过滤,再比较形参类型与实参类型。
问题9:对于原型bean在第二次时否可以省略判断,即对于原型bean,我们可以缓存下这个构造方法或者工厂方法?
答:可以,提升性能,我们需要在BeanDefinition接口中添加4个方法。
/* 下面的四个方法是供beanFactory中使用的 */
public Constructor<?> getConstructor();
public void setConstructor(Constructor<?> constructor);
public Method getFactoryMethod();
public void setFactoryMethod(Method factoryMethod);
问题10:循环依赖如何处理?(注意:面试题来喽)
分析:循环依赖会导致什么问题? 答:会导致死循环,所以我们时是不允许循环依赖的,尤其是构造函数的循环依赖
答:将正在构造的bean进行缓存,构造完成后移除,当遇到bean的依赖时,首先查看依赖的bean是否在构造中,如果在,则抛出异常。
2属性依赖
问题1:属性依赖时什么?、
答:某个属性依赖某个值
问题2:该如何描述一个属性依赖?
答:属性名,只,定义一个类来表示这个俩值
问题3:会有多个属性依赖,怎么存放
答:list
问题4:属性只的情况和构造参数值时一样的吗?
答:是
2
3 /**
4 * 属性值依赖定义
5 *
6 */
7 public class PropertyValue {
8
9 private String name;
10
11 private Object value;
12
13 public PropertyValue(String name, Object value) {
14 super();
15 this.name = name;
16 this.value = value;
17 }
18
19 public String getName() {
20 return name;
21 }
22
23 public void setName(String name) {
24 this.name = name;
25 }
26
27 public Object getValue() {
28 return value;
29 }
30
31 public void setValue(Object value) {
32 this.value = value;
33 }
34
35 }
注意:BeanDefinition中添加List<PropertyValue > getPropertyValues(); 以便BeanFactory可以获取属性依赖的信息
最后总结下循环依赖:
1构造实例对象时(即构造参数循环依赖),会陷入死循环,不允许构造对象时循环依赖的
2属性可以循环依赖, 因为bean已经构建完毕。