第七章 7.1 剖析Spring框架中用到的经典设计模式

分类:01_设计模式

标签:

7.1.1 Spring中工厂模式的应用

Spring的设计理念

7.1.1.1 Spring中的Bean组件

Bean组件定义在Spring的org.springframework.beans包下,解决了以下几个问题:

这个包下的所有类主要解决了三件事:

Spring Bean的创建是典型的工厂模式,它的顶级接口是BeanFactory。

137.jpg

BeanFactory有三个子类:ListableBeanFactory、HierarchicalBeanFactory和AutowireCapableBeanFactory。目的是为了区分Spring内部对象处理和转化的数据限制

但是从图中可以发现最终的默认实现类是DefaultListableBeanFactory,它实现了所有的接口

7.1.1.2 Spring中的BeanFactory

Spring中的BeanFactory就是简单工厂模式的体现,根据传入一个唯一的标识来获得Bean对象

BeanFactory,以Factory结尾,表示它是一个工厂(接口), 它负责生产和管理bean的一个工厂。在Spring中,BeanFactory是工厂的顶层接口,也是IOC容器的核心接口,因此BeanFactory中定义了管理Bean的通用方法,如 getBeancontainsBean 等.

它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。

136.jpg

BeanFactory只是个接口,并不是IOC容器的具体实现,所以Spring容器给出了很多种实现,如 DefaultListableBeanFactoryXmlBeanFactoryApplicationContext等,其中XmlBeanFactory就是常用的一个,该实现将以XML方式描述组成应用的对象及对象间的依赖关系。


1) BeanFactory源码解析


public interface BeanFactory {
   
   /**
    对FactoryBean的转移定义,因为如果使用bean的名字来检索FactoryBean得到的是对象是工厂生成的对象,
    如果想得到工厂本身就需要转移
   */
   String FACTORY_BEAN_PREFIX = "&";

   //根据Bean的名字 获取IOC容器中对应的实例
   Object getBean(String var1) throws BeansException;

   
   //根据Bean的名字和class类型得到bean实例,增加了类型安全验证机制
   <T> T getBean(String var1, Class<T> var2) throws BeansException;

   Object getBean(String var1, Object... var2) throws BeansException;

   <T> T getBean(Class<T> var1) throws BeansException;

   <T> T getBean(Class<T> var1, Object... var2) throws BeansException;

   <T> ObjectProvider<T> getBeanProvider(Class<T> var1);

   <T> ObjectProvider<T> getBeanProvider(ResolvableType var1);

   
  //查看Bean容器中是否存在对应的实例,存在返回true 否则返回false
   boolean containsBean(String var1);

   //根据Bean的名字 判断这个bean是不是单例
   boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;

   boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;

   boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;

   boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;

   //得到bean实例的class类型
   @Nullable
   Class<?> getType(String var1) throws NoSuchBeanDefinitionException;

   @Nullable
   Class<?> getType(String var1, boolean var2) throws NoSuchBeanDefinitionException;

   
   //得到bean的别名
   String[] getAliases(String var1);
}

BeanFactory的使用场景

  1. 从IOC容器中获取Bean(Name or Type)

  2. 检索IOC容器中是否包含了指定的对象

  3. 判断Bean是否为单例

2) BeanFactory的使用


public class User {

   private int id;

   private String name;

   private Friends friends;

   public User() {
   }

   public User(Friends friends) {
       this.friends = friends;
   }

 //get set......
}

public class Friends {

   private List<String> names;

   public Friends() {
   }

   public List<String> getNames() {
       return names;
   }

   public void setNames(List<String> names) {
       this.names = names;
   }
}

配置文件


<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:p="http://www.springframework.org/schema/p"
      xmlns:context="http://www.springframework.org/schema/context"
      xmlns:mvc="http://www.springframework.org/schema/mvc"
      xmlns:task="http://www.springframework.org/schema/task"
      xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.2.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
       http://www.springframework.org/schema/task
       http://www.springframework.org/schema/task/spring-task-4.2.xsd">
   
   <bean id="User" class="com.example.factory.User">
       <property name="friends" ref="UserFriends" />
   </bean>
   <bean id="UserFriends" class="com.example.factory.Friends">
       <property name="names">
           <list>
               <value>"LiLi"</value>
               <value>"LuLu"</value>
           </list>
       </property>
   </bean>
</beans>

测试

public class SpringTest01 {

    public static void main(String[] args) {

        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");

        User user = context.getBean("user", User.class);
        user.setId(1001);
        user.setName("大远");

        List<String> names = user.getFriends().getNames();
        for (String name : names) {
            System.out.println(user.getName() + "的朋友包括: " + name);
        }
    }
}

7.1.1.3 Spring中的FactoryBean

