Spring

Spring框架一------Spring

1.SSM框架

●Spring+SpringMVC(model ,view,controller)+Mybatis

●Spring:使用注解和自动装配,即IOC,AOP(业务层)

●SpringMVC:使用MVC模型,将流程控制代码放到controller层处理,将业务逻辑代码放到service层处理(表示层)

●Mybatis:在持久层(Repository)与数据进行交互

2.什么是Spring

●一款轻量级(体积小,引用jar包少)的开源的JAVAEE框架。两个核心IOC和AOP。

3.什么是IOC

●IOC就是控制反转,即将对象的创建以及对象之间的调用过程交给容器来管理,我们只需要在Spring配置文件里配置相应的bean,或者在类上加上相应的注解,并设置相关属性,让Spring容器来生成类的实例对象以及管理对象,这样就不需要自己在类中new对象,而是通过反射来注入,使用IOC可以降低耦合度。

●IOC的底层原理:xml配置文件解析+工厂模式+反射

●SpringIOC的思想基于IOC容器完成,IOC容器的底层就是对象工程,Spring提供两种IOC容器实现方式BeanFactory和ApplicationContext(两个都是接口)。

​ ■BeanFactory:先加载Bean配置文件(不创建对象),并通过xmlBeanFactory去创建工厂Bean,最后通过工厂Bean的getBean()方法得到所需要的bean。使用这个对象的时候才去创建。

​ ■ApplicationContext容器:BeanFactory的子接口,先加载bean配置文件,并使用FileSystemXmlApplicationContext去创建工厂Bean,加载的时候就创建对象了。(一般使用这个进行web开发,将创建对象这种耗时耗资源的操作都在服务器启动前实现比较好)

●DI(依赖注入):与控制反转其实是一个概念,当某个角色需要另外一个角色协助的时候,在传统的程序设计过程中,通常由调用者来创建被调用者的实例,但在Spring中创建被调用者的工作不再由调用者来完成,因此称为控制反转。创建被调用者的工作由Spring来完成,然后注入调用者。例如容器在运行时直接将某个类需要用到的一些对象提供给他。Spring为DI提供了三种方式:

​ ■Setter方法:直接设置属性

​ ■构造方法:使用有参构造注入

