Spring
1.谈谈你对Spring的理解 难度系数
Spring 是一个开源框架,为简化企业级应用开发而生。Spring 可以是使简单的JavaBean 实现以前只有EJB 才能实现的功能。Spring 是一个 IOC 和 AOP 容器框架。
Spring 容器的主要核心是:
控制反转(IOC),传统的 java 开发模式中,当需要一个对象时,我们会自己使用 new 或者 getInstance 等直接或者间接调用构造方法创建一个对象。而在 spring 开发模式中,spring 容器使用了工厂模式为我们创建了所需要的对象,不需要我们自己创建了,直接调用spring 提供的对象就可以了,这是控制反转的思想。
依赖注入(DI),spring 使用 javaBean 对象的 set 方法或者带参数的构造方法为我们在创建所需对象时将其属性自动设置所需要的值的过程,就是依赖注入的思想。
面向切面编程(AOP),在面向对象编程(oop)思想中,我们将事物纵向抽成一个个的对象。而在面向切面编程中,我们将一个个的对象某些类似的方面横向抽成一个切面,对这个切面进行一些如权限控制、事物管理,记录日志等公用操作处理的过程就是面向切面编程的思想。AOP 底层是动态代理,如果是接口采用 JDK 动态代理,如果是类采用CGLIB 方式实现动态代理。
2.Spring中常见的设计模式
-
工厂模式:Spring使用工厂模式,通过BeanFactory和ApplicationContext来创建对象
-
单例模式:Bean默认为单例模式
-
适配器模式:Spring AOP的增强或通知(Advice)使用到了适配器模式,Spring MVC中也是用到了适配器模式适配Controller
-
模板方法:可以将相同部分的代码放在父类中,而将不同的代码放入不同的子类中,用来解决代码重复的问题。比如RestTemplate, JmsTemplate, JpaTemplate
-
策略模式:例如Resource的实现类,针对不同的资源文件,实现了不同方式的资源获取策略
-
代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术
-
桥接模式:可以根据客户的需求能够动态切换不同的数据源。比如我们的项目需要连接多个数据库,客户在每次访问中根据需要会去访问不同的数据库
3.Spring的三级缓存
三级缓存就是三个Map对象
/**
单例对象的缓存:bean名称—bean实例,即:所谓的单例池。
表示已经经历了完整生命周期的Bean对象
第一级缓存
*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/**
早期的单例对象的高速缓存: bean名称—bean实例。
表示 Bean的生命周期还没走完(Bean的属性还未填充)就把这个 Bean存入该缓存中也就是实例化但未初始化的 bean放入该缓存里
第二级缓存
*/
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/**
单例工厂的高速缓存:bean名称—ObjectFactory
表示存放生成 bean的工厂
第三级缓存
*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
4.Bean的生命周期
-
实例化 Instantiation
- Bean实例化的时机也分为两种,BeanFactory管理的Bean是在使用到Bean的时候才会实例化Bean,ApplicantContext管理的Bean在容器初始化的时候就回完成Bean实例化。
-
属性赋值 Populate
-
初始化 Initialization
-
检查Aware的相关接口依赖,作用是:让Bean拿到容器的一些资源,如BeanNameAware 和 BeanName
-
后处理器-->进行一些前置和后置的处理
-
生命周期接口->InitializingBean和DisposableBean 接口就是用来定义初始化方法和销毁方法的
-
配置生命周期方法
-
-
销毁 Destruction
-
Bean容器在配置文件中找到Person Bean的定义,这个可以说是妈妈怀上了。
-
Bean容器使用Java 反射API创建Bean的实例,孩子出生了。
-
Person声明了属性no、name,它们会被设置,相当于注册身份证号和姓名。如果属性本身是Bean,则将对其进行解析和设置。
-
Person类实现了BeanNameAware接口,通过传递Bean的名称来调用setBeanName()方法,相当于起个学名。
-
Person类实现了BeanFactoryAware接口,通过传递BeanFactory对象的实例来调用setBeanFactory()方法,就像是选了一个学校。
-
PersonBean实现了BeanPostProcessor接口,在初始化之前调用用postProcessBeforeInitialization()方法,相当于入学报名。
-
PersonBean类实现了InitializingBean接口,在设置了配置文件中定义的所有Bean属性后,调用afterPropertiesSet()方法,就像是入学登记。
-
配置文件中的Bean定义包含init-method属性,该属性的值将解析为Person类中的方法名称,初始化的时候会调用这个方法,成长不是走个流程,还需要自己不断努力。
-
Bean Factory对象如果附加了Bean 后置处理器,就会调用postProcessAfterInitialization()方法,毕业了,总得拿个证。
-
Person类实现了DisposableBean接口,则当Application不再需要Bean引用时,将调用destroy()方法,简单说,就是人挂了。
-
配置文件中的Person Bean定义包含destroy-method属性,所以会调用Person类中的相应方法定义,相当于选好地儿,埋了
5.Bean的作用域
-
singleton
-
prototype
-
在创建Bean的时候
@Scope("prototype") //@Scope(ConfigurableListableBeanFactory.SCOPE_PROTOTYPE) //通过Spring内置定义的字符串设置属性也行 @Beans public Cat cat(){ Cat cat = new Cat(); cat.setName("莫妮卡"); return cat; }
-
还有四种是在Spring MVC中才生效的:
- request(请求作用域): 一次http请求中会生成一个新的Bean实例类似于prototype
场景: 一次http的请求和响应共享Bean.
- session(会话作用域): 在http session中定义一个Bean实例
场景: 在用户会话中共享Bean.例如:可以用来记录一个用户的登录信息
- application(全局作用域): 在⼀个http servlet Context中,定义⼀个Bean实例
场景:Web应用的上下文信息.
- websocket(HTTP WebSocket作用域): 在一个HTTP WebSocket生命周期中,定义一个Bean实例
场景:WebSocket的每次会话中,保存了⼀个Map结构的头信息,将⽤来包裹客户端消息头。第⼀ 次初始化后,直到WebSocket结束都是同⼀个Bean.
6.Spring的IOC容器,介绍一下?
IOC:控制反转,原因如果想要创建一个对象,是使用new或者通过反射getInstance等方式,这样创建对象,耦合性太高,也不利于复用;引用了SpringIOC,把对象的创建权利交给IOC容器,在使用的时候,可以通过DI(@Autowired等)自动注入,而且IOC的Bean通常情况下也是单例的,可以节约资源;方便集中管理和维护,降低耦合度。
7.Spring的IOC的实现机制。
工厂模式 + 反射
public class IOC {
public static void main(String[] args) {
/*加载
* <bean class="com.xing.Spring.User"> 写xml的时候,会把这些信息写到这里。
* */
new ClassPathXmlApplicationContext("applicationContext.xml");
User user = Factory.getUser("com.xing.Spring.User");
}
}
class Factory{
public static User getUser(String classPath){
User user = null;
try {
user = (User) Class.forName(classPath).newInstance();
}catch (Exception e){
e.printStackTrace();
}
return user;
}
}
class User{
private String name;
public void setName(String name) {
this.name = name;
}
}
8.IOC 和 DI的区别
IOC是用来解决耦合的设计思想,是控制反转的,创建对象的权利交给Spring。
DI是IOC的实现,是IOC中重要的一环,用来把容器中的对象进行注入。
9.紧耦合和松耦合区别,如何写松耦合的代码。
-
紧耦合
- 类和类之间的高度依赖。
-
松耦合
- 松耦合是通过促进单一职责和关注点分离、依赖倒置和设计原则实现的。
10.BeanFactory的作用
-
BeanFactory是Spring中非常核心的一个顶层接口它是Bean的"工厂"、它的主要职责就是生产Bean。
-
它实现了简单工厂的设计模式,通过调用getBean传入标识生产一个Bean。
-
它有非常多的实现类、每个工厂都有不同的职责(单一职责)功能,最强大的工厂是DefaultlistableBeanFactory Spring底层就是使用的该实现工#行生产Bean的
-
BeanFactory他是容器 Spring容器(管理者某个对象的生命周期)
11.BeanDefinition的作用
-
存储Bean的定义信息。
-
之后BeanFactory就可以根据这些信息,通过反射获得一个Bean对象。
Bean的XML信息 ----存储----> BeanDefinition ----获取----> BeanFactory 创建bean对象。
一个Bean信息就对应一个BeanDefinition ,BeanDefinition放在一个集合中。
12.BeanFactory 和 ApplicationContext区别
相同
- 都可以作为容器,管理Bean的生命周期。
不同
-
BeanFactory用于Bean的生产
-
ApplicationContext实现了BeanFactory接口,也有getBean的方法,底层也是通过BeanFactory来获得。
-
相对于BeanFactory,ApplicationContext会自动注册Bean,也会加载环境变量,实现Spring的事件监听等等;ApplicationContext做的事更多。
13.Spring IOC容器的加载过程
四个形态
概念态---<Bean信息>---> 定义态----BeanDefinition---->纯净态----DI---->成熟态
对应Bean的生命周期
实例化 --> 属性注入 ---> 实例化
概念态---->定义态
-
实例化一个ApplicationContext的对象
-
调用bean工厂后置处理器完成扫描;
-
循环解析扫描出来的类信息;
-
实例化一个BeanDefinition对象来存储解析出来的信息
-
把实例化好的beanDefinition对象put到beanDefinitionMap当中缓存起来以便后面实例化bean;
-
再次调用其他bean工厂后置处理器
定义态----BeanDefinition---->纯净态
-
当然spring还会干很多事情,比如国际化,比如注册BeanPostProcessor等等,如果我们只关心如何实例化一个bean的话那么这一步就是spring调用finishBeanFactoryinitialization方法来实例化单例的bean,实例化之前spring要做验证需要遍历所有扫描出来的类,依次判断这个bean是否Lazy,是否prototype,是否 abstract等等;
-
如果验证完成spring在实例化一个bean之前需要推断构造方法,因为spring实例化对象是通过构造方法反射,故而需要知道用哪个构造方法;
-
推断完构造方法之后spring调用构造方法反射实例化一个对象;注意我这里说的是对象、对象、对象;这个时候对象已经实例化出来了,但是并不是一个完整的bean,最简单的体现是这个时候实例化出来的对象属性是没有注入,所以不是一个完整的bean;10:spring处理合并后的beanDefinition
纯净态----DI---->成熟态
-
Spring处理合并的BeanDefinition
-
判断是否需要完成属性注入
-
如果需要,则开始注入
初始化
-
判断bean的类型回调Aware接口。
-
调用生命周期回调方法
-
如需要代理完成代理
创建完成
- 放到单例池中去。