首先FactoryBean是一个Bean,但又不仅仅是一个Bean,这样听起来矛盾,但为啥又这样说呢?其实在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。但对FactoryBean而言,这个FactoryBean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂方法模式和修饰器模式类似

1) 为什么需要FactoryBean?

  1. 在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。FactoryBean接口对于Spring框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂Bean的细节,给上层应用带来了便利。

  2. 由于第三方库不能直接注册到spring容器,于是可以实现org.springframework.bean.factory.FactoryBean接口,然后给出自己对象的实例化代码即可。

2 ) FactoryBean的使用特点

  1. 当用户使用容器本身时,可以使用转义字符"&"来得到FactoryBean本身,以区别通过FactoryBean产生的实例对象和FactoryBean对象本身。

  2. 在BeanFactory中通过如下代码定义了该转义字符:

     StringFACTORY_BEAN_PREFIX = "&";
  3. 举例

    如果MyObject是一个FactoryBean,则使用&MyObject得到的是MyObject对象,而不是MyObject产生出来的对象。

3) FactoryBean的代码示例


@Configuration
@ComponentScan("com.example.factory_bean")
public class AppConfig {
}

@Component("studentBean")
public class StudentBean implements FactoryBean {

   //返回工厂中的实例
   @Override
   public Object getObject() throws Exception {
       //这里并不一定要返回MyBean自身的实例,可以是其他任何对象的实例。
       return new TeacherBean();
   }

   //该方法返回的类型是在IOC容器中getBean所匹配的类型
   @Override
   public Class<?> getObjectType() {
       return StudentBean.class;
   }

   public void study(){
       System.out.println("学生学习......");
   }
}

public class TeacherBean {

   public void teach(){
       System.out.println("老师教书......");
   }
}

public class Test01 {

   public static void main(String[] args) {

       AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
       //StudentBean studentBean = (StudentBean)context.getBean("studentBean");

       //加上&符号,返回工厂中的实例
//        StudentBean studentBean = (StudentBean)context.getBean("&studentBean");
//        studentBean.study();

       TeacherBean teacherBean = (TeacherBean) context.getBean("studentBean");
       teacherBean.teach();
   }
}

3) FactoryBean源码分析


public interface FactoryBean<T> {
   String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";

   /**
   getObject()方法: 会返回该FactoryBean生产的对象实例,我们需要实现该方法,以给出自己的对象实例化逻辑
   这个方法也是FactoryBean的核心.
   */
   @Nullable
   T getObject() throws Exception;

   /**
   getObjectType()方法: 仅返回getObject() 方法所返回的对象类型,如果预先无法确定,返回NULL,
   这个方法返回类型是在IOC容器中getBean所匹配的类型
   */
   @Nullable
   Class<?> getObjectType();

   //该方法的结果用于表明 工厂方法getObject() 所生产的 对象是否要以单例形式存储在容器中如果以单例存在就返回true,否则返回false
   default boolean isSingleton() {
       return true;
   }
}

FactoryBean表现的是一个工厂的职责,如果一个BeanA 是实现FactoryBean接口,那么A就是变成了一个工厂,根据A的名称获取到的实际上是工厂调用getObject()方法返回的对象,而不是对象本身,如果想获取工厂对象本身,需要在名称前面加上 '&'符号

使用场景

BeanFactory与FactoryBean区别

BeanFactory和ApplicationContext的区别

7.1.2 Spring中观察者模式的应用

7.1.2.1 观察者模式与发布订阅模式的异同

观察者模式它是用于建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应的作出反应.

在观察者模式中发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以应对多个观察者,而且这些观察者之间可以没有任何相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展.

观察者模式的别名有发布-订阅(Publish/Subscribe)模式, 我们来看一下观察者模式与发布订阅模式结构上的区别

134.jpg

观察者模式里,只有两个角色 —— 观察者 + 被观察者; 而发布订阅模式里,却不仅仅只有发布者和订阅者两个角色,还有一个管理并执行消息队列的 “经纪人Broker”

观察者和被观察者,是松耦合的关系;发布者和订阅者,则完全不存在耦合

7.1.2.2 Spring中的观察者模式

Spring 基于观察者模式,实现了自身的事件机制也就是事件驱动模型,事件驱动模型通常也被理解成观察者或者发布/订阅模型。

spring事件模型提供如下几个角色

1) 事件:ApplicationEvent

2) 事件监听:ApplicationListener

3) 事件源:ApplicationEventPublisher

4) 事件管理:ApplicationEventMulticaster

7.1.2.3 事件监听案例

实现一个需求:当调用一个类的方法完成时,该类发布事件,事件监听器监听该类的事件并执行的自己的方法逻辑

