赞
踩
2021最新Java面经整理 | 框架篇(一)Spring框架
目录
三、Spring的两大核心接口:BeanFactory和ApplicationContext
Spring是一个轻量级的IoC和AOP容器框架。是为Java应用程序提供基础性服务的一套框架,目的是用于简化企业应用程序的开发,它使得开发者只需要关心业务需求。
看结构图,这些组件被分别整合在核心容器(Core Container)、AOP(Aspect Oriented Programming)、设备支持(Instrument)、数据访问及集成(Data Access/Integratioin)、Web、报文发送(Messaging)、Test等模块。
Spring的优点:
Spring框架最核心的原理:IOC 和 AOP。IOC让相互协作的组件保持松散的耦合,而AOP编程允许把遍布于应用各层的功能分离出来形成可重用的功能组件。
(1)IOC就是控制反转,是指创建对象的控制权的转移,以前创建对象的主动权和时机是由自己把控的,而现在这种权力转移到Spring容器中,并由容器根据配置文件去创建实例和管理各个实例之间的依赖关系,对象与对象之间松散耦合,也利于功能的复用。DI依赖注入,和控制反转是同一个概念的不同角度的描述,即 应用程序在运行时依赖IOC容器来动态注入对象需要的外部资源。
(2)最直观的表达就是,IOC让对象的创建不用去new了,可以由Spring容器自动生产,使用java的反射机制,根据配置文件在运行时动态的去创建对象以及管理对象,并调用对象的方法的。
(3)Spring的IOC常用的有三种注入方式 :构造器注入、setter方法注入、基于注解注入(@Autowired)。
(1)AOP的理解
OOP面向对象,允许开发者定义纵向的关系,但并适用于定义横向的关系,导致了大量代码的重复,而不利于各个模块的重用。
AOP,面向切面编程,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理。
(2)静态代理(AspectJ)和动态代理(Spring AOP)
AOP实现的关键在于代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。
静态代理与动态代理区别:在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。
(3)Spring AOP动态代理的两种方式(JDK和CGLIB)
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。
(4)AOP的核心概念
切入点(pointcut)和连接点(join point)匹配的概念是AOP的关键,这使得AOP不同于其它仅仅提供拦截功能的旧技术。 切入点使得定位通知(advice)可独立于OO层次。 例如,一个提供声明式事务管理的around通知可以被应用到一组横跨多个对象中的方法上(例如服务层的所有业务操作)。
BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。其中ApplicationContext是BeanFactory的子接口。
1、ApplicationContext 是 BeanFactory 的子接口,功能更全
BeanFactory是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。ApplicationContext是BeanFactory的派生接口,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:
2、加载方式
3、创建方式
4、注册方式
BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。
Bean是Spring容器中最重要的元素,Spring容器本身是一个大工厂,主要的功能就是管理好Bean,为此引入了IOC和AOP机制。
在Spring容器中,Bean的作用域用于确定将什么类型的Bean实例返回给调用者。
Spring通过DI(依赖注入)实现IOC(控制反转),主要有两种注入方式,
(1)基于注解 @Autowired 的自动装配(最常用)
@Autowired(required=true) // 主要有三个属性值:constructor,byName,byType
在介绍注解注入的方式前,先简单了解bean的一个属性autowire,autowire主要有三个属性值:constructor,byName,byType。
@Autowired 注解,默认是以byType的方式去匹配类型相同的bean,如果只匹配到一个,那么就直接注入该bean,无论要注入的 bean 的 name 是什么;如果匹配到多个,就会调用 DefaultListableBeanFactory 的 determineAutowireCandidate 方法来决定具体注入哪个bean。
determineAutowireCandidate 的匹配规则是:
可以简单的理解为先以 ByType 的方式去匹配,如果匹配到了多个再以 ByName 的方式去匹配,找到了对应的 bean 就去注入,没找到就抛出异常。
(2)基于构造方法注入
在spring的配置文件中注册UserService,将UserDaoJdbc通过constructor-arg标签注入到UserService的某个有参数的构造方法中。如果只有一个有参数的构造方法并且参数类型与注入的bean的类型匹配,那就会注入到该构造方法中。
<!-- 注册userService -->
<bean id="userService" class="com.spring.service.impl.UserService">
<constructor-arg ref="userDaoJdbc"></constructor-arg> <!-- 构造方法注入 -->
</bean>
<!-- 注册jdbc实现的dao -->
<bean id="userDaoJdbc" class="com.spring.dao.impl.UserDaoJdbc"></bean>
(3)基于Setter方法注入
Setter方法注入,即通过setXxx()方法注入Bean的属性值或依赖对象,具有可选择性和灵活性高的优点,是实际应用中最常采用的注入方式。Setter方法注入要求Bean提供一个默认的构造函数,并为需要注入的属性提供对应的Setter方法。Spring先调用Bean的默认构造函数实例化Bean对象,然后通过反射的方式调用Setter方法注入属性值。
<!-- 注册userService -->
<bean id="userService" class="com.spring.service.impl.UserService">
<property name="UserDao" ref="userDaoMyBatis"></property> <!-- Setter属性注入-->
</bean>
<!-- 注册mybatis实现的dao -->
<bean id="userDaoMyBatis" class="com.spring.dao.impl.UserDaoMyBatis"></bean>
在spring中,对象无需自己查找或创建与其关联的其他对象,由容器负责把需要相互协作的对象引用赋予各个对象,使用autowire来配置自动装载模式。在Spring框架xml配置中共有5种自动装配:
@Autowired 注解,默认是以byType的方式去匹配类型相同的bean。@Autowired可用于:构造函数、成员变量、Setter方法。@Autowired和@Resource之间的区别
先看下 Servlet 的生命周期:实例化,初始init,接收请求service,销毁destroy。Spring 容器中 Bean 的生命周期有点类似,具体如下。
Spring Bean 的生命周期分为四个阶段和多个扩展点。扩展点又可以分为影响多个Bean和影响单个Bean。实例化和属性赋值对应构造方法和setter方法的注入,初始化和销毁是用户能自定义扩展的两个阶段。
(1)四个阶段(实例化 -> 属性赋值 -> 初始化 -> 销毁)
(2)多个扩展点
(3)生命周期
单例Bean是Spring容器默认的方式,所有线程都共享一个单例实例Bean,确实会存在并发的问题。对于原型Bean,每次创建一个新对象,也就是线程之间并不存在Bean共享,自然是不会有线程安全的问题。
单例Bean分两种:无状态的单例Bean(线程安全)和有状态的单例Bean(线程不安全)。
在Spring容器中,Bean默认的都是单例作用域,无状态的Bean在多线程环境下共享是没有问题的。有状态Bean的线程安全问题推荐使用ThreadLocal进行处理。主要的解决办法有,
ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了“时间换空间”的方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要排队。而ThreadLocal采用了“空间换时间”的方式。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。
Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。真正的数据库层的事务提交和回滚是通过binlog或者redo log实现的。
Spring支持编程式事务和声明式事务两种方式,而声明式事务是我们在项目中最常用的事务方式。
声明式事务最大的优点就是不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明或通过@Transactional注解的方式,便可以将事务规则应用到业务逻辑中。声明式事务管理要优于编程式事务管理,这正是Spring倡导的非侵入式的开发方式,使业务代码不受污染,只要加上注解就可以获得完全的事务支持。唯一不足地方是,最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。
隔离级别 | 说明 |
ISOLATION_DEFAULT | PlatfromTransactionManager 默认的隔离级别,使用数据库默认的事务隔离级别。 |
ISOLATION_READ_UNCOMMITTED | 读未提交。这是事务最低的隔离级别,它允许令外一个事务可以看到这个事务未提交的数据,这种隔离级别会产生脏读,不可重复读和幻像读。 |
ISOLATION_READ_COMMITTED | 读已提交。保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。 |
ISOLATION_REPEATABLE_READ | 可重复读。这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。 |
ISOLATION_SERIALIZABLE | 序列化。这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。 |
Spring事务的传播行为说的是,当多个事务同时存在的时候,Spring如何处理这些事务的行为。
事务行为 | 说明 |
PROPAGATION_REQUIRED | 支持当前事务,假设当前没有事务。就新建一个事务 |
PROPAGATION_SUPPORTS | 支持当前事务,假设当前没有事务,就以非事务方式运行 |
PROPAGATION_MANDATORY | 支持当前事务,假设当前没有事务,就抛出异常 |
PROPAGATION_REQUIRES_NEW | 新建事务,假设当前存在事务。把当前事务挂起 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式运行操作。假设当前存在事务,就把当前事务挂起 |
PROPAGATION_NEVER | 以非事务方式运行,假设当前存在事务,则抛出异常 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 |
1、工厂模式
Spring使用工厂模式可以通过 BeanFactory 或 ApplicationContext 创建 bean 对象。两者的区别,
ApplicationContext的三个实现类:
2、单例模式
Spring 中 Bean 的默认作用域就是 singleton(单例)的。使用单例模式的好处,
3、代理模式
Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术。
4、模板方法
用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate等。
5、观察者模式
定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现:ApplicationListener。
Spring 提供了以下5种标准的事件,
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。