学习笔记:Spring框架源码Part.2——核心

学习视频链接:https://www.bilibili.com/video/BV1zd4y1L7YD

Spring学习笔记——核心

  • 前言
  • 第三章 容器和上下文
    • 一、认识bean工厂
      • 1、基础能力
      • 2、更强的枚举能力
      • 3、灵活的分层能力
      • 4、构建和自动装配的能力
      • 5、更强的配置能力
      • 6、更多配置项
      • 7、工厂的生命周期
    • 二、bean工厂的创建
    • 三、了解扩展点
    • 四、认识ApplicationContext
  • 第四章 刷新容器
  • 第五章 完成单例bean的初始化


前言

例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。


第三章 容器和上下文

本章节,我们会深入了解【容器和上下文】,并从源码的角度做出深度的解析。目前,我们所接触的容器主要有servlet容器和spring的容器,容器在应用程序用中十分重要,大致有以下好处:

1、统一管理,使用容器通常需要按照统一的规范,比如我们的Jakarta Servlet™ 6.0规范,spring6.0规范等等。按照特定规范,编写规范的容器内容(servlet,bean),可以更好的管理容器内容的生命周期。

2、隔离应用,屏蔽外界的复杂性,更专注于业务开发。理论上,我们可以不明白容器是如何启动的,如何建立了连接等等,可以将更多的精力放在业务上。

3、分层管理,每个层边界清晰,各司其职,如mvc的容器就和spring的容器不一样,又互相关联。

一、认识bean工厂

bean工厂是我们spring容器的载体,是spring上下文的主要内容。下图展示了我们整个bean工厂的常见的接口和类以及功能,其中我们需要注意几点:

  1. 不同的接口展现了不同的能力,是对子类能力的抽象
  2. 抽象类构建通用方法的实现,是通用核心方法的具体实现
  3. 具体类完成特定功能的实现,是特定功能的具体实现

有了这样的思想,我们才能更好的去阅读源码:

在这里插入图片描述

1、基础能力

从接口的定义中,我们可以完全了解bean的基础功能,主要是获取bean以及获取bean的一些基本特征:

public interface BeanFactory {// 这个变量在获取一个FactoryBean时使用,后边详细介绍String FACTORY_BEAN_PREFIX = "&";// 工厂的核心方法,提供了多种获取单个实例bean的能力Object getBean(String name) throws BeansException;<T> T getBean(String name, 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;<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);boolean containsBean(String name);boolean isSingleton(String name) throws NoSuchBeanDefinitionException;boolean isPrototype(String name) throws NoSuchBeanDefinitionException;@NullableClass<?> getType(String name) throws NoSuchBeanDefinitionException;Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;String[] getAliases(String name);
}

ObjectProvider是为了解决隐式注入时产生的问题而提出的概念。

spring4.3之前,我们的bean如果需要使用特定构造器进行构造时必须使用@Autowired注解:

@Service
public class UserService {private UserDao userDao;@Autowiredpublic UserService(UserDao userDao) {this.userDao = userDao;}
}

spring4.3之后,就可以不写,容器会隐式为你注入

在当容器的bean可用且唯一时当然没有问题,但是容器如果没有这个bean就会出问题:

Parameter 0 of constructor in com.ydlclass.UserService required a bean of type 'com.ydlclass.UserDao' that could not be found.

此时ObjectProvider就发挥作用了:

@Service
public class UserService {private UserDao userDao;public UserService(ObjectProvider<UserDao> userDao) {this.userDao = userDao.getIfUnique();}
}

这样注入的好处很明显,如果容器中不存在UserDao或者存在多个UserDao时,可以从容处理。

ObjectProvider接口如下,他继承自ObjectFactory,这个接口后边也会常用:

public interface <T> extends ObjectFactory<T>, Iterable<T> {T getObject(Object... args) throws BeansException;// 处理判断有可用的bean的时候我们怎么做,可以重写T getIfAvailable() throws BeansException;default T getIfAvailable(Supplier<T> defaultSupplier) throws BeansException {T dependency = getIfAvailable();return (dependency != null ? dependency : defaultSupplier.get());}default void ifAvailable(Consumer<T> dependencyConsumer) throws BeansException {T dependency = getIfAvailable();if (dependency != null) {dependencyConsumer.accept(dependency);}}// 处理判断只有唯一的bean的时候我们怎么做,可以重写T getIfUnique() throws BeansException;default T getIfUnique(Supplier<T> defaultSupplier) throws BeansException {T dependency = getIfUnique();return (dependency != null ? dependency : defaultSupplier.get());}default void ifUnique(Consumer<T> dependencyConsumer) throws BeansException {T dependency = getIfUnique();if (dependency != null) {dependencyConsumer.accept(dependency);}}// 当匹配多个时,可以迭代处理@Overridedefault Iterator<T> iterator() {return stream().iterator();}
}

ObjectFactory作为一个对象工厂函数式接口更是简单:

@FunctionalInterface
public interface ObjectFactory<T> {/*** Return an instance (possibly shared or independent)* of the object managed by this factory.* @return the resulting instance* @throws BeansException in case of creation errors*/T getObject() throws BeansException;}

修改UserService,新增login方法:

public class UserService {   public void login(ObjectFactory<User> userFactory) {User user = userFactory.getObject();System.out.println("使用[" + user + "]进行登录");}
}

编写测试用例:

@Test
public void testObjectFactory() {UserService service = new UserService();service.login(() -> new User("tom", 23));// 更复杂的实现,甚至可能是代理,如符合某种条件就生成代理service.login(() -> {int random = new Random().nextInt(100);System.out.println(random);return random > 50 ? new User("tom", 24) : new User("jerry", 33);});
}

2、更强的枚举能力

ListableBeanFactory为后续的实现类提供了更强的枚举能力,这些能力可能不对外公开,但是对于子类操作容器的操作bean十分重要,我们不妨看看他提供的接口:

public interface ListableBeanFactory extends BeanFactory {// 对BeanDefinition的细节操作boolean containsBeanDefinition(String beanName);int getBeanDefinitionCount();String[] getBeanDefinitionNames();// 根据类型枚举出所有的Bean的名字String[] getBeanNamesForType(ResolvableType type);String[] getBeanNamesForType(@Nullable Class<?> type);String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit);// 根据类型获取bean的一个map<T> Map<String, T> getBeansOfType(@Nullable Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)throws BeansException;// 根据注解获得bean的名称String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType);// 根据注解获得beanMap<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException;// 获取一个bean的注解<A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType)throws NoSuchBeanDefinitionException;
}

3、灵活的分层能力

分层的能力十分重要,这在web工程里有典型应用,spring和springmvc会建立两个独立的上下文,后续涉及web工程时我们再深入讲解,分层之后各司其职,更易管理:

public interface HierarchicalBeanFactory extends BeanFactory {// 返回bean工厂的父工厂@NullableBeanFactory getParentBeanFactory();// 此方法忽略祖先上下文定义的bean,只会查询本地工厂boolean containsLocalBean(String name);
}

可以写如下测试用例测试:

@Test
public void testHierarchicalBeanFactory() {// 创建父工厂DefaultListableBeanFactory parent = new DefaultListableBeanFactory();parent.registerSingleton("user", new User("ziang.zhang", 24));// 创建子工厂,独自管理各个层级的内容DefaultListableBeanFactory child = new DefaultListableBeanFactory();// 设置父子关联关系child.setParentBeanFactory(parent);// 子工厂可以访问父工厂logger.info("child contains user ? " + child.containsBean("user"));logger.info("child local contains user ? " + child.containsLocalBean("user"));logger.info("get bean user in child factory: " + child.getBean("user"));
}

4、构建和自动装配的能力

这个接口的实现及其复杂,主要是赋予子类自动装配的能力,是容器最核心的接口,这个接口定义了bean的创建以及装配能力,同时细粒度的控制了bean的生命周期:

public interface AutowireCapableBeanFactory extends BeanFactory {// 不进行外部bean的自动装配的常量,BeanFactoryAware等和注释驱动的注入仍将被应用int AUTOWIRE_NO = 0;// 注入方式的常量,按类型或名称int AUTOWIRE_BY_NAME = 1;int AUTOWIRE_BY_TYPE = 2;int AUTOWIRE_CONSTRUCTOR = 3;//-------------------------------------------------------------------------// 用于创建实例的典型方法,//-------------------------------------------------------------------------<T> T createBean(Class<T> beanClass) throws BeansException;// 自动装配的能力void autowireBean(Object existingBean) throws BeansException;// 对bean进行一些配置,如调用aware接口Object configureBean(Object existingBean, String beanName) throws BeansException;//-------------------------------------------------------------------------// 用于对bean生命周期进行细粒度控制的专门方法// 主要是三块内容:创建(实例化)  属性填充(装配)  初始化//-------------------------------------------------------------------------/*** 生命周期第一步(创建)* 按照指定的装配策略根据class生成一个完整的bean的实例* 执行bean的完全初始化,包括所有适用的BeanPostProcessors* @param dependencyCheck 是否对对象执行依赖项检查(不适用于自动装配构造函数,因此忽略)* @return 新的bean的实例*/Object createBean(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;/*** 生命周期第二步(装配)* 通过应用 after-instantiation 和 property post-processing (例如注释驱动的注入)* 来填充给定的bean实例。 */Object autowire(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;/*** 通过指定的自动装配方式来对给定的Bean进行自动装配。*/void autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck) throws BeansException;/*** 将具体值的bean定义的属性值应用到给定的bean实例。* 是属性填充的重要步骤*/void applyBeanPropertyValues(Object existingBean, String beanName) throws BeansException;/*** 初始化前的回调* 将BeanPostProcessors应用到给定的现有bean实例* 调用它们的postProcessBeforeInitialization方法   */Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException;/*** 初始化给定的原始bean,应用工厂回调,如setBeanName和setBeanFactory,* 当然也包括应用所有的bean post processors*/Object initializeBean(Object existingBean, String beanName) throws BeansException;/*** 初始化后的回调* 将BeanPostProcessors应用到给定的现有bean实例* 调用它们的postProcessAfterInitialization方法   */Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException;/*** 销毁给定的bean实例* 使用DisposableBean接口定义的销毁方法 */void destroyBean(Object existingBean);//-------------------------------------------------------------------------// 解决匹配注入点的方法// 在注入的时候通过以下的方法匹配属性与之对应的bean//-------------------------------------------------------------------------/*** 解析唯一匹配给定对象类型(如果有的话)的bean实例,包括它的bean名称。* 比如我们调用getBean(User.class) 会匹配一个或多个bean,需要该方法进行选择* 这实际上是getBean(Class)的一个变体。*/<T> NamedBeanHolder<T> resolveNamedBean(Class<T> requiredType) throws BeansException;/*** 解析给定bean名称的bean实例, 向目标工厂方法公开提供依赖描述符。* 这实际上是getBean(String, Class)的一个变体,*/Object resolveBeanByName(String name, DependencyDescriptor descriptor) throws BeansException;/*** 针对此工厂中定义的bean解析指定的依赖项,注入的时候很有用。* 通过一个依赖的描述(对方法,字段,构造器的抽象),获得一个bean*/@NullableObject resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName) throws BeansException;

接口中我们遇到如下的类,这里简单看看:

NamedBeanHolder简单的包装了beanName和beanInstance,我们通过【别名或者类型】查找bean时可以返回这个结果:

public class NamedBeanHolder<T> implements NamedBean {private final String beanName;private final T beanInstance;
}

DependencyDescriptor是一个依赖描述符,这个类可以包装构造函数参数、方法参数或字段,允许统一访问它们的元数据,这在注入的时候很有用,比如给定一个【构造参数的描述】或【字段的描述】或【setter方法】,找到与之匹配的bean。

public class DependencyDescriptor extends InjectionPoint implements Serializable {private final Class<?> declaringClass;@Nullableprivate String methodName;@Nullableprivate Class<?>[] parameterTypes;private int parameterIndex;@Nullableprivate String fieldName;private final boolean required;private final boolean eager;private int nestingLevel = 1;@Nullableprivate Class<?> containingClass;@Nullableprivate transient volatile ResolvableType resolvableType;@Nullableprivate transient volatile TypeDescriptor typeDescriptor;
}

5、更强的配置能力

这个bean工厂接口并不用于正常的应用程序代码中。这个扩展接口只是为了允许框架内部的即插即用和对bean工厂配置方法的特殊访问,具体如下:

public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry {// 标准单例作用域的范围标识符:"singleton"。自定义作用域可以通过registerScope添加。String SCOPE_SINGLETON = "singleton";String SCOPE_PROTOTYPE = "prototype";/*** 设置此bean工厂的父级。*/void setParentBeanFactory(BeanFactory parentBeanFactory) throws IllegalStateException;/*** 将class loader设置为加载中的bean类,默认是线程上下文类装入器。*/void setBeanClassLoader(@Nullable ClassLoader beanClassLoader);/*** 返回这个工厂的的class loader,用于加载bean类*/@NullableClassLoader getBeanClassLoader();/*** 指定一个临时ClassLoader用于类型匹配,默认为none。* 如果涉及到加载时织入,则通常只指定临时ClassLoader,以确保实际的bean类尽可能延迟地加载。* 一旦BeanFactory完成引导阶段,临时加载器就会被移除。*/void setTempClassLoader(@Nullable ClassLoader tempClassLoader);// 返回用于类型匹配的临时ClassLoader,ClassLoader getTempClassLoader();/*** 设置是否缓存bean元数据,例如给定的bean定义(以合并的方式)和resolved bean classes。* 关闭此标志可启用bean Definition和特定bean类的热刷新。* 如果该标志关闭,则任何bean实例的创建都将重新查询bean class loader以获得新解析的类。*/void setCacheBeanMetadata(boolean cacheBeanMetadata);boolean isCacheBeanMetadata();// 指定bean的表达式分析器void setBeanExpressionResolver(@Nullable BeanExpressionResolver resolver);BeanExpressionResolver getBeanExpressionResolver();// 设置和获取转化服务void setConversionService(@Nullable ConversionService conversionService);ConversionService getConversionService();// 添加属性编辑器void addPropertyEditorRegistrar(PropertyEditorRegistrar registrar);void registerCustomEditor(Class<?> requiredType, Class<? extends PropertyEditor> propertyEditorClass);void copyRegisteredEditorsTo(PropertyEditorRegistry registry);/*** 设置一个自定义类型转换器,这个BeanFactory应该使用它来转换bean属性值、构造函数参数值等。* 这将覆盖默认的PropertyEditor机制,从而使任何自定义编辑器或自定义编辑器注册器变得无关紧要。*/TypeConverter getTypeConverter();/*** 为注入的值添加一个String解析器,如“aa${bb}cc”。*/void addEmbeddedValueResolver(StringValueResolver valueResolver);boolean hasEmbeddedValueResolver();String resolveEmbeddedValue(String value);// 添加和获取bean的后置处理器void addBeanPostProcessor(BeanPostProcessor beanPostProcessor);int getBeanPostProcessorCount();// 注册自定义的作用范围void registerScope(String scopeName, Scope scope);String[] getRegisteredScopeNames();Scope getRegisteredScope(String scopeName);//为这个bean工厂设置{@code ApplicationStartup},用来记录启动步骤void setApplicationStartup(ApplicationStartup applicationStartup);ApplicationStartup getApplicationStartup();// 从一个bean工厂拷贝配置void copyConfigurationFrom(ConfigurableBeanFactory otherFactory);// 注册别名void registerAlias(String beanName, String alias) throws BeanDefinitionStoreException;// 获得合并的bean的定义,后边细讲BeanDefinition getMergedBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;// 是否是工厂beanboolean isFactoryBean(String name) throws NoSuchBeanDefinitionException;// 控制指定bean当前的创建状态。仅供容器内部使用。void setCurrentlyInCreation(String beanName, boolean inCreation);boolean isCurrentlyInCreation(String beanName);// 为给定的bean注册一个依赖bean,在给定的bean被销毁之前销毁它。void registerDependentBean(String beanName, String dependentBeanName);// 如果有的话,返回依赖于指定bean的所有bean的名称。String[] getDependentBeans(String beanName);// 如果有的话,返回指定bean所依赖的所有bean的名称。String[] getDependenciesForBean(String beanName);// 根据bean名称销毁给定的bean实例(通常是从该工厂获得的原型实例)。void destroyBean(String beanName, Object beanInstance);// 销毁指定的【作用域bean】void destroyScopedBean(String beanName);// 销毁此工厂中的所有单例bean,包括已注册为一次性的内部bean。在工厂关闭时被召回。void destroySingletons();
}

6、更多配置项

AbstractAutowireCapableBeanFactory为子类提供了更多的配置项如下:

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactoryimplements AutowireCapableBeanFactory {// 实例化策略,默认使用CglibSubclassingInstantiationStrategyprivate InstantiationStrategy instantiationStrategy;// 方法参数名解析策略@Nullableprivate ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();// 是否允许循环引用(会详细介绍)private boolean allowCircularReferences = true;// 是否在循环引用的情况下注入一个原始bean实例,即使注入的bean最终被包装类。private boolean allowRawInjectionDespiteWrapping = false;// 要在依赖项检查和自动装配时忽略的依赖项类型,如类对象的集合:例如,字符串。默认值为none。private final Set<Class<?>> ignoredDependencyTypes = new HashSet<>();// 在依赖项检查和自动装配时忽略的依赖项接口,如类对象集。缺省情况下,只有BeanFactory接口被忽略。private final Set<Class<?>> ignoredDependencyInterfaces = new HashSet<>();// 当前创建的bean的名称,保存在ThreadLocal中。private final NamedThreadLocal<String> currentlyCreatedBean = new NamedThreadLocal<>("Currently created bean");// 未完成的FactoryBean实例的缓存private final ConcurrentMap<String, BeanWrapper> factoryBeanInstanceCache = new ConcurrentHashMap<>();// 每个工厂类的候选工厂方法缓存private final ConcurrentMap<Class<?>, Method[]> factoryMethodCandidateCache = new ConcurrentHashMap<>();// 过滤后的PropertyDescriptors缓存:bean类到PropertyDescriptor数组。private final ConcurrentMap<Class<?>, PropertyDescriptor[]> filteredPropertyDescriptorsCache =new ConcurrentHashMap<>();/*** 构造器*/public AbstractAutowireCapableBeanFactory() {super();ignoreDependencyInterface(BeanNameAware.class);ignoreDependencyInterface(BeanFactoryAware.class);ignoreDependencyInterface(BeanClassLoaderAware.class);this.instantiationStrategy = new CglibSubclassingInstantiationStrategy();}public AbstractAutowireCapableBeanFactory(@Nullable BeanFactory parentBeanFactory)     {this();setParentBeanFactory(parentBeanFactory);}// 设置初始化策略,这里用到了策略设计模式public void setInstantiationStrategy(InstantiationStrategy instantiationStrategy) {this.instantiationStrategy = instantiationStrategy;}public InstantiationStrategy getInstantiationStrategy() {return this.instantiationStrategy;}// 设置参数名称解析器public void setParameterNameDiscoverer(@Nullable ParameterNameDiscoverer parameterNameDiscoverer) {this.parameterNameDiscoverer = parameterNameDiscoverer;}public ParameterNameDiscoverer getParameterNameDiscoverer() {return this.parameterNameDiscoverer;}// 后边会详细讲解// 设置是否允许bean之间的循环引用—并自动尝试解析它们。// 默认为“true”。springboot中默认是false。// 注意:通常建议不要依赖bean之间的循环引用。// 重构您的应用程序逻辑,使涉及的两个bean委托给封装它们的公共逻辑的第三个bean。public void setAllowCircularReferences(boolean allowCircularReferences) {this.allowCircularReferences = allowCircularReferences;}public boolean isAllowCircularReferences() {return this.allowCircularReferences;}/*** 设置是否允许将一个bean的原始实例注入到其他bean的属性中尽管注入的bean最终会被包装(例如,通过AOP自动代理)。* 这只会在循环引用无法通过其他方式解决的情况下作为最后的手段使用:* 从本质上讲,宁愿注入一个原始实例,也不愿整个bean装配过程失败。* 从Spring 2.0开始,默认为“false”。*/public void setAllowRawInjectionDespiteWrapping(boolean allowRawInjectionDespiteWrapping) {this.allowRawInjectionDespiteWrapping = allowRawInjectionDespiteWrapping;}public boolean isAllowRawInjectionDespiteWrapping() {return this.allowRawInjectionDespiteWrapping;}/*** 忽略自动装配的给定依赖类型:* 例如,String。默认值为none。*/public void ignoreDependencyType(Class<?> type) {this.ignoredDependencyTypes.add(type);}// 忽略自动装配的给定依赖接口。public void ignoreDependencyInterface(Class<?> ifc) {this.ignoredDependencyInterfaces.add(ifc);}
}

如下的测试用例中我们使用spring的简单实例化策略和BeanWrapper工具,实现了bean的实例化和属性填充:

@Test
public void testInstantiation() throws ClassNotFoundException {// 编写bean的定义RootBeanDefinition beanDefinition = new RootBeanDefinition();beanDefinition.setBeanClassName("com.ydlclass.User");MutablePropertyValues propertyValues = new MutablePropertyValues();propertyValues.addPropertyValue("name","it楠老师");propertyValues.addPropertyValue("age",32);beanDefinition.setPropertyValues(propertyValues);beanDefinition.resolveBeanClass(Thread.currentThread().getContextClassLoader());// 我们模仿一个beanDefinition如何变成一个beanDefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();beanFactory.registerBeanDefinition("user",beanDefinition);// 实例化SimpleInstantiationStrategy instantiationStrategy = new SimpleInstantiationStrategy();Object user = instantiationStrategy.instantiate(beanDefinition, "user", beanFactory);// 属性填充BeanWrapper wrapper = new BeanWrapperImpl(user);wrapper.setPropertyValues(beanDefinition.getPropertyValues());logger.info("The user is [{}]",user);
}

这个小例子一定要好好看一看,对后续的内容很有帮助。

我们可以阅读一下简单的实例化策略:

@Override
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {//在将 XML 配置解析成 BeanDefinition 的时候,bean标签的lookup-method和replaced-method会被分别解析成 LookupOverride 和 ReplaceOverride 对象,添加到 BeanDefinition 的methodOverrides成员变量中。它们的作用是通过配置来覆盖 Bean 原有的方法实现。这个我们一会看。if (!bd.hasMethodOverrides()) {Constructor<?> constructorToUse;synchronized (bd.constructorArgumentLock) {constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;if (constructorToUse == null) {final Class<?> clazz = bd.getBeanClass();if (clazz.isInterface()) {throw new BeanInstantiationException(clazz, "Specified class is an interface");}try {constructorToUse = clazz.getDeclaredConstructor();bd.resolvedConstructorOrFactoryMethod = constructorToUse;}catch (Throwable ex) {throw new BeanInstantiationException(clazz, "No default constructor found", ex);}}}// 直接使用原生的反射生成实例return BeanUtils.instantiateClass(constructorToUse);}else {// 有需要重写的方法,我们则生成一个cglib的子类return instantiateWithMethodInjection(bd, beanName, owner);}
}

现在,我们来看一下以下两个配置项(不常用,知道就行了)也是来解释hasMethodOverrides的含义:

1、lookup-method注入

lookup-method注入是spring动态改变bean里方法的实现。指定方法的返回值为某个已经存在的bean。

下面的例子,告诉我们这个方法怎么使用:

public abstract class CommandManager {public Object process(Object commandState) {// grab a new instance of the appropriate Command interfaceCommand command = createCommand();// set the state on the (hopefully brand new) Command instancecommand.setState(commandState);return command.execute();}// okay... but where is the implementation of this method?protected abstract Command createCommand();
}

配置如下:

<bean id="manager"class="com.xxx.lookupmethod.CommandManager"> // createCommand方法将返回commad这个bean<lookup-method bean="command" name="createCommand"/> 
</bean>
<bean id="command" class="com.xxx.lookupmethod.MyCommand">
</bean>

2、replaced-method注入

replaced-method注入是spring动态改变bean方法的一种实现。他可以改变的方法执行逻辑,将方法进行替换,定义替换方法的类(需要继承接口org.springframework.beans.factory.support.MethodReplacer)接口。

public class MyValueCalculator {public String computeValue(String input) {// some real code...}// some other methods...
}/*** meant to be used to override the existing computeValue(String)* implementation in MyValueCalculator*/
public class ReplacementComputeValue implements MethodReplacer {public Object reimplement(Object o, Method m, Object[] args) throws Throwable {// get the input value, work with it, and return a computed resultString input = (String) args[0];...return ...;}
}

配置:

<bean id="myValueCalculator" class="x.y.z.MyValueCalculator"><!-- arbitrary method replacement --><replaced-method name="computeValue" replacer="replacementComputeValue"><arg-type>String</arg-type></replaced-method>
</bean><bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>

注意:从源码中得知,基于以上两种方式注入后,bean需要使用cglib生成子类完成目标,由于采用cglib生成之类的方式,所以需要用来动态注入的类,不能是final修饰的;需要动态注入的方法,也不能是final修饰的。

7、工厂的生命周期

bean工厂的生命周期比较简单:start->onRefresh->Running->onClose->stop

每一个生命周期节点都会完成大量的工作,我们后边的内容会详细介绍:

public interface Lifecycle {void start();void stop();boolean isRunning();
}public interface LifecycleProcessor extends Lifecycle {/*** 上下文刷新通知,例如自动启动组件。*/void onRefresh();/*** 上下文关闭阶段的通知,例如自动停止组件。*/void onClose();}

二、bean工厂的创建

一个典型的功能强大的bean工厂实现,就是DefaultListableBeanFactory,事实上我们的容器中维护的beanFactory都是这个类的实例;

我们看一下该类的类图:

在这里插入图片描述

这里简单的总结一下,一个完整的bean工厂应该具备哪些能力?