假设这个类是Request、发布的事件是ReuqestEvent、事件监听者是ReuqestListener。当调用Request的doRequest方法时,发布事件。

代码如下


/**
* 定义事件
* @author spikeCong
* @date 2022/10/24
**/
public class RequestEvent  extends ApplicationEvent {

   public RequestEvent(Object source) {
       super(source);
   }
}

/**
* 发布事件
* @author spikeCong
* @date 2022/10/24
**/
@Component
public class Request {

   @Autowired
   private ApplicationContext applicationContext;

   public void doRequest(){
       System.out.println("调用Request类的doRequest方法发送一个请求......");
       applicationContext.publishEvent(new RequestEvent(this));
   }
}

/**
* 监听事件
* @author spikeCong
* @date 2022/10/24
**/
@Component
public class RequestListener implements ApplicationListener<RequestEvent> {

   @Override
   public void onApplicationEvent(RequestEvent requestEvent) {
       System.out.println("监听到RequestEvent事件,执行本方法");
   }
}

public class SpringEventTest {

   public static void main(String[] args) {
       ApplicationContext context =
               new AnnotationConfigApplicationContext("com.mashibing.pubsub");

       Request request = (Request) context.getBean("request");

       //调用方法发布事件
       request.doRequest();
   }
}

//打印日志
调用Request类的doRequest方法发送一个请求......
监听到RequestEvent事件,执行本方法

7.1.2.4 事件机制工作流程

上面代码的执行流程

146.jpg

  1. 监听器什么时候注册到IOC容器

    注册的开始逻辑是在AbstractApplicationContext类的refresh方法,该方法包含了整个IOC容器初始化所有方法。其中有一个registerListeners()方法就是注册系统监听者(spring自带的)和自定义监听器的。


    public void refresh() throws BeansException, IllegalStateException {
          //BeanFactory准备工作完成后进行的后置处理工作
                   this.postProcessBeanFactory(beanFactory);
       
          //执行BeanFactoryPostProcessor的方法;
                   this.invokeBeanFactoryPostProcessors(beanFactory);
       
          //注册BeanPostProcessor(Bean的后置处理器),在创建bean的前后等执行
                   this.registerBeanPostProcessors(beanFactory);
       
          //初始化MessageSource组件(做国际化功能;消息绑定,消息解析);
                   this.initMessageSource();
       
          //初始化事件派发器
                   this.initApplicationEventMulticaster();
       
          ////子类重写这个方法,在容器刷新的时候可以自定义逻辑;如创建Tomcat,Jetty等WEB服务器
                   this.onRefresh();
       
                       //注册应用的监听器。就是注册实现了ApplicationListener接口的监听器bean,这些监听器是注册到ApplicationEventMulticaster中的
                   this.registerListeners();
       
          //初始化所有剩下的非懒加载的单例bean
                   this.finishBeanFactoryInitialization(beanFactory);
       
          //完成context的刷新
                   this.finishRefresh();
       }

    看registerListeners的关键方法体,其中的两个方法addApplicationListener和addApplicationListenerBean,从方法可以看出是添加监听者。


    protected void registerListeners() {
       Iterator var1 = this.getApplicationListeners().iterator();

       while(var1.hasNext()) {
           ApplicationListener<?> listener = (ApplicationListener)var1.next();
           this.getApplicationEventMulticaster().addApplicationListener(listener);
       }

       String[] listenerBeanNames = this.getBeanNamesForType(ApplicationListener.class, true, false);
       String[] var7 = listenerBeanNames;
       int var3 = listenerBeanNames.length;

       for(int var4 = 0; var4 < var3; ++var4) {
           String listenerBeanName = var7[var4];
           this.getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
       }
    }

    那么最后将监听者放到哪里了呢?就是ApplicationEventMulticaster接口的子类

    该接口主要两个职责,维护ApplicationListener相关类和发布事件。

    实现在默认实现类AbstractApplicationEventMulticaster,最后将Listener放到了内部类ListenerRetriever两个set集合中


    private class ListenerRetriever {
           public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet();
           public final Set<String> applicationListenerBeans = new LinkedHashSet();
    }

    ListenerRetriever被称为监听器注册表。

  2. Spring如何发布的事件并通知监听者

    这个注意的有两个方法

    1) publishEvent方法


    protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
       Assert.notNull(event, "Event must not be null");
       Object applicationEvent;
       
       //尝试转换为ApplicationEvent或者PayloadApplicationEvent,如果是PayloadApplicationEvent则获取eventType
       if (event instanceof ApplicationEvent) {
           applicationEvent = (ApplicationEvent)event;
       } else {
           applicationEvent = new PayloadApplicationEvent(this, event);
           if (eventType == null) {
               eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
           }
       }

     
       if (this.earlyApplicationEvents != null) {
            //判断earlyApplicationEvents是否为空(也就是早期事件还没有被发布-说明广播器还没有实例化好),如果不为空则将当前事件放入集合
           this.earlyApplicationEvents.add(applicationEvent);
       } else {
           //否则获取ApplicationEventMulticaster调用其multicastEvent将事件广播出去。本文这里获取到的广播器实例是SimpleApplicationEventMulticaster。
           this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);
       }

       //将事件交给父类处理
       if (this.parent != null) {
           if (this.parent instanceof AbstractApplicationContext) {
               ((AbstractApplicationContext)this.parent).publishEvent(event, eventType);
           } else {
               this.parent.publishEvent(event);
           }
       }

    }

    2) multicastEvent方法

    继续进入到multicastEvent方法,该方法有两种方式调用invokeListener,通过线程池和直接调用,进一步说就是通过异步和同步两种方式调用.


    public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
       
       //解析事件类型
       ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
       
       //获取执行器
       Executor executor = this.getTaskExecutor();
       
       // 获取合适的ApplicationListener,循环调用监听器的onApplicationEvent方法
       Iterator var5 = this.getApplicationListeners(event, type).iterator();

       while(var5.hasNext()) {
           ApplicationListener<?> listener = (ApplicationListener)var5.next();
           if (executor != null) {
               //如果executor不为null,则交给executor去调用监听器
               executor.execute(() -> {
                   this.invokeListener(listener, event);
               });
           } else {
               //否则,使用当前主线程直接调用监听器;
               this.invokeListener(listener, event);
           }
       }

    }

    3) invokeListener方法


    // 该方法增加了错误处理逻辑,然后调用doInvokeListener
    protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
       ErrorHandler errorHandler = this.getErrorHandler();
       if (errorHandler != null) {
           try {
               this.doInvokeListener(listener, event);
           } catch (Throwable var5) {
               errorHandler.handleError(var5);
           }
       } else {
           this.doInvokeListener(listener, event);
       }

    }

    private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
       //直接调用了listener接口的onApplicationEvent方法
       listener.onApplicationEvent(event);  
    }
    • AbstractApplicationContext实现了ApplicationEventPublisher 接口的publishEvent方法

