踩坑记:根据类型获取Spring容器中的Bean

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介:

在项目开发中遇到了这样的一个问题:有同事在BeanFactoryPostProcessor的实现方法中写了类似这样的一段代码:

@Component
public class BeanFactoryPostProcessorImpl implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        //就是这一段代码
        beanFactory.getBean(GetBeanByType.class);
    }
}

然后导致了一些实现了FactoryBean接口的Bean被提前创建了,当时感觉很奇怪,我只是根据传入的Class会获取Bean,为什么会导致一些实现了FactoryBean接口的Bean也被创建呢?其实即使我们是根据类型从Spring容器中获取Bean,但是Spring容器在实现的时候,最底层还是会调用getBean(java.lang.String, java.lang.Class, java.lang.Object...)这个方法获取Bean,那么当我们根据类型从Spring容器中获取Bean的时候就先找到这个类型对应的在Spring容器中的beanName,问题就出在获取这个beanName的地方。真正出问题的是AbstractBeanFactory#isTypeMatch(java.lang.String, org.springframework.core.ResolvableType)这个方法。下面我们来分一下获取beanName的这个过程。
其入口方法为:DefaultListableBeanFactory#getBeanNamesForType(java.lang.Class<?>),代码如下:

    public String[] getBeanNamesForType(Class<?> type) {
        //这里传入的三个参数 type是bean的Class 第二个参数的意思是 是否包含非单例的bean
        //第三个参数很重要 这里传入的值为true 这里传入true 代表允许提前初始化Bean
        //如果这个值为false的话 代表不允许提前初始化Bean 问题就出在这个值为true!!!
        return getBeanNamesForType(type, true, true);
    }
    @Override
    public String[] getBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
        //如果是在容器启动过程中 这个值为false 如果在容器启动完成之后 这个值会变为true 这个值的更改在
        //DefaultListableBeanFactory#freezeConfiguration 可以看一下它的调用关系
        //下面的内容就很简单了  我们主要分析doGetBeanNamesForType这个方法
        if (!isConfigurationFrozen() || type == null || !allowEagerInit) {
            return doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, allowEagerInit);
        }
        //这里是从缓存中获取,includeNonSingletons是否包含非单例的方法
        Map<Class<?>, String[]> cache =
                (includeNonSingletons ? this.allBeanNamesByType : this.singletonBeanNamesByType);
        String[] resolvedBeanNames = cache.get(type);
        if (resolvedBeanNames != null) {
            return resolvedBeanNames;
        }
        resolvedBeanNames = doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, true);
        if (ClassUtils.isCacheSafe(type, getBeanClassLoader())) {
            cache.put(type, resolvedBeanNames);
        }
        return resolvedBeanNames;
    }

