文章目录
- 一、spring总结
- 1、Spring三级缓存解决什么问题?
- 2、Spring支持的几种bean的作用域?
- 3、pring bean是否是线程安全?
- 4、*Spring框架中bean的生命周期*?
- 5、哪些是重要的bean生命周期方法?你能重载它们吗?
- 6、什么是bean装配?
- 7、解释不同方式的自动装配?
- 8、Spring如何实现AOP的?
- 9、Spring事务支持?
- 10、spring 8种事务失效场景?
- 11、BeanFactory和FactoryBean与ApplicationContext的区别?
一、spring总结
1、Spring三级缓存解决什么问题?
Map<String, Object> singletonObjects: 一级缓存,也就是我们平常理解的单例池,存放已经完整经历了完整生命周期的bean对象。
Map<String, Object> earlySingletonObjects: 二级缓存,存储早期暴露出来的bean对象,bean的生命周期未结束。(属性还未填充完)
Map<String,ObjectFactory<?> > singletonFactories: 三级缓存,存储生成bean的工厂
二级缓存可以解决没有aop的循环依赖。核心思路是
使用二级缓存来提前曝光创建过程中的对象
。使用二级缓存来解决aop循环依赖的问题是:bean对象属性填充的应该是代理对象
,而不是源对象。三级缓存解决aop循环依赖核心思路:
打破bean的生命周期,属性赋值时aop提前创建代理对象
。
2、Spring支持的几种bean的作用域?
singleton: bean在每个Springioc容器中只有一个实例。
prototype:一个bean的定义可以有多个实例。
request:每次http请求都会创建一个bean,该作用域仅在基于web的SpringApplicationContext情形下有效。
session:在一个HTTPSession中,一个bean定义对应一个实例。该作用域仅在基于web的SpringApplicationContext情形下有效。
global-session:在一个全局的HTTPSession中,一个bean定义对应一个实例。该作用域仅在基于web的SpringApplicationContext情形下有效。
缺省的Springbean的作用域是Singleton.
3、pring bean是否是线程安全?
Spring框架中默认是单例bean,是无状态的,不是线程安全的
*线程安全部分处理:
RequestContextHolder:用户上下文
SynchronizationManager:事务同步器实现
LocaleContextHolder:上下文
//spring通过ThreadLocal包裹对象保证多线程安全
private static final ThreadLocal localeContextHolder = new NamedThreadLocal<>(“LocaleContext”);
4、Spring框架中bean的生命周期?
Spring容器从XML文件中读取bean的定义,并实例化bean。
Spring根据bean的定义填充所有的属性。
如果bean实现了BeanNameAware接口,Spring传递bean的ID到setBeanName方法。
如果Bean实现了BeanFactoryAware接口,Spring传递beanfactory给setBeanFactory方法。
如果有任何与bean相关联的BeanPostProcessors,Spring会在postProcesserBeforeInitialization()方法内调用它们。
如果bean实现IntializingBean了,调用它的afterPropertySet方法,如果bean声明了初始化方法,调用此初始化方法。
如果有BeanPostProcessors和bean关联,这些bean的postProcessAfterInitialization()方法将被调用。
如果bean实现了DisposableBean,它将调用destroy()方法。
5、哪些是重要的bean生命周期方法?你能重载它们吗?
有两个重要的bean生命周期方法,第一个是setup,它是在容器加载bean的时候被调用。第二个方法是teardown它是在容器卸载类的时候被调用。
bean标签有两个重要的属性(init-method和destroy-method)。用它们你可以自己定制初始化和注销方法。它们也有相应的注解(@PostConstruct和@PreDestroy)。
6、什么是bean装配?
装配,或bean装配是指在Spring容器中把bean组装到一起,前提是容器需要知道bean的依赖关系,如何通过依赖注入来把它们装配到一起。
自动装配: Spring容器能够自动装配相互合作的bean,这意味着容器不需要和配置,能通过Bean工厂自动处理bean之间的协作。
7、解释不同方式的自动装配?
有五种自动装配的方式,可以用来指导Spring容器用自动装配方式来进行依赖注入。
no:默认的方式是不进行自动装配,通过显式设置ref属性来进行装配。
byName:通过参数名自动装配,Spring容器在配置文件中发现bean的autowire属性被设置成byname,之后容器试图匹配、装配和该bean的属性具有相同名字的bean。
byType:通过参数类型自动装配,Spring容器在配置文件中发现bean的autowire属性被设置成byType,之后容器试图匹配、装配和该bean的属性具有相同类型的bean。如果有多个bean符合条件,则抛出错误。
constructor:这个方式类似于****byType,但是要提供给构造器参数,如果没有确定的带参数的构造器参数类型,将会抛出异常。
autodetect:首先尝试使用constructor来自动装配,如果无法工作,则使用byType方式。
自动装配有哪些局限性:
重写:你仍需用和配置来定义依赖,意味着总要重写自动装配。
基本数据类型:你不能自动装配简单的属性,如基本数据类型,String字符串,和类。
模糊特性:自动装配不如显式装配精确,如果有可能,建议使用显式装配。
8、Spring如何实现AOP的?
spring aop基于 AspectJ动态代理 实现切面编程,实现下面5类通知方法
before:前置通知,在一个方法执行前被调用。
after: 在方法执行之后调用的通知,无论方法执行是否成功。
after-returning: 仅当方法成功完成后执行的通知。
after-throwing: 在方法抛出异常退出时执行的通知。
around: 在方法执行之前和之后调用的通知。
java动态代理技术有:AspectJ、JDK_Proxy、CGLIB_Proxy
AspectJ: spring默认基于AspectJ实现,AspectJ动态代理也是最好用的
JDK_Proxy、CGLIB_Proxy: 没有AspectJ好用
9、Spring事务支持?
事务代码隔离 & 事务传播级别 &事务本质
事务代码隔离:
spring默认是基于单例bean,线程不安全,其实现事务隔离SynchronizationManager接口及实现都基于Threadlocal修饰,提供线程安全隔离能力。
事务传播级别:
1、PROPAGATION_REQUIRED ,默认的spring事务传播级别,使用该级别的特点是,如果上下文中已经存在事务,那么就加入到事务中执行,如果当前上下文中不存在事务,则新建事务执行。所以这个级别通常能满足处理大多数的业务场景。
2、PROPAGATION_SUPPORTS ,从字面意思就知道,supports,支持,该传播级别的特点是,如果上下文存在事务,则支持事务加入事务,如果没有事务,则使用非事务的方式执行。所以说,并非所有的包在transactionTemplate.execute中的代码都会有事务支持。这个通常是用来处理那些并非原子性的非核心业务逻辑操作。应用场景较少。
3、PROPAGATION_MANDATORY , 该级别的事务要求上下文中必须要存在事务,否则就会抛出异常!配置该方式的传播级别是有效的控制上下文调用代码遗漏添加事务控制的保证手段。比如一段代码不能单独被调用执行,但是一旦被调用,就必须有事务包含的情况,就可以使用这个传播级别。
4、PROPAGATION_REQUIRES_NEW ,从字面即可知道,new,每次都要一个新事务,该传播级别的特点是,每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。
5、PROPAGATION_NOT_SUPPORTED ,这个也可以从字面得知,not supported ,不支持,当前级别的特点就是上下文中存在事务,则挂起事务,执行当前逻辑,结束后恢复上下文的事务。
6、PROPAGATION_NEVER ,该事务更严格,上面一个事务传播级别只是不支持而已,有事务就挂起,而PROPAGATION_NEVER传播级别要求上下文中不能存在事务,一旦有事务,就抛出runtime异常,强制停止执行!这个级别上辈子跟事务有仇。
7、PROPAGATION_NESTED ,字面也可知道,nested,嵌套级别事务。该传播级别特征是,如果上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务
10、spring 8种事务失效场景?
1、数据库引擎不支持事务
从 MySQL 5.5.5 开始的默认存储引擎是:InnoDB,之前默认的都是:MyISAM,所以这点要值得注意,底层引擎不支持事务再怎么搞都是白搭。2、没有被 Spring 管理
@Service public class OrderServiceImpl implements OrderService { @Transactional public void updateOrder(Order order) { // update order }}
如果此时把 @Service 注解注释掉,这个类就不会被加载成一个 Bean,那这个类就不会被 Spring 管理了,事务自然就失效了
3、方法不是 public 的
@Transactional 只能用于 public 的方法上,否则事务会失效,如果要用在非 public 方法上,可以开启 AspectJ 代理模式。4、自身调用问题
@Service public class OrderServiceImpl implements OrderService { public void update(Order order) { updateOrder(order); } @Transactional public void updateOrder(Order order) { // update order }}
update方法上面没有加 @Transactional 注解,调用有 @Transactional 注解的 updateOrder 方法,updateOrder 方法上的事务管用吗? 答案:管用先调updateOrder,在调update,事务会加入。反之就不管用。
再来看下面这个例子:
@Service public class OrderServiceImpl implements OrderService { @Transactional public void update(Order order) { updateOrder(order); } @Transactional(propagation = Propagation.REQUIRES_NEW) public void updateOrder(Order order) { // update order }}
这次在 update 方法上加了 @Transactional,updateOrder 加了 REQUIRES_NEW 新开启一个事务,那么新开的事务管用么? 答案:不管用
5、数据源没有配置事务管理器
@Bean public PlatformTransactionManager transactionManager(DataSource dataSource) { return new DataSourceTransactionManager(dataSource); }
如上面所示,当前数据源若没有配置事务管理器,那也是白搭!
6、不支持事务
@Service public class OrderServiceImpl implements OrderService { @Transactional public void update(Order order) { updateOrder(order); } @Transactional(propagation = Propagation.NOT_SUPPORTED) public void updateOrder(Order order) { // update order }}
Propagation.NOT_SUPPORTED: 表示不以事务运行,当前若存在事务则挂起,详细的可以参考《事务隔离级别和传播机制》这篇文章。
都主动不支持以事务方式运行了,那事务生效也是白搭!
7、异常被吃了
// @Service public class OrderServiceImpl implements OrderService { @Transactional public void updateOrder(Order order) { try { // update order } catch { } }}
8、异常类型错误
// @Service
public class OrderServiceImpl implements OrderService {
@Transactional
public void updateOrder(Order order) {
try {
// update order
} catch {
throw new Exception(“更新错误”);
}
}}
这样事务也是不生效的,因为默认回滚的是:RuntimeException,如果你想触发其他异常的回滚,需要在注解上配置一下,如:
@Transactional(rollbackFor = Exception.class)
这个配置仅限于 Throwable 异常类及其子类。
1、spring的事务注解@Transactional只能放在public修饰的方法上才起作用,如果放在其他非public(private,protected)方法上,虽然不报错,但是事务不起作用
2、如果采用spring+spring mvc,则context:component-scan重复扫描问题可能会引起事务失败。
如果spring和mvc的配置文件中都扫描了service层,那么事务就会失效。
原因:因为按照spring配置文件的加载顺序来讲,先加载springmvc配置文件,再加载spring配置文件,我们的事物一般都在srping配置文件中进行配置,如果此时在加载srpingMVC配置文件的时候,把servlce也给注册了,但是此时事物还没加载,也就导致后面的事物无法成功注入到service中。所以把对service的扫描放在spring配置文件中或是其他配置文件中。3、如使用mysql且引擎是MyISAM,则事务会不起作用,原因是MyISAM不支持事务,可以改成InnoDB引擎
4、 @Transactional 注解开启配置,必须放到listener里加载,如果放到DispatcherServlet的配置里,事务也是不起作用的。
如果spring和mvc的配置文件中都扫描了service层,那么事务就会失效。
原因:因为按照spring配置文件的加载顺序来讲,先加载springmvc配置文件,再加载spring配置文件,我们的事物一般都在srping配置文件中进行配置,如果此时在加载srpingMVC配置文件的时候,把servlce也给注册了,但是此时事物还没加载,也就导致后面的事物无法成功注入到service中。所以把对service的扫描放在spring配置文件中或是其他配置文件中。5、Spring团队建议在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。在接口上使用 @Transactional 注解,只能当你设置了基于接口的代理时它才生效。因为注解是 不能继承 的,这就意味着如果正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装。
6、在业务代码中如果抛出RuntimeException异常,事务回滚;但是抛出Exception,事务不回滚;
7、如果在加有事务的方法内,使用了try…catch…语句块对异常进行了捕获,而catch语句块没有throw new RuntimeExecption异常,事务也不会回滚。
8、在类A里面有方法a 和方法b, 然后方法b上面用 @Transactional加了方法级别的事务,在方法a里面 调用了方法b, 方法b里面的事务不会生效。原因是在同一个类之中,方法互相调用,切面无效 ,而不仅仅是事务。这里事务之所以无效,是因为spring的事务是通过aop实现的。
11、BeanFactory和FactoryBean与ApplicationContext的区别?
Spring提供了两种不同的IOC容器,一个是BeanFactory,另一个是ApplicationContext,他们都是Java Interface,ApplicationContext继承于BeanFactory(ApplicationContext继承ListableBeanFactory,而ListableBeanFactory继承BeanFactory)。
1、相同点
①他们都可以用来配置XML属性,也支持属性的自动注入。②BeanFactory和ApplicationContext都提供一种方式:使用getBean(“bean name”)获取容器中的Bean。
2、不同点
①当调用getBean()方法时,BeanFactory实例化bean,而ApplicationContext在容器启动的时候就已经实例化单例的bean了,不会在调用getBean()方法时再实例化。
②BeanFactory不支持国际化,即i18n,但ApplicationContext提供了对它的支持。
③BeanFactory与ApplicationContext的另一个区别是能够将事件发布到注册为监听器的bean。
④BeanFactory的一个核心实现是XMLBeanFactory,而ApplicationContext的一个核心实现是ClassPathXmlApplicationContext,Web容器的环境我们使用WebApplicationContext并且增加了getServeltContext方法。
⑤如果使用自动注入并使用BeanFactory,则需要API注册AutoWiredBeanPostProcessor,如果使用ApplicationContext,则可以使用XML进行配置。
二、FactoryBean使用规则
FactoryBean 用来生成某一个类型Bean实例,它最大的一个作用是:可以让我们自定义Bean的创建过程。比如说你有一些同属于某一类型的Bean对象需要被创建,但是它们自己有各自的特点,你只需要把他们的特点注入FactoryBean中,就可以生产出属于该类型的各种实例。
//定义类
@Data
public class Car {
private String carBrand; //汽车品牌
private String carPrice; //汽车价格
}
//具体创建对象过程
@Component
public class MyFactoryBean01 implements FactoryBean<Car> {
/**
* 当通过getBean获取一个FactoryBean时,返回的并不是FactoryBean本身,而是其创建的对象;
* 如果要获取FactoryBean对象本身,请在参数前面加一个&符号来获取。
*/
@Override
public Car getObject() throws Exception {
Car car = new Car();
car.setCarBrand("奔驰");
car.setCarPrice("999999");
return car;
}
/**
*获取对象的类型
*/
@Override
public Class<Car> getObjectType() {
return Car.class;
}
/**
*该Bean对象是否是单例模式
*/
@Override
public boolean isSingleton() {
return false;
}
}
//获取对象打印
@SpringBootTest
public class MyFactoryBean01Test {
@Autowired
private ApplicationContext applicationContext;
@Test
public void test01() throws Exception {
//调用getBean()方法获取Bean对象:
Object object = applicationContext.getBean("myFactoryBean01");
}
}
//输出
@Test
public void test01() throws Exception {
Object object = applicationContext.getBean("myFactoryBean01");
System.out.println("该对象的类型 = " + object.toString());
}
三、总结
简单来说,BeanFactory提供基本的IOC和DI功能,而ApplicationContext提供高级功能,BeanFactory可用于测试和非生产使用,而ApplicationContext是功能更丰富的容器实现,应该更优于BeanFactory。
ean(“myFactoryBean01”);
}
}
//输出
@Test
public void test01() throws Exception {
Object object = applicationContext.getBean(“myFactoryBean01”);
System.out.println("该对象的类型 = " + object.toString());
}
[外链图片转存中...(img-DWjFyzg1-1685086737370)]
**三、总结**
简单来说,BeanFactory提供基本的IOC和DI功能,而ApplicationContext提供高级功能,BeanFactory可用于测试和非生产使用,而ApplicationContext是功能更丰富的容器实现,应该更优于BeanFactory。