7.1.3 结合设计模式自定义SpringIOC

7.1.3.1 Spring IOC核心组件

1) BeanFactory

BeanFactory作为最顶层的一个接口,定义了IoC容器的基本功能规范

137.jpg

从类图中我们可以发现最终的默认实现类是DefaultListableBeanFactory,它实现了所有的接口。那么为何要定义这么多层次的接口呢?每个接口都有它的使用场合,主要是为了区分在Spring内部操作过程中对象的传递和转化,对对象的数据访问所做的限制。

例如,

这三个接口共同定义了Bean的集合、Bean之间的关系及Bean行为。

在BeanFactory里只对IoC容器的基本行为做了定义,根本不关心你的Bean是如何定义及怎样加载的。正如我们只关心能从工厂里得到什么产品,不关心工厂是怎么生产这些产品的。


2 ) ApplicationContext

BeanFactory有一个很重要的子接口,就是ApplicationContext接口,该接口主要来规范容器中的bean对象是非延时加载,即在创建容器对象的时候就对象bean进行初始化,并存储到一个容器中。


//延时加载
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("bean.xml"));

//立即加载
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
User user = context.getBean("user", User.class);

ApplicationContext 的子类主要包含两个方面:

要知道工厂是如何产生对象的,我们需要看具体的IoC容器实现,Spring提供了许多IoC容器实现,比如:

138.jpg

总体来说 ApplicationContext 必须要完成以下几件事:

3) Bean定义:BeanDefinition

这里的 BeanDefinition 就是我们所说的 Spring 的 Bean,我们自己定义的各个 Bean 其实会转换成一个个 BeanDefinition 存在于 Spring 的 BeanFactory 中


public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
       implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
//DefaultListableBeanFactory 中使用 Map 结构保存所有的 BeanDefinition 信息
   private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
}  

BeanDefinition 中保存了我们的 Bean 信息,比如这个 Bean 指向的是哪个类、是否是单例的、是否懒加载、这个 Bean 依赖了哪些 Bean 等等。


4) BeanDefinitionReader

Bean的解析过程非常复杂,功能被分得很细,因为这里需要被扩展的地方很多,必须保证足够的灵活性,以应对可能的变化。Bean的解析主要就是对Spring配置文件的解析。

这个解析过程主要通过BeanDefinitionReader来完成,看看Spring中BeanDefinitionReader的类结构图,如下图所示。

                     140.jpg

BeanDefinitionReader接口定义的功能


