关于java:Spring用了哪些设计模式能讲讲吗

34次阅读

共计 17943 个字符,预计需要花费 45 分钟才能阅读完成。

概述

一个优良的框架必定离不开各种设计模式的使用,Spring 框架也不例外。因为网上很多文章比拟散乱,所以想总结一下在 Spring 中用到的设计模式,心愿大家看完之后能对 spring 有更深层次的了解。

工厂模式

工厂模式咱们都晓得是把创建对象交给工厂,以此来升高类与类之间的耦合。工厂模式在 Spring 中的利用十分宽泛,这里举的例子是 ApplicationContext 和 BeanFactory,这也是 Spring 的 IOC 容器的根底。

首先看 BeanFactory,这是最底层的接口。

public interface BeanFactory {Object getBean(String name) throws BeansException;
    
    <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;
    
    Object getBean(String name, Object... args) throws BeansException;
    
    <T> T getBean(Class<T> requiredType) throws BeansException;
    
    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
    // 省略...
}
复制代码

《2020 最新 Java 根底精讲视频教程和学习路线!》
ApplicationContext 则是扩大类,也是一个接口,他的作用是当容器启动时,一次性创立所有的 bean。

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
    @Nullable
    String getId();

    String getApplicationName();

    String getDisplayName();

    long getStartupDate();

    @Nullable
    ApplicationContext getParent();

    AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;}
复制代码

ApplicationContext 有三个实现类,别离是:

  • ClassPathXmlApplication:从类门路 ClassPath 中寻找指定的 XML 配置文件,找到并装载 ApplicationContext 的实例化工作。
  • FileSystemXMLApplicationContext:从指定的文件系统门路中寻找指定的 XML 配置文件,找到并装载 ApplicationContext 的实例化工作。
  • XMLWebApplicationContext:从 Web 零碎中的 XML 文件载入 Bean 定义的信息,Web 利用寻找指定的 XML 配置文件,找到并装载实现 ApplicationContext 的实例化工作。

因而这几个类的关系咱们分明了,类图就是这样:

在哪里初始化呢,这讲起来有些简单,就不开展细讲,提一下。次要看 AbstractApplicationContext 类的 refresh()办法。

@Override
public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {
        // 省略...
        try {
            // 省略...
            // 初始化所有的单实例 Bean(没有配置赖加载的)
            finishBeanFactoryInitialization(beanFactory);
        }catch (BeansException ex) {// 省略...}finally {// 省略...}
    }
}
复制代码

单例模式

在零碎中,有很多对象咱们都只须要一个,比方线程池、Spring 的上下文对象,日志对象等等。单例模式的益处在于 对一些重量级的对象,省略了创建对象破费的工夫,缩小了零碎的开销 ,第二点是应用单例 能够缩小 new 操作的次数,缩小了 GC 线程回收内存的压力

实际上,在 Spring 中的 Bean 默认的作用域就是 singleton(单例) 的。如何实现的呢?

次要看 DefaultSingletonBeanRegistry 的 getSingleton()办法:

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
    /** 保留单例 Objects 的缓存汇合 ConcurrentHashMap,key:beanName --> value:bean 实例 */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(beanName, "Bean name must not be null");
        synchronized (this.singletonObjects) {
            // 查看缓存中是否有实例,如果缓存中有实例,间接返回
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
                // 省略...
                try {
                    // 通过 singletonFactory 获取单例
                    singletonObject = singletonFactory.getObject();
                    newSingleton = true;
                }
                // 省略...
                if (newSingleton) {addSingleton(beanName, singletonObject);
                }
            }
            // 返回实例
            return singletonObject;
        }
    }
    
    protected void addSingleton(String beanName, Object singletonObject) {synchronized (this.singletonObjects) {this.singletonObjects.put(beanName, singletonObject);
        this.singletonFactories.remove(beanName);
        this.earlySingletonObjects.remove(beanName);
        this.registeredSingletons.add(beanName);
      }
    }
}
复制代码

