BeanFactory和FactoryBean的区别
BeanFactory
- IOC最基础的容器
- 为其他容器提供了规范
- XmlBeanFactory、ApplicationContext等容器都是实现了BeanFactory,在此基础上添加了新的功能
FactoryBean
- 当IOC中的容器实现了FactoryBean接口后,通过getBean(beanName)获得的对象不是FactoryBean接口实现的对象,而是getObject()中返回的对象
- 要想获得Factory实现的对象,需要使用getBean("&beanName")
- 在IOC的基础上给Bean添加了简单工厂模式和装饰模式
SpringIOC的初始化过程
- 定位资源文件的位置
ClassPathResource res = new ClassPathResource(path)
- 解析、加载资源文件
DefaultListenableBeanFactory factory = new DefaultListenableBeanFactory()
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory)
read.loadBeanDefinitions(res)
读取、解析xml文件中的元素,并将每个元素包装成BeanDefinition类 - 注册到IOC容器
将BeanDefinition对象注册到一个IOC维护的HashMap结构的BeanDefinitionMap中,在注册时bean并没有完成依赖注入(set),直到第一次调用getBean的时候,才会完成bean的依赖注入过程
BeanFactory和ApplicationContext的区别
BeanFactory
- 负责读取Bean的配置文件,管理Bean的加载、实例化,维护Bean之间的依赖关系,负责Bean的生命周期
ApplicationContext
- 除了BeanFactory的功能外,还添加了更完善的框架功能
- 国际化访问
- 事件传递,通过ApplicationContextAware接口
Bean的生命周期,是如何被管理的
- Bean自身方法: 的和
- Bean级生命周期接口: BeanNameAware、BeanFactoryAware、InitializerBean和DisposableBean
- 容器级生命周期接口: InstantiationAwareBeanPostProcessor和BeanPostProcessor
- 工厂后处理器接口: AspectJWeavingEnabler和ConfigurationClassPostProcessor
初始化容器时
- Bean的构造函数
- 按顺序注入属性值
- 调用BeanNameAware接口
- 调用BeanFactoryAware接口
- 调用InitializerBean接口
- 调用接口
销毁容器时
- 调用DisposableBean接口
- 调用接口
Spring Bean的注入方式
设值注入
setter()
<bean><property name='' value=''></property></bean>
构造注入
constructor()
<bean><constructor-args name='' value=''/></bean>
实现AOP的方式
- Java动态代理: 针对接口的实例创建(类必须实现接口)
- CGLib生成代理: (类可以不实现接口)
- BeanNameAutoProxyCreator实现AOP
- 使用注解AspectJ实现AOP
Spring是如何管理事务的,事务管理机制
事务并发存在的问题
- 脏读: 一个事务读到另一个事务未提交的更新数据
- 不可重复读: 一个事务两次读同一条数据,可是两次读到的数据不一致
- 幻读: 一个事务执行两次,但第二次比第一次多出来一些数据
- 丢失更新: 撤销一个事务时,把其它事务已提交的更新数据覆盖了
JDBC定义的事务隔离级别
- TRANSACTION_NONE_JDBC: 驱动不支持事务
- TRANSACTION_READ_UNCONMITTED: 允许脏读、不可重复读、幻读 (隔离级别最低,并发度最高)
- TRANSACTRION_READ_COMMITTED:禁止脏读,允许不可重复读、幻读 (锁定正在读取的那行数据)
- TRANSACTION_REPEATABLE_READ: 禁止脏读、不可重复读,允许幻读 (锁定读取的所有行数据)
- TRANSACTION_SERIALIZABLE: 禁止脏读、不可重复读、幻读 (锁表)
Spring事务的隔离级别
- ISOLATION_DEFAULT: 使用数据库默认的事务隔离级别
- ISOLATION_READ_UNCOMMITTED: 允许另一个事务看到这个事务未提交的数据,允许脏读、不可重复读、幻读
- ISOLATION_READ_COMMITTED: 保证一个事务修改的数据提交后才能被另一个事务读取
- ISOLATION_REPEATABLE_READ: 防止脏读、不可重复读,允许幻读
- ISOLATION_SERIALIZABLE: 事务被处理成顺序执行,防止脏读、不可重复读、幻读,但性能开销大
Spring的事务传播机制
- PROPAGATION_REQUIRED: 支持当前的事务,如果没有事务,则创建一个新的事务
- PROPAGATION_SUPPORTS: 支持当前的事务,如果没有事务,则以无事务的方式执行
- PROPAGATION_MANDATORY: 支持当前事务,如果没有事务,则抛出异常
- PROPAGATION_REQUIRES_NEW: 新建事务,如果当前事务存在,则将事务挂起
- PROPAGATION_NOT_SUPPORTED: 以非事务的方式执行操作,如果当前存在事务,就把事务挂起
- PROPAGATION_NEVER: 以非事务的方式执行,如果当前存在事务,则抛出异常
- PROPAGATION_NESTED: 如果当前存在事务,则在嵌套事务内执行;如果没有执行,则创建一个新的事务
Spring中用到的设计模式
- 简单工厂
- 工厂方法
- 单例模式
- 适配器模式
- 包装器模式
- 代理模式
- 观察者模式
- 策略模式
- 模版方法模式
SpringMVC的工作原理
流程
- 用户发送请求至前端控制器DispatcherServlet
- DispatcherServlet接到请求后调用HandlerMapping处理器映射器
- 处理器映射到找到具体的处理器(根据xml配置、注解),生成处理器对象和处理器拦截器,一并返回给DispatherServlet
- DispatcherServlet调用HandlerAdapter处理器适配器
- HandlerAdapter经过适配调用具体的适配器(Controller)
- Controller执行完成返回ModelAndView
- HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet
- DispatcherServlet将ModelAndView传给ViewResolver视图解析器
- ViewResolver解析后返回具体的View
- DispatcherServlet根据View进行视图渲染
- DispatcherServlet相应用户
组件
- DispatcherServlet: 前端控制器,整个流程控制的中心,控制其他组件的执行,统一调度,降低组件之间的耦合
- HandlerMapping: 通过扩展处理器映射器实现不同的映射方式,如: 配置文件方式,实现接口方式,注解方式
- HandlerAdapter: 通过扩展处理器适配器,支持更多类型的适配器
- ViewResolver: 通过扩展视图解析器,支持更多类型的视图解析,如: tsp、freemarker、pdf、excel
Spring 循环注入的原理
用构造注入会抛错
- Spring容器将每一个正在创建的Bean标识符放到一个“当前正在创建Bean池”中,Bean标识符在创建过程中一直保存在这个池中;若创建Bean时发现自己已经在这个池中,会抛错BeanCurrentlyInCreationException,创建完成Bean后将标识符从池中移除
单例setter注入
- 只能解决单例情况下循环注入,因为对于setter注入造成的依赖是Spring容器提前暴露刚完成构造器注入但未完成其它步骤的Bean来完成的
非单例的setter注入
- Spring无法完成循环注入,因为prototype作用域是Bean,Spring容器不进行缓存,因此无法提前暴露一个创建中的Bean
SpringAOP的理解, 各个术语,怎么合作
- AspectJ(切面): 通常是一个类,里面可以定义切入点和通知
- JoinPoint(连接点): 程序执行过程中明确的点,一般是方法的调用
- Advice(通知): AOP在特定的切入点上执行的增强处理,有before、after、afterReturning、afterThrowing、around
- Pointcut(切入点): 带有通知的连接点,在程序中主要体现为书写切入点表达式
# Spring如何保证Controller的并发安全
- Spring单例模式下用ThreadLocal来切换不同线程之间的参数
- ThreadLocal为了保证线程安全,ThreadLocal的key就是当前线程的实例
- 虽然是一个实例在操作,但是不同线程下数据相互之间都是隔离的,运行时创建和销毁的Bean减少了