【一起学Spring】Bean生命周期十步拆解法之实例化Bean

引言

学习贵在持之以恒,写文章也是;工作时间8H好好工作,写文章的时间只有晚上啦。但也是只有在晚上,夜深人静的时候,更能专注于写作。为自己加油,鼓劲!

 

 今天晚上我们继续进行【一起学Spring】专栏,我将Spring的生命周期按流程进行拆解,分为以下核心十步来进行讲解和学习,如下:

  1. Bean扫描和加载
  2. 实例化
  3. 属性填充
  4. 初始化Bean
  5. 回调Aware接口
  6. 初始化之前的操作
  7. 执行初始化
  8. 初始化后的操作
  9. 存放单例池
  10. Bean销毁

那我们今天呢,我们先来看下实例化

目标

先看项目结构最终的UML图

随着功能点的增加,相较于上一节中,增加了不少接口和类文件,其中黄颜色背景区域是Bean实例化的核心类交互图。另外,我也对BeanFactory的实现增加了一些工厂类。以上主要实现的功能如下:

1、通过重新定义BeanDefinition,填充要实例化的Class类型,最终通过CGLIB的方式进行实例化

2、增加Bean单例池,用于对象实例化后,将对象注册到单例池中,如果在同获取同一名称的对象,直接从Bean单例池中进行获取

3、增加Bean实例化流程,定义实例化的策略类,当前最终是通过CLIB的方式调用类的无参构造函数进行实例化。

基于以上功能,我们定义了:

1、基于BeanFactory扩展了HierarchicalBeanFactory,ConfigurableBeanFactory接口

2、增加了AbstractBeanDefinition抽象类,用于配置Bean的Class类型配置信息

3、增加了单例池注册接口SingletonBeanRegistry和其实现类DefaultSingletonBeanRegistry

4、增加了实例化策略接口InstantiationStrategy和其实现类SimpleInstantiationStrategy,CglibSubclassingInstantiationStrategy;以及基于CGLIB的最终创建static类CglibSubclassCreator

准备

IDE:idea
JDK: 1.8
Maven: 3.9.5

实践

项目结构

使用tree命令查看项目目录及文件
因为我是Windows系统,使用命令如下:

tree /f

目录结构如下

│  pom.xml
│
├─assets
└─src
    ├─main
    │  └─java
    │      └─cn
    │          └─itdebug
    │              └─spring
    │                  └─beans
    │                      │  BeansException.java
    │                      │
    │                      ├─factory
    │                      │  │  BeanFactory.java
    │                      │  │  HierarchicalBeanFactory.java
    │                      │  │
    │                      │  ├─config
    │                      │  │      BeanDefinition.java
    │                      │  │      ConfigurableBeanFactory.java
    │                      │  │      SingletonBeanRegistry.java
    │                      │  │
    │                      │  └─support
    │                      │          AbstractAutowireCapableBeanFactory.java
    │                      │          AbstractBeanDefinition.java
    │                      │          AbstractBeanFactory.java
    │                      │          BeanDefinitionRegistry.java
    │                      │          CglibSubclassingInstantiationStrategy.java
    │                      │          DefaultListableBeanFactory.java
    │                      │          DefaultSingletonBeanRegistry.java
    │                      │          FactoryBeanRegistrySupport.java
    │                      │          InstantiationStrategy.java
    │                      │          RootBeanDefinition.java
    │                      │          SimpleInstantiationStrategy.java
    │                      │
    │                      └─util
    │                              Assert.java
    │                              CollectionUtils.java
    │                              ObjectUtils.java
    │                              StringUtils.java
    │
    └─test
        └─java
            └─cn
                └─itdebug
                    └─spring
                        └─beans
                            └─factory
                                │  BeanFactoryTest.java
                                │
                                └─model
                                        Baozi.java

代码实现

1、基于BeanFactory扩展了HierarchicalBeanFactory,ConfigurableBeanFactory接口

HierarchicalBeanFactory:

/**
 * Sub-interface implemented by bean factories that can be part
 * of a hierarchy.
 *
 * <p>The corresponding <code>setParentBeanFactory</code> method for bean
 * factories that allow setting the parent in a configurable
 * fashion can be found in the ConfigurableBeanFactory interface.
 *
 * 由 Bean 工厂实现的子接口,可以是层次结构的一部分。
 * <p><code><code> 可以在 ConfigurableBeanFactory 接口中找到允许以可配置方式设置父级的 Bean 工厂的相应 setParentBeanFactory 方法。
 *
 * @创建人 Eric.Lu
 * @创建时间 2023/12/26
 * @描述
 */
public interface HierarchicalBeanFactory extends BeanFactory {
}

ConfigurableBeanFactory: 

/**
 * Configuration interface to be implemented by most bean factories. Provides
 * facilities to configure a bean factory, in addition to the bean factory
 * client methods in the {@link org.springframework.beans.factory.BeanFactory}
 * interface.
 *
 * <p>This bean factory interface is not meant to be used in normal application
 * code: Stick to {@link org.springframework.beans.factory.BeanFactory} or
 * {@link org.springframework.beans.factory.ListableBeanFactory} for typical
 * needs. This extended interface is just meant to allow for framework-internal
 * plug'n'play and for special access to bean factory configuration methods.
 *
 * 大多数 Bean 工厂要实现的配置接口。除了 {@link org.springframework.beans.factory.BeanFactory}
 * 接口中的 Bean 工厂客户端方法外,还提供用于配置 Bean 工厂的工具。
 *
 * <p>这个 Bean 工厂接口不适合在普通应用程序代码中使用:对于典型需求,
 * 请坚持使用 {@link org.springframework.beans.factory.BeanFactory}
 * 或 {@link org.springframework.beans.factory.ListableBeanFactory}。
 * 这个扩展接口只是为了允许框架内部即插即用和对 Bean 工厂配置方法的特殊访问。
 *
 * @创建人 Eric.Lu
 * @创建时间 2023/12/26
 * @描述
 */
public interface ConfigurableBeanFactory extends HierarchicalBeanFactory,SingletonBeanRegistry{
}
2、增加了AbstractBeanDefinition抽象类,用于配置Bean的Class类型配置信息
/**
 * @创建人 Eric.Lu
 * @创建时间 2023/12/25
 * @描述
 */
public abstract class AbstractBeanDefinition implements BeanDefinition {

	private volatile Object beanClass;

	@Override
	public String getBeanClassName() {
		Object beanClassObject = this.beanClass;
		if (beanClassObject instanceof Class) {
			return ((Class<?>) beanClassObject).getName();
		}
		else {
			return (String) beanClassObject;
		}
	}

	@Override
	public void setBeanClassName(String beanClassName) {
		this.beanClass = beanClassName;
	}

	public Class<?> getBeanClass() throws BeansException {
		Object beanClassObject = this.beanClass;
		if (beanClassObject == null) {
			throw new BeansException("No bean class specified on bean definition");
		}
		if (!(beanClassObject instanceof Class)) {
			throw new BeansException(
					"Bean class name [" + beanClassObject + "] has not been resolved into an actual Class");
		}
		return (Class) beanClassObject;
	}

	public void setBeanClass(Class<?> beanClass) {
		this.beanClass = beanClass;
	}
}
3、增加了单例池注册接口SingletonBeanRegistry和其实现类DefaultSingletonBeanRegistry

SingletonBeanRegistry:

/**
 * Interface that defines a registry for shared bean instances.
 * Can be implemented by {@link BeanFactory}
 * implementations in order to expose their singleton management facility
 * in a uniform manner.
 *
 * 定义共享 Bean 实例的注册表的接口。可以通过 {@link BeanFactory} 实现来实现,以便以统一的方式公开其单例管理工具。
 * @创建人 Eric.Lu
 * @创建时间 2023/12/26
 * @描述
 */
public interface SingletonBeanRegistry {