DefaultListableBeanFactory#doGetBeanNamesForType

    private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
        List<String> result = new ArrayList<String>();
        // Check all bean definitions.
        // 注意这里是循环Spring容器中所有的BeanDefinition
        for (String beanName : this.beanDefinitionNames) {
            // Only consider bean as eligible if the bean name
            // is not defined as alias for some other bean.
            //beanName不是别名
            if (!isAlias(beanName)) {
                try {
                    //这里你可以暂时理解为根据BeanName获取BeanDefinition
                    RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                    // Only check bean definition if it is complete.
                    //下面这个判断比较长 一个一个的来说一下
                    //mbd.isAbstract()判断这个类是不是抽象类 默认是false
                    //allowEagerInit  是否允许提前初始化 这个值是传入的值  我们这里传入的值是true 即允许提前初始化
                    //mbd.hasBeanClass()  是否已经有BeanClass了
                    //mbd.isLazyInit()是否允许延迟初始化 默认false
                    //isAllowEagerClassLoading() 是否允许提前加载  上面这四个值是或的关系  只有有一个为true就可以
                    //requiresEagerInitForType 检查特殊的Bean是否需要提前被初始化 这里传入的是工厂方法的名字
                    //我们把这个条件总结一下 如果这个Bean不是一个抽象类,并且(被允许提前初始化 或者 (这个Bean的  
                    //BeanDefinition设置了BeanClass了 或者 这个Bean没有设置延迟加载 或者 即使这个Bean标注为延迟加载了,也可以被提前加载(全局变量)  并且 BeanDefinition中的FactoryBeanName是否允许被提前初始化))
                    //根据我们前面的分析 allowEagerInit 的值为true 这个Bean也不是个抽象类,所以会继续下面的流程
                    if (!mbd.isAbstract() && (allowEagerInit ||
                            ((mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading())) &&
                                    !requiresEagerInitForType(mbd.getFactoryBeanName()))) {
                        // In case of FactoryBean, match object created by FactoryBean.
                        // 判断是不是FactoryBean
                        boolean isFactoryBean = isFactoryBean(beanName, mbd);
                        BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
                        // 这个地方是判断是不是找到匹配的beanName 这里关键的是isTypeMatch这个方法
                        // 在这个方法中会导致一些FactoryBean被提前实例化 所以这里给我们的一个提示是:
                        // 在Spring容器的启动阶段,调用isTypeMatch这个方法去判断Bean的类型的时候要慎重一些。 
                        // 我们要重点分析isTypeMatch这个方法
                        boolean matchFound =
                                (allowEagerInit || !isFactoryBean ||
                                        (dbd != null && !mbd.isLazyInit()) || containsSingleton(beanName)) &&
                                (includeNonSingletons ||
                                        (dbd != null ? mbd.isSingleton() : isSingleton(beanName))) &&
                                isTypeMatch(beanName, type);
                        //如果类型不匹配 并且还是FactoryBean类型的BeanDefinition
                        if (!matchFound && isFactoryBean) {
                            // In case of FactoryBean, try to match FactoryBean instance itself next.
                            //这里讲beanName变为&beanName
                            beanName = FACTORY_BEAN_PREFIX + beanName;
                            //继续判断类型匹配不匹配
                            matchFound = (includeNonSingletons || mbd.isSingleton()) && isTypeMatch(beanName, type);
                        }
                        //如果类型相匹配的话 则将beanName放入到结果集中
                        if (matchFound) {
                            result.add(beanName);
                        }
                    }
                }
                catch (CannotLoadBeanClassException ex) {
                }
                catch (BeanDefinitionStoreException ex) {
                }
            }
        }
        //这一部分的处理流程大致相同
        // Check manually registered singletons too.
        for (String beanName : this.manualSingletonNames) {
            try {
                // In case of FactoryBean, match object created by FactoryBean.
                if (isFactoryBean(beanName)) {
                    if ((includeNonSingletons || isSingleton(beanName)) && isTypeMatch(beanName, type)) {
                        result.add(beanName);
                        // Match found for this bean: do not match FactoryBean itself anymore.
                        continue;
                    }
                    // In case of FactoryBean, try to match FactoryBean itself next.
                    beanName = FACTORY_BEAN_PREFIX + beanName;
                }
                // Match raw bean instance (might be raw FactoryBean).
                if (isTypeMatch(beanName, type)) {
                    result.add(beanName);
                }
            }
            catch (NoSuchBeanDefinitionException ex) {
            }
        }
        return StringUtils.toStringArray(result);
    }

