根据Spring中的核心IoC深度理解设计模式(一)

学习Spring系列,永远离不开的就是IoC控制反转和AOP面向切面编程,并且在其中充满了设计模式的魅力.
之前面试也被问到过,简单的理解过程是不够的,败在了理解源码上面,为了今后的学习,想用源码去理解一下到底什么IoC和AOP。

首先是IoC,所谓控制反转,就是把原先我们代码里面需要实现的对象创建、依赖的代码,反转给容器来帮忙实现。那么必然的我们需要创建一个容器,同时需要一种描述来让容器知道需要创建的对象与对象的关系。这个描述最具体表现就是我们可配置的文件。即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。
引入IoC的目的是(1)脱开、降低类之间的耦合;(2)倡导面向接口编程、实施依赖倒换原则; (3)提高系统可插入、可测试、可修改等特性。

在Sping IoC的体系结构中BeanFactory作为最顶层的一个接口类,它定义了IoC容器的基本功能规范。并且为了区分在 Spring 内部在操作过程中对象的传递和转化过程中,对对象的数据访问做限制,使用了多层接口
ListableBeanFactory 接口表示这些 Bean 是可列表的. HierarchicalBeanFactory 表示的是这些 Bean 是有继承关系的,也就是每个Bean 有可能有父 Bean。
AutowireCapableBeanFactory 接口定义 Bean 的自动装配规则。
默认实现类是 DefaultListableBeanFactory,他实现了所有的接口.

Bean的创建是典型的工厂模式,
因此我们借代码来了解一下三种工厂模式
首先是简单工厂模式,
比如说我喜欢女孩子,那抽象一个女孩子的基类或者接口,

package com.DesignModel.SimpleFactory;
/*新建一个女生的抽象类*/
public abstract class Girl
{
/*描述女孩的样子*/
public abstract void desc();
}

class wzGirl extends Girl{

    @Override
    public void desc() {
        System.out.println("我是文静的女孩子,乖巧懂事惹人爱");
    }
}
class xgGirl extends Girl {

    @Override
    public void desc() {
        System.out.println("我是性感的女孩子,肤白貌美大长腿");
    }
}
class kaGirl extends Girl{

    @Override
    public void desc() {
        System.out.println("我是可爱的女孩子,俏皮开朗更动人");
    }
}
/*然后我们就可以开一个女子学校了,让大家可以选择自己喜欢的类型*/
class girlSchool{
    public static final int TYPE_WZ= 1;//文静的女孩子
    public static final int TYPE_XG = 2;//性感的女孩子
    public static final int TYPE_KA = 3;//可爱的女孩子

    public static Girl createGirls(int type) {
        switch (type) {
            case TYPE_WZ:
                return new wzGirl();
            case TYPE_XG:
                return new xgGirl();
            case TYPE_KA:
            default:
                return new kaGirl();
        }
    }

    public static void main(String[] args) {
         /*现在我要选择一个文静的女孩子*/
        Girl girl=girlSchool.createGirls(girlSchool.TYPE_WZ);
        girl.desc();
    }
}
看girlschool就给了我们一个文静的女孩子,是不是很直接,要什么给什么.

简单工厂类的特点就是
1 它是一个具体的类,非接口 抽象类。有一个重要的create()方法,利用if或者 switch创建产品并返回。
2 create()方法通常是静态的,所以也称之为静态工厂。
缺点
1 扩展性差(我想增加一类型的女孩子,除了新增一个女孩类,还需要修改工厂类方法)
2 不同的产品需要不同额外参数的时候 不支持。

接着看第二个,叫做工厂方法模式
这个模式提供一个用于创建对象的接口(工厂接口),让其实现类(工厂实现类)决定实例化哪一个类(产品类),并且由该实现类创建对应类的实例。
作用是可以一定程度增加扩展性,若增加一个产品实现,只需要实现产品接口,修改工厂创建产品的方法
可以一定程度增加代码的封装性、可读性
在hibernate里通过sessionFactory创建session、通过代理方式生成ws客户端时,通过工厂构建报文中格式化数据的对象都用到了这个工厂方法模式.
即应用在用户知道要创建这个类但不关心对象是如何创建的情况下,
用这些类举个例子
先是有一个工厂类接口
然后是抽象类写方法,这个MyAbstractMessage,就是组装消息
然后是用户进行操作了类
第四个是午餐产品发送消息的方法接口
都五个是午餐工厂,制作午餐的
剩下三个是产品类
作为工厂方法模式,要点就是要提供一个产品类的接口(MyAbstractMessage)。产品类均要实现这个接口(也可以是abstract类,即抽象产品)。
提供一个工厂类的接口。工厂类均要实现这个接口(图中LunchFactory)。
由工厂实现类(MylunchFactory)创建产品类的实例。工厂实现类应有一个方法,用来实例化产品类。
 

用户操作类的代码

package com.DesignModel.SimpleFactory.FactoryMethod;