  1. bean工厂的基础能力,枚举bean,分层,自动装配、独立配置等。

  2. 注册单例bean(包括Factorybean)的能力。

  3. 注册别名的能力。

三、了解扩展点

spring给我们提供了很多的扩展点,这些扩展点可以允许我们在spring上下文启动的任意环节进行干预,实现自己的逻辑。我们大致将spring的启动环节画一个图,当然他可能不准确,我们只是想将大致的环节勾勒出来,然后看看其中的可以扩展的地方。

在这里插入图片描述

spring给我们提供的常用的扩展点称为POSTProcessor,也叫后置器。而我们最常使用的BeanFactoryPostProcessor和BeanPostProcessor,我们可以写如下的用例做测试:

BeanFactoryPostProcessor,我们在beanFactory创建后打印了一句话:

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {// 这是BeanFactoryPostProcess的唯一回调// 回调中会有beanFactory传入进来,我们可以做一些自己的事情// 比如注册一些特殊的beanDefinition等@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {System.out.println(beanFactory.getClass() + "----postProcessAfterInitialization");}
}

BeanPostProcessor,我们在bean的初始化前后分别打印了一句话:

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {// 在这两个方法中我们可以获得这个bean和他的名字// 我们可以对bean做包赚处理等@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println(beanName+"----postProcessBeforeInitialization");return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println(beanName+"----postProcessAfterInitialization");return bean;}
}

启动工程,查看结果。

四、认识ApplicationContext

ApplicationContext是应用程序的中央接口,提供一些便利的功能,引导Spring的程序进行启动,他是我们学习spring中的重中之重,理解了ApplicationContext我们就学会了spring。

spring上下文提供了丰富的功能,拥有强大的能力,一切的spring的知识内其实都是为了服务一个ApplicationContext,下图展示了SpringApplication中的一些核心接口和类:

在这里插入图片描述

一个ApplicationContext继承了五个接口,共同为一个上下文赋能,这些接口我们都已经接触过了:

  1. EnvironmentCapable:提供一个上下文环境的能力
  2. ListableBeanFactory:枚举bean工厂的bean的能力
  3. HierarchicalBeanFactory:分层的能力
  4. MessageSource:国际化的能力
  5. ApplicationEventPublisher:发布事件的能力
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,MessageSource, ApplicationEventPublisher, ResourcePatternResolver {// 返回一个@NullableString getId();// 返回此上下文所属的已部署应用程序的名称。String getApplicationName();// 返回此上下文的显示名称String getDisplayName();// 返回第一次加载此上下文时的时间戳。long getStartupDate();// 返回父上下文@NullableApplicationContext getParent();// 为此上下文公开AutowireCapableBeanFactory功能。AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}

其子接口ConfigurableApplicationContext对ApplicationContext进行了扩展:

public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {// 定义配置路径的分隔符String CONFIG_LOCATION_DELIMITERS = ",; \t\n";// ConversionService的名字String CONVERSION_SERVICE_BEAN_NAME = "conversionService";String LOAD_TIME_WEAVER_BEAN_NAME = "loadTimeWeaver";String ENVIRONMENT_BEAN_NAME = "environment";String SYSTEM_PROPERTIES_BEAN_NAME = "systemProperties";String SYSTEM_ENVIRONMENT_BEAN_NAME = "systemEnvironment";String APPLICATION_STARTUP_BEAN_NAME = "applicationStartup";String SHUTDOWN_HOOK_THREAD_NAME = "SpringContextShutdownHook";// 给上下文设置一个唯一的idvoid setId(String id);// 设置一个父上下文void setParent(@Nullable ApplicationContext parent);// 配置和获取环境对象void setEnvironment(ConfigurableEnvironment environment);ConfigurableEnvironment getEnvironment();// 设置一个启动器void setApplicationStartup(ApplicationStartup applicationStartup);ApplicationStartup getApplicationStartup();// 增加BeanFactoryPostProcessorvoid addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor);// 增加/删除Listenervoid addApplicationListener(ApplicationListener<?> listener);void removeApplicationListener(ApplicationListener<?> listener);// 设置类加载器void setClassLoader(ClassLoader classLoader);// 增加协议解析策略,我们可以定义对应解析器解析自定义协议void addProtocolResolver(ProtocolResolver resolver);// 刷新容器,核心中的核心void refresh() throws BeansException, IllegalStateException;// 向JVM运行时注册一个关闭钩子,在JVM关闭时关闭此上下文,除非它当时已经关闭,下边有个小例子。void registerShutdownHook();// 关闭这个容器释放她锁定的所有的资源@Overridevoid close();// 确定一个上下文是否是活跃的,已经被刷新,没有关闭boolean isActive();// 获得上下文中持有的bean工厂ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
}

我们可体会一下registerShutdownHook的用法,我们自己写一个容器类继承GenericApplicationContext:

public class MyGenericApplicationContext extends GenericApplicationContext {private Thread shutdownHook = null;@Overridepublic void registerShutdownHook() {if (this.shutdownHook == null) {// No shutdown hook registered yet.this.shutdownHook = new Thread(SHUTDOWN_HOOK_THREAD_NAME) {@Overridepublic void run() {System.out.println("容器关闭了,该干啥干啥吧!");}};Runtime.getRuntime().addShutdownHook(this.shutdownHook);}}
}

我们编写如下测试类,然后分析一下,我们注册的线程会在何时调用:

public class TestBeanFactory {@Testpublic void testXmlConfig() throws InterruptedException {MyGenericApplicationContext myGenericApplicationContext = new MyGenericApplicationContext();myGenericApplicationContext.registerShutdownHook();System.out.println("before");Thread.sleep(2000);System.out.println("after");}
}

结果:
before
after
容器关闭了,该干啥干啥吧!

我们在之前的学习中常常使用如下的ApplicationContext实现:FileSystemXmlApplicationContext,ClassPathXmlApplicationContext这里就不演示了。

spring3.0之后我们更推荐如下的实现类,GenericApplicationContext作为一个通用的上下文类,给我们提供了丰富的实现,如下:

@Test
public void testXmlConfig(){GenericXmlApplicationContext xmlApplicationContext = new GenericXmlApplicationContext("classpath:spring.xml");User user = xmlApplicationContext.getBean(User.class);System.out.println(user);// --------------------------------------------xmlApplicationContext = new GenericXmlApplicationContext("file:D://spring.xml");user = xmlApplicationContext.getBean(User.class);System.out.println(user);
}

我们观察一下这个实现类的构造器如下,整个构造的过程如下,在之前的课程中,我们已经学习了加载beanDefintion的过程,下个章节我们就进入【刷新容器的部分】:

public GenericXmlApplicationContext(Resource... resources) {// 1、加载资源load(resources);// 2、刷新容器refresh();
}

当然实现类内容和过程基本一致,如下:

public AnnotationConfigApplicationContext(String... basePackages) {this();scan(basePackages);refresh();
}

refresh()方法被定义在AbstractApplicationContext中,该方法会引导spring上下文的启动,所以十分重要,下边我们会单独出一个大章节进行讲解。

第四章 刷新容器


第五章 完成单例bean的初始化


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/5075.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

linux守护进程与后台进程的区别

守护进程与后台进程有以下区别&#xff1a; 1. 概念与定义 后台进程&#xff1a; 是指在操作系统后台运行的进程&#xff0c;它不与用户直接交互&#xff08;没有连接到用户的终端&#xff09;。用户在终端中启动一个程序并让其在后台运行&#xff08;如通过在命令后加“&…

【360】基于springboot的志愿服务管理系统

摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装志愿服务管理系统软件来发挥其高效地信息处理的作用&#x…

【LLM Agents体验】Dify框架的安装指南

Dify简介&#xff1a; 核心功能‌12 ‌Dify是一款开源的大语言模型(LLM)应用开发平台&#xff0c;融合了后端即服务&#xff08;Backend as a Service, BaaS&#xff09;和LLMOps的理念&#xff0c;使开发者可以快速搭建生产级的生成式AI应用。LLMOps涵盖了大型语言模型的开发、…

TODO Error occurred while trying to proxy:【】

文章目录 场景异常解决 场景 使用 Ant Disign Pro 连接本地接口。 异常 Error occurred while trying to proxy: localhost:8000/api/login/account?token%20%20123[HPM] Error occurred while proxying request localhost:8000/api/login/account?token%20%20123 to http…

Linux 文件基本属性

1.Linux 文件基本属性 Linux 系统是一种典型的多用户系统&#xff0c;不同用户处于不同地位&#xff0c;拥有不同的权限。为了保护系统的安全性&#xff0c;Linux 系统对不同的用户访问同一文件&#xff08;包括目录文件&#xff09;的权限做了不同的规定。Linux 通常使用以下两…

数据结构-归并排序笔记

【数据结构】八大排序(超详解附动图源码)_数据结构排序-CSDN博客 看这个学思路 一 归并排序介绍: 归并排序(MERGE-SORT)是利用归并的思想实现的排序方法&#xff0c;该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解&#xf…

关于使用python pptx生成或“复制”PPT页面的问题

先说两个结论&#xff1a; 对于主题不完全相同的页面&#xff0c;pptx 无法完全复制PPT页面&#xff0c;文字图片可以复制&#xff0c;但是背景之类的无法复制pptx 无法直接在指定页码或者指定页面后插入页面 今天做项目的时候&#xff0c;需要根据PPT模板使用python生成相应…

Uniapp底部导航栏设置(附带PS填充图标教程)

首先需要注册和登录ifconfont官网&#xff0c;然后创建项目添加需要的图标 创建和添加图标库请参考&#xff1a;Uniapp在Vue环境中引入iconfont图标库&#xff08;详细教程&#xff09; 打开iconfont官网&#xff0c;找到之前添加的图标库&#xff0c;下载png图片 如果需要的…

算法——双指针

目录 前言一、什么是双指针二、算法特点三、算法实现步骤四、常见形式五、应用场景与示例六、优势与注意事项七、双指针算法动态图解八、经典例题[1. 回文判定](https://www.lanqiao.cn/problems/1371/learning/?page1&first_category_id1&name%E5%9B%9E%E6%96%87%E5%…

L6.【LeetCode笔记】合并两个有序链表

1.题目 https://leetcode.cn/problems/merge-two-sorted-lists/ 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4]示例 2&…

类的加载机制

一、类的生命周期 类从被加载到虚拟机内存中开始到卸载出内存为止&#xff0c;它的整个生命周期可以简单概括为 7 个阶段&#xff1a; 加载&#xff08;Loading&#xff09;验证&#xff08;Verification&#xff09;准备&#xff08;Preparation&#xff09;解析&#xff08…

接口测试用例设计的关键步骤与技巧解析

接口测试是确保系统组件之间高效、稳定交互的重要环节。然而&#xff0c;设计出合理的接口测试用例&#xff0c;并不是一件轻而易举的事。如何通过高质量的测试用例揭示潜在问题&#xff1f;今天带你深度解析接口测试用例设计的关键步骤和实用技巧&#xff0c;助你在测试领域更…

Java线程6种生命周期及转换

多线程技术是我们后端工程师在面试的时候必问的一个知识点&#xff0c;今天就来盘点一下多线程的相关知识&#xff0c; 先来说下进程&#xff0c;线程及线程的生命周期&#xff1a; 进程&#xff1a;进程就是正在进行中的程序&#xff0c;是没有生命的实体&#xff0c;只有在运…

柯桥学英语|老外说“You‘re cheap”,不是“你真便宜”!真正的意思是什么?

在跨文化交流中&#xff0c;误解常常源于对语言字面意义的直接翻译。今天&#xff0c;我们就来揭开一个常见的误解——“Youre cheap”的真实含义&#xff0c;并探讨与之相关的英文表达。 “Youre cheap”的真实含义 当老外对你说“Youre cheap”&#xff0c;千万别以为他们在…

IDEA构建JavaWeb项目,并通过Tomcat成功运行

目录 一、Tomcat简介 二、Tomcat安装步骤 1.选择分支下载 2.点击下载zip安装包 3.解压到没有中文、空格和特殊字符的目录下 4.双击bin目录下的startup.bat脚本启动Tomcat 5.浏览器访问Tomcat 6.关闭Tomcat服务器 三、Tomcat目录介绍 四、WEB项目的标准结构 五、WEB…

电商行业企业员工培训的在线知识库构建

在电商行业&#xff0c;员工的培训和发展对于保持竞争力至关重要。随着电子商务的兴起和消费者行为的变化&#xff0c;电商行业需要不断适应新的市场趋势。在线培训知识库作为一种有效的培训工具&#xff0c;可以帮助企业提升员工技能&#xff0c;优化客户服务&#xff0c;增强…

参数跟丢了之JS生成器和包装器

如需转载请注明出处.欢迎小伙伴一起讨论技术. 逆向网址:aHR0cHM6Ly91bmlvbi5qZC5jb20vcHJvTWFuYWdlci9pbmRleD9wYWdlTm89MQ 跟踪接口:aHR0cHM6Ly9hcGkubS5qZC5jb20vYXBp 跟踪参数:h5st 本文目标:记录学习下自定义的生成器和包装器,不做具体的参数加密逻辑分析 直接启动器进…

信息学奥赛一本通 1395:烦人的幻灯片(slides)

【题目链接】 ybt 1395&#xff1a;烦人的幻灯片(slides) 【题目考点】 1. 图论&#xff1a;拓扑排序 【解题思路】 先理解题意&#xff1a; 如图&#xff0c;每张幻灯片是一个矩形&#xff0c;在该矩形范围内有一个位置写了这张幻灯片的编号。但实际情况是幻灯片是透明…

SpringBoot框架学习总结 及 整合 JDBC Mybatis-plus JPA Redis 我的学习笔记

SpringBoot框架学习总结 及 整合 JDBC Mybatis-plus JPA Redis 我的学习笔记 一、SpringBoot概述二、创建SpringBoot程序1. 使用maven方式创构建2. 使用Spring Initializr构建3. SpringBoot热部署4. SpringBoot的跨域处理 三、基础配置1.配置文件的作用2.配置文件格式2.yaml3.S…

真题--数组循环题目

1.逆序数表达数组2.用数组表示费波纳希数列3.用数组排序4.二维数组转置5.找到二维数组其中的最大数值6.输出字符数组7.字符数组输出菱形图案8.输入一行字符&#xff0c;统计有多少单词9.有三个字符串&#xff0c;找到最大字符串 1.逆序数表达数组 #include<stdio.h> int…