下面我们来看看isTypeMatch这个方法的内容


    @Override
    public boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException {
        //这里是对beanName进行转换 如:如果传入的beanName是&beanName,则将&去掉。如果传入的beanName有别名的话,则替换为别名
        String beanName = transformedBeanName(name);
        
        // Check manually registered singletons.
        //根据beanName获取bean的实例 前提是其对应的bean已经被实例化了 或者正在实例化中
        Object beanInstance = getSingleton(beanName, false);
        if (beanInstance != null) {
            //如果是FactoryBean类型的实例
            if (beanInstance instanceof FactoryBean) {
                //是否要获取在FactoryBean中创建的Bean实例 调用 getObject getObjectType方法
                //这个地方的判断条件是 传入的beanName是否以&开头 如果以&开头,则返回true
                if (!BeanFactoryUtils.isFactoryDereference(name)) {
                    //这里是调用FactoryBean的getObjectType方法 来获取bean的类型
                    Class<?> type = getTypeForFactoryBean((FactoryBean<?>) beanInstance);
                    return (type != null && typeToMatch.isAssignableFrom(type));
                }
                else {
                    //直接获取FactoryBean类型的实例
                    return typeToMatch.isInstance(beanInstance);
                }
            }
            //判断beanName是否以&开头,如果以&开头的话 则返回true
            else if (!BeanFactoryUtils.isFactoryDereference(name)) {
                //如果beanName不是以&开头 则直接进行类型比较
                if (typeToMatch.isInstance(beanInstance)) {
                    // Direct match for exposed instance?
                    return true;
                }
                //泛型的处理
                else if (typeToMatch.hasGenerics() && containsBeanDefinition(beanName)) {
                    // Generics potentially only match on the target class, not on the proxy...
                    RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                    Class<?> targetType = mbd.getTargetType();
                    if (targetType != null && targetType != ClassUtils.getUserClass(beanInstance) &&
                            typeToMatch.isAssignableFrom(targetType)) {
                        // Check raw class match as well, making sure it's exposed on the proxy.
                        Class<?> classToMatch = typeToMatch.resolve();
                        return (classToMatch == null || classToMatch.isInstance(beanInstance));
                    }
                }
            }
            return false;
        }
        else if (containsSingleton(beanName) && !containsBeanDefinition(beanName)) {
            // null instance registered
            return false;
        }

        // No singleton instance found -> check bean definition.
        //这里是从父BeanFactory中进行查找匹配 查找匹配的过程是一样的。
        BeanFactory parentBeanFactory = getParentBeanFactory();
        if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
            // No bean definition found in this factory -> delegate to parent.
            return parentBeanFactory.isTypeMatch(originalBeanName(name), typeToMatch);
        }

        // Retrieve corresponding bean definition.
        //根据beanName重新检索BeanDefinition 这里返回一个RootBeanDefinition 如果父子容器中存在相同类型的bean会进行合并
        RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
        //解析出 class的类型
        Class<?> classToMatch = typeToMatch.resolve();
        if (classToMatch == null) {
            classToMatch = FactoryBean.class;
        }
        //如果传入的class类型为FactoryBean类型 则直接返回FactoryBean类型的数组 否则会将传入的class的类型和FactoryBean组装在一起
        Class<?>[] typesToMatch = (FactoryBean.class == classToMatch ?
                new Class<?>[] {classToMatch} : new Class<?>[] {FactoryBean.class, classToMatch});

        // Check decorated bean definition, if any: We assume it'll be easier
        // to determine the decorated bean's type than the proxy's type.
        //这个地方是获取最原始的BeanDefinition 不是组装之后的 RootBeanDefinition
        BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
        //如果dbd 存在并且 beanName不是以&开头
        if (dbd != null && !BeanFactoryUtils.isFactoryDereference(name)) {
            RootBeanDefinition tbd = getMergedBeanDefinition(dbd.getBeanName(), dbd.getBeanDefinition(), mbd);
            //这里是取BeanDefinition中的bean类型
            Class<?> targetClass = predictBeanType(dbd.getBeanName(), tbd, typesToMatch);
            if (targetClass != null && !FactoryBean.class.isAssignableFrom(targetClass)) {
                return typeToMatch.isAssignableFrom(targetClass);
            }
        }
        //获取目标类型 通常是去BeanDefinition中的resolvedTargetType
        Class<?> beanType = predictBeanType(beanName, mbd, typesToMatch);
        if (beanType == null) {
            return false;
        }
        
        // Check bean class whether we're dealing with a FactoryBean.
        //如果BeanDefinition中的beanType是FactoryBean类型
        if (FactoryBean.class.isAssignableFrom(beanType)) {
            if (!BeanFactoryUtils.isFactoryDereference(name)) {
                // If it's a FactoryBean, we want to look at what it creates, not the factory class.
                //就是这个地方 会将FactoryBean类型的Bean进行实例化
                //我们来分析这个方法
                beanType = getTypeForFactoryBean(beanName, mbd);
                if (beanType == null) {
                    return false;
                }
            }
        }
        else if (BeanFactoryUtils.isFactoryDereference(name)) {
            // Special case: A SmartInstantiationAwareBeanPostProcessor returned a non-FactoryBean
            // type but we nevertheless are being asked to dereference a FactoryBean...
            // Let's check the original bean class and proceed with it if it is a FactoryBean.
            //这个地方需要注意的是 SmartInstantiationAwareBeanPostProcessor类型的BeanPostProcessor会对返回的Bean类型做修改
            beanType = predictBeanType(beanName, mbd, FactoryBean.class);
            if (beanType == null || !FactoryBean.class.isAssignableFrom(beanType)) {
                return false;
            }
        }

        ResolvableType resolvableType = mbd.targetType;
        if (resolvableType == null) {
            //工厂方法创建的Bean
            resolvableType = mbd.factoryMethodReturnType;
        }
        if (resolvableType != null && resolvableType.resolve() == beanType) {
            return typeToMatch.isAssignableFrom(resolvableType);
        }
        return typeToMatch.isAssignableFrom(beanType);
    }

