手写Spring与基本原理--简易版

news2024/12/23 18:10:32

文章目录

  • 手写Spring与基本原理解析
    • 简介
    • 写一个简单的Bean加载容器
      • 定义一个抽象所有类的BeanDefinition
      • 定义一个工厂存储所有的类
      • 测试
    • 实现Bean的注册定义和获取
    • 基于Cglib实现含构造函数的类实例化策略
    • Bean对象注入属性和依赖Bean的功能
    • Spring.xml解析和注册Bean对象
    • 实现应用上下文
    • 向虚拟机注册钩子,实现Bean对象的初始化和销毁方法
    • 感知容器对象
    • Bean对象作用域以及FactoryBean的实现和使用
    • 基于观察者实现,容器事件和事件监听器
    • AOP切面
    • AOP动态代理,融入到Bean的生命周期
    • 通过注解配置和包自动扫描的方式完成Bean对象的注册
    • 通过注解给属性注入配置和Bean对象
    • 循环依赖
    • 事务功能设计
    • 实际使用--JDBCTemplate
    • 整合ORM框架

手写Spring与基本原理解析

详细请跳转 https://bugstack.cn/md/spring/develop-spring
这里是学习上述小博哥的《手写Spring》做的简易记录,夹杂着很多个人私活,建议直接看链接

简介

从SpringBean的加载流程博文中可以知道Spring的简易加载流程
Spring bean的生命周期包括实例化、属性赋值、初始化前回调、自定义初始化、初始化后回调、初始化完成、销毁前回调、自定义销毁这些阶段和回调方法。
接下来讲根据上述流程逐步深入

写一个简单的Bean加载容器

定义一个抽象所有类的BeanDefinition

首先进行简单的容器设计,我们需要将一个个的类进行统一的定义,这个定义名称为BeanDefinition,他包含众多的属性,包括是否是单例,类的名称等等。为了减少代码的复杂性,这里不再展开
简单定义这个类

public class BeanDefinition {
    private Object object;

    public BeanDefinition(Object object) {
        this.object = object;
    }

    public Object getBean() {
        return object;
    }
}

在这个类中,BeanDefinition单纯代表这个类的统一接口,真正的类是object,后面我们可以通过getBean的方式直接获取这个object。

定义一个工厂存储所有的类

实体类有了统一的接口后,就可以建立一个工厂,这个工厂叫做“Bean”工厂,建立的所有的类都以Map的方式放到这个工程中,后续可以直接调用
Map中主键就是我们定义的名称,也就是平常xml中的bean的id

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class BeanFactory {
    private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    public Object getBean(String name) {
        return beanDefinitionMap.get(name).getBean();
    }
    public void registerBeanDefinition(String name, BeanDefinition beanDefinition){
        beanDefinitionMap.put(name, beanDefinition);
    }
}

这样,一个简易的容器实际上已经建好了,我们只需要往这个bean里面塞class就可以

测试

定义一个实体类UserService

public class UserService {
    public void query(){
        System.out.println("用户名称查询");
    }
}

测试

public class ApiTest {
    public static void main(String[] args) {
        //定义好bean工厂
        BeanFactory beanFactory = new BeanFactory();
        //注册bean对象
        BeanDefinition beanDefinition = new BeanDefinition(new UserService());
        beanFactory.registerBeanDefinition("userService", beanDefinition);
        //获取类对象
        UserService userService = (UserService) beanFactory.getBean("userService");
        userService.query();
    }
}

这样一个简单的Spring Bean容器实际就建立好了

实现Bean的注册定义和获取

定义 BeanFactory 这样一个 Bean 工厂,提供 Bean 的获取方法 getBean(String name),之后这个 Bean 工厂接口由抽象类 AbstractBeanFactory 实现。
BeanFactory 的定义由 AbstractBeanFactory 抽象类实现接口的 getBean 方法
而 AbstractBeanFactory 又继承了实现了 SingletonBeanRegistry 的DefaultSingletonBeanRegistry 类。这样 AbstractBeanFactory 抽象类就具备了单例 Bean 的注册功能。
AbstractBeanFactory 中又定义了两个抽象方法:getBeanDefinition(String beanName)、createBean(String beanName, BeanDefinition beanDefinition) ,而这两个抽象方法分别由 DefaultListableBeanFactory、AbstractAutowireCapableBeanFactory 实现。
最终 DefaultListableBeanFactory 还会继承抽象类 AbstractAutowireCapableBeanFactory 也就可以调用抽象类中的 createBean 方法了。