从源码中能够看出,是通过 ConcurrentHashMap 的形式,如果在 Map 中存在则间接返回,如果不存在则创立,并且 put 进 Map 汇合中,并且整段逻辑是应用同步代码块包住的,所以是线程平安的。

策略模式

策略模式,简略来说就是封装好一组策略算法,内部客户端依据不同的条件抉择不同的策略算法解决问题。这在很多框架,还有日常开发都会用到的一种设计模式。在 Spring 中,我这里举的例子是 Resource 类,这是所有资源拜访类所实现的接口。

针对不同的拜访资源的形式,Spring 定义了不同的 Resource 类的实现类。咱们看一张类图:

简略介绍一下 Resource 的实现类:

  • UrlResource:拜访网络资源的实现类。
  • ServletContextResource:拜访绝对于 ServletContext 门路里的资源的实现类。
  • ByteArrayResource:拜访字节数组资源的实现类。
  • PathResource:拜访文件门路资源的实现类。
  • ClassPathResource:拜访类加载门路里资源的实现类。

写一段伪代码来示范一下 Resource 类的应用:

@RequestMapping(value = "/resource", method = RequestMethod.GET)
public String resource(@RequestParam(name = "type") String type,
                       @RequestParam(name = "arg") String arg) throws Exception {
    Resource resource;
    // 这里能够优化为通过工厂模式,依据 type 创立 Resource 的实现类
    if ("classpath".equals(type)) {
        //classpath 下的资源
        resource = new ClassPathResource(arg);
    } else if ("file".equals(type)) {
        // 本地文件系统的资源
        resource = new PathResource(arg);
    } else if ("url".equals(type)) {
        // 网络资源
        resource = new UrlResource(arg);
    } else {return "fail";}
    InputStream is = resource.getInputStream();
    ByteArrayOutputStream os = new ByteArrayOutputStream();
    int i;
    while ((i = is.read()) != -1) {os.write(i);
    }
    String result = new String(os.toByteArray(), StandardCharsets.UTF_8);
    is.close();
    os.close();
    return "type:" + type + ",arg:" + arg + "rn" + result;
}
复制代码

这就是策略模式的思维,通过内部条件应用不同的算法解决问题。其实很简略,因为每个实现类的 getInputStream()办法都不一样,咱们看 ClassPathResource 的源码,是通过类加载器加载资源:

public class ClassPathResource extends AbstractFileResolvingResource {

    private final String path;

    @Nullable
    private ClassLoader classLoader;

    @Nullable
    private Class<?> clazz;
    
    @Override
    public InputStream getInputStream() throws IOException {
        InputStream is;
        // 通过类加载器加载类门路下的资源
        if (this.clazz != null) {is = this.clazz.getResourceAsStream(this.path);
        }
        else if (this.classLoader != null) {is = this.classLoader.getResourceAsStream(this.path);
        }
        else {is = ClassLoader.getSystemResourceAsStream(this.path);
        }
        // 如果输出流 is 为 null,则报错
        if (is == null) {throw new FileNotFoundException(getDescription() + "cannot be opened because it does not exist");
        }
        // 返回 InputStream
        return is;
    }
}
复制代码

再看 UrlResource 的源码,获取 InputStream 的实现又是另一种策略。

public class UrlResource extends AbstractFileResolvingResource {
    @Nullable
    private final URI uri;

    private final URL url;

    private final URL cleanedUrl;
    
    @Override
    public InputStream getInputStream() throws IOException {
        // 获取连贯
        URLConnection con = this.url.openConnection();
        ResourceUtils.useCachesIfNecessary(con);
        try {
            // 获取输出流,并返回
            return con.getInputStream();}
        catch (IOException ex) {// Close the HTTP connection (if applicable).
            if (con instanceof HttpURLConnection) {((HttpURLConnection) con).disconnect();}
            throw ex;
        }
    }
}
复制代码

代理模式

Spring 除了 IOC(管制反转)之外的另一个外围就是 AOP(面向切面编程)。AOP 可能将与业务无关的,却被业务模块所独特调用的逻辑 ( 比方日志,权限管制等等 ) 封装起来,缩小零碎反复代码,升高零碎之间的耦合,有利于零碎的保护和扩大。