isTypeMatch这个方法就是进行各种Bean类型的判断,如是已经实例化的FactoryBean可能会调用它的getObjectType方法获取Bean的类型,或者从BeanDefinition中获取Bean的类型,并且如果是未实例化的FactoryBean,为了进行Bean类型的判断会导致FactoryBean的实例化。
下面我们来看看org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getTypeForFactoryBean这个方法的内容:

    protected Class<?> getTypeForFactoryBean(String beanName, RootBeanDefinition mbd) {
        //工程Bean和工厂方法名 根据工厂方法创建Bean
        String factoryBeanName = mbd.getFactoryBeanName();
        String factoryMethodName = mbd.getFactoryMethodName();
        
        if (factoryBeanName != null) {
            if (factoryMethodName != null) {
                // Try to obtain the FactoryBean's object type from its factory method declaration
                // without instantiating the containing bean at all.
                BeanDefinition fbDef = getBeanDefinition(factoryBeanName);
                if (fbDef instanceof AbstractBeanDefinition) {
                    AbstractBeanDefinition afbDef = (AbstractBeanDefinition) fbDef;
                    if (afbDef.hasBeanClass()) {
                        Class<?> result = getTypeForFactoryBeanFromMethod(afbDef.getBeanClass(), factoryMethodName);
                        if (result != null) {
                            return result;
                        }
                    }
                }
            }
            // If not resolvable above and the referenced factory bean doesn't exist yet,
            // exit here - we don't want to force the creation of another bean just to
            // obtain a FactoryBean's object type...
            if (!isBeanEligibleForMetadataCaching(factoryBeanName)) {
                return null;
            }
        }

        // Let's obtain a shortcut instance for an early getObjectType() call...
        //getSingletonFactoryBeanForTypeCheck和getNonSingletonFactoryBeanForTypeCheck这两个方法是创建FactoryBean的实例了
        FactoryBean<?> fb = (mbd.isSingleton() ?
                getSingletonFactoryBeanForTypeCheck(beanName, mbd) :
                getNonSingletonFactoryBeanForTypeCheck(beanName, mbd));

        if (fb != null) {
            // Try to obtain the FactoryBean's object type from this early stage of the instance.
            //调用getObjectType方法
            Class<?> result = getTypeForFactoryBean(fb);
            if (result != null) {
                return result;
            }
            else {
                // No type found for shortcut FactoryBean instance:
                // fall back to full creation of the FactoryBean instance.
                return super.getTypeForFactoryBean(beanName, mbd);
            }
        }

        if (factoryBeanName == null && mbd.hasBeanClass()) {
            // No early bean instantiation possible: determine FactoryBean's type from
            // static factory method signature or from class inheritance hierarchy...
            if (factoryMethodName != null) {
                return getTypeForFactoryBeanFromMethod(mbd.getBeanClass(), factoryMethodName);
            }
            else {
                return GenericTypeResolver.resolveTypeArgument(mbd.getBeanClass(), FactoryBean.class);
            }
        }

        return null;
    }