	/**
	 * Register the given existing object as singleton in the bean registry,
	 * under the given bean name.
	 * <p>The given instance is supposed to be fully initialized; the registry
	 * will not perform any initialization callbacks (in particular, it won't
	 * call InitializingBean's <code>afterPropertiesSet</code> method).
	 * The given instance will not receive any destruction callbacks
	 * (like DisposableBean's <code>destroy</code> method) either.
	 * <p>When running within a full BeanFactory: <b>Register a bean definition
	 * instead of an existing instance if your bean is supposed to receive
	 * initialization and/or destruction callbacks.</b>
	 * <p>Typically invoked during registry configuration, but can also be used
	 * for runtime registration of singletons. As a consequence, a registry
	 * implementation should synchronize singleton access; it will have to do
	 * this anyway if it supports a BeanFactory's lazy initialization of singletons.
	 *
	 * 在 Bean 注册表中,在给定的 Bean 名称下,将给定的现有对象注册为单例。
	 * <p>给定的实例应该被完全初始化;注册表不会执行任何初始化回调(特别是,它不会调用
	 * InitializingBean 的 <code>afterPropertiesSet<code> 方法)。
	 * 给定的实例也不会收到任何销毁回调(如 DisposableBean 的 <code>destroy<code> 方法)。
	 * <p>在完整的 BeanFactory 中运行时:如果您的 Bean <b>应该接收初始化和/或销毁回调,
	 * 请注册一个 Bean 定义而不是现有实例。<b>
	 * <p>通常在注册表配置期间调用,但也可用于单例的运行时注册。
	 * 因此,注册表实现应同步单例访问;
	 * 如果它支持 BeanFactory 的单例延迟初始化,它无论如何都必须这样做。
	 *
	 * @param beanName the name of the bean
	 * @param singletonObject the existing singleton object
	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
	 * @see org.springframework.beans.factory.DisposableBean#destroy
	 * @see org.springframework.beans.factory.support.BeanDefinitionRegistry#registerBeanDefinition
	 */
	void registerSingleton(String beanName, Object singletonObject);

	/**
	 * Return the (raw) singleton object registered under the given name.
	 * <p>Only checks already instantiated singletons; does not return an Object
	 * for singleton bean definitions which have not been instantiated yet.
	 * <p>The main purpose of this method is to access manually registered singletons
	 * (see {@link #registerSingleton}). Can also be used to access a singleton
	 * defined by a bean definition that already been created, in a raw fashion.
	 * <p><b>NOTE:</b> This lookup method is not aware of FactoryBean prefixes or aliases.
	 * You need to resolve the canonical bean name first before obtaining the singleton instance.
	 *
	 * 返回在给定名称下注册的(原始)单例对象。仅检查已实例化的单例;
	 * <p>不返回尚未实例化的单例 Bean 定义的 Object。
	 * <p>此方法的主要用途是访问手动注册的单例
	 * (请参阅 {@link registerSingleton})。还可用于以原始方式访问由已创建的 Bean 定义定义的单例。
	 * <p><b>注意:<b>此查找方法无法识别 FactoryBean 前缀或别名。
	 * 在获取单例实例之前,您需要先解析规范 Bean 名称。
	 *
	 * @param beanName the name of the bean to look for
	 * @return the registered singleton object, or <code>null</code> if none found
	 * @see ConfigurableListableBeanFactory#getBeanDefinition
	 */
	Object getSingleton(String beanName);
}

DefaultSingletonBeanRegistry:

/**
 * @创建人 Eric.Lu
 * @创建时间 2023/12/26
 * @描述
 */
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {

	/**
	 * Internal marker for a null singleton object:
	 * used as marker value for concurrent Maps (which don't support null values).
	 *
	 * 空单例对象的内部标记:用作并发映射(不支持空值)的标记值。
	 */
	protected static final Object NULL_OBJECT = new Object();

	/** Logger available to subclasses */
	protected final Log logger = LogFactory.getLog(getClass());

	/** Cache of singleton objects: bean name --> bean instance */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>();

	@Override
	public void registerSingleton(String beanName, Object singletonObject) {
		Assert.notNull(beanName, "'beanName' must not be null");
		synchronized (this.singletonObjects) {
			Object oldObject = this.singletonObjects.get(beanName);
			if (oldObject != null) {
				throw new IllegalStateException("Could not register object [" + singletonObject +
						"] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");
			}
			addSingleton(beanName, singletonObject);
		}
	}

	/**
	 * Add the given singleton object to the singleton cache of this factory.
	 * <p>To be called for eager registration of singletons.
	 * @param beanName the name of the bean
	 * @param singletonObject the singleton object
	 */
	protected void addSingleton(String beanName, Object singletonObject) {
		synchronized (this.singletonObjects) {
			this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
		}
	}

	@Override
	public Object getSingleton(String beanName) {
		return getSingleton(beanName, true);
	}

	/**
	 * Return the (raw) singleton object registered under the given name.
	 * <p>Checks already instantiated singletons and also allows for an early
	 * reference to a currently created singleton (resolving a circular reference).
	 *
	 * 返回在给定名称下注册的(原始)单例对象。<p>检查已实例化的单例,并允许对当前创建的单例的早期引用(解析循环引用)。
	 *
	 * @param beanName the name of the bean to look for
	 * @param allowEarlyReference whether early references should be created or not
	 * @return the registered singleton object, or <code>null</code> if none found
	 */
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		Object singletonObject = this.singletonObjects.get(beanName);
		return (singletonObject != NULL_OBJECT ? singletonObject : null);
	}
}
4、增加了实例化策略接口InstantiationStrategy和其实现类SimpleInstantiationStrategy,CglibSubclassingInstantiationStrategy;以及基于CGLIB的最终创建static类CglibSubclassCreator

InstantiationStrategy:

/**
 * @创建人 Eric.Lu
 * @创建时间 2023/12/25
 * @描述
 */
public interface InstantiationStrategy {

	Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner)
			throws BeansException;
}

SimpleInstantiationStrategy:

/**
 * @创建人 Eric.Lu
 * @创建时间 2023/12/25
 * @描述
 */
public class SimpleInstantiationStrategy implements InstantiationStrategy{

	@Override
	public Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) throws BeansException {
		// Must generate CGLIB subclass.
		return instantiateWithMethodInjection(beanDefinition, beanName, owner);
	}

	protected Object instantiateWithMethodInjection(
			RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) {

		throw new UnsupportedOperationException(
				"Method Injection not supported in SimpleInstantiationStrategy");
	}
}

CglibSubclassingInstantiationStrategy:

/**
 * @创建人 Eric.Lu
 * @创建时间 2023/12/25
 * @描述
 */
public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationStrategy {

	@Override
	protected Object instantiateWithMethodInjection(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) {
		// Must generate CGLIB subclass.
		return new CglibSubclassCreator(beanDefinition, owner).instantiate(null, null);
	}

	/**
	 * An inner class so we don't have a CGLIB dependency in core.
	 */
	private static class CglibSubclassCreator {

		private static final Log logger = LogFactory.getLog(CglibSubclassCreator.class);

		private final RootBeanDefinition beanDefinition;
		private final BeanFactory owner;

		public CglibSubclassCreator(RootBeanDefinition beanDefinition, BeanFactory owner) {
			this.beanDefinition = beanDefinition;
			this.owner = owner;
		}

		/**
		 * Create a new instance of a dynamically generated subclasses implementing the
		 * required lookups.
		 *
		 * @param ctor constructor to use. If this is <code>null</code>, use the
		 *             no-arg constructor (no parameterization, or Setter Injection)
		 * @param args arguments to use for the constructor.
		 *             Ignored if the ctor parameter is <code>null</code>.
		 * @param ctor 构造函数使用。如果此值为 <code>null<code>,
		 *             请使用 no-arg 构造函数(无参数化或 Setter 注入)
		 * @return new instance of the dynamically generated class
		 * <p>
		 * 创建动态生成的子类的新实例,以实现所需的查找。
		 * @param用于构造函数的 args 参数。如果 ctor 参数为 null,则忽略<code><code>。
		 * @return动态生成的类的新实例
		 */
		public Object instantiate(Constructor ctor, Object[] args) {
			Enhancer enhancer = new Enhancer();
			enhancer.setSuperclass(this.beanDefinition.getBeanClass());
			enhancer.setCallback(NoOp.INSTANCE);
//			enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> methodProxy.invokeSuper(o, objects));
			return (ctor == null) ?
					enhancer.create() :
					enhancer.create(ctor.getParameterTypes(), args);
		}
	}
}
5、为实现以上功能,AbstractBeanFactory、AbstractAutowireCapableBeanFactory、DefaultListableBeanFactory等流程也进行了代码重新编排