Spring AOP 次要是基于动静代理实现的,如果要代理的类,实现了某个接口,则应用 JDK 动静代理,如果没有实现接口则应用 Cglib 动静代理。

咱们看 DefaultAopProxyFactory 的 createAopProxy()办法,Spring 通过此办法创立动静代理类:

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
    @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {throw new AopConfigException("TargetSource cannot determine target class:" + "Either an interface or a target is required for proxy creation.");
            }
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {return new JdkDynamicAopProxy(config);
        }
    }
}
复制代码

JDK 动静代理和 Cglib 动静代理的区别:

  • JDK 动静代理只能对实现了接口的类生成代理,没有实现接口的类不能应用。
  • Cglib 动静代理即便被代理的类没有实现接口,也能够应用,因为 Cglib 动静代理是应用继承被代理类的形式进行扩大。
  • Cglib 动静代理是通过继承的形式,笼罩被代理类的办法来进行代理,所以如果办法是被 final 润饰的话,就不能进行代理。

从源码中能够看出,Spring 会先判断是否实现了接口,如果实现了接口就应用 JDK 动静代理,如果没有实现接口则应用 Cglib 动静代理,也能够通过配置,强制应用 Cglib 动静代理,配置如下:

<aop:aspectj-autoproxy proxy-target-class="true"/>
复制代码

模板模式

模板模式在 Spring 中用得太多了,它定义一个算法的骨架,而将一些步骤提早到子类中。个别定义一个抽象类为骨架,子类重写抽象类中的模板办法实现算法骨架中特定的步骤。模板模式能够不扭转一个算法的构造即可从新定义该算法的某些特定步骤。

Spring 中的事务管理器就使用模板模式的设计,首先看 PlatformTransactionManager 类。这是最底层的接口,定义提交和回滚的办法。

public interface PlatformTransactionManager {TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
    
    void commit(TransactionStatus status) throws TransactionException;
    
    void rollback(TransactionStatus status) throws TransactionException;
}
复制代码

毫无意外,应用了抽象类作为骨架,接着看 AbstractPlatformTransactionManager 类。

@Override
public final void commit(TransactionStatus status) throws TransactionException {
    // 省略...
    DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
    if (defStatus.isLocalRollbackOnly()) {
        // 省略...
        // 调用 processRollback()
        processRollback(defStatus, false);
        return;
    }

    if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
        // 省略...
        // 调用 processRollback()
        processRollback(defStatus, true);
        return;
    }
    // 调用 processCommit()
    processCommit(defStatus);
}