上面这个方法的内容是:如果是以工厂方法来创建Bean的话,则从工厂方法中返回bean类型,如果是FactoryBean类型的Bean的话,会实例化FactoryBean类型的Bean。实例化是在getSingletonFactoryBeanForTypeCheck或getNonSingletonFactoryBeanForTypeCheck方法中完成的。

    private FactoryBean<?> getSingletonFactoryBeanForTypeCheck(String beanName, RootBeanDefinition mbd) {
        synchronized (getSingletonMutex()) {
            //先从缓存中获取
            BeanWrapper bw = this.factoryBeanInstanceCache.get(beanName);
            if (bw != null) {
                return (FactoryBean<?>) bw.getWrappedInstance();
            }
            //这个bean是不是正在创建中
            if (isSingletonCurrentlyInCreation(beanName) ||
                    (mbd.getFactoryBeanName() != null && isSingletonCurrentlyInCreation(mbd.getFactoryBeanName()))) {
                return null;
            }

            Object instance = null;
            try {
                // Mark this bean as currently in creation, even if just partially.
                //判断是不是正在创建中的bean
                beforeSingletonCreation(beanName);
                // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
                //Bean实例化之前的一些处理 由Spring容器中InstantiationAwareBeanPostProcessor类型的实例来完成
                instance = resolveBeforeInstantiation(beanName, mbd);
                if (instance == null) {
                    //创建Bean的实例 看到这里就很明了了
                    bw = createBeanInstance(beanName, mbd, null);
                    instance = bw.getWrappedInstance();
                }
            }
            finally {
                // Finished partial creation of this bean.
                afterSingletonCreation(beanName);
            }

            FactoryBean<?> fb = getFactoryBean(beanName, instance);
            if (bw != null) {
                this.factoryBeanInstanceCache.put(beanName, bw);
            }
            return fb;
        }
    }

按照我们上面的分析,在doGetBeanNamesForType这个方法中,

private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) 

allowEagerInit是一个非常重要的参数,这个参数可以控制要不要提前实例化一些Bean。为什么这样说呢?因为要不要调用isTypeMatch这个方法在很大程度上是由这个条件控制的

if (!mbd.isAbstract() && (allowEagerInit ||((mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading())) &&!requiresEagerInitForType(mbd.getFactoryBeanName())))

通常我们的BeanDefinition都不是抽象类的,所以第一个条件为true,我们来看第二个条件,就是后面的那一大堆

(allowEagerInit ||((mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading())) &&!requiresEagerInitForType(mbd.getFactoryBeanName()))

这个条件分为两部分,是一个或的条件。如果allowEagerInit 为true的话,则整个条件为true,如果allowEagerInit 为false呢?mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()这三个条件基本上也是为true但是这个地方是一个与的条件,那么还要判断一下requiresEagerInitForType(mbd.getFactoryBeanName())这个方法是返回的true还是false。

    private boolean requiresEagerInitForType(String factoryBeanName) {
        //factoryBeanName不为null  isFactoryBean(factoryBeanName) 是否为FactoryBeanBean 
        //containsSingleton(factoryBeanName)) 如果这个Bean已经被实例化了 这个返回的值为true
        return (factoryBeanName != null && isFactoryBean(factoryBeanName) && !containsSingleton(factoryBeanName));
    }

因为我们在这篇文章中的分析是根据类型获取Bean会导致FactoryBean类型的Bean被提前实例化,所以factoryBeanName不为null,isFactoryBean(factoryBeanName) 为true,!containsSingleton(factoryBeanName)同样为true。综合requiresEagerInitForType这个方法返回true,那么

((mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading())) &&!requiresEagerInitForType(mbd.getFactoryBeanName())

会返回false,那这个这一大段的逻辑也就返回false了。
所以我们在根据类型获取Bean的时候要谨慎一点,如果整个Spring容器已经启动完成之后,根据类型获取Bean是没有问题的,如果是在Spring容器的启动过程中根据类型获取Bean可能会导致一些意想不到的结果。