public interface BeanDefinitionReader {

/*
 下面的loadBeanDefinitions都是加载bean定义,从指定的资源中
*/
int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;
int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException;
int loadBeanDefinitions(String location) throws BeanDefinitionStoreException;
int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException;
}


5) BeanFactory后置处理器

后置处理器是一种拓展机制,贯穿Spring Bean的生命周期

后置处理器分为两类:

BeanFactory后置处理器:BeanFactoryPostProcessor

实现该接口,可以在spring的bean创建之前,修改bean的定义属性

141.jpg


public interface BeanFactoryPostProcessor {

   /*
    *  该接口只有一个方法postProcessBeanFactory,方法参数是ConfigurableListableBeanFactory,通过该
       参数,可以获取BeanDefinition
   */
   void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}


6) Bean后置处理器:BeanPostProcessor

BeanPostProcessor是Spring IOC容器给我们提供的一个扩展接口

实现该接口,可以在spring容器实例化bean之后,在执行bean的初始化方法前后,添加一些处理逻辑

143.jpg


public interface BeanPostProcessor {
   //bean初始化方法调用前被调用
   Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
   //bean初始化方法调用后被调用
   Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

7.1.3.2 IOC流程图

145.jpg

  1. 容器环境的初始化(系统、JVM 、解析器、类加载器等等)

  2. Bean工厂的初始化(IOC容器首先会销毁旧工厂,旧Bean、创建新的工厂)

  3. 读取:通过BeanDefinitonReader读取我们项目中的配置(application.xml)

  4. 定义:通过解析xml文件内容,将里面的Bean解析成BeanDefinition(未实例化、未初始化)

  5. 将解析得到的BeanDefinition,存储到工厂类的Map容器中

  6. 调用 BeanFactoryPostProcessor 该方法是一种功能增强,可以在这个步骤对已经完成初始化的 BeanFactory 进行属性覆盖,或是修改已经注册到 BeanFactory 的 BeanDefinition

  7. 通过反射实例化bean对象

  8. 进入到Bean实例化流程,首先设置对象属性

  9. 检查Aware相关接口,并设置相关依赖

  10. 前置处理器,执行BeanPostProcesser的before方法对bean进行扩展

  11. 检查是否有实现initializingBean 回调接口,如果实现就要回调其中的AftpropertiesSet() 方法,(通过可以完成一些配置的加载)

  12. 检查是否有配置自定义的init-method ,

  13. 后置处理器执行BeanPostProcesser 的after方法 -->  AOP就是在这个阶段完成的, 在这里判断bean对象是否实现接口,实现就使用JDK代理,否则选择CGLIB

  14. 对象创建完成,添加到BeanFactory的单例池中

7.1.3.3 自定义SpringIOC

对下面的配置文件进行解析,并自定义SpringIOC, 对涉及到的对象进行管理。


<?xml version="1.0" encoding="UTF-8"?>
<beans>
   <bean id="courseService" class="com.mashibing.service.impl.CourseServiceImpl">
       <property name="courseDao" ref="courseDao"></property>
   </bean>
   <bean id="courseDao" class="com.mashibing.dao.impl.CourseDaoImpl"></bean>
</beans>
1) 创建与Bean相关的pojo类

package com.mashibing.framework.beans;

/**
* 该类用来封装bean标签下的property子标签的属性
*      1.name属性
*      2.ref属性
*      3.value属性: 给基本数据类型及string类型数据赋的值
* @author spikeCong
* @date 2022/10/26
**/
public class PropertyValue {

   private String name;

   private String ref;

   private String value;

   public PropertyValue() {
   }

   public PropertyValue(String name, String ref, String value) {
       this.name = name;
       this.ref = ref;
       this.value = value;
   }

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public String getRef() {
       return ref;
   }

   public void setRef(String ref) {
       this.ref = ref;
   }

   public String getValue() {
       return value;
   }

   public void setValue(String value) {
       this.value = value;
   }
}

package com.mashibing.framework.beans;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
* 该类用来存储和遍历多个PropertyValue对象
* @author spikeCong
* @date 2022/10/26
**/
public class MutablePropertyValues implements Iterable<PropertyValue>{

   //定义List集合,存储PropertyValue的容器
   private final List<PropertyValue> propertyValueList;

   //空参构造中 初始化一个list
   public MutablePropertyValues() {
       this.propertyValueList = new ArrayList<PropertyValue>();
   }

   //有参构造 接收一个外部传入的list,赋值propertyValueList属性
   public MutablePropertyValues(List<PropertyValue> propertyValueList) {
       if(propertyValueList == null){
           this.propertyValueList = new ArrayList<PropertyValue>();
       }else{
           this.propertyValueList = propertyValueList;
       }
   }