public class MyFactoryMethodMain {
    public static void main(String[] args) {
        LunchFactory lunchFactory = new  MyLunchFactory();
        MyLunch myLunch;
        // 对于这个消费者来说,不用知道如何生产message这个产品,耦合度降低
        try {
            // 先来一个鸡腿
            myLunch = lunchFactory.createMessage("Drumstick");
            myLunch.sendMesage();

            // 来一个蛋糕
            myLunch = lunchFactory.createMessage("Cake");
            myLunch.sendMesage();

            // 再来一杯茶饮
            myLunch = lunchFactory.createMessage("Tea");
            myLunch.sendMesage();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
午餐工厂实现类的代码

package com.DesignModel.SimpleFactory.FactoryMethod;

import java.util.HashMap;
import java.util.Map;

public class MyLunchFactory implements LunchFactory{

    @Override
    public MyLunch createMessage(String messageType) {
        MyLunch lunch;
        Map<String, Object> messageParam = new HashMap<String, Object>();
        // 根据某些条件去选择究竟创建哪一个具体的实现对象,条件可以传入的,也可以从其它途径获取。
        // 鸡腿
        if ("Drumstick".equals(messageType)) {
            lunch = new MyMessageDrumstick();
            messageParam.put("Drumstick", "500");
        } else
            // 茶饮
            if ("tea".equals(messageType)) {
                lunch= new MyMessageTea();
                messageParam.put("RedTea", "100");
            } else
                // 蛋糕
                if ("cake".equals(messageType)) {
                    lunch = new MyMessageCake();
                    messageParam.put("cake", "50");
                } else
                // 默认生产蛋糕这个产品
                {
                    lunch= new MyMessageDrumstick();
                    messageParam.put("cake", "50");
                }
             lunch.setMessageParam(messageParam);
             return lunch;
    }
}

接下来是最重要的抽象工厂模式,也是工厂模式的灵魂

借用网上大佬的总结:
抽象工厂模式是工厂方法模式的升级版本,他用来创建一组相关或者相互依赖的对象。他与工厂方法模式的区别就在于,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则是针对的多个产品等级结构。在编程中,通常一个产品结构,表现为一个接口或者抽象类,也就是说,工厂方法模式提供的所有产品都是衍生自同一个接口或抽象类,而抽象工厂模式所提供的产品则是衍生自不同的接口或抽象类。
在抽象工厂模式中,有一个产品族的概念:所谓的产品族,是指位于不同产品等级结构中功能相关联的产品组成的家族。抽象工厂模式所提供的一系列产品就组成一个产品族;而工厂方法提供的一系列产品称为一个等级结构。

简单来说,三个奶球的榴莲冰激凌,和三个奶球的香蕉冰激凌是一个产品族。
但是三个奶球的榴莲冰激凌和两个奶球的榴莲冰激凌,三个奶球的香蕉冰激凌,两个个奶球的香蕉冰激凌就属于两个不同的重量级的。所以得用抽象工厂。
总的来说
使用工厂模式的目的一般就是为了解耦,当需要创建的对象是一系列相互关联或相互依赖的产品族时,便可以使用抽象工厂模式。说的更明白一点,就是一个继承体系中,如果存在着多个等级结构(即存在着多个抽象类),并且分属各个等级结构中的实现类之间存在着一定的关联或者约束,就可以使用抽象工厂模式。假如各个等级结构中的实现类之间不存在关联或约束,则使用多个独立的工厂来对产品进行创建,则更合适一点。
代码举例

interface IProduct1 {  
    public void show();  
}  
interface IProduct2 {  
    public void show();  
}  
  
class Product1 implements IProduct1 {  
    public void show() {  
        System.out.println("这是1型产品");  
    }  
}  
class Product2 implements IProduct2 {  
    public void show() {  
        System.out.println("这是2型产品");  
    }  
}  
  
interface IFactory {  
    public IProduct1 createProduct1();  
    public IProduct2 createProduct2();  
}  
class Factory implements IFactory{  
    public IProduct1 createProduct1() {  
        return new Product1();  
    }  
    public IProduct2 createProduct2() {  
        return new Product2();  
    }  
}  
  
public class Client {  
    public static void main(String[] args){  
        IFactory factory = new Factory();  
        factory.createProduct1().show();  
        factory.createProduct2().show();  
    }  

--------------------- 
看完了这三种工厂模式,我们再回头看Spring 里面Beanfactory的代码就很容易明白了。
在BeanFactory里只对IOC容器的基本行为作了定义,根本不关心你的bean是如何定义怎样加载的。正如我们只关心工厂里得到什么的产品对象,至于工厂是怎么生产这些对象的,这个基本的接口不关心。
public interface BeanFactory {    
      
      //对FactoryBean的转义定义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象,    
     //如果需要得到工厂本身,需要转义           
      String FACTORY_BEAN_PREFIX = "&"; 
        
      //根据bean的名字,获取在IOC容器中得到bean实例    
      Object getBean(String name) throws BeansException;    
    
     //根据bean的名字和Class类型来得到bean实例,增加了类型安全验证机制。    
      Object getBean(String name, Class requiredType) throws BeansException;    
     
     //提供对bean的检索,看看是否在IOC容器有这个名字的bean    
      boolean containsBean(String name);    
     
     //根据bean名字得到bean实例,并同时判断这个bean是不是单例    
     boolean isSingleton(String name) throws NoSuchBeanDefinitionException;    
     
     //得到bean实例的Class类型    
    Class getType(String name) throws NoSuchBeanDefinitionException;    
    
    //得到bean的别名,如果根据别名检索,那么其原名也会被检索出来    
   String[] getAliases(String name);    
    
 }

而要知道工厂是如何产生对象的,我们需要看具体的IOC容器实现。

接下来说一说IOC容器的初始化。主要是三步:
Resource定位(Bean的定义文件定位)
将Resource定位好的资源载入到BeanDefinition
将BeanDefiniton注册到容器中

关于容器主要有两个系列,一个是比较low的IOC容器,叫做XmlBeanFactory
看一下源码

public class XmlBeanFactory extends DefaultListableBeanFactory{


     private final XmlBeanDefinitionReader reader; 
 

     public XmlBeanFactory(Resource resource)throws BeansException{
         this(resource, null);
     }
     

     public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory)
          throws BeansException{
         super(parentBeanFactory);
         this.reader = new XmlBeanDefinitionReader(this);
         this.reader.loadBeanDefinitions(resource);
    }
 }

我们发现是简单的继承了BeanFactory的子类DefaultListableBeanFactory类,它包含了基本Spirng IOC容器所具有的重要功能,在spring中实际上已经把它当成默认的IoC容器来使用。

另外一个是ApplicationContext

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory,  
 HierarchicalBeanFactory,
 MessageSource, 
 ApplicationEventPublisher,
 ResourcePatternResolver ,
 ResourceLoader

 

是BeanFactory的增强版。更易与SpringAOP集成、消息资源处理(国际化处理)、事件传递及各种不同应用层的context实现(如针对web应用的WebApplicationContext)。 简而言之,BeanFactory提供了配制框架及基本功能,而ApplicationContext则增加了更多支持企业核心内容的功能。ApplicationContext完全由BeanFactory扩展而来,因而BeanFactory所具备的能力和行为也适用于ApplicationContext。

以ApplicationContext举例来说明一下初始化的三个步骤
1.定位Resources
ApplicationContext的所有实现类都实现RecourceLoader接口,因此可以直接调用getResource(参数)获取Resoure对象。不同的ApplicatonContext实现类使用getResource方法取得的资源类型不同。

2.第二步 将Resource定位好的资源载入到BeanDefinition
BeanDefinition相当于一个数据结构,这个数据结构的生成过程是根据定位的resource资源对象中的bean而来的,这些bean在Spirng IoC容器内部表示成了的BeanDefintion这样的数据结构,IoC容器对bean的管理和依赖注入的实现都是通过操作BeanDefinition来进行的。

源码载入BeanDefinition的过程protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        Resource[] configResources = getConfigResources();//获取所有定位到的resource资源位置(用户定义)
        if (configResources != null) {
            reader.loadBeanDefinitions(configResources);//载入resources
        }
        String[] configLocations = getConfigLocations();//获取所有本地配置文件的位置(容器自身)
        if (configLocations != null) {
            reader.loadBeanDefinitions(configLocations);//载入resources
        }
}

第三步,将BeanDefiniton注册到容器中
最终Bean配置会被解析成BeanDefinition封装到BeanDefinitionHolder类中,之后
beanFactory.registerBeanDefinition(beanName,bdHolder.getBeanDefinition()),
注册到
DefaultListableBeanFactory.beanDefinitionMap中。之后客户端如果要获取Bean对象,Spring容器会根据注册的BeanDefinition信息进行实例化。

初始化结束之后,要进行的就是依赖注入
依赖注入的时间:
(1)用户第一次通过getBean方法向IoC容索要Bean时,IoC容器触发依赖注入。

(2).当用户在Bean定义资源中为元素配置了lazy-init属性,即让容器在解析注册Bean定义时进行预实例化,触发依赖注入。

在Spring中,如果Bean定义的单态模式(Singleton),则容器在创建之前先从缓存中查找,以确保整个容器中只存在一个实例对象。如果Bean定义的是原型模式(Prototype),则容器每次都会创建一个新的实例对象。除此之外,Bean定义还可以扩展为指定其生命周期范围。例如Request和Session。

Spring IoC容器是如何将属性的值注入到Bean实例对象中去的:

(1).对于集合类型的属性,将其属性值解析为目标类型的集合后直接赋值给属性。

(2).对于非集合类型的属性,大量使用了JDK的反射和内省机制,通过属性的getter方法(reader method)获取指定属性注入以前的值,同时调用属性的setter方法(writer method)为属性设置注入后的值。
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值