Spring框架大概讲一下
在Spring出现之前,企业级Web开发都是采用JavaEE标准和EJB来实现的,但是EJB这种实现太重了,并不适合小型Web项目,所以Spring的提出者认为用Java Bean和依赖注入的方式就能实现企业级Web开发,而Spring的出现也被成为是传统JavaEE的新起点。
Spring把普通的Java类看作是一个Java Bean,然后通过依赖注入的方式组装一个个Bean,并放入IOC容器中进行管理,再配合AOP切面的特性,使得其大大简化了企业级Web开发的成本,耦合度也更低。
所以在Spring出来之后就慢慢火热,随着他的发展,覆盖了Web应用的方方面面,对各种中间件等也提供了兼容,慢慢成为了一个支持企业级Web开发的全站式框架。
然后有利也有弊,Spring对其他主流框架、中间件都有支持,所以就带来个问题,引入各种框架时,需要加各种配置,慢慢的就变得很麻烦,所以在这个背景下,SpringBoot产生了。我理解的SpringBoot就是,简化了配置,提供了各种场景启动器,例如对不同框架整合的支持时,可以不用那些配置就方便的实现。
JavaEE
首先我们说下什么是J2EE和J2SE。
- J2SE是Java的基础版本,其实就是JDK(集合、多线程、IO等API)、JVM这些,也就是Java语言的基础。
- J2EE是Java Web应用的一套标准,是在J2SE的基础上构建的,这套标准就是制定了怎么去创建Web应用,具体来说,这套规定标准包括:Servlet(如何处理Web请求)、JPA(如何处理数据存取)、JMS(如何处理消息队列)等等。我认为J2EE实现的Web应用就是用了Servlet标准来处理Web请求,用JPA来做数据存取,然后还有像JMS来实现消息队列等等,通过这些标准来构建一个企业级的Web应用。
Java中有个组织叫JCP,就是一群专家,然后他们会对各种标准提案JSR进行审核,如果通过那这个标准就是推荐需要遵守的,J2EE就是一系列JSR的合集,共同构成了Web开发的一套的标准。
JavaEE和Spring
Spring并没有完全超出J2EE,或者说Spring中有些部分是对J2EE标准的封装,以达到更好的实现,例如,SpringMVC其实本质还是Servlet。
这里提一下Servlet规范,Servlet和Servlet容器是相辅相成的,我们用到tomcat、JBoss这些都是实现了Servlet标准的Servlet容器,所以Servlet的接口代码都可以在这些平台上运行,所以从这个角度来说,J2EE和Spring都遵守了Servlet标准。
EJB
把封装了业务逻辑的Java类(相当于一个组件的概念)打包放在服务器上,然后通过RMI(远程方法调用)的方式调用这个封装了业务逻辑的功能块,通过这种方式来构建企业级Web服务。
但很明显,这种方式大大增加了网络开销,并且实现也更复杂。
Java Bean
Spring中用Java Bean替代了EJB,Java Bean其实就是一个普通的Java类,例如一个Service里面封装业务逻辑,这就是Java Bean,我们可以通过依赖注入的方式把他注入到别的类中,这样就可以替代EJB了。
IOC
控制反转的理解
先来说说IOC的含义,控制反转,意思就是原来的A类中使用B类,需要new一个B类的实例,而控制反转之后,在A中并不会直接创建B的实例,而是交给第三方(IOC容器)来实现,在Spring中这个第三方就是IOC容器,也就是说,B创建之后放到了IOC容器中,然后A中需要使用B实例的时候,从IOC容器中取,然后通过依赖注入的方式塞到A中使用。
依赖注入的几种方式
构造方法注入
@Component
public class MyBeanConstructor {
private AnotherBean anotherBeanConstructor;
@Autowired
public MyBeanConstructor(AnotherBean anotherBeanConstructor){
this.anotherBeanConstructor = anotherBeanConstructor;
}
}
setter注入
@Component
public class MyBeanSet {
private AnotherBean anotherBeanSet;
@Autowired
public void setAnotherBeanSet(AnotherBean anotherBeanSet) {
this.anotherBeanSet = anotherBeanSet;
}
}
属性注入
平时开发都是用的这种
@Component
public class MyBeanProperty {
@Autowired
private AnotherBean anotherBeanProperty;
}
IOC容器
BeanFactory和ApplicationContext
Spring中主要有两种IOC容器:BeanFactory和ApplicationContext。这两个容器的区别如下:
- BeanFactory是基础的容器,实现了最基础的功能,ApplicationContext扩展了BeanFactory,继承了BeanFactory,提供了更多的功能,例如AOP等。
BeanFactory容器创建之后,只是完成了XML配置文件解析到内存的BeanDefinition容器的过程,在调用getBean()的时候才会去创建Bean;而ApplicationContext默认是容器启动之后就会创建所有配置的Bean,如果Bean设置了懒加载,那么会在后面调用getBean()的时候创建,这么做的目的可以在web服务启动的时候就检测了所有的配置,而不是运行过程中使用才出错。
IOC容器的启动过程
对于ApplicationContext容器来说,启动的过程可以分为两大部分:
XML解析成内存中的BeanDefinition,并放入一个Map中;
- 把BeanDefinition解析成Bean,并放入三级缓存。
BeanFactory容器启动只是完成了第一步,在调用getBean()的时候,创建Bean。
上图是ApplicationContext容器启动的过程,我认为比较重要是两个,一个是obtainFreshBeanFactory()这里会实现BeanDefinition的装载;一个是finishBeanFactoryInitialization()这里会实现Bean的创建,主要关注Bean的创建。
XML->BeanDefinition
- Resource资源定位,把资源解析为BeanDefinition;
- 把资源解析为BeanDefinition,并加载到内存;
向IOC容器中注册BeanDefinition(注册的时候会把BeanDefinition放入一个Map中,在方法DefaultListableBeanFactory.registerBeanDefinition()中)。
BeanDefinition->Bean/Bean生命周期
如上文所说,Bean的生命周期是从finishBeanFactoryInitialization()开始的,调用过程如下。
AbstractApplicationContext.finishBeanFactoryInitialization()->
beanFactory.preInstantiateSingletons()->
AbstractBeanFactory.getBean()->
AbstractAutowireCapableBeanFactory.doCreateBean()-> 流程的主干
AbstractAutowireCapableBeanFactory.doCreateBean()是创建Bean的核心,整个Bean生命周期如下。doCreateBean()主要关注三个部分就可以了:
实例化Bean。createBeanInstance()方法
- 属性注入。populateBean()方法
- 处理Bean初始化之后的各种事件(就是设置属性值后面的各种回调方法)。initializeBean()方法
实例化Bean createBeanInstance
这里总的来说就是,基于BeanDefinition,通过forName()方法加载Class对象,然后通过newInstance()方法来创建的实例。 PS:就是通过反射来创建Bean。
AbstractAutowireCapableBeanFactory.createBeanInstance()-> doCreateBean中会调用过来
AbstractAutowireCapableBeanFactory.instantiateBean()->
SimpleInstantiationStrategy.instantiate()->
BeanUtils.instantiateClass()-> 这里通过反射的方式实例化了Bean
属性注入 populateBean
属性注入是在AbstractAutowireCapableBeanFactory.populateBean()方法中的,这里面会对@Autowired注解等进行解析,最终调用applyPropertyValues()方法完成注入,本质是通过反射实现的。
Bean初始化之后的各种事件 initializeBean
initializeBean()方法里面就是对各种回调方法的处理,Bean如果实现了这些接口,就会去执行,还有就是执行配置的init方法。
如果Bean是单例的,那么就会交给IOC容器管理(放入一级缓存),并且容器还要负责Bean的销毁,如果Bean有配置销毁方法和接口,那么在销毁之前还会进行调用;如果Bean是多例的,那么直接交给创建线程就可以了。
如何解决循环依赖
Spring解决循环依赖问题主要是依靠三级缓存来实现的。具体来说是这样的:
在实例化Bean对象createBeanInstance()和属性注入populateBean()之间,有一个addSingletonFactory()的方法,这个方法会把刚刚创建的Bean实例放入三级缓存singletonFactories中,此时的Bean只完成了实例化,相当于只分配了内存和赋初值,属性还没有注入。但通过放入三级缓存的操作,提前暴露了这个Bean,缓存中持有了这个Bean的引用,为后面依赖的Bean注入提供了条件。
例如:当有两个Bean A和B相互依赖。我们先创建A,在实例化A之后,将A放入了三级缓存,然后为A注入属性的时候,发现需要B,B还未创建,这时候去创建B,B走到属性注入的时候,因为A已经放在了三级缓存,所以B是可以通过缓存来一级一级获取到A的,这样B中注入A就可以实现了,并且最后会把B放入一级缓存,然后在返回去继续为A注入属性,A就可以从一级缓存中找到B,这样就可以解决循环依赖了。
三级缓存分别是:单例Bean缓存、Bean的早期引用缓存、Bean工厂的缓存。单例Bean缓存很重要,因为后面从容器中获取Bean就是从这里获取的;Bean工厂的缓存也很重要,因为这是解决循环依赖的关键,提前暴露的Bean就是放在这里的。
二级缓存(Bean的早期引用缓存)好像不是很重要,但二级缓存能够提高加载的效率,避免每次都从Bean工厂缓存中获取。
Spring只能解决setter注入或属性注入的单例Bean的循环依赖问题,不能解决构造器注入或多例Bean的循环依赖问题。不能解决会直接抛出异常。
Bean作用域
默认Bean是单例的,也可以设置成多例、request(一个请求一个Bean)、session(一个session一个Bean)、global session(全局的session)。
我们的service、controller这些都是单例的,也就是说所有的请求都调用这个实例对象,所以会存在线程安全问题,那这个时候,我们需要尽量避免使用实例变量,或者注意加锁这些处理。
@Autowird和@Resource注解
- @Autowird是Spring的注解,@Resource是JDK自带的注解,但是Spring也支持这个注解
- @Autowird是按照类型注入的,如果要想按照名称注入,@Autowird配合@Qualifier
- @Resource中使用参数name就是用名称注入,使用参数type就是用类型注入
AOP
OOP和AOP的区别
OOP面向对象,更侧重于将业务中的实体和属性这些进行封装,也就是说可能,更符合对象的思想,把对象的东西都封装整合起来放在一个类中,但这种方式不好处理一些横向的问题,或者说一些对象都要具有的功能时,OOP可能就需要在所有的对象中都封装这个具体的功能。
这个时候AOP就能更好地发挥作用了,AOP是面向切面的,更适合解决这种横向问题。AOP实现原理
Spring中通过后置处理器来实现对Bean的封装代理,通过动态代理来实现AOP。
对于接口实现的,用JDK动态代理,JDK动态代理本质是通过反射来实现的;不是接口实现这种的,就用CGLIB来实现代理,本质是通过读入代理类的class文件,然后修改字节码,生成代理子类来实现的。JDK动态代理只能对实现接口的类进行代理;CGLIB动态代理是通过生成子类并重写父类方法来实现代理的,所以代理类不能用final修饰。
MVC处理请求的过程
- 用户发送请求至前端控制器DispatcherServlet
- DispatcherServlet收到请求调用HandlerMapping处理器映射器
- 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)并返回给DispatcherServlet
- DispatcherServlet通过HandlerAdapter处理器适配器调用处理器
- 执行处理器(Controller,也叫后端控制器)
- Controller执行完成返回ModelAndView
- HandlerAdapter将controller执行结果(ModelAndView)返回给DispatchServlet
- DispatchServlet将ModelAndView传给ViewResolver视图解析器(如果加上@responsebody注解,则返回值不通过viewResolver,而是直接返回object)
- ViewResolver解析后返回具体View
- DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
- DispatcherServlet响应用户