Spring常见的坑

Spring Bean的默认名称生成策略导致的空指针

源码分析:
public static String decapitalize(String name) {
    if (name == null || name.length() == 0) {
        return name;
    }

//如果类名长度大于1,且第一个和第二个都是大写,会自动返回原类名(CUstomer ==>CUstomer)
    if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
                    Character.isUpperCase(name.charAt(0))){
        return name;
    }
    char chars[] = name.toCharArray();
    chars[0] = Character.toLowerCase(chars[0]);
    return new String(chars);
}

解决办法:直接指定名称 @component(“cUstomer”)

使用了@autowired,仍然出现了空指针

错误一:忘记了将当前类标记为spring bean

错误二:在改正1的基础上使用new去获取对象

错误三:spring默认的扫描机制是 启动类所在的包及其子包,把bean定义在外面

解决办法:使用@componentScan(value={“v1”,”v2”,”v3”})---定位到包名

 直接指定值            只去扫描某些bean                    不去扫描某些bean         使用时才会被初始化

不使用自动注入,获取上下位

一:对spring容器和应用上下文的理解:

Spring容器的核心是负责管理对象,并不只是创建对象,他负责了对象的整个生命周期-创建,装配,销毁;应用上下文可以认为是spring容器的一种实现,也就是操作容器的容器类对象有两个功能(1)把需要管理的对象放入容器中(2)取得容器中的bean.

获取应用上下文的四种方式

第一种:

自定义一个上述接口的实现类

在启动类中注册自定义的实现类

第二种:

  更新                                               启动                                    停止                       关闭

实现:

第三种:

 Springboot启动程序的返回

第四种:

实现:

自定义ApplicationUtils工具类:

public class ApplicationUtils implements ApplicationContextAware {
    private static ApplicationContext application = null;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if (application == null) {
            this.application = applicationContext;
        }
    }
    public static ApplicationContext getApplicationContext() {
        return application;
    }
    /**
     * 通过名字获取bean
     */
    public static Object getBean(String name) {
        return getApplicationContext().getBean(name);
    }
    /**
     * 通过class获取bean
     */
    public static <T> T getBean(Class<T> clazz) {
        return getApplicationContext().getBean(clazz);
    }
    /**
     * 通过class获取bean
     */
    public static <T> T getBean(String name,Class<T> clazz) {
        return getApplicationContext().getBean(name,clazz);

多线程下Bean的数据不符合预期

Spring默认会生成单例的bean

有状态的bean和无状态的bean是否定义了全局变量去存储信息

多线程下操作的是同一个bean

为了解决上述问题:

类似于threadlocal在每个线程中都有一个专属线程与实例的映射

在类上面@scope(BeanDefinition.Scope_PROTOTYPE)设为原型模式(默认情况下是使用单例的bean,在多线程下是不安全的)

单例bean的优势:
减少新生成实例的消耗,减少jvm的垃圾回收,可以快速获取到bean

单例bean的劣势:
线程不安全

Spring设计默认单例bean的理由:

少创建实例,垃圾回收便捷,使用缓存快速获取

存在多个可用bean异常

与spring bean相关的注解:
@autowired:属于spring框架,默认使用类型(ByType进行注入)存在多个相同类型bean,@autowired则会使用byname注入

@qualifer:结合@autowired自动注入策略由bytype变为byname

@resource:javaEE自带的注解,默认是byname自动注入,找不到按bytype注入

@primary存在多个相同类型的bean,用于定义首选项

关于bean注入常见的两类异常

  1. 只定义了接口,但没有实现,抛出NoSuchBeanDefinitionException异常

解决办法:@autowired(requierd = false)

  1. 定义了接口的多个实现类,只使用了@autowired实现注入,抛出NoUniqueBeanDefinitionException异常

解决办法:直接指明使用的时候哪个bean使用@autowired和@quailfier

Spring bean出现了循环依赖

错误提示:Is there an unresolvable circular reference

使用私有方式注入和使用构造方法注入区别:
解决办法:

  1. 通过私有方式注入解决循环依赖问题
  2. 通过set方法注入

解决循环依赖的原因(spring只能解决单例模式下的循环依赖):

New出来分配内存空间 填充bean中定义的field  标注了postconstruct注解 做一些自定义初始化操作

循环依赖的时机出现在第一步和第二步,第一步new一个对象,是去调用他的构造函数.构造函数调用失败,spring就不会再继续执行,在使用构造函数进行注入时spring不能帮助我们解决循环依赖的问题,因为bean都没有实例化成功.

解决依赖的关键在第二步

A创建过程中需要使用B,A就将自己放在三级缓存中,先去实例化B,B实例化过程中需要A,B先去检查一级缓存,没有再去检查二级缓存,还没有再去检查三级缓存找到了A,就将三级缓存中的A删除,放到二级缓存中,此时B顺利的完成了初始化.将自己放到了一级缓存中B里面的A仍然是一个创建中的状态,实例化完成,但是没有填充属性,拿到B,再去完成A的创建,并将A放到一级缓存中

建议:(1)尽量使用构造函数注入,避免依赖;(2)需要使用依赖时尽量使用私有方法注入或者set方法注入

BeanfactorypostProcessor和BeanpostProcessor接口

BeanpostProcessor执行时机是spring bean被实例化后

PostProcessorBeforeInitialization:在bea初始化之前执行(在执行bean的构造方法之后,InitializingBean接口的afterPropertiesSet之前执行)

PostProcessorAfterInitialization:InitializingBean接口的afterPropertiesSet之后执行

场景1:引入了一个第三方类,它是单例的,把它变为原型模式(BeanfactorypostProcessor)可以在实例化之前执行

public class ThirdPartyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        /**
         * 可以通过beanFactory拿到bean的定义,
         * 使用注解夜市通过bean的定义@Scope(BeanDefinition.SCOPE_PROTOTYPE)
         */

//代替了注解的方式
      BeanDefinition thirdPartyClassdefinition = beanFactory.getBeanDefinition("thirdPartyClass");
        thirdPartyClassdefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);

@transactional注解没有生效

关于spring事务:
事务管理是业务系统中开发不可或缺的一部分,spring提供了两种事务管理机制:   编程式事务:在代码中手动的管理代码的提交与回滚等等操作,代码的侵入性比较强,基本上是样板式的代码,非常的难以维护和修改(废弃)

    声明式事务 :

@transactional注解可以标识在三处

接口  ;类    ;    类方法

标注在类上,类中的public方法才会有效(数据库要支持回滚)

@transaction有很多属性,都非常重要

rollbackFor:指定能够触发事务回滚的异常类型(指定需要回滚的异常类), 类型为class[]默认值为空数组,当然,若只有一个异常类时,可以不使用数 组

norollbackFor:指定不需要回滚的异常类,类型为class[]默认值为空数组, 当然,若只有一个异常类时,可以不使用数组

主动捕获异常不能进行回滚

非运行异常抛出(没有进行捕获)不能进行回滚

运行时异常(没有捕获)能够进行回滚

主动指定可以进行回滚的异常:

同一个类中一个不标注事务的方法去调用标志了事务的方法,事务会失效

uploading.4e448015.gif转存失败重新上传取消uploading.4e448015.gif转存失败重新上传取消

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值