​ ■注解方法:(Autowired Resourse value)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gmyiOM82-1650540657599)(http://luxiaolumm.gitee.io/luxiao-lu-mm/pic/148.png)]

●基于注解的Bean管理

​ ■注解扫描器:@ComponentScan(basePackages = 包名)

​ ■@Configuraion配置类注解:完全注解开发,替代xml文件

​ ■对象创建,在类上加入其中一个注解,@Componet,@Service,@Controller,@Reposity。四个功能是一样的,只是为了用在不同层表示当前组件的作用。

​ ■属性注入:

​ ▼@AutoWired:根据属性类型自动装配,如果一个类型只有一个实现实例属性的话,可以用这个

​ ▼@Qualifier:根据名称进行注入,与AutoWired一起使用

​ ▼@Resource:根据名称和类型进行注入(有name属性)

​ ▼@Value:注入基本类型(有value属性)

4.什么是JavaBean

●一种符合特殊规范的类,所有属性都为private;提供默认的构造方法,提供setter和getter;实现serializable接口

5.Bean的生命周期

●Spring根据@ComponentScan找到带有@Componet,@Service,@Controller,@Resposity的类,然后解析并将Bean的定义保存,将bean定义发送到Spring容器中创建bean实例。

●Spring为创建的bean实例设置对象属性(依赖注入)

●将bean实例传递给Bean后置处理器的方法postProcessBeaforeInitialization

●调用bean的初始化方法(需要进行配置初始化的方法)

●将bean实例传递给Bean后置处理器方法postProcessAfterInitialization

●使用bean

●当容器关闭时,调用Bean的销毁方法(销毁方法也需要配置)

6.什么是AOP

(发生时间:有循环依赖就是在bean实例化以后,没有循环依赖就是bean初始化后)

●AOP即面向切面编程,利用了代理模式。AOP是对OOP(Object Oriented Programming 面向对象编程)的补充完善。OOP通过封装、继承和多态建立了一种对象层次结构,但是OOP只允许定义从上到下的关系,不适合定义从左到右的关系。

●AOP通过动态代理的方式,对消息进行截取和装饰,可以将各种交叉业务逻辑封装成一个切面,然后注入到目标对象(具体业务逻辑)中,通俗来讲,就是在不改变源代码的情况下,在主干功能里添加新功能。

●底层原理-动态代理:

​ ■JDK动态代理:通过反射来接受被代理的类,要求被代理的类必须试下一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。Proxy类基于getProxy方法和传递的参数创建动态代理类。InvocationHandler接口则基于invoke()激发动态代理对象的方法。

​ ▼创建被代理对象

​ ▼创建InvocationHandler实现类,重写Invoke方法并封装被代理对象

​ ▼获取被代理对象的实现接口集合以及类加载器

​ ▼使用Proxy类的newProxyInstance(加载器,接口集合,动态代理实例)创建代理

​ ■CGLib动态代理:如果目标类没有实现接口,那么SpringAOP会选择使用CGLib来动态代理目标类。CGLib是一个代码生成的类库,可以在运行时动态的生成某个类的子类(通过修改字节码来实现代理)。CGLib通过集成方式做的动态代理,所以如果某个类被标记为final,那么是无法使用CGLib做动态代理的。

​ ■JDK动态代理比CGLib执行速度快,但性能不如CGLib

​ ■代码实现:https://juejin.cn/post/6844903762025250824#heading-3

●术语:

​ ■连接点:类中哪些方法可以被增强,称为连接点

​ ■切入点:实际被增强的方法,称为切入点

​ ■通知:实际增强的逻辑部分称为通知

​ ▼前置通知:在方法前面执行

​ ▼后置通知:在方法后面执行

​ ▼环绕通知:在方法前后都有

​ ▼异常通知:只有增强的方法出现异常的时候才会执行

​ ▼最终通知:一定会执行

​ ▼切面:一个动作,把通知应用到切入点的过程

●优势:使用AOP可以对业务逻辑的各个部分进行隔离(不用写到一起了),从而使得业务逻辑各个部分之间的耦合度降低,提高程序的可重用性,以及开发效率。

7.SpringAOP和AspectJ AOP的区别

●SpringAOP属于运行时增强,基于代理实现,而AspectJAOP属于编译时增强,基于字节码操作。

●SpringAOP已经集成了AspectJ,AspectJ是Java生态系统中最完整的AOP框架,功能比SpringAOP强大,但是SpringAOP相对简单。

●如果切面较少,两者性能差异不大,如果多的话,AspectJ性能更好。

8.Spring中使用了哪些设计模式

●工厂模式:IOC容器就像是一个工厂,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,不用考虑对象是如何创建出来的。IOC容器负责创建对象,将对象链接到一起,配置这些对象。

​ ■通过BeanFactory创建对象(延迟注入,直到使用某个bean的时候才会注入,启动的时候更快)

​ ■通过ApplicationContext创建对象(容器启动时一次性创建所有bean):实现类,ClassPathXmlApplication,FileSystemXmlApplication,xmlWebApplication。

●依赖注入:是实现控制反转的一种设计模式,依赖注入就是将实例变量传到一个对象中去。

●单例模式:Spring中bean的默认作用域就是singleton(单例)的。可以改为以下几种:

​ ■Prototype:每次请求就会创建一个新的bean实例

​ ■request:每次http请求就会产生一个新的bean,该bean仅在http request内有效

​ ■session:每次http请求就会产生一个新的bean,该bean仅在当前http session内有效。

​ ■global-session:全局session作用域

​ ■为什么要设置成默认单例模式:

​ ▼减少了创建实例产生的资源消耗(创建bean肯定就会消耗资源)

​ ▼减少JVM垃圾回收(因为只有一个)

​ ▼可以快速获取到bean(单例bean会被保存到map中,相当于缓存)

​ ▼缺点:所有请求共享一个bean,而bean的成员变量在堆内存中,不能保证线程安全。

​ ▼缺点的解决:使用ThreadLocal为每个使用bean变量的线程建立变量副本(将request对象放入当前线程的thread的ThreadLocal.threadLocalMap属性中,在请求结束后会销毁ThreadLocal),每个线程可以修改自己内部的副本变量,不会影响到其他线程中副本变量,实现了线程间数据的隔离和数据封闭。

●代理模式:SpringAOP就是基于动态代理的,SpringAOP会使用JDK proxy或者CGLIB实现动态代理,将一些通用的功能抽象出来,在需要使用的地方直接使用。

9.Spring中的事务管理

●事务即Service是真正的业务,比如转账(而不是加钱和扣钱),对应JAVAEE的Service层,而Dao层只是对数据的操作,与业务无关(这里写加钱,扣钱)。

●创建Service,在Service中注入dao,在倒注入JdbcTemplate,在jdbcTemplate中注入DataSource

●@Transactional事务注解,加到类上,则类中所有方法都添加事务,加到方法上则仅为这个方法添加事务。在注解后面可以设置参数来进行配置,以下为常用参数:

​ ■事务隔离级别:isolation

​ ▼READ UNCOMMITTED(读未提交):三个问题都有

​ ▼READ COMMITTED(读已提交):存在不可重复读,幻读

​ ▼REPEATABLE READ(可重复读):存在幻读

​ ▼SERIALIZABLE(串行化):解决了幻读

​ ■事务传播行为:propagation 多个事务相互调用,整个过程如何管理(7种)

​ ▼Required(默认):如当前方法(调用方法)不在事务中就创建一个新事务,如果当前方法在事务中,就加入此事务。

​ ▼Required_news():不管调用方是否有事务,都自身新建事务

​ ▼Supports:如果调用者有事务则加入,如果没有,那也以非事务运行(与不加事务注解一样)

​ ▼Not_supports:一直以非事务形式运行

​ ▼Never:无论谁调用我,你都不能有事务,有就抛异常

​ ▼Mandatory:调用者有事务就加入,没有就抛出异常

​ ▼Nested(嵌套):如果调用者有事务我就创建子事务,父事务提交后,子事务再提交,调用者无事务,就新建事务(子异常,父不一定异常,父异常 ,子一定异常)

​ ■超时时间:timeout 事务需要在一定时间内进行提交否则回滚,默认-1,以s为单位

​ ■是否只读:readOnly 决定事务是否可以增删改查

​ ■回滚:rollbackfor 出现哪些异常就回滚

​ ■不回滚:noRollballFor 设置哪些异常不回滚

●在配置类上使用EnableTransactionManagement开启事务

10.Spring中的过滤器和拦截器

●过滤器(Filter):依赖于Servlet容器,是在请求进入容器之后,还未进入Servlet之前进行预处理,并且在请求结束返回给前端之前进行后期处理。可以对几乎所有请求进行过滤,过滤器实例只能在容器初始化时调用一次。使用过滤器可以取你所想取,忽视不想要的东西,其底层实现方式是基于函数回调的。自定义过滤器需要实现Filter接口,doFilter方法(init和destroy可以不实现,有默认实现)。方法有一个参数filterchain是一个回调接口,基于函数回调实现,并在FilterConfig中通过FilterRegistrationBean配置过滤器。

●拦截器:依赖于Web框架,是一个Spring组件,由Spring容器管理,不仅可以用于web程序,还可以用于Application和swing程序中。实现HandlerInterceptor接口,在preHandler中处理请求,并通过实现WebMbcConfigurer接口,固废addInterceptors方法,在addPathPatterns中设置拦截规则。

●二者区别:

​ ■过滤器依赖于servlet容器,只能在servlet容器中使用,只用于web,拦截器依赖于Spring容器,可以在Spring容器中使用,不单用于web还可以用于Application Swing等

​ ■过滤器控制粒度比较粗,只能在请求进来时进行处理,对请求进行响应和包装;拦截器提供更细粒度的控制,可以在controller对请求处理之前或者之后被调用,也可以在渲染视图呈现给用户后调用。

​ ■中断链执行的难易程度不同:拦截器可以preHandler方法内返回false进行中断;过滤器比较复杂,需要处理请求和相应对象来引发中断,需要额外动作,比如将用户重定向到错误页面。

​ ■过滤器能做的拦截器基本都能做,所以优先使用拦截器。

11.Spring循环依赖

●循环依赖:如果配置了A,B两个Bean对象互相依赖,会抛出StackOverFlowError,因为创建A的时候依赖B,创建B的时候又依赖A,造成死循环。

●Spring通过三级缓存(三个map)和提前曝光机制来解决这个问题:

​ ■一级缓存singletonObject(可以看做单例池):该缓存key = beanName,value = bean;这里的bean是已经创建完成的。该bean经历实例化,属性填充,初始化以及各类的后置处理。所以一旦需要获取bean时,我们第一时间就会找一级缓存。

​ ■二级缓存earlySingletonObject(可以看做是半成品池):该缓存key = beanName,value = bean;这里与一级缓存的区别为该缓存所获取到的bean是提前曝光出来的,是还没有创建完成的,即获取到的bean只能确保完成了实例化,但是属性填充以及初始化还没有完成,因此该bean还没有创建完成,仅仅能作为指针提前曝光,被其他bean引用。

​ ■三级缓存singletoFactories(可以看做是工厂池):该缓存key = beanName, value = beanFactory,在bean实例化后,属性填充以及初始化之前,如果允许提前曝光,spring会将实例化后的bean提前曝光,也就是把该bean转换为beanFactory并加入到三级缓存。在需要引用提前曝光对象时通过singletonFactory.getObject()获取

●获取bean的流程:

​ ■从ObjectFactory(bean工厂)中直接获取

​ ■bean工厂先看SingletoBeanRegistry(一级缓存)中是否有,有就直接返回,没有就进行创建,创建好了丢到单例池中。

●循环依赖问题的解决

​ ■创建bean对象A,实例化后添加到三级缓存中

​ ■属性填充A的过程中需要创建beanB

​ ■B的填充过程中需要A,在一级缓存中没有找到,就去三级缓存中创建,在三级缓存中有添加提前曝光度 A工厂

​ ■创建后完成了B创建的整个步骤,然后将B放入单例池中,并填充作为A的属性

​ ■BeanA的创建完成,将A放入一级缓存中,并删除三级缓存中的A

●AOP代理问题:在创建完成后单例池中保存的是A的代理对象,而B中引用的是半成品池中原本的A对象。二级缓存解决了循环依赖问题但是解决解决不了AOP问题。

●整体流程:

​ ■创建Bean对象,此时属性全为null(只是实例化而已 )

​ ■添加到三级缓存,加进去的是Factory(A),只有在发生提前引用(需要的时候,单例池中没有)的时候,才会发生作用,创建半成品A。

​ ■填充属性,循环依赖情况下,A/B循环依赖。当填充A属性的时候会去获取BeanB,BeanB又需要实例化+属性注入

​ ■B中发现有个属性为A,然后去getBean(A),发现一级缓存中没有

​ ■于是去三级缓存中,拿到了A的ObjectFactory。

​ ■然后调用ObjectFactory的getObject(),获取A的半成品

​ ■然后调用AOP后置处理器的getEarlyBeanReference方法拿到代理后的bean,并且从三级缓存中删除了A的ObjectFactory,然后将代理了不完整A的代理对象放入二级缓存。

​ ■B中属性已经填充完成,且填充的field是代理后的A,称为proxyA。

​ ■B继续其他的后续处理,如果需要的话,在初始化完成后,B会进行AOP动态代理,创建并返回代理类ProxyB

​ ■B处理完成后,被填充到当前A的field中(如果满足切面则填充的是proxyB)

​ ■同时将B或proxyB放到一级缓存中,删除二级三级缓存

​ ■对A进行后置处理,调用aop后置处理器的postProcessAfter

​ ■从二级缓存中获取proxyA并放入一级缓存

●为什么需要二级缓存:如果只有一级缓存的话,根据提前曝光机制,半成品与成品Bean都在一级缓存池中,如果有其他线程去缓存里获取这个bean,则得到的bean可能不完整。

●为什么要三级缓存:如果不考虑AOP代理的话,二级缓存足够了,如果只有二级缓存,在B进行属性填充的时候,就没办法等到A初始化完成后生成Proxy然后再被B的属性引用。所以设置三级缓存,用来存放一个工厂对象ObjectFactory,作用就是在满足切面的时候将AOP提前,也就是将生成ProxyA的过程从初始化A后提前至B的属性填充的时候。

●AOP时机

​ ■没有循环依赖的话就是在Bean初始化完成后创建动态代理

​ ■如果有就是在bean实例化之后创建。

12.Spring异常处理器

●Spring提供一个处理异常的接口:HandlerExceptionResolver可以基于配置处理,下面介绍常用的注解处理

​ ■ControllerAdvice:标记在类上,将当前类表示为处理异常的组件

​ ■ExceptionHandler:标记在方法上,设置标着方法处理的异常,出现这个异常时会调用这个方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值