   //获取当前容器对应的迭代器对象
   @Override
   public Iterator<PropertyValue> iterator() {

       //直接获取List集合中的迭代器
       return propertyValueList.iterator();
   }

   //获取所有的PropertyValue
   public PropertyValue[] getPropertyValues(){
       //将集合转换为数组并返回
       return propertyValueList.toArray(new PropertyValue[0]); //new PropertyValue[0]声明返回的数组类型
   }

   //根据name属性值获取PropertyValue
   public PropertyValue getPropertyValue(String propertyName){
       //遍历集合对象
       for (PropertyValue propertyValue : propertyValueList) {
           if(propertyValue.getName().equals(propertyName)){
               return propertyValue;
           }
       }

       return null;
   }

   //判断集合是否为空,是否存储PropertyValue
   public boolean isEmpty(){
       return propertyValueList.isEmpty();
   }

   //向集合中添加
   public MutablePropertyValues addPropertyValue(PropertyValue value){
       //判断集合中存储的propertyvalue对象.是否重复,重复就进行覆盖
       for (int i = 0; i < propertyValueList.size(); i++) {
           //获取集合中每一个 PropertyValue
           PropertyValue currentPv = propertyValueList.get(i);

           //判断当前的pv的name属性 是否与传入的相同,如果相同就覆盖
           if(currentPv.getName().equals(value.getName())){
               propertyValueList.set(i,value);
               return this;
           }
       }

       //没有重复
       this.propertyValueList.add(value);
       return this;  //目的是实现链式编程
   }

   //判断是否有指定name属性值的对象
   public boolean contains(String propertyName){
       return getPropertyValue(propertyName) != null;
   }
}

/**
* 封装Bean标签数据的类,包括id与class以及子标签的数据
* @author spikeCong
* @date 2022/10/27
**/
public class BeanDefinition {

   private String id;

   private String className;

   private MutablePropertyValues propertyValues;

   public BeanDefinition() {
       propertyValues = new MutablePropertyValues();
   }

   public String getId() {
       return id;
   }

   public void setId(String id) {
       this.id = id;
   }

   public String getClassName() {
       return className;
   }

   public void setClassName(String className) {
       this.className = className;
   }

   public MutablePropertyValues getPropertyValues() {
       return propertyValues;
   }

   public void setPropertyValues(MutablePropertyValues propertyValues) {
       this.propertyValues = propertyValues;
   }
}
2) 创建注册表相关的类

BeanDefinition对象存取的操作, 其实是在BeanDefinitionRegistry接口中定义的,它被称为是BeanDefinition的注册中心.


//源码
public interface BeanDefinitionRegistry extends AliasRegistry {

void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
  throws BeanDefinitionStoreException;

void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

boolean containsBeanDefinition(String beanName);

String[] getBeanDefinitionNames();
   
int getBeanDefinitionCount();
   
boolean isBeanNameInUse(String beanName);
}

BeanDefinitionRegistry继承结构图如下:

147.jpg

BeanDefinitionRegistry接口的子实现类主要有以下两个:


public interface BeanDefinitionRegistry {

   //注册BeanDefinition对象到注册表中
   void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);

   //从注册表中删除指定名称的BeanDefinition对象
   void removeBeanDefinition(String beanName) throws Exception;

   //根据名称从注册表中获取BeanDefinition对象
   BeanDefinition getBeanDefinition(String beanName) throws Exception;

   //判断注册表中是否包含指定名称的BeanDefinition对象
   boolean containsBeanDefinition(String beanName);

   //获取注册表中BeanDefinition对象的个数
   int getBeanDefinitionCount();

   //获取注册表中所有的BeanDefinition的名称
   String[] getBeanDefinitionNames();
}

public class SimpleBeanDefinitionRegistry implements BeanDefinitionRegistry {

   private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<String, BeanDefinition>();

   @Override
   public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
       beanDefinitionMap.put(beanName,beanDefinition);
   }

   @Override
   public void removeBeanDefinition(String beanName) throws Exception {
       beanDefinitionMap.remove(beanName);
   }

   @Override
   public BeanDefinition getBeanDefinition(String beanName) throws Exception {
       return beanDefinitionMap.get(beanName);
   }

   @Override
   public boolean containsBeanDefinition(String beanName) {
       return beanDefinitionMap.containsKey(beanName);
   }

   @Override
   public int getBeanDefinitionCount() {
       return beanDefinitionMap.size();
   }

   @Override
   public String[] getBeanDefinitionNames() {
       return beanDefinitionMap.keySet().toArray(new String[1]);
   }
}


3) 创建解析器相关的类

BeanDefinitionReader接口


/**
* 该类定义解析配置文件规则的接口
* @author spikeCong
* @date 2022/10/28
**/
public interface BeanDefinitionReader {