基于Cglib实现含构造函数的类实例化策略

通过策略模式拆分单例构造与有参数构造,判断条件为是否有参数,构造时使用Cglib

1、BeanFactory 中我们重载了一个含有入参信息 args 的 getBean 方法,这样就可以方便的传递入参给构造函数实例化了。
2、在实例化接口 instantiate 方法中添加必要的入参信息,包括:beanDefinition、 beanName、ctor、args
其中 Constructor 你可能会有一点陌生,它是 java.lang.reflect 包下的 Constructor 类,里面包含了一些必要的类信息,有这个参数的 目的就是为了拿到符合入参信息相对应的构造函数。
而 args 就是一个具体的入参信息了,最终实例化时候会用到。
3、在 AbstractAutowireCapableBeanFactory 抽象类中定义了一个创建对象的实例化策略属性类 InstantiationStrategy instantiationStrategy,这里我们选择了 Cglib 的实现类。
4、抽取 createBeanInstance 方法,在这个方法中需要注意 Constructor 代表了你有多少个构造函数,通过 beanClass.getDeclaredConstructors() 方式可以获取到你所有的构造函数,是一个集合。
5、循环比对出构造函数集合与入参信息 args 的匹配情况

Bean对象注入属性和依赖Bean的功能

属性填充是在 Bean 使用 newInstance 或者 Cglib 创建后,开始补全属性信息,那么就可以在类 AbstractAutowireCapableBeanFactory 的 createBean 方法中添加补全属性方法

在BeanDefinition中增加一个 PropertyValues,PropertyValues中引用一个List,其中所有的属性会放到这个list中,PropertyValue是一种类似Map的name-valu结构,name是属性名称,value是具体引用,这个value如果是引用的其他类,那么就要用到BeanReference。

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

具体填充是在AbstractAutowireCapableBeanFactory中的 applyPropertyValues 方法进行具体的填充操作,如果遇到的是 BeanReference,那么就需要递归获取 Bean 实例,调用 getBean 方法。

Spring.xml解析和注册Bean对象

把 Bean 的定义、注册和初始化交给 Spring.xml 配置化处理,那么就需要实现两大块内容,分别是:资源加载器、xml资源处理类,实现过程主要以对接口 Resource、ResourceLoader 的实现,而另外 BeanDefinitionReader 接口则是对资源的具体使用,将配置信息注册到 Spring 容器中去。

Resource 的资源加载器的实现中包括了,ClassPath、系统文件、云配置文件,这三部分与 Spring 源码中的设计和实现保持一致,最终在 DefaultResourceLoader 中做具体的调用。
接口:BeanDefinitionReader、抽象类:AbstractBeanDefinitionReader、实现类:XmlBeanDefinitionReader,这三部分内容主要是合理清晰的处理了资源读取后的注册 Bean 容器操作。
关键代码:
解析标签填入beanfinition

// 解析标签:property
Element property = (Element) bean.getChildNodes().item(j);
String attrName = property.getAttribute("name");
String attrValue = property.getAttribute("value");
String attrRef = property.getAttribute("ref");
// 获取属性值:引入对象、值对象
Object value = StrUtil.isNotEmpty(attrRef) ? new BeanReference(attrRef) : attrValue;
// 创建属性信息
PropertyValue propertyValue = new PropertyValue(attrName, value);
beanDefinition.getPropertyValues().addPropertyValue(propertyValue);

注册 BeanDefinition

getRegistry().registerBeanDefinition(beanName, beanDefinition);

实现应用上下文

引入应用上下文,进行资源扫描与加载,为Bean对象实例化过程添加扩展机制,为bean对象执行修改、记录和替换等动作。
过程:
加载–》注册–》修改–》实例化–》扩展
满足于对 Bean 对象扩展的两个接口,其实也是 Spring 框架中非常具有重量级的两个接口:BeanFactoryPostProcessor与BeanPostProcessor
BeanFactoryPostProcessor,是由 Spring 框架组建提供的容器扩展机制,允许在 Bean 对象注册后但未实例化之前,对 Bean 的定义信息 BeanDefinition 执行修改操作。定义如下

