概述
一个优良的框架必定离不开各种设计模式的使用,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()办法。
@Overridepublic 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类。
@Overridepublic 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@Nullablepublic 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。
@FunctionalInterfacepublic interface ApplicationEventPublisher { default void publishEvent(ApplicationEvent event) { publishEvent((Object) event); } void publishEvent(Object event);}复制代码
@FunctionalInterface示意这是一个函数式接口,函数式接口只有一个形象办法。ApplicationContext类又继承了
ApplicationEventPublisher类,所以咱们能够应用ApplicationContext公布事件。
事件监听者
公布事件后须要有事件的监听者,事件监听者通过实现接口ApplicationListener来定义,这是一个函数式接口,并且带有泛型,要求E参数是ApplicationEvent的子类。
@FunctionalInterfacepublic 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。
@Componentpublic class PayListener implements ApplicationListener<PayApplicationEvent> { @Override public void onApplicationEvent(PayApplicationEvent event) { String message = event.getMessage(); System.out.println("监听到PayApplicationEvent事件,音讯为:" + message); }}复制代码
最初咱们应用ApplicationContext公布事件。
@SpringBootApplicationpublic 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...
起源:掘金
著作权归作者所有。商业转载请分割作者取得受权,非商业转载请注明出处。