后端之手写Spring

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介: 我们学Java的基本上都会使用Spring进行开发,而Spring中最为核心的又是IOC和AOP,接下来的内容是在学习手写Spring渐进式源码实践`这本书后的学习总结,看是否我们能开发出一个`mini-Spring`。因为后期代码会很多,而且基本上都是在前一版的基础上进行扩展。这里我只声明每一章的目标扩展点是啥。

我们学Java的基本上都会使用Spring进行开发,而Spring中最为核心的又是IOC和AOP,接下来的内容是在学习手写Spring渐进式源码实践这本书后的学习总结,看是否我们能开发出一个mini-Spring。因为后期代码会很多,而且基本上都是在前一版的基础上进行扩展。这里我只声明每一章的目标扩展点是啥,具体从Github上获取源码:https://github.com/fuzhengwei/book-small-spring

第一章:实现一个简单的Spring Bean容器

​ 先不深究Spring源码,我就看自己平时使用Spring时的体会,使用Spring时,通过XML配置文件或者通过注解,声明哪些类是需要注入到容器中的,到自己使用时,可以从容器中获取该类对象。那这不就是我们基础中学的Collection或者Map就能实现的操作嘛,因为我需要频繁的从容器中获取指定类对象,所以查询返回的效率需要非常高,那就我们就用Map来实现,先不要想那么多。

public class BeanFactory {
        // 用Map来存储Bean
    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);
    }
}

​ 这里我们使用BeanDefinition来包裹实际的对象,因为我们知道一个Bean不仅拥有自己属性,还拥有一些通用属性,比如:单例模式还是原型模式,Bean内需要填充的属性,这里我们只做包裹,其余的后面实现。

public class BeanDefinition {
    // 实际填充的对象
    private Object bean;
         // 省略 构造器 和 get方法。。
}

​ 我们来测试一下:

@Test
public void test_BeanFactory(){
    // 1.初始化 BeanFactory
    BeanFactory beanFactory = new BeanFactory();
    // 2.注入bean
    BeanDefinition beanDefinition = new BeanDefinition(new UserService());
    beanFactory.registerBeanDefinition("userService", beanDefinition);
    // 3.获取bean
    UserService userService = (UserService) beanFactory.getBean("userService");
    userService.queryUserInfo();
}

第二章:实现Bean对象的定义,注册和获取

​ 在上面我们可以看到我是创建好了的UserService放入容器中,这和我们之前学的依赖倒置并不符合,我们应只指定哪些Bean需要加载到容器中,具体的对象实例应又BeanFactory自己管理,所以我们修改BeanDefinition,在内只存放Bean的Class对象。

public class BeanDefinition {
    private Class beanClass;
      // 构造器 get,set方法
}

​ BeanFactory也修改为接口,定义获取Bean的方式,具体Bean存储与获取交给子类来实现,使职责单一。

​ 我们在这里先假设每个Bean都是单例的,创建SingletonBeanRegistry接口,定义注册和获取单例对象的方式,剩下的交给子类具体实现。

​ 现在系统中有两个容器,一个是BeanDefinitionMap负责存储加载到容器的Bean信息,一个是singletonObjects负责存储已经创建好的单例对象。具体可看下面的UML类图,关系还是很清楚的。BeanFactory在实例化对象时,通过BeanDefinitionMap中的类信息,通过反射直接创建对象,并放入容器中。

​ Spring中有大量的接口与抽象类,我个人感觉是将职责划分清楚,容易未来系统的扩展性,有一点是通过抽象类去实现接口,并定义自己的抽象方法,而且可以实现一部分的接口方法,这样既可以扩容接口,又能保证自己可以只实现自己职责内的方法。

第三章:基于Cglib实现含有构造函数的类实例化策略

​ 在第二章中我们在AbstractAutowireCapableBeanFactory::createBean中通过反射进行Bean的实例化,这一章我们引入了实例化策略:InstantiationStrategy,并通过JDK和Cglib两个种方式去实现。

JDK 和 Cglib实例化对象有什么区别

​ CGLib创建的动态代理对象性能比JDK创建的动态代理对象的性能高不少,但是CGLib在创建代理对象时所花费的时间却比JDK多得多,所以对于单例的对象,因为无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更为合适一些。同时,由于CGLib由于是采用动态创建子类的方法,对于final方法,无法进行代理。

第四章:注入属性与依赖对象

​ 比方说我UserService内需要注入UserDao来操作数据库,所以我在实例化Bean后,应将所需的属性和依赖注入到Bean中,而这部分信息通过PropertyValues记录,并封装到BeanDefinition中。在实例化后应通过对应BeanDefinition获取到需要哪些内容,并进行填充处理。

第五章:资源加载器解析文件注册对象

​ 这里我们要加入XML解析,在上面我们都是手动将所有信息通过Java注册到BeanFactory的,这不利于维护,现实Spring中我们都是使用Xml进行配置或者注解开发,所以我们需要定义一个ResourceLoader去加载各种数据(XML文件,URL,Classpath)。

资源加载,读取指定资源,转化为Resource,核心是获取文件的二进制流为后面解析
资源解析:利用XML解析器解析 Spring.xml文件,获取所有Bean信息定义,并将其封装为BeanDefinition并注册
容器初始化:完成实例的实例化与属性填充

​ 这一章类扩展的很多,建议仔细读两遍源码,着重关注资源加载解析部分即可,其余流程大体不变。

第六章:实现应用上下文

重点:

引入应用上下文,进行资源扫描与加载,为Bean对象实例化过程添加扩展机制,允许加载Bean对象和在其实例化前后进行修改和扩展。

核心:

增加BeanPostProcessor 和 BeanFactoryPostProcessor

有啥用:

比如说我们想在Bean的实例化之前,对BeanDefinition做出记录或修改,影响Bean的实例化,或者在Bean的初始化方法前后进行一系列操作,这是后面章节的基础部分,实现容器感知,AOP等技术的实现

AbstractApplicationContext::refresh() 刷新容器

@Override
public void refresh() throws BeansException {
    // 1. 创建 BeanFactory,并加载 BeanDefinition
    refreshBeanFactory();

    // 2. 获取 BeanFactory
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();

    // 3. 在 Bean 实例化之前,执行 BeanFactoryPostProcessor ,这时候Bean还没有实例化,找到所有的BeanFactoryPostProcessor,进行处理,可以修改BeanDefinition !!!
    invokeBeanFactoryPostProcessors(beanFactory);

    // 4. BeanPostProcessor 需要提前于其他 Bean 对象实例化之前执行注册操作,找到所有的BeanPostProcessor,添加到一个容器中,等对象实例化时进行处理  !!! 
    registerBeanPostProcessors(beanFactory);

    // 5. 提前实例化单例Bean对象
    beanFactory.preInstantiateSingletons();
}

加载出所有注册的BeanFactoryPostProcessor,普通Bean实例化前操作

private void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    Map<String, BeanFactoryPostProcessor> beanFactoryPostProcessorMap = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);
    for (BeanFactoryPostProcessor beanFactoryPostProcessor : beanFactoryPostProcessorMap.values()) {
        beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
    }
}

加载出所有注册的BeanPostProcessor,必须加载并注册,因为这些Bean是转为针对普通Bean实例化后操作的。

private void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    Map<String, BeanPostProcessor> beanPostProcessorMap = beanFactory.getBeansOfType(BeanPostProcessor.class);
    for (BeanPostProcessor beanPostProcessor : beanPostProcessorMap.values()) {
        beanFactory.addBeanPostProcessor(beanPostProcessor);
    }
}
public void preInstantiateSingletons() throws BeansException {
    // 实例化所有未实例化的Bean
    beanDefinitionMap.keySet().forEach(this::getBean);
}

protected <T> T doGetBean(final String name, final Object[] args) {
              // 先从单例工厂找,有则直接返回,这样就不担心之前加载的Processor再被加载处理了
        Object bean = getSingleton(name);
        if (bean != null) {
            return (T) bean;
        }
        // 获取Bean对应的定义信息
        BeanDefinition beanDefinition = getBeanDefinition(name);
              // 实例化Bean
        return (T) createBean(name, beanDefinition, args);
    }

@Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
        Object bean = null;
        try {
            // 将 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);
        }
                // 将Bean注入单例容器中
        registerSingleton(beanName, bean);
        return bean;
    }

private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {
        // 1. 执行 BeanPostProcessor Before 处理 !!!
        Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);

        // 待完成内容:invokeInitMethods(beanName, wrappedBean, beanDefinition);  这里就是未来init方法调用的地方
        invokeInitMethods(beanName, wrappedBean, beanDefinition);

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

第七章:Bean对象的初始化与销毁

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id="userDao" class="cn.bugstack.springframework.test.bean.UserDao"
          init-method="initDataMethod"
          destroy-method="destroyDataMethod"/>
</beans>

​ 在Bean定义信息中我们指定了初始化方法(和对象构造器方法无关)和销毁方法。加入了init-method 和 destroy-method。我们可以在Xml解析中,读取出该数据,并把该数据保存到BeanDefinition中。这里的销毁方法,我们利用JVM的Hook进行调用。

public class BeanDefinition {
    private Class beanClass;

    private PropertyValues propertyValues;

    private String initMethodName;

    private String destroyMethodName;
  
  // ...get/set/construct方法
}

​ 除了在Xml中进行设置初始化和销毁方式外,我们也可以设计两个接口,让Bean来实现对应的方法,未来初始化时执行相应方法。

public interface InitializingBean {
    /**
     * Bean 处理了属性填充后调用
     */
    void afterPropertiesSet() throws Exception;
}

public interface DisposableBean {
      /**
     * Bean 销毁时执行
     */
    void destroy() throws Exception;
}

先说初始化方法:

@Override
protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
    Object bean = null;
    try {
          // 前面的操作
        // 执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理方法  !!! 
        bean = initializeBean(beanName, bean, beanDefinition);
    } catch (Exception e) {
        throw new BeansException("Instantiation of bean failed", e);
    }

    // 注册实现了 DisposableBean 接口的 Bean 对象  !!! 这里需要保存起来哪些Bean是含有销毁时方法的,在最后需要执行
    registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);

    registerSingleton(beanName, bean);
    return bean;
}
private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {
    // 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(wrappedBean, beanName);
    return wrappedBean;
}
初始化方法调用
private void invokeInitMethods(String beanName, Object bean, BeanDefinition beanDefinition) throws Exception {
    // 1. 实现接口 InitializingBean
    if (bean instanceof InitializingBean) {
        ((InitializingBean) bean).afterPropertiesSet();
    }

    // 2. 注解配置 init-method {判断是为了避免二次执行初始化}
    String initMethodName = beanDefinition.getInitMethodName();
    if (StrUtil.isNotEmpty(initMethodName) && !(bean instanceof InitializingBean)) {
        Method initMethod = beanDefinition.getBeanClass().getMethod(initMethodName);
        if (null == initMethod) {
            throw new BeansException("Could not find an init method named '" + initMethodName + "' on bean with name '" + beanName + "'");
        }
        initMethod.invoke(bean);
    }
}
注册含有Destroy方法的Bean
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefinition) {
    if (bean instanceof DisposableBean || StrUtil.isNotEmpty(beanDefinition.getDestroyMethodName())) {
        registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, beanDefinition));
    }
}

那现在初始化方法已经执行完了,Destroy方法什么时候执行? 这里我们用到了JVM的钩子函数,我们先定义接口

public interface ConfigurableApplicationContext extends ApplicationContext {
    void refresh() throws BeansException;

    void registerShutdownHook();

    void close();
}

public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext { 
@Override
    public void registerShutdownHook() {
        Runtime.getRuntime().addShutdownHook(new Thread(this::close));
    }

    @Override
    public void close() {
        getBeanFactory().destroySingletons();
    }
}
将DisposableBean封装为DisposableBeanAdapter,未来调用
public class DisposableBeanAdapter implements DisposableBean {
            @Override
        public void destroy() throws Exception {
            // 1. 实现接口 DisposableBean
            if (bean instanceof DisposableBean) {
                ((DisposableBean) bean).destroy();
            }

            // 2. 注解配置 destroy-method {判断是为了避免二次执行销毁}
            if (StrUtil.isNotEmpty(destroyMethodName) && !(bean instanceof DisposableBean && "destroy".equals(this.destroyMethodName))) {
                Method destroyMethod = bean.getClass().getMethod(destroyMethodName);
                if (null == destroyMethod) {
                    throw new BeansException("Couldn't find a destroy method named '" + destroyMethodName + "' on bean with name '" + beanName + "'");
                }
                destroyMethod.invoke(bean);
            }
        }
}
Close() 时具体的操作
public void close() {
    getBeanFactory().destroySingletons();
}

public void destroySingletons() {
        Set<String> keySet = this.disposableBeans.keySet();
        Object[] disposableBeanNames = keySet.toArray();

        for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
            Object beanName = disposableBeanNames[i];
            // 从单例工厂中删除
            DisposableBean disposableBean = disposableBeans.remove(beanName);
            try {
                // 调用的是DisposableBeanAdapter重载的Destroy,最终调用Bean自身定义的Destroy。
                disposableBean.destroy();
            } catch (Exception e) {
                throw new BeansException("Destroy method on bean with name '" + beanName + "' threw an exception", e);
            }
        }
    }

第八章:感知容器对象

需求:在Bean初始化时,能够感知到容器对象,通过容器对象进行一系列操作

定义Aware接口,表示是可感知的对象,具体由子类实现

@Override
public void refresh() throws BeansException {
    // 1. 创建 BeanFactory,并加载 BeanDefinition
    // 2. 获取 BeanFactory

    // 3. 添加 ApplicationContextAwareProcessor,让继承自 ApplicationContextAware 的 Bean 对象都能感知所属的 ApplicationContext !!! 
    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

    // 4. 在 Bean 实例化之前,执行 BeanFactoryPostProcessor 
    // 5. BeanPostProcessor 需要提前于其他 Bean 对象实例化之前执行注册操作
    // 6. 提前实例化单例Bean对象
}

这里的第三步,因为无法在Bean初始化时感知到ApplicationContext属性,所以将ApplicationContext写入一个BeanPostProcessor,这样在执行初始化前就可以获取并分配ApplicationContext了。

AbstractAutowireCapableBeanFactory:createBean()调用

private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {

    // 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);
        }
    }
}

​ 总之当Bean实现Aware实现类后,当Bean被注入属性后,就会将对应的容器对象进行填充,得到一个完整的对象。

第九章:对象作用域与FactoryBean

FactoryBean 和 BeanFactory有什么区别?

​ BeanFactory是用来获取Bean的,FactoryBean它本身是一个Bean,但它本身也是一个工厂,可以内部通过动态代理,凝聚出一个功能更完善的Bean,未来这个获取Bean获取的就是FactoryBean内部代理的Bean,可对原本的方法做出调整,而不需要修改原先基本的代码。

为什么需要FactoryBean?

​ 可以扩充自己的对象功能了。MyBatis 就是实现了一个 MapperFactoryBean 类,在 getObject 方法中提供 SqlSession 对执行 CRUD 方法的操作

如何使用FactoryBean?

​ 定义一个类实现FactoryBean接口,实现内部的getObject(),然后注册到容器中即可。

AbstractBeanFactory::doGetBean

protected <T> T doGetBean(final String name, final Object[] args) {
    Object sharedInstance = getSingleton(name);
    if (null != sharedInstance) {
        // 如果是 FactoryBean,则需要调用 FactoryBean#getObject,获取FactoryBean中定义的元素
        return (T) getObjectForBeanInstance(sharedInstance, name);
    }

    BeanDefinition beanDefinition = getBeanDefinition(name);
    Object bean = createBean(name, beanDefinition, args);
    return (T) getObjectForBeanInstance(bean, name);
}
private Object getObjectForBeanInstance(Object beanInstance, String beanName) {
    // 判断是否为FactoryBean,如果不是则直接返回
      if (!(beanInstance instanceof FactoryBean)) {
        return beanInstance;
    }
        // 缓存中找
    Object object = getCachedObjectForFactoryBean(beanName);

    if (object == null) {
        FactoryBean<?> factoryBean = (FactoryBean<?>) beanInstance;
        // !!!  获取或创建FactoryBean内对象
        object = getObjectFromFactoryBean(factoryBean, beanName);
    }

    return object;
}
protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName) {
    if (factory.isSingleton()) {
        Object object = this.factoryBeanObjectCache.get(beanName);
        if (object == null) {
            object = doGetObjectFromFactoryBean(factory, beanName);
            this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));
        }
        return (object != NULL_OBJECT ? object : null);
    } else {
        return doGetObjectFromFactoryBean(factory, beanName);
    }
}

第十章:容器事件与事件监听器

​ 这里将用到观察者模式,比如说我们有一个注册用户的业务,注册业务成功后,需要发送短信通知,我们最初就是将注册代码与发送短信代码写在了一起,后来自己定义观察者与被观察者,实际上Spring为了更好的扩容性和灵活性,加入了事件监听机制,我们下面来自己设计如何控制事件:

定义和实现事件
事件广播器: 可用于添加,删除监听,广播事件,通过对应的监听器进行通知
事件监听器,自定义监听器,执行自定义事件触发方法
事件如何发送?并触发对应的事件相应方法?
@Test
public void test_event() {
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
    // 发送事件
    applicationContext.publishEvent(new CustomEvent(applicationContext, 1019129009086763L, "成功了!"));
    applicationContext.registerShutdownHook();
}
@Override
public void publishEvent(ApplicationEvent event) {
    // 通过事件广播器进行通知
    applicationEventMulticaster.multicastEvent(event);
}
@Override
public void multicastEvent(final ApplicationEvent event) {
    // 找到事件对应的监听器,并执行方法
    for (final ApplicationListener listener : getApplicationListeners(event)) {
        listener.onApplicationEvent(event);
    }
}
/**
 * 监听器是否对该事件感兴趣
 */
protected boolean supportsEvent(ApplicationListener<ApplicationEvent> applicationListener, ApplicationEvent event) {
    Class<? extends ApplicationListener> listenerClass = applicationListener.getClass();

    // 按照 CglibSubclassingInstantiationStrategy、SimpleInstantiationStrategy 不同的实例化类型,需要判断后获取目标 class
    Class<?> targetClass = ClassUtils.isCglibProxyClass(listenerClass) ? listenerClass.getSuperclass() : listenerClass;
    Type genericInterface = targetClass.getGenericInterfaces()[0];

    Type actualTypeArgument = ((ParameterizedType) genericInterface).getActualTypeArguments()[0];
    String className = actualTypeArgument.getTypeName();
    Class<?> eventClassName;
    try {
        eventClassName = Class.forName(className);
    } catch (ClassNotFoundException e) {
        throw new BeansException("wrong event class name: " + className);
    }
    // 判定此 eventClassName 对象所表示的类或接口与指定的 event.getClass() 参数所表示的类或接口是否相同,或是否是其超类或超接口。
    // isAssignableFrom是用来判断子类和父类的关系的,或者接口的实现类和接口的关系的,默认所有的类的终极父类都是Object。如果A.isAssignableFrom(B)结果是true,证明B可以转换成为A,也就是A可以由B转换而来。
    return eventClassName.isAssignableFrom(event.getClass());
}
目录
相关文章
|
5月前
|
小程序 数据可视化 Java
Java+后端Spring boot 开发的全套UWB定位方案,0.1米高精度定位系统源码
UWB定位系统由硬件定位设备、定位引擎和应用软件组成。该定位系统应用软件支持PC端和移动端访问,并提供位置实时显示、历史轨迹回放、人员考勤、电子围栏、行为分析、智能巡检等功能。定位精度高达10cm,同时具备高动态、高容量、低功耗的优点。应用场景包括:隧道、化工、工厂、煤矿、工地、电厂、养老、展馆、整车、机房、机场等。
110 8
|
6月前
|
前端开发 安全 Java
Spring Boot 三招组合拳,手把手教你打出优雅的后端接口
Spring Boot 三招组合拳,手把手教你打出优雅的后端接口
94 0
|
5月前
|
IDE Java 开发工具
Spring Boot:加速Java后端开发的现代化利器
在当今快速迭代的软件开发环境中,Spring Boot 已成为Java后端开发领域的首选框架。作为Spring家族的一员,它以“约定优于配置”的设计理念,极大地简化了传统Spring应用的配置和部署过程,让开发者能够更加专注于业务逻辑的实现。本文将探讨Spring Boot的核心优势,并通过一个简单的示例展示如何快速启动一个基于Spring Boot的Java Web应用。
140 1
|
3月前
|
Java 数据库连接 Spring
后端框架入门超详细 三部曲 Spring 、SpringMVC、Mybatis、SSM框架整合案例 【爆肝整理五万字】
文章是关于Spring、SpringMVC、Mybatis三个后端框架的超详细入门教程,包括基础知识讲解、代码案例及SSM框架整合的实战应用,旨在帮助读者全面理解并掌握这些框架的使用。
后端框架入门超详细 三部曲 Spring 、SpringMVC、Mybatis、SSM框架整合案例 【爆肝整理五万字】
|
3月前
|
XML Java 数据格式
后端框架学习1-----Spring
这篇文章是Spring学习笔记,涵盖了Spring框架的基本概念、IoC容器、依赖注入(DI)的方式(包括set注入和构造函数注入)、复杂类型依赖注入、Bean的作用域和自动装配,以及Spring注解开发方式,包括@Autowired和@Qualifier注解的使用,以及@Component及其衍生注解的功能。
后端框架学习1-----Spring
|
3月前
|
前端开发 Java Spring
Spring与Angular/React/Vue:当后端大佬遇上前端三杰,会擦出怎样的火花?一场技术的盛宴,你准备好了吗?
【8月更文挑战第31天】Spring框架与Angular、React、Vue等前端框架的集成是现代Web应用开发的核心。通过RESTful API、WebSocket及GraphQL等方式,Spring能与前端框架高效互动,提供快速且功能丰富的应用。RESTful API简单有效,适用于基本数据交互;WebSocket支持实时通信,适合聊天应用和数据监控;GraphQL则提供更精确的数据查询能力。开发者可根据需求选择合适的集成方式,提升用户体验和应用功能。
92 0
|
3月前
|
前端开发 JavaScript Java
【Azure 应用服务】App Service For Windows 中如何设置代理实现前端静态文件和后端Java Spring Boot Jar包
【Azure 应用服务】App Service For Windows 中如何设置代理实现前端静态文件和后端Java Spring Boot Jar包
|
5月前
|
缓存 NoSQL Java
后端开发中缓存的作用以及基于Spring框架演示实现缓存
后端开发中缓存的作用以及基于Spring框架演示实现缓存
48 1
|
5月前
|
监控 Java 关系型数据库
java版MES系统源码,后端采用 Spring Boot 多模块架构
MES系统采用Vue3的vue-element-plus-admin为后台,Spring Boot多模块架构,支持MySQL、Oracle等数据库,具备SaaS多租户功能。核心功能包括车间计划排程、工艺流程配置、生产质量管理、进度追踪、库存和排班管理等,全面覆盖生产运营关键环节。
java版MES系统源码,后端采用 Spring Boot 多模块架构
|
5月前
|
存储 前端开发 Java
Spring第三课,Lombok工具包下载,对应图书管理系统列表和登录界面的后端代码,分层思想
Spring第三课,Lombok工具包下载,对应图书管理系统列表和登录界面的后端代码,分层思想

热门文章

最新文章

下一篇
无影云桌面