public interface BeanFactoryPostProcessor {

    /**
     * 在所有的 BeanDefinition 加载完成后,实例化 Bean 对象之前,提供修改 BeanDefinition 属性的机制
     *
     * @param beanFactory
     * @throws BeansException
     */
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}

refresh() 定义实现过程,包括:
1、创建 BeanFactory,并加载 BeanDefinition
2、获取 BeanFactory
3、在 Bean 实例化之前,执行 BeanFactoryPostProcessor (Invoke factory processors registered as beans in the context.)
4、BeanPostProcessor 需要提前于其他 Bean 对象实例化之前执行注册操作
5、提前实例化单例Bean对象

BeanPostProcessor,也是 Spring 提供的扩展机制,不过 BeanPostProcessor 是在 Bean 对象实例化之后修改 Bean 对象,也可以替换 Bean 对象。这部分与后面要实现的 AOP 有着密切的关系。
定义如下:

public interface BeanPostProcessor {

    /**
     * 在 Bean 对象执行初始化方法之前,执行此方法
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

    /**
     * 在 Bean 对象执行初始化方法之后,执行此方法
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}
 

在Bean创建时完成前置和后置处理:AbstractAutowireCapableBeanFactory
initializeBean方法:
// 1. 执行 BeanPostProcessor Before 处理
Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);
// 待完成内容:invokeInitMethods(beanName, wrappedBean, beanDefinition);
invokeInitMethods(beanName, wrappedBean, beanDefinition);
// 2. 执行 BeanPostProcessor After 处理
wrappedBean = applyBeanPostProcessorsAfterInitialization(bean, beanName);

向虚拟机注册钩子,实现Bean对象的初始化和销毁方法

spring.xml 配置中添加 init-method、destroy-method 两个注解
在配置文件加载的过程中,把注解配置一并定义到 BeanDefinition 的属性当中。这样在 initializeBean 初始化操作的工程中,就可以通过反射的方式来调用配置在 Bean 定义属性当中的方法信息了
1、定义初始化和销毁方法的接口:InitializingBean和DisposableBean
在一些需要结合 Spring 实现的组件中,经常会使用这两个方法来做一些参数的初始化和销毁操作。比如接口暴漏、数据库数据读取、配置文件加载等等。
2、BeanDefinition 新增加了两个属性:initMethodName、destroyMethodName,这两个属性是为了在 spring.xml 配置的 Bean 对象中,可以配置 init-method=“initDataMethod” destroy-method=“destroyDataMethod” 操作

最终实现接口的效果是一样的。只不过1是接口方法的直接调用,2是在配置文件中读取到方法反射调用

感知容器对象

感知容器定义一个标签Aware,继承了这个标签的接口会在初始化和BeanPostProcessor Before之前进行处理
核心代码:

       // invokeAwareMethods
        if (bean instanceof Aware) {
            if (bean instanceof BeanFactoryAware) {
                ((BeanFactoryAware) bean).setBeanFactory(this);
            }
            if (bean instanceof BeanClassLoaderAware){
                ((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
            }
            if (bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).setBeanName(beanName);
            }
        }

        // 1. 执行 BeanPostProcessor Before 处理
        Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);

        // 执行 Bean 对象的初始化方法
        try {
            invokeInitMethods(beanName, wrappedBean, beanDefinition);
        } catch (Exception e) {
            throw new BeansException("Invocation of init method of bean[" + beanName + "] failed", e);
        }

        // 2. 执行 BeanPostProcessor After 处理
        wrappedBean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
        return wrappedBean;

有几个常用的Aware接口
BeanNameAware, BeanClassLoaderAware, ApplicationContextAware, BeanFactoryAware
具体功能望文生义即可

也可以自定义Aware接口:
1、接口继承Aware
2、bean实现接口
3、实现接口方法
4、bean中具体执行

Bean对象作用域以及FactoryBean的实现和使用

注意BeanFactory与FactoryBean的区别:
BeanFactory是bean的工厂,FactoryBean是一个工厂Bean,他也可以通过BeanFactory获得,算是一个特殊的bean,用于定义创建和配置复杂对象。通过实现 FactoryBean 接口,你可以自定义对象的创建逻辑,并将其纳入 Spring 容器的管理。

BeanDefinition中
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
这两个属性决定着单例还是多例

基于观察者实现,容器事件和事件监听器

以围绕实现 event 事件定义、发布、监听功能实现和把事件的相关内容使用 AbstractApplicationContext#refresh 进行注册和处理操作。

实际上是定义三个角色
ApplicationEventPublisher,事件发布者,并在实现类中提供事件监听功能,是整个一个事件的发布接口,所有的事件都需要从这个接口发布出去。
ApplicationEventMulticaster 接口是注册监听器和发布事件的广播器,提供添加、移除和发布事件方法,在事件广播器中定义了添加监听和删除监听的方法以及一个广播事件的方法 multicastEvent 最终推送时间消息也会经过这个接口方法来处理谁该接收事件。
ApplicationEvent:实现此接口定义具体事件

在抽象应用上下文 AbstractApplicationContext#refresh 中,主要新增了 初始化事件发布者、注册事件监听器、发布容器刷新完成事件,三个方法用于处理事件操作。
初始化事件发布者(initApplicationEventMulticaster),主要用于实例化一个 SimpleApplicationEventMulticaster,这是一个事件广播器。
注册事件监听器(registerListeners),通过 getBeansOfType 方法获取到所有从 spring.xml 中加载到的事件配置 Bean 对象。
发布容器刷新完成事件(finishRefresh),发布了第一个服务器启动完成后的事件,这个事件通过 publishEvent 发布出去,其实也就是调用了 applicationEventMulticaster.multicastEvent(event); 方法。
最后是一个 close 方法中,新增加了发布一个容器关闭事件。publishEvent(new ContextClosedEvent(this));

AOP切面

核心代码

        // 目标对象(可以替换成任何的目标对象)
        Object targetObj = new UserService();
        // AOP 代理
        IUserService proxy = (IUserService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), targetObj.getClass().getInterfaces(), new InvocationHandler() {
            // 方法匹配器
            MethodMatcher methodMatcher = new AspectJExpressionPointcut("execution(* cn.bugstack.springframework.test.bean.IUserService.*(..))");
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (methodMatcher.matches(method, targetObj.getClass())) {
                    // 方法拦截器
                    MethodInterceptor methodInterceptor = invocation -> {
                        long start = System.currentTimeMillis();
                        try {
                            return invocation.proceed();
                        } finally {
                            System.out.println("监控 - Begin By AOP");
                            System.out.println("方法名称:" + invocation.getMethod().getName());
                            System.out.println("方法耗时:" + (System.currentTimeMillis() - start) + "ms");
                            System.out.println("监控 - End\r\n");
                        }
                    };
                    // 反射调用
                    return methodInterceptor.invoke(new ReflectiveMethodInvocation(targetObj, method, args));
                }
                return method.invoke(targetObj, args);
            }
        });
        String result = proxy.queryUserInfo();
        System.out.println("测试结果:" + result);
    }
}

在这里插入图片描述

Pointcut:切入点接口,定义用于获取 ClassFilter、MethodMatcher 的两个类,这两个接口获取都是切点表达式提供的内容。
ClassFilter:定义类匹配类,用于切点找到给定的接口和目标类。
MethodMatcher:方法匹配,找到表达式范围内匹配下的目标类和方法

AspectJExpressionPointcut:实现了 Pointcut、ClassFilter、MethodMatcher,三个接口定义方法,同时这个类主要是对 aspectj 包提供的表达式校验方法使用。
用于把代理、拦截、匹配的各项属性包装到一个类中,方便在 Proxy 实现类进行使用。

public class AdvisedSupport {
    // 被代理的目标对象
    private TargetSource targetSource;
    // 方法拦截器
    private MethodInterceptor methodInterceptor;
    // 方法匹配器(检查目标方法是否符合通知条件)
    private MethodMatcher methodMatcher;
    // ...get/set
}

最后进行代理抽象实现(JDK&Cglib)

AOP动态代理,融入到Bean的生命周期

BeanPostProcessor 接口实现继承的 InstantiationAwareBeanPostProcessor 接口后,做了一个自动代理创建的类 DefaultAdvisorAutoProxyCreator,这个类的就是用于处理整个 AOP 代理融入到 Bean 生命周期中的核心类。
DefaultAdvisorAutoProxyCreator 会依赖于拦截器、代理工厂和Pointcut与Advisor的包装服务 AspectJExpressionPointcutAdvisor,由它提供切面、拦截方法和表达式。

融入Bean生命周期的自动代理创建者,如下

public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {

    private DefaultListableBeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = (DefaultListableBeanFactory) beanFactory;
    }

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {

        if (isInfrastructureClass(beanClass)) return null;
        //获取aspc表达式
        Collection<AspectJExpressionPointcutAdvisor> advisors = beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class).values();
        //获取通知信息
        for (AspectJExpressionPointcutAdvisor advisor : advisors) {
            ClassFilter classFilter = advisor.getPointcut().getClassFilter();
            if (!classFilter.matches(beanClass)) continue;
            //构造包括代理类,要代理的信息的类
            AdvisedSupport advisedSupport = new AdvisedSupport();
            TargetSource targetSource = null;
            try {
                targetSource = new TargetSource(beanClass.getDeclaredConstructor().newInstance());
            } catch (Exception e) {
                e.printStackTrace();
            }
            advisedSupport.setTargetSource(targetSource);
            advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
            advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
            advisedSupport.setProxyTargetClass(false);
            //生成代理类
            return new ProxyFactory(advisedSupport).getProxy();

        }

        return null;
    }
}

创建bean

    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
        Object bean = null;
        try {
            // 判断是否返回代理 Bean 对象
            bean = resolveBeforeInstantiation(beanName, beanDefinition);
            if (null != bean) {
                return bean;
            }

            bean = createBeanInstance(beanDefinition, beanName, args);
            // 给 bean 填充属性
            applyPropertyValues(beanName, bean, beanDefinition);
            // 执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理方法
            bean = initializeBean(beanName, bean, beanDefinition);
        } catch (Exception e) {
            throw new BeansException("Instantiation of bean failed.", e);
        }

        // 注册实现了 DisposableBean 接口的 Bean 对象
        registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);

        // 判断 SCOPE_SINGLETON,SCOPE_PROTOTYPE
        if (beanDefinition.isSingleton()) {
            registerSingleton(beanName, bean);
        }
        return bean;
    }

通过注解配置和包自动扫描的方式完成Bean对象的注册

在XmlBeanDefinitionReader中解析<context:component-scan />标签,扫描类组装BeanDefinition然后注册到容器中的操作在ClassPathBeanDefinitionScanner#doScan中实现。
主要包括的就是 xml 解析类 XmlBeanDefinitionReader 对 ClassPathBeanDefinitionScanner#doScan 的使用。

依赖于 BeanFactoryPostProcessor 在 Bean 生命周期的属性,可以在 Bean 对象实例化之前,改变属性信息。所以这里通过实现 BeanFactoryPostProcessor 接口,完成对配置文件的加载以及摘取占位符中的在属性文件里的配置。
这样就可以把提取到的配置信息放置到属性配置中了,buffer.replace(startIdx, stopIdx + 1, propVal); propertyValues.addPropertyValue

Component注解扫描原理:
定义

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Component {

    String value() default "";

}

1、解析xml,在得到有component-scan是开启注解扫描功能,XmlBeanDefinitionReader

    protected void doLoadBeanDefinitions(InputStream inputStream) throws ClassNotFoundException, DocumentException {
        SAXReader reader = new SAXReader();
        Document document = reader.read(inputStream);
        Element root = document.getRootElement();

        // 解析 context:component-scan 标签,扫描包中的类并提取相关信息,用于组装 BeanDefinition
        Element componentScan = root.element("component-scan");
        if (null != componentScan) {
            String scanPath = componentScan.attributeValue("base-package");
            if (StrUtil.isEmpty(scanPath)) {
                throw new BeansException("The value of base-package attribute can not be empty or null");
            }
            scanPackage(scanPath);
        }
        // ... 省略其他
        // 注册 BeanDefinition
        getRegistry().registerBeanDefinition(beanName, beanDefinition);
    }
    private void scanPackage(String scanPath) {
        String[] basePackages = StrUtil.splitToArray(scanPath, ',');
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(getRegistry());
        scanner.doScan(basePackages);
    }

2、扫描所有的要扫描的package,并注册,ClassPathBeanDefinitionScanner

  public void doScan(String... basePackages) {
        for (String basePackage : basePackages) {
        //发现注解,下面详细看
            Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
            for (BeanDefinition beanDefinition : candidates) {
                // 解析 Bean 的作用域 singleton、prototype
                String beanScope = resolveBeanScope(beanDefinition);
                if (StrUtil.isNotEmpty(beanScope)) {
                    beanDefinition.setScope(beanScope);
                }
                registry.registerBeanDefinition(determineBeanName(beanDefinition), beanDefinition);
            }
        }
    }

3、上述处理注解对象的装配,ClassPathScanningCandidateComponentProvider

    public Set<BeanDefinition> findCandidateComponents(String basePackage) {
        Set<BeanDefinition> candidates = new LinkedHashSet<>();
        //扫描所有的包含Component的类,并加入candidates,最后返回
        Set<Class<?>> classes = ClassUtil.scanPackageByAnnotation(basePackage, Component.class);
        for (Class<?> clazz : classes) {
            candidates.add(new BeanDefinition(clazz));
        }
        return candidates;
    }

通过注解给属性注入配置和Bean对象

围绕实现接口 InstantiationAwareBeanPostProcessor 的类 AutowiredAnnotationBeanPostProcessor 作为入口点,被 AbstractAutowireCapableBeanFactory创建 Bean 对象过程中调用扫描整个类的属性配置中含有自定义注解 Value、Autowired、Qualifier,的属性值。

AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues

 @Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        // 1. 处理注解 @Value
        Class<?> clazz = bean.getClass();
        clazz = ClassUtils.isCglibProxyClass(clazz) ? clazz.getSuperclass() : clazz;

        Field[] declaredFields = clazz.getDeclaredFields();

        for (Field field : declaredFields) {
            Value valueAnnotation = field.getAnnotation(Value.class);
            if (null != valueAnnotation) {
                String value = valueAnnotation.value();
                value = beanFactory.resolveEmbeddedValue(value);
                BeanUtil.setFieldValue(bean, field.getName(), value);
            }
        }

        // 2. 处理注解 @Autowired
        for (Field field : declaredFields) {
            Autowired autowiredAnnotation = field.getAnnotation(Autowired.class);
            if (null != autowiredAnnotation) {
                Class<?> fieldType = field.getType();
                String dependentBeanName = null;
                Qualifier qualifierAnnotation = field.getAnnotation(Qualifier.class);
                Object dependentBean = null;
                if (null != qualifierAnnotation) {
                    dependentBeanName = qualifierAnnotation.value();
                    dependentBean = beanFactory.getBean(dependentBeanName, fieldType);
                } else {
                    dependentBean = beanFactory.getBean(fieldType);
                }
                BeanUtil.setFieldValue(bean, field.getName(), dependentBean);
            }
        }

        return pvs;
    }

循环依赖

循环依赖主要分为这三种,自身依赖于自身、互相循环依赖、多组循环依赖。
循环依赖需要用到三个缓存,这三个缓存分别存放了成品对象、半成品对象(未填充属性值)、代理对象,分阶段存放对象内容,来解决循环依赖问题。
用于解决循环依赖需要用到三个缓存,这三个缓存分别存放了成品对象、半成品对象(未填充属性值)、代理对象,分阶段存放对象内容,来解决循环依赖问题。
关于循环依赖在我们目前的 Spring 框架中扩展起来也并不会太复杂,主要就是对于创建对象的提前暴露,如果是工厂对象则会使用 getEarlyBeanReference 逻辑提前将工厂🏭对象存放到三级缓存中。等到后续获取对象的时候实际拿到的是工厂对象中 getObject,这个才是最终的实际对象。

事务功能设计

基本原理:通过AOP的方式来设置关闭数据库的自动提交事务,如:connection.setAutoCommit(false),然后在程序中在合适的位置进行手动提交事务和回滚事务

实际使用–JDBCTemplate

JdbcTemplate可以作为一个普通的bean来管理,里面定义了对数据库的操作,实际上是依赖的各种数据库的驱动。
想要完成连接数据库也需要引入其他的bean,例如DriverManagerDataSource连接池技术。

整合ORM框架

实际上是将ORM框架的连接信息和执行sql的信息交给Spring来管理
例如整合mybatis框架时实现了一个SqlsessionFactoryBuild的工厂类对sqlSession进行管理,同时扫描到众多的执行sql的bean,在ORM完成sql到bean的方法的映射后,注入到spring中,直接执行就能与数据库关联

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

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

相关文章

STM32CubeMX学习(二) USB CDC 双向通信

STM32CubeMX学习&#xff08;二&#xff09; USB CDC 双向通信 简介CubeMX新建工程&#xff08;串口LED&#xff09;测试串口和LED串口接收测试USB CDC通信 简介 利用正点原子F407探索者开发板&#xff0c;测试基于USB CDC的双向数据通信。 CubeMX新建工程&#xff08;串口LE…

ES6+ 面试常问题

一、let const var 的区别 1. var&#xff1a; 没有块级作用域的概念&#xff0c;有函数作用域和全局作用域的概念全局作用域性下创建变量会被挂在到 windows 上存在变量提升同一作用域下&#xff0c;可以重复赋值创建未初始化&#xff0c;值为 undefined 2. let&#xff1a…

2023年末,软件测试面试题总结与分享

大家好&#xff0c;最近有不少小伙伴在后台留言&#xff0c;得准备年后面试了&#xff0c;又不知道从何下手&#xff01;为了帮大家节约时间&#xff0c;特意准备了一份面试相关的资料&#xff0c;内容非常的全面&#xff0c;真的可以好好补一补&#xff0c;希望大家在都能拿到…

天擎终端安全管理系统clientinfobymid存在SQL注入漏洞

产品简介 奇安信天擎终端安全管理系统是面向政企单位推出的一体化终端安全产品解决方案。该产品集防病毒、终端安全管控、终端准入、终端审计、外设管控、EDR等功能于一体&#xff0c;兼容不同操作系统和计算平台&#xff0c;帮助客户实现平台一体化、功能一体化、数据一体化的…

《PCI Express体系结构导读》随记 —— 第I篇 第1章 PCI总线的基本知识(16)

接前一篇文章&#xff1a;《PCI Express体系结构导读》随记 —— 第I篇 第1章 PCI总线的基本知识&#xff08;15&#xff09; 1.3 PCI总线的存储器读写总线事务 1.3.5 Delayed传送方式 如前文所述&#xff0c;当处理器使用Non-Posted总线周期对PCI设备进行操作、或者PCI设备使…

Android MVVM 写法

前言 Model&#xff1a;负责数据逻辑 View&#xff1a;负责视图逻辑 ViewModel&#xff1a;负责业务逻辑 持有关系&#xff1a; 1、ViewModel 持有 View 2、ViewModel 持有 Model 3、Model 持有 ViewModel 辅助工具&#xff1a;DataBinding 执行流程&#xff1a;View &g…

linux源码编译升级安装openssl3.0.1导致系统启动失败的问题解决

前两天在安装curl的时候&#xff0c;提示openssl版本太老了&#xff0c;原有的版本是openssl1.0的版本&#xff0c;需要将其升级到openssl3的版本。 直接使用命令行sudo apt install默认安装的还是openssl1.1.1版本&#xff0c;因此决定使用源码自行安装。 具体的安装过程就不赘…

webpack打包批量替换路径(string-replace-webpack-plugin插件)

string-replace-webpack-plugin 是一个用于在 webpack 打包后的文件中替换字符串的插件。它可以用于将特定字符串替换为其他字符串&#xff0c;例如将敏感信息从源代码中移除或对特定文本进行本地化处理。比如文件的html、css、js中的路径地址想批量更改一下 http://localhost:…

海德堡UV灯电源维修eta Plus Elc PE22-400-210

uv灯电源维修故障包括&#xff1a; 1、电压不稳&#xff1a;检查uv打印机的电压&#xff0c;设置一个稳压箱即可。 2、温度过高&#xff1a;uv打印机温度过高也会影响uv灯&#xff0c;可以更换为水冷式循环降温。 3、水箱里的信号线接触不好&#xff1a;将两边的信号线对调&…

leetcode刷题记录07(2023-04-30)【二叉树展开为链表 | 买卖股票的最佳时机 | 二叉树中的最大路径和(递归) | 最长连续序列(并查集)】

114. 二叉树展开为链表 给你二叉树的根结点 root &#xff0c;请你将它展开为一个单链表&#xff1a; 展开后的单链表应该同样使用 TreeNode &#xff0c;其中 right 子指针指向链表中下一个结点&#xff0c;而左子指针始终为 null 。 展开后的单链表应该与二叉树 先序遍历 顺…

ArcGIS批量计算shp面积并导出shp数据总面积(建模法)

在处理shp数据时&#xff0c; 又是我们需要知道许多个shp字段的批量计算&#xff0c;例如计算shp的总面积、面积平均值等&#xff0c;但是单个查看shp文件的属性进行汇总过于繁琐&#xff0c;因此可以借助建模批处理来计算。 首先准备数据&#xff1a;一个含有多个shp的文件夹。…

前后端分离nodejs+vue+ElementUi网上订餐系统69b9

课题主要分为两大模块&#xff1a;即管理员模块和用户模块&#xff0c;主要功能包括个人中心、用户管理、菜品类型管理、菜品信息管理、留言反馈、在线交流、系统管理、订单管理等&#xff1b; 运行软件:vscode 前端nodejsvueElementUi 语言 node.js 框架&#xff1a;Express/k…

Lumerical------按键中断程序执行

Lumerical------中断程序执行 引言正文 引言 在 Lumerical 中&#xff0c;很多时候我们需要通过 sweep 的方式来获取我们想要的结果&#xff0c;然而&#xff0c;有时候当我们运行程序后发现书写的脚本有问题时&#xff0c;我们想要强行终止程序的执行&#xff0c;该怎么办呢&…

代码随想录-刷题第四十二天

0-1背包理论基础 0-1背包问题介绍 0-1背包问题&#xff1a;有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i]&#xff0c;得到的价值是value[i] 。每件物品只能用一次&#xff0c;求解将哪些物品装入背包里物品价值总和最大。 0-1背包问题可以使用回溯法进…

Collector收集器的高级用法

Collectors收集器的高级用法 场景1&#xff1a;获取关联的班级名称 原先如果需要通过关联字段拿到其他表的某个字段&#xff0c;只能遍历List匹配获取 for (Student student : studentList) {Long clazzId student.getClazzId();// 遍历班级列表&#xff0c;获取学生对应班级…

【ArkTS入门】ArkTS开发初探:语言特点和开发特点

什么是ArkTS&#xff1f; ArkTS是一个为鸿蒙组件而生的框架&#xff0c;语法亲人好用。基于TypeScript&#xff0c;ArkTS拓展了声明式UI、状态管理等的能力&#xff0c;从本质上来讲&#xff0c;是TypeScript的扩展&#xff0c;主要服务于前端。 ArkTS的开发可以满足“一次开…

vue-cli3/webpack打包时去掉console.log调试信息

文章目录 前言一、terser-webpack-plugin是什么&#xff1f;二、使用配置vue-cli项目 前言 开发环境下&#xff0c;console.log调试信息&#xff0c;有助于我们找到错误&#xff0c;但在生产环境&#xff0c;不需要console.log打印调试信息&#xff0c;所以打包时需要将consol…

【React源码 - ReactDom.render发生了什么】

在React开发中&#xff0c;在入口文件我们都会执行ReactDom.render来讲整个应用挂载在主document中&#xff0c;那其中发生了什么&#xff0c;React是如何讲我们写的JSX代码&#xff0c;一步一步更新Fiber进而挂载渲染的呢。本文主要是基于react17.0.2的源码以及自己的理解来简…

thinkphp+vue+mysql企业车辆管理系统m117l

“企业车辆管理系统”是运用php语言和vue框架&#xff0c;以Mysql数据库为基础而发出来的。为保证我国经济的持续性发展&#xff0c;必须要让互联网信息时代在我国日益壮大&#xff0c;蓬勃发展。伴随着信息社会的飞速发展&#xff0c;企业车辆管理系统所面临的问题也一个接一个…

大数据背后的绿色收割:基于Hadoop的农产品价格信息智能分析

大数据背后的绿色收割&#xff1a;基于Hadoop的农产品价格信息智能分析 引言正文1. 数据获取与准备2. 数据清洗与处理3. Hadoop数据分析引擎的运用4. MySQL数据库的集成5. 创新性的可视化6. 结论与展望 结语 引言 随着信息技术的不断发展&#xff0c;农业领域也在数字化的浪潮…