// 这个办法定义了骨架,外面会调用一个 doRollback()的模板办法
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {if (status.hasSavepoint()) {// 省略...}
    else if (status.isNewTransaction()) {// 调用 doRollback()模板办法
        doRollback(status);
    }
    else {// 省略...}
    // 省略了很多代码...
}

private void processCommit(DefaultTransactionStatus status) throws TransactionException {
    // 省略...
    if (status.hasSavepoint()) {// 省略...}
    else if (status.isNewTransaction()) {
        // 省略...
        // 调用 doCommit()模板办法
        doCommit(status);
    }
    else if (isFailEarlyOnGlobalRollbackOnly()) {unexpectedRollback = status.isGlobalRollbackOnly();
    }
    // 省略了很多代码...
}

// 模板办法 doRollback(),把重要的步骤提早到子类去实现
protected abstract void doRollback(DefaultTransactionStatus status) throws TransactionException;

// 模板办法 doCommit(),把重要的步骤提早到子类去实现
protected abstract void doCommit(DefaultTransactionStatus status) throws TransactionException;
复制代码

模板办法则由各种事务管理器的实现类去实现,也就是把骨架中重要的 doRollback()提早到子类。一般来说,Spring 默认是应用的事务管理器的实现类是 DataSourceTransactionManager。

// 通过继承 AbstractPlatformTransactionManager 抽象类
public class DataSourceTransactionManager extends AbstractPlatformTransactionManager
        implements ResourceTransactionManager, InitializingBean {// 重写 doCommit()办法,实现具体 commit 的逻辑
    @Override
    protected void doCommit(DefaultTransactionStatus status) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
        Connection con = txObject.getConnectionHolder().getConnection();
        if (status.isDebug()) {logger.debug("Committing JDBC transaction on Connection [" + con + "]");
        }
        try {con.commit();
        }
        catch (SQLException ex) {throw new TransactionSystemException("Could not commit JDBC transaction", ex);
        }
    }
    
    // 重写 doRollback()办法,实现具体的 rollback 的逻辑
    @Override
    protected void doRollback(DefaultTransactionStatus status) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
    Connection con = txObject.getConnectionHolder().getConnection();
    if (status.isDebug()) {logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
    }
    try {con.rollback();
    }
    catch (SQLException ex) {throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
    }
  }
}
复制代码

如果你是用 Hibernate 框架,Hibernate 也有本身的实现,这就体现了设计模式的开闭准则,通过继承或者组合的形式进行扩大,而不是间接批改类的代码。Hibernate 的事务管理器则是 HibernateTransactionManager。

public class HibernateTransactionManager extends AbstractPlatformTransactionManager
        implements ResourceTransactionManager, BeanFactoryAware, InitializingBean {// 重写 doCommit()办法,实现 Hibernate 的具体 commit 的逻辑
  @Override
  protected void doCommit(DefaultTransactionStatus status) {HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction();
      Transaction hibTx = txObject.getSessionHolder().getTransaction();
      Assert.state(hibTx != null, "No Hibernate transaction");
      if (status.isDebug()) {
        logger.debug("Committing Hibernate transaction on Session [" +
            txObject.getSessionHolder().getSession() + "]");
      }
      try {hibTx.commit();
      }
      catch (org.hibernate.TransactionException ex) {throw new TransactionSystemException("Could not commit Hibernate transaction", ex);
      }
          // 省略...
    }
    
  // 重写 doRollback()办法,实现 Hibernate 的具体 rollback 的逻辑
  @Override
  protected void doRollback(DefaultTransactionStatus status) {HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction();
    Transaction hibTx = txObject.getSessionHolder().getTransaction();
    Assert.state(hibTx != null, "No Hibernate transaction");
    // 省略...
    try {hibTx.rollback();
    }
    catch (org.hibernate.TransactionException ex) {throw new TransactionSystemException("Could not roll back Hibernate transaction", ex);
    }
        // 省略...
    finally {if (!txObject.isNewSession() && !this.hibernateManagedSession) {txObject.getSessionHolder().getSession().clear();
      }
    }
  }
}
复制代码

其实模板模式在日常开发中也常常用,比方一个办法中,前后代码都一样,只有两头有一部分操作不同,就能够应用模板模式进行优化代码,这能够大大地缩小冗余的代码,十分实用。

适配器模式与责任链模式

适配器模式是一种结构型设计模式,它 能使接口不兼容的对象可能相互合作,将一个类的接口,转换成客户冀望的另外一个接口

在 SpringAOP 中有一个很重要的性能就是应用的 Advice(告诉)来加强被代理类的性能,Advice 次要有 MethodBeforeAdvice、AfterReturningAdvice、ThrowsAdvice 这几种。每个 Advice 都有对应的拦截器,如下所示:

Spring 须要将每个 Advice 都封装成对应的拦截器类型返回给容器,所以 须要应用适配器模式对 Advice 进行转换。对应的就有三个适配器,咱们看个类图:

适配器在 Spring 中是怎么把告诉类和拦挡类进行转换的呢,咱们先看适配器的接口。定义了两个办法,别离是 supportsAdvice()和 getInterceptor()。

public interface AdvisorAdapter {
    // 判断告诉类是否匹配
    boolean supportsAdvice(Advice advice);
    // 传入告诉类,返回对应的拦挡类
    MethodInterceptor getInterceptor(Advisor advisor);
}
复制代码

其实很简略,能够看出转换的办法就是 getInterceptor(),通过 supportsAdvice()进行判断。咱们看前置告诉的适配器的实现类 MethodBeforeAdviceAdapter。

class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
    // 判断是否匹配 MethodBeforeAdvice 告诉类
    @Override
    public boolean supportsAdvice(Advice advice) {return (advice instanceof MethodBeforeAdvice);
    }
    // 传入 MethodBeforeAdvice,转换为 MethodBeforeAdviceInterceptor 拦挡类
    @Override
    public MethodInterceptor getInterceptor(Advisor advisor) {MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
        return new MethodBeforeAdviceInterceptor(advice);
    }
}
复制代码