   //获取注册表对象
   BeanDefinitionRegistry getRegistry();

   //加载配置文件并在注册表中进行注册
   void loadBeanDefinitions(String configLocation) throws Exception;
}

XmlBeanDefinitionReader类


/**
* 该类是对XML文件进行解析的类
* @author spikeCong
* @date 2022/10/28
**/
public class XmlBeanDefinitionReader implements BeanDefinitionReader {

   //声明注册表对象(将配置文件与注册表解耦,通过Reader降低耦合性)
   private BeanDefinitionRegistry registry;

   public XmlBeanDefinitionReader() {
       registry = new SimpleBeanDefinitionRegistry();
   }

   @Override
   public BeanDefinitionRegistry getRegistry() {
       return registry;
   }

   //加载配置文件
   @Override
   public void loadBeanDefinitions(String configLocation) throws Exception {
       //使用dom4j解析xml
       SAXReader reader = new SAXReader();

       //获取配置文件,类路径下
       InputStream is = XmlBeanDefinitionReader.class.getClassLoader().getResourceAsStream(configLocation);

       //获取document文档对象
       Document document = reader.read(is);

       Element rootElement = document.getRootElement();
       //解析bean标签
       parseBean(rootElement);
   }

   private void parseBean(Element rootElement) {

       //获取所有的bean标签
       List<Element> elements = rootElement.elements();

       //遍历获取每个bean标签的属性值和子标签property
       for (Element element : elements) {

           String id = element.attributeValue("id");
           String className = element.attributeValue("class");

           //封装到beanDefinition
           BeanDefinition beanDefinition = new BeanDefinition();
           beanDefinition.setId(id);
           beanDefinition.setClassName(className);

           //获取property
           List<Element> list = element.elements("property");

           MutablePropertyValues mutablePropertyValues = new MutablePropertyValues();

           //遍历,封装propertyValue,并保存到mutablePropertyValues
           for (Element element1 : list) {
               String name = element1.attributeValue("name");
               String ref = element1.attributeValue("ref");
               String value = element1.attributeValue("value");
               PropertyValue propertyValue = new PropertyValue(name,ref,value);
               mutablePropertyValues.addPropertyValue(propertyValue);
           }

           //将mutablePropertyValues封装到beanDefinition
           beanDefinition.setPropertyValues(mutablePropertyValues);

           System.out.println(beanDefinition);
           //将beanDefinition注册到注册表
           registry.registerBeanDefinition(id,beanDefinition);
       }
   }
   
   public static void main(String[] args) throws Exception {

       XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader();

       reader.loadBeanDefinitions("bean.xml");
   }

}


4) 创建IOC容器相关的类

1) BeanFactory接口

在该接口中定义IOC容器的统一规范和获取bean对象的方法。


/**
* IOC容器父接口
* @author spikeCong
* @date 2022/10/28
**/
public interface BeanFactory {

   Object getBean(String name)throws Exception;

   //泛型方法,传入当前类或者其子类
   <T> T getBean(String name ,Class<? extends T> clazz)throws Exception;
}

2) ApplicationContext接口

该接口的所有的子实现类对bean对象的创建都是非延时的,所以在该接口中定义 refresh() 方法,该方法主要完成以下两个功能:


/**
* 定义非延时加载功能
* @author spikeCong
* @date 2022/10/28
**/
public interface ApplicationContext extends BeanFactory {

   //进行配置文件加载,并进行对象创建
   void refresh();
}

3) AbstractApplicationContext类


/**
* ApplicationContext接口的子实现类
*      创建容器对象时,加载配置文件,对bean进行初始化
* @author spikeCong
* @date 2022/10/28
**/
public abstract class AbstractApplicationContext implements ApplicationContext {

   //声明解析器变量
   protected BeanDefinitionReader beanDefinitionReader;

   //定义存储bean对象的Map集合
   protected Map<String,Object> singletonObjects = new HashMap<>();

   //声明配置文件类路径的变量
   protected String configLocation;

   @Override
   public void refresh() {

       //加载beanDefinition对象
       try {
           beanDefinitionReader.loadBeanDefinitions(configLocation);
           //初始化bean
           finishBeanInitialization();
       } catch (Exception e) {
           e.printStackTrace();
       }

   }

   //bean初始化
   protected  void finishBeanInitialization() throws Exception {
       //获取对应的注册表对象
       BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();

       //获取beanDefinition对象
       String[] beanNames = registry.getBeanDefinitionNames();
       for (String beanName : beanNames) {
           //进行bean的初始化
           getBean(beanName);
       }
   };
}

4) ClassPathXmlApplicationContext类