AbstractBeanFactory:

/**
 * @创建人 Eric.Lu
 * @创建时间 2023/12/24
 * @描述
 */
public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements ConfigurableBeanFactory {

	/** Map from bean name to merged RootBeanDefinition */
	private final Map<String, RootBeanDefinition> mergedBeanDefinitions =
			new ConcurrentHashMap<String, RootBeanDefinition>();

	@Override
	public Object getBean(String name) throws BeansException {
		return doGetBean(name, null, null, false);
	}

	protected <T> T doGetBean(
			final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
			throws BeansException {

		// 提前检查对象是否已存在,如果存在,直接返回
		Object sharedInstance = getSingleton(name);
		if(sharedInstance != null && args == null) {
			logger.info("Returning cached instance of singleton bean '" + name + "'");
		} else {
			final RootBeanDefinition mbd = getMergedLocalBeanDefinition(name);
			sharedInstance = createBean(name, mbd, args);
			addSingleton(name, sharedInstance);
		}
		return (T) sharedInstance;
	}


	protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
		// Quick check on the concurrent map first, with minimal locking.
		RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
		if (mbd != null) {
			return mbd;
		}
		return (RootBeanDefinition) getBeanDefinition(beanName);
	}

	protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;

	protected abstract Object createBean(String beanName, RootBeanDefinition mbd, Object[] args)
			throws BeansException;
}

AbstractAutowireCapableBeanFactory:

/**
 * @创建人 Eric.Lu
 * @创建时间 2023/12/24
 * @描述
 */
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {

	private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

	@Override
	protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeansException {
		return instantiateBean(beanName, mbd);
	}

	protected Object instantiateBean(final String beanName, final RootBeanDefinition mbd) {
		final BeanFactory parent = this;
		return getInstantiationStrategy().instantiate(mbd, beanName, parent);
	}

	/**
	 * Set the instantiation strategy to use for creating bean instances.
	 * Default is CglibSubclassingInstantiationStrategy.
	 *
	 * 设置用于创建 Bean 实例的实例化策略。默认值为 CglibSubclassingInstantiationStrategy。
	 * @see CglibSubclassingInstantiationStrategy
	 */
	public void setInstantiationStrategy(InstantiationStrategy instantiationStrategy) {
		this.instantiationStrategy = instantiationStrategy;
	}

	/**
	 * Return the instantiation strategy to use for creating bean instances.
	 *
	 * 返回用于创建 Bean 实例的实例化策略。
	 */
	protected InstantiationStrategy getInstantiationStrategy() {
		return this.instantiationStrategy;
	}
}
DefaultListableBeanFactory:
/**
 * Default implementation of the
 * {@link org.springframework.beans.factory.ListableBeanFactory} and
 * {@link BeanDefinitionRegistry} interfaces: a full-fledged bean factory
 * based on bean definition objects.
 *
 * <p>Typical usage is registering all bean definitions first (possibly read
 * from a bean definition file), before accessing beans. Bean definition lookup
 * is therefore an inexpensive operation in a local bean definition table,
 * operating on pre-built bean definition metadata objects.
 *
 * <p>Can be used as a standalone bean factory, or as a superclass for custom
 * bean factories. Note that readers for specific bean definition formats are
 * typically implemented separately rather than as bean factory subclasses:
 * see for example {@link PropertiesBeanDefinitionReader} and
 * {@link org.springframework.beans.factory.xml.XmlBeanDefinitionReader}.
 *
 * <p>For an alternative implementation of the
 * {@link org.springframework.beans.factory.ListableBeanFactory} interface,
 * have a look at {@link StaticListableBeanFactory}, which manages existing
 * bean instances rather than creating new ones based on bean definitions.
 *
 * {@link org.springframework.beans.factory.ListableBeanFactory} 和 {@link BeanDefinitionRegistry} 接口的默认实现:
 * 基于 Bean 定义对象的成熟 Bean 工厂。
 * <p>典型的用法是先注册所有 Bean 定义(可能从 Bean 定义文件中读取),然后再访问 Bean。
 * 因此,Bean 定义查找是本地 Bean 定义表中的一项低成本操作,它对预构建的 Bean 定义元数据对象进行操作。
 * 可以用作独立的 Bean 工厂<p>,也可以用作自定义 Bean 工厂的超类。
 * 请注意,特定 Bean 定义格式的读取器通常是单独实现的,而不是作为 Bean 工厂子类实现的:
 * 例如,参见 {@link PropertiesBeanDefinitionReader} 和 {@link org.springframework.beans.factory.xml.XmlBeanDefinitionReader}。
 * <p>有关{@link org.springframework.beans.factory.ListableBeanFactory}接口的替代实现,请查看{@link StaticListableBeanFactory},它管理现有的bean实例,
 * 而不是基于bean定义创建新的bean实例。
 *
 * @创建人 Eric.Lu
 * @创建时间 2023/12/24
 * @描述
 */
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements BeanDefinitionRegistry{


	/** Map of bean definition objects, keyed by bean name */
	private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();

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

	@Override
	public BeanDefinition getBeanDefinition(String beanName) throws BeansException {
		BeanDefinition bd = this.beanDefinitionMap.get(beanName);
		if (bd == null) {
			logger.info("No bean named '" + beanName + "' found in " + this);
			throw new BeansException(beanName);
		}
		return bd;
	}
}

