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注入常见的两类异常
- 只定义了接口,但没有实现,抛出NoSuchBeanDefinitionException异常
解决办法:@autowired(requierd = false)
- 定义了接口的多个实现类,只使用了@autowired实现注入,抛出NoUniqueBeanDefinitionException异常
解决办法:直接指明使用的时候哪个bean使用@autowired和@quailfier
Spring bean出现了循环依赖
错误提示:Is there an unresolvable circular reference
使用私有方式注入和使用构造方法注入区别:
解决办法:
- 通过私有方式注入解决循环依赖问题
- 通过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[]默认值为空数组, 当然,若只有一个异常类时,可以不使用数组
主动捕获异常不能进行回滚
非运行异常抛出(没有进行捕获)不能进行回滚
运行时异常(没有捕获)能够进行回滚
主动指定可以进行回滚的异常:
同一个类中一个不标注事务的方法去调用标志了事务的方法,事务会失效