该类主要是加载类路径下的配置文件,并进行bean对象的创建,主要完成以下功能:


/**
* IOC容器具体的子实现类,加载XML格式配置文件
* @author spikeCong
* @date 2022/10/28
**/
public class ClassPathXmlApplicationContext extends AbstractApplicationContext{

   public ClassPathXmlApplicationContext(String configLocation) {
       this.configLocation = configLocation;
       //构建解析器对象
       this.beanDefinitionReader = new XmlBeanDefinitionReader();

       this.refresh();
   }

   //跟据bean的对象名称获取bean对象
   @Override
   public Object getBean(String name) throws Exception {
       //判断对象容器中是否包含指定名称的bean对象,如果包含就返回,否则自行创建
       Object obj = singletonObjects.get(name);
       if(obj != null){
           return obj;
       }

       //自行创建,获取beanDefinition对象
       BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();
       BeanDefinition beanDefinition = registry.getBeanDefinition(name);

       //通过反射创建对象
       String className = beanDefinition.getClassName();
       Class<?> clazz = Class.forName(className);
       Object beanObj = clazz.newInstance();

       //CourseService与UserDao存依赖,所以要将UserDao一同初始化,进行依赖注入
       MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
       for (PropertyValue propertyValue : propertyValues) {
           //获取name属性值
           String propertyName = propertyValue.getName();
           //获取Value属性
           String value = propertyValue.getValue();
           //获取ref属性
           String ref = propertyValue.getRef();

           //ref与value只能存在一个
           if(ref != null && !"".equals(ref)){
               //获取依赖的bean对象,拼接set set+Course
               Object bean = getBean(ref);
               String methodName = StringUtils.getSetterMethodFieldName(propertyName);

               //获取所有方法对象
               Method[] methods = clazz.getMethods();
               for (Method method : methods) {
                   if(methodName.equals(method.getName())){
                       //执行该set方法
                       method.invoke(beanObj,bean);
                   }
               }
           }

           if(value != null && !"".equals(value)){
               String methodName = StringUtils.getSetterMethodFieldName(propertyName);
               //获取method
               Method method = clazz.getMethod(methodName, String.class);
               method.invoke(beanObj,value);
           }
       }

       //在返回beanObj之前 ,需要将对象存储到Map容器中
       this.singletonObjects.put(name,beanObj);


       return beanObj;
   }

   @Override
   public <T> T getBean(String name, Class<? extends T> clazz) throws Exception {
       Object bean = getBean(name);
       if(bean == null){
           return null;
       }

      return clazz.cast(bean);
   }
}
5) 自定义IOC容器测试

第一步: 将我们写好的自定义IOC容器项目,安装到maven仓库中,使其他项目可以引入其依赖


//依赖信息
<dependencies>
   <dependency>
       <groupId>com.mashibing</groupId>
       <artifactId>user_defined_springioc</artifactId>
       <version>1.0-SNAPSHOT</version>
   </dependency>
</dependencies>

第二步: 创建一个新的maven项目,引入上面的依赖,项目结构如下


第三步: 完成代码编写


public interface CourseDao {
   public void add();
}

public class CourseDaoImpl implements CourseDao {

   //value注入
   private String courseName;

   public String getCourseName() {
       return courseName;
   }

   public void setCourseName(String courseName) {
       this.courseName = courseName;
   }

   public CourseDaoImpl() {
       System.out.println("CourseDaoImpl创建了......");
   }

   @Override
   public void add() {
       System.out.println("CourseDaoImpl的add方法执行了......" + courseName);
   }
}

public interface CourseService {

   public void add();
}

public class CourseServiceImpl implements CourseService {

   public CourseServiceImpl() {
       System.out.println("CourseServiceImpl创建了......");
   }

   private CourseDao courseDao;

   public void setCourseDao(CourseDao courseDao) {
       this.courseDao = courseDao;
   }

   @Override
   public void add() {
       System.out.println("CourseServiceImpl的add方法执行了......");
       courseDao.add();
   }
}

<?xml version="1.0" encoding="UTF-8"?>
<beans>
   <bean id="courseService" class="com.mashibing.test_springioc.service.impl.CourseServiceImpl">
       <property name="courseDao" ref="courseDao"></property>
   </bean>
   <bean id="courseDao" class="com.mashibing.test_springioc.dao.impl.CourseDaoImpl">
       <property name="courseName" value="java"></property>
   </bean>
</beans>

public class CourseController{

   public static void main(String[] args) {
       //1.创建Spring的容器对象
       ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

       //2.从容器对象中获取CourseService对象
       CourseService courseService = context.getBean("courseService", CourseService.class);

       //3.调用UserService的add方法
       courseService.add();
   }
}
6) 案例中使用到的设计模式


修改内容