Spring基础
什么是Spring框架?
Spring是一款开源的轻量级java开发框架。旨在提高开发人员的开发效率以及系统的可维护性。
我们一般说的Spring框架指的是Spring Framework。它是很多模块的集合。使用这些可以方便协助我们进行开发,比如Spring支持IOC(控制反转)和AOP(切面编程),可以很方便的对数据库进行访问,可以很方便的集成三方组件,对单元测试友好,支持Restful接口等。
Spring最核心的思想是不重新造轮子,开箱即用,提高开发效率。
Spring包含哪些模块?
Core Container(核心组件)
Spring框架的核心模块就是core。也可以说是基础模块,主要提供IOC依赖注入功能的支持。Spring的其他所有功能都依赖于该模块。我们从上面的依赖图就可以看出。
- spring-core:Spring框架基本的核心工具类
- spring-beans:提供对bean创建,配置,管理等功能
- spring-context:提供对国际化,事件传播,资源加载等功能
- spring-expression:提供对表达式语言SpELD的支持,只依赖core模块,可以单独使用。
AOP
- spring-aspects:该模块为与AspectJ的集成提供支持
- spring-aop:提供了面向切面编程的实现
- spring-instrument:提供了为JVM添加代理的功能。具体来讲,它为tomcat提供了一个植入代理。能够为tomcat传递类文件。就像这些文件是被类加载器加载的一样。
Data Access
- spring-jdbc:提供了对数据库访问的抽象JDBC,不同的数据库都有自己独立的api用于操作数据库。而java程序只要和JDBC API交互,这样就屏蔽了数据库的影响。
- spring-tx:提供对事务的支持
- spring-orm:提供对hibernate,jpa,mybatis等ORM框架的支持
- spring-oxm:提供一个抽象层支持OXM,例如JAXB,Castor,XMLBeans等
- spring-jms:消息服务,spring4.1之后,还提供了对spring-messaging模块的继承。
Spring Web
- spring-web:对web功能的实现提供一些最基础的支持
- spring-webmvc:提供对spring mvc的实现
- spring-websocket:提供对webSocket的支持。让客户端和服务端进行双向通信。
- spring-webflux:提供对webflux的支持。是spring5.0中引入的响应式框架。它不需要servlet api,是完全异步。
Spring Test
spring团队提倡测试驱动开发(TDD)。有了控制反转的帮助,单元测试和集成测试变得更简单。spring的测试模块对junit,testNG,Mockito等等常用的测试框架支持的都比较好。
Spring,Spring MVC,Spring Boot之间的关系
Spring包含了多个功能模块,其中最重要的是spring-core(提供ioc依赖注入功能的支持),而Spring MVC功能的实现需要依赖该模块。
spring-mvc也是Spring中一个很重要的模块,主要赋予spring快速构建MVC架构的web程序的能力。M是模型,V是试图,C是控制器。其核心思想是通过将业务逻辑,数据,显示分离来组织代码。
而使用Spring进行开发的时候各种配置很麻烦,需要用到XML或者java显示配置,于是Spring Boot诞生了。
Spring旨在简化开发,Spring Boot旨在简化Spring的开发。Spring Boot只是简化了配置,其实本质上还是Spring MVC框架,只是做到了开箱即用而已。
Spring IoC
Spring IoC是什么
IoC(Inverse of Control:控制反转)是一种设计思想,而不是一个具体的技术实现。IoC的思想就是将原本在程序中手动创建对象的控制权限交由Spring框架管理。不过IoC并非spring特有,其它语言中也有应用。
为什么叫控制反转?
- 控制:指的是对象创建的权力
-
反转:控制权交给外部环境
将对象之间的相互依赖关系交给IoC容器来管理,并由IoC容器完成对象的注入。这样可以很大程度简化应用开发。把应用从复杂的依赖关系中解放出来。IoC容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。
在实际项目中一个Service类可能依赖很多其它的类,加入我们需要实例化这个Service,可能每次都要搞清楚Service所有底层类的构造函数。如果利用IoC的话,只需要配置好,然后在需要的地方引用就行了。降低了项目的开发难度,提高了可维护性。
在Spring中,IoC容器是Spring用来实现IoC的载体,实际上IoC容器就是个Map,Map中key是对象名,value是各种对象。
Spring时代我们一般用xml配置bean,后来觉得xml很麻烦,spring boot注解配置就流行起来了。
什么是Spring Bean?
简单来说Bean代指那些被IoC容器所管理的对象。
我们需要告诉IoC容器帮我们管理哪些对象。这个是通过配置来定义的。之前的xml文件,和现在常用的注解配置,也可以通过java配置类配置。
将一个类声明为Bean有哪些注解?
- @Component:可标注任意类为Spring组件。如果不知道处于哪层,可以用此注解标注。
- @Repository:对应持久层即Dao层。主要用于数据库相关操作
- @Service:对应服务层。涉及到业务逻辑
- @Controller:对应spring mvc的控制层,用于接收用户请求并调用service层返回数据给前端
@Component与@Bean的区别是什么?
- @Component注解用于类,@Bean用于方法
- @Component通常是通过类路径扫描自动装配到spring容器中。@Bean注解通常我们在标有该注解的方法中定义产生这个bean。
- @Bean注解比@Component注解的自定义性强,很多地方我们只能通过@Bean注解来注册bean。比如我们引用三方库中的类需要装配到Spring容器时,只能通过@Bean来实现。
注入Bean的注解有哪些?
- @Autowired: spring提供的注解
- @Resource:jdk提供的注解
- @Inject:jdk提供的,但是我没用过
@Autowired和@Resource的区别是什么?
Autowired属于spring内置的注解,默认的注入方式是byType根据类型匹配。也就是说会优先根据接口类型区匹配并注入bean(接口的实现类)。
比如A接口,有A1,A2,A3三个实现类,如果用@Autowired注入A。那么就无法正确的注入对象了。这种情况下注入方式会变成byName,如果说注入的bean名称是a2,那么会匹配到A2这个类。所以建议在使用@Autowired的时候用@Qualifier指定名称Resource是JDK提供的注解,优先根据byName注入,如果无法通过名称匹配会根据byType注入。
也就是说当一个接口存在多个实现类的时候,两者都需要指定名称。Autowired是通过@Qualifier注解来显示指定名称。@Resource可以通过name属性来指定名称。
Bean的作用域有哪些?
- singleton:单例bean,容器中只有唯一的bean实例。spring中bean默认都是单例。
- prototype:非单例bean,每次获取都创建一个新的bean实例。
- request(仅web应用可用):每次http请求都会产生一个新的bean。该bean仅在当前http request中有效
- session(仅web应用可用):每一次来自新session的http请求都会生成一个新的会话bean。该bean仅在当前http request中有效
- application/global-session(仅web应用可用):每个web应用在启动时创建一个bean,该bean仅在当前应用启动时间内有效。
- websocket(仅web应用可用):每一个websocket会话产生一个新的bean。
我们可以在配置bean的时候通过@Score注解配置作用域
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Person personPrototype() {
return new Person();
}
单例Bean的线程安全问题
单例Bean存在线程安全问题,主要是因为当多个线程操作同一个对象的时候是存在资源竞争的。常见的两种解决办法如下:
- 在Bean中尽量避免定义可变的成员变量。
- 在类中定义一个ThreadLocal的成员变量,将需要的可变成员标量保存在ThreadLocal中。
不过大部分情况Bean都是武装的,这种情况下Bean是线程安全的。
Bean的生命周期
- Bean容器找到配置文件中的Spring Bean的定义
- Bean容器利用java Reflection API创建一个Bean实例
- 如果涉及到一些属性值利用set()方法设置一些属性
- 如果Bean实现了BeanNameAware接口,调用serBeanName()方法传入Bean的名字
- 如果实现了BeanClassLoaderAware 接口,调用 setBeanClassLoader()方法,传入 ClassLoader对象的实例。
- 如果 Bean 实现了 BeanFactoryAware 接口,调用 setBeanFactory()方法,传入 BeanFactory对象的实例。
- 反正实现了什么xxxAware接口,就调用相应接口的方法。
- 如果有和加载这个Bean的Spring容器相关的BeanPostProcessor对象。执行postProcessBeforeInitialization() 方法。
- 如果Bean实现了InitialzingBean接口,执行afterPropertiesSet()方法。
- 如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcessor 对象,执行postProcessAfterInitialization() 方法。
- 当要销毁 Bean 的时候,如果 Bean 实现了 DisposableBean 接口,执行 destroy() 方法。
-
当要销毁 Bean 的时候,如果 Bean 在配置文件中的定义包含 destroy-method 属性,执行指定的方法。
Spring AoP
谈谈对AOP的了解
AOP面向切面编程,能将那些与业务无关,却为业务模块共同调用的逻辑或者责任封装起来,便于减少系统的重复代码,降低模块间的耦合度。并有利于维护和扩展。
Spring AOP就是基于动态代理的,如果要代理的对象实现了某个接口,那么Spring AOP就会使用JDK代理去创建对象,而对于没有实现接口的对象,就无法使用JDK代理,这时候Spring AOP会使用Cglib生成一个被代理对象的子类来作为代理。
当然我们也可以使用AspectJ,AspectJ算是java生态系统中最完整的AOP框架了。
AOP切面编程涉及到的一些术语:
- 目标(Target) 被通知的对象
- 代理(Proxy) 向目标对象应用通知之后创建的代理对象
- 连接点(JoinPoint) 目标对象的所属类中,定义的所有方法均为连接点
- 切入点(Pointcut) 被切面拦截 / 增强的连接点(切入点一定是连接点,连接点不一定是切入点)
- 通知(Advice) 增强的逻辑 / 代码,也即拦截到目标对象的连接点之后要做的事情
- 切面(Aspect) 切入点(Pointcut)+通知(Advice)
- Weaving(织入) 将通知应用到目标对象,进而生成代理对象的过程动作
Spring AOP和AspectJ AOP有什么区别?
Spring AOP属于运行时增强,而AspectJ是编译时增强。Spring AOP基于代理,而AspectJ基于字节码操作。
AspectJ功能更强大,而Spring AOP更简单。
如果我们切面比较少,两者性能差别不大。如果切面太多,建议AspectJ。它比Spring AOP快很多。
AspectJ定义的通知类型有哪些?
- Before(前置通知):目标对象的方法调用前触发
- After(后置通知):目标对象的方法调用后触发
- AfterReturning(返回通知):目标对象方法调用完成,在返回结果值之后触发
- AfterThrowing(异常通知):目标对象的方法运行中抛出/触发异常触发。这个和上面的AfterReturning是互斥的。
- Around(环绕通知):可以任意在目标对象的方法调用前后搞事情,甚至不调用目标对象的方法。
多个切面的顺序如何控制?
- 可以用@Order注解定义切面顺序(值越小,优先级越高)
- 实现Ordered接口重写getOrder方法(返回值越小,优先级越高)
Spring MVC
说说对Spring mvc的了解
mvc是模型Model,视图View,控制器Controller的简写。其核心思想是将业务逻辑,数据,显示分离来组织代码。MVC是一种思想,也算是一种设计模式。
Model1时代
不得不说到一个很古老的技术JSP,JSP既是控制层,优势表现层。这种模式的问题是代码复用率低,而且前后端一起开发,很难维护和定位问题。我是17年入行的,当时学习有学到这个技术,但是我工作以后就已经变成了mvc前后端分离的模式,所以对于jsp属于能看懂,但是不熟悉的程度。
Model2时代
据说是Java Bean(Model)+JSP(View)+Servlet(Controller)开发模式,这就是早期的JavaWeb MVC开发模式。
- Model:系统涉及的数据,也就是dao和bean
- View:展示模型中的数据,只是用来展示
- Controller:处理用户请求都发送给它,返回数据给JSP并展示给用户。
Spring MVC时代
随着spring轻量级框架的流行。spring生态圈出了Spring MVC框架。是当前最优秀的MVC框架。相比于Struts2.Spring MVC更加简单和方便,开发效率更高。并且spring MVC运行速度很快。
MVC是一种设计模式,Spring MVC是一款优秀的MVC框架。可以帮我们进行更简洁的web层的开发,并且它天生与spring框架集成。Spring MVC下我们一般把后端项目分为Service层,Dao层,Entity层,Controller层。
Spring MVC的核心组件有哪些?
- DispatcherServlet:核心的中央处理器。负责接收请求,分发,并给予客户端响应。
- HandlerMapping:处理器映射器。根据uri去匹配查找能处理的Handler,并会将请求涉及到的拦截器和Handler一起封装。
- HandlerAdapter:处理器适配器。根据HandlerMapping找到的handler适配执行对应的handler。
- Handler:请求处理器。处理实际请求的处理器。
- ViewResolver:视图解析器。根据Handler返回的逻辑视图/视图,解析并渲染真正的视图,并传递给DispatcherServlet响应客户端。
SpringMVC工作原理
流程说明:
- 客户端发送请求,DispatcherServlet拦截请求
- DispatcherServlet根据请求信息调用HandlerMapping,HandlerMapping根据uri去匹配查找能处理的Handler(就是我们说的Controller控制器)。并将请求涉及到的拦截器和Handler一起封装。
- DispatcherServlet调用HandlerAdapter适配器执行Handler。
- Handler完成对用户请求的处理后,返回一个ModelAndView对象给DispatcherServlet,这个对象里包含了数据模型和相应的视图信息。M哦的历史返回的数据对象。View是逻辑上的视图。
- ViewResolver会根据逻辑View查找实际的View。
- DispatcherServlet把返回的Model传给View(视图渲染)。
- 把View返回给请求者。
统一异常处理怎么做?
可以使用注解的方式统一异常处理。@ControllerAdvice和@ExceptionHandler这两个注解。
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
@ExceptionHandler(BaseException.class)
public ResponseEntity<?> handleAppException(BaseException ex, HttpServletRequest request) {
//......
}
@ExceptionHandler(value = ResourceNotFoundException.class)
public ResponseEntity<ErrorReponse> handleResourceNotFoundException(ResourceNotFoundException ex, HttpServletRequest request) {
//......
}
}
这种异常处理方式下,会给所有或者指定的Controller植入异常处理的逻辑(AOP)。当Controller中的方法抛出异常的时候,由被@ExceptionHandler注解修饰的方法进行处理。
ExceptionHandlerMethodResolver 中 getMappedMethod 方法决定了异常具体被哪个被 @ExceptionHandler 注解修饰的方法处理异常。
@Nullable
private Method getMappedMethod(Class<? extends Throwable> exceptionType) {
List<Class<? extends Throwable>> matches = new ArrayList<>();
//找到可以处理的所有异常信息。mappedMethods 中存放了异常和处理异常的方法的对应关系
for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {
if (mappedException.isAssignableFrom(exceptionType)) {
matches.add(mappedException);
}
}
// 不为空说明有方法处理异常
if (!matches.isEmpty()) {
// 按照匹配程度从小到大排序
matches.sort(new ExceptionDepthComparator(exceptionType));
// 返回处理异常的方法
return this.mappedMethods.get(matches.get(0));
}
else {
return null;
}
}
从源码看出:getMappedMethod()会首先找到可以匹配处理异常的所有方法信息,然后对其进行从小到大排序,最后取最小的那一个匹配的方法。
Spring框架中用到了哪些设计模式?
依赖注入是实现控制反转的一种设计模式。控制反转是一个思想,a对象依赖b,如果是创建a的时候去创建b,这是一种直接模式。而IOC控制反转的思想是让a和b失去直接联系。当创建a的时候需要b,直接从容器中拿b给a用。而这个实现就是依赖注入。所以说依赖注入是实现控制反转的一种设计模式。
-
工厂设计模式。spring可以通过BeanFactory或者ApplicationContext创建bean对象。
- 说一下BeanFactory和ApplicationContext的区别:
BeanFactory是延迟注入,用到才会注入。因此不太影响程序启动
ApplicationContext是容器启动的时候不管用没用到一次性创建。而且ApplicationContext扩展了BeanFactory。其有三个实现类,分别是;类文件扫描,xml文件扫描和yml/properties文件扫描。
- 说一下BeanFactory和ApplicationContext的区别:
单例设计模式:spring中bean的默认作用域就是单例的。其实现的方式是通过注册表。其实就是个key。id是bean名称,值是单例对象本身。在获取bean的时候如果注册表存在则直接取。如果不存在创建(创建过程中用了双重检测锁机制)后加入到注册表中。并返回。
代理设计模式:AOP就是通过动态代理实现的。如果代理的对象实现了某接口,就通过jdk去创建代理对象。如果没有实现接口。就用cglib生成被代理对象的子类来代理(代理模式就是用a对象去代理b。我们可以理解中介代理了房东一样)。
模板方法模式:几乎所有的Template都是用到了模板方法。大概意思就是先设置一个模板,然后让子类去按照这个模板实现。其中包括有些事必须按照模板来实现,有些要自己去填充。就好像上班一样,不管任何岗位,上班,下班是固定的,但是每个人的工作内容又不一样的。但是 上班 + 工作 +下班,这三个算是一个模板。
观察者模式: spring中事件驱动模型(监听机制)就属于观察者模式。继承ApplicationEvent实现。这个模式比较简单,理论上就是当a发生改变,b也要同步做点什么。这样可以说b在观察a。
适配器模式:就是将一个接口转换成客户希望的另一个接口。mvc的实现中用到了适配器处理器,其实现的功能的就是适配。
本篇笔记就记到这里,如果稍微帮到你了记得点个喜欢点个关注。也祝大家工作顺顺利利!