单元测试验证

验证目标:

1、实例化成功

2、再次获取对象是从对象池进行获取,返回的对象地址一致

3、因为是通过CGLIB创建对象,打印的地址格式应是:....$$EnhancerByCGLIB$$....

验证代码如下:
public class BeanFactoryTest extends TestCase {

	public void testDefaultListableBeanFactory() {

		/*
		 * 1、创建DefaultListableBeanFactory
		 * 2、改造BeanDefinition,由固定对象变为Class对象
		 * 3、在获取对象时,通过对象名称到beanDefinition中查找对象定义,并最终通过CGLIB代理创建对象
		 * 4、实例化对象后,将对象注册到单例注册表中
		 * 5、验证再次获取同一名称bean,获取的对象是否从单例注册表中进行获取
		 *
		 */
		DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
		BeanDefinition rootBeanDefinition = new RootBeanDefinition(Baozi.class);
		beanFactory.registerBeanDefinition("baozi", rootBeanDefinition);
		Assert.assertEquals(beanFactory.getBean("baozi"), beanFactory.getBean("baozi"));
		System.out.println(beanFactory.getBean("baozi").getClass());
	}

}
验证结果如下:

验证通过

总结

随着功能的增加,类文件也在增加,我们唯有使用好我们的设计模式和设计原则,才能保证后续继续增加的情况下,让我们的代码方便扩展和维护。同样,在工作中的我们写代码也是一样。通过这一讲,我们可以继续学习到一些设计模式:
1、策略模式

在编写我们实例化代码中,我们定义了InstantiationStrategy 策略接口,并使用CglibSubclassingInstantiationStrategy 的实现类。但当前代码中存在不完整的地方,你觉得哪里可以继续优化呢?

代码源码

learning-spring

最后

还是要说一下,如果想要写好代码,还是要实践,提高自己的动手能力。另外还需拿出一点时间,进行思考和写作。

那非常感谢小伙伴们阅读到结尾,本期的文章就分享到这里,希望可以帮到大家,谢谢。 👉 如果你觉得本篇文章有帮助到您,鼓励一下编程哥吧! `点击`【`关注+点赞+收藏+评论+转发` 】支持一下哟 😛 您的支持就是我更新的最大动力。👇

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值