相关文章
|
10天前
|
XML 安全 Java
|
1月前
|
缓存 Java Spring
实战指南:四种调整 Spring Bean 初始化顺序的方案
本文探讨了如何调整 Spring Boot 中 Bean 的初始化顺序,以满足业务需求。文章通过四种方案进行了详细分析: 1. **方案一 (@Order)**:通过 `@Order` 注解设置 Bean 的初始化顺序,但发现 `@PostConstruct` 会影响顺序。 2. **方案二 (SmartInitializingSingleton)**:在所有单例 Bean 初始化后执行额外的初始化工作,但无法精确控制特定 Bean 的顺序。 3. **方案三 (@DependsOn)**:通过 `@DependsOn` 注解指定 Bean 之间的依赖关系,成功实现顺序控制,但耦合性较高。
实战指南:四种调整 Spring Bean 初始化顺序的方案
|
8天前
|
安全 Java 开发者
Spring容器中的bean是线程安全的吗?
Spring容器中的bean默认为单例模式,多线程环境下若操作共享成员变量,易引发线程安全问题。Spring未对单例bean做线程安全处理,需开发者自行解决。通常,Spring bean(如Controller、Service、Dao)无状态变化,故多为线程安全。若涉及线程安全问题,可通过编码或设置bean作用域为prototype解决。
17 1
|
1月前
|
Java 开发者 Spring
Spring高手之路24——事务类型及传播行为实战指南
本篇文章深入探讨了Spring中的事务管理,特别是事务传播行为(如REQUIRES_NEW和NESTED)的应用与区别。通过详实的示例和优化的时序图,全面解析如何在实际项目中使用这些高级事务控制技巧,以提升开发者的Spring事务管理能力。
46 1
Spring高手之路24——事务类型及传播行为实战指南
|
2月前
|
XML Java 数据格式
Spring从入门到入土(bean的一些子标签及注解的使用)
本文详细介绍了Spring框架中Bean的创建和使用,包括使用XML配置文件中的标签和注解来创建和管理Bean,以及如何通过构造器、Setter方法和属性注入来配置Bean。
75 9
Spring从入门到入土(bean的一些子标签及注解的使用)
|
20天前
|
前端开发 Java Docker
使用Docker容器化部署Spring Boot应用程序
使用Docker容器化部署Spring Boot应用程序
|
22天前
|
Java Docker 微服务
利用Docker容器化部署Spring Boot应用
利用Docker容器化部署Spring Boot应用
44 0
|
2月前
|
Java 测试技术 Windows
咦!Spring容器里为什么没有我需要的Bean?
【10月更文挑战第11天】项目经理给小菜分配了一个紧急需求,小菜迅速搭建了一个SpringBoot项目并完成了开发。然而,启动测试时发现接口404,原因是控制器包不在默认扫描路径下。通过配置`@ComponentScan`的`basePackages`字段,解决了问题。总结:`@SpringBootApplication`默认只扫描当前包下的组件,需要扫描其他包时需配置`@ComponentScan`。
|
2月前
|
XML Java 数据格式
Spring IOC容器的深度解析及实战应用
【10月更文挑战第14天】在软件工程中,随着系统规模的扩大,对象间的依赖关系变得越来越复杂,这导致了系统的高耦合度,增加了开发和维护的难度。为解决这一问题,Michael Mattson在1996年提出了IOC(Inversion of Control,控制反转)理论,旨在降低对象间的耦合度,提高系统的灵活性和可维护性。Spring框架正是基于这一理论,通过IOC容器实现了对象间的依赖注入和生命周期管理。
76 0
|
2月前
|
安全 算法 Java
强大!基于Spring Boot 3.3 六种策略识别上传文件类型
【10月更文挑战第1天】在Web开发中,文件上传是一个常见的功能需求。然而,如何确保上传的文件类型符合预期,防止恶意文件入侵,是开发者必须面对的挑战。本文将围绕“基于Spring Boot 3.3 六种策略识别上传文件类型”这一主题,分享一些工作学习中的技术干货,帮助大家提升文件上传的安全性和效率。
61 0