getInterceptor()办法中,调用了对应的拦挡类的结构器创立对应的拦截器返回,传入告诉类 advice 作为参数。接着咱们看拦截器 MethodBeforeAdviceInterceptor。

public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
  // 成员变量,告诉类
  private MethodBeforeAdvice advice;
    
  // 定义了有参结构器,内部通过有参结构器创立 MethodBeforeAdviceInterceptor
  public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {Assert.notNull(advice, "Advice must not be null");
      this.advice = advice;
  }
  
  // 当调用拦截器的 invoke 办法时,就调用告诉类的 before()办法,实现前置告诉
  @Override
  public Object invoke(MethodInvocation mi) throws Throwable {// 调用告诉类的 before()办法,实现前置告诉
      this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
      return mi.proceed();}

}
复制代码

那么在哪里初始化这些适配器呢,咱们看 DefaultAdvisorAdapterRegistry()。

public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {private final List<AdvisorAdapter> adapters = new ArrayList<>(3);

  public DefaultAdvisorAdapterRegistry() {
    // 初始化适配器,增加到 adapters 汇合,也就是注册
    registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
    registerAdvisorAdapter(new AfterReturningAdviceAdapter());
    registerAdvisorAdapter(new ThrowsAdviceAdapter());
  }
    
  @Override
  public void registerAdvisorAdapter(AdvisorAdapter adapter) {this.adapters.add(adapter);
  }
    
    // 获取所有的拦截器
    @Override
    public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {List<MethodInterceptor> interceptors = new ArrayList<>(3);
        Advice advice = advisor.getAdvice();
        if (advice instanceof MethodInterceptor) {interceptors.add((MethodInterceptor) advice);
        }
        // 遍历 adapters 汇合
        for (AdvisorAdapter adapter : this.adapters) {// 调用 supportsAdvice()办法,判断入参的 advisor 是否有匹配的适配器
            if (adapter.supportsAdvice(advice)) {// 如果匹配,则调用 getInterceptor()转换成对应的拦截器,增加到 interceptors 汇合中
                interceptors.add(adapter.getInterceptor(advisor));
            }
        }
        if (interceptors.isEmpty()) {throw new UnknownAdviceTypeException(advisor.getAdvice());
        }
        // 返回拦截器汇合
        return interceptors.toArray(new MethodInterceptor[0]);
    }
    
}
复制代码

适配器模式在这里就是把告诉类转为拦挡类,转为拦挡类之后,就增加到拦截器汇合中。增加到拦截器汇合之后,就用到了责任链模式,在 ReflectiveMethodInvocation 类被调用,咱们看 JDK 动静代理 JdkDynamicAopProxy 的 invoke()办法。

@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    MethodInvocation invocation;
    // 这里就是获取拦截器汇合,最初就会调用到上文说的 getInterceptors()
    List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    if (chain.isEmpty()) {// 省略...}else {
        // 创立一个 MethodInvocation
        invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
        // 调用 proceed()办法,底层会通过指针遍历拦截器汇合,而后实现前置告诉等性能
        retVal = invocation.proceed();}
    // 省略...
}
复制代码

最初就在 ReflectiveMethodInvocation 里调用 proceed()办法,proceed()办法是一个递归的办法,通过指针管制递归的完结。这是很典型的责任链模式。

public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {
    protected final List<?> interceptorsAndDynamicMethodMatchers;
    // 指针
    private int currentInterceptorIndex = -1;
    
    protected ReflectiveMethodInvocation(Object proxy, @Nullable Object target, Method method, @Nullable Object[] arguments, @Nullable Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {
        // 省略...
        // 拦截器的汇合
        this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
    }
    
    @Override
    @Nullable
    public Object proceed() throws Throwable {
        //    We start with an index of -1 and increment early.
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
            // 递归完结
            return invokeJoinpoint();}
        // 获取拦截器,并且以后的指针 +1
        Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
        if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
            InterceptorAndDynamicMethodMatcher dm =
                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
            if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {return dm.interceptor.invoke(this);
            }
            else {
                // 匹配失败,跳过,递归下一个
                return proceed();}
        }
        else {// 匹配拦截器,强转为拦截器,而后执行 invoke()办法,而后就会调用拦截器里的成员变量的 before(),afterReturning()等等,实现前置告诉,后置告诉,异样告诉
            return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
        }
    }
}
复制代码

这里可能没学过责任链模式的同学会看得有点晕,然而学过责任链模式应该很容易看懂,这其实跟 SpringMVC 的拦截器的逻辑实现简直一样的。

观察者模式

观察者模式是一种对象行为型模式,当一个对象发生变化时,这个对象所依赖的对象也会做出反馈。Spring 事件驱动模型就是观察者模式很经典的一个利用。

事件角色

在 Spring 事件驱动模型中,首先有事件角色 ApplicationEvent,这是一个抽象类,抽象类下有四个实现类代表四种事件。

  • ContextStartedEvent:ApplicationContext 启动后触发的事件。
  • ContextStoppedEvent:ApplicationContext 进行后触发的事件。
  • ContextRefreshedEvent:ApplicationContext 初始化或刷新实现后触发的事件。
  • ContextClosedEvent:ApplicationContext 敞开后触发的事件。

事件发布者

有了事件之后,须要有个发布者公布事件,发布者对应的类是 ApplicationEventPublisher。

@FunctionalInterface
public interface ApplicationEventPublisher {default void publishEvent(ApplicationEvent event) {publishEvent((Object) event);
    }
    
    void publishEvent(Object event);

}
复制代码

@FunctionalInterface 示意这是一个函数式接口,函数式接口只有一个形象办法。ApplicationContext 类又继承了

ApplicationEventPublisher 类,所以咱们能够应用 ApplicationContext 公布事件。

事件监听者

公布事件后须要有事件的监听者,事件监听者通过实现接口 ApplicationListener 来定义,这是一个函数式接口,并且带有泛型,要求 E 参数是 ApplicationEvent 的子类。

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {void onApplicationEvent(E event);
}
复制代码

上面咱们演示一下怎么应用,首先继承抽象类 ApplicationEvent 定义一个事件角色 PayApplicationEvent。

public class PayApplicationEvent extends ApplicationEvent {

    private String message;

    public PayApplicationEvent(Object source, String message) {super(source);
        this.message = message;
    }

    public String getMessage() {return message;}
}
复制代码

接着定义一个 PayApplicationEvent 事件的监听者 PayListener。

@Component
public class PayListener implements ApplicationListener<PayApplicationEvent> {
    
    @Override
    public void onApplicationEvent(PayApplicationEvent event) {String message = event.getMessage();
        System.out.println("监听到 PayApplicationEvent 事件,音讯为:" + message);
    }
}
复制代码

最初咱们应用 ApplicationContext 公布事件。

@SpringBootApplication
public class SpringmvcApplication {public static void main(String[] args) throws Exception {ApplicationContext applicationContext = SpringApplication.run(SpringmvcApplication.class, args);
        applicationContext.publishEvent(new PayApplicationEvent(applicationContext,"胜利领取 100 元!"));
    }

}
复制代码

启动之后咱们能够看到控制台打印:

絮叨

实际上,Spring 中应用到的设计模式在源码中随处可见,并不止我列举的这些,所以 Spring 的源码十分值得去浏览和学习,受害良多。反过来看,如果不会设计模式,读起源码来也是十分吃力的,所以我倡议还是先学会设计模式再去学习源码。

作者:java 技术爱好者
链接:https://juejin.cn/post/690574…
起源:掘金
著作权归作者所有。商业转载请分割作者取得受权,非商业转载请注明出处。

正文完
 0