框架框架框架

spring的优点:

  • 通过Spring提供的IoC容器和DI机制,我们可以将对象之间的依赖关系交由Spring进行控制,方便解耦,简化开发。
  • Spring提供的AOP面向切面的编程,解决一些面向对象不好解决的问题。比如对日志、事物的几种管理。
  • 方便集成各种优秀的框架,简化开发。

一、spring AOP、IOC

1、AOP:动态代理+反射实现的。通过动态代理在一个方法前后加上相应的逻辑代码,通过反射调用该方法。

动态代理的方法:

jdk动态代理:基于接口的代理,被代理的类必须实现一个接口。(参数:被代理类的classloader,被代理类的interface,代理invokehandle)

  • java原生支持,不需要任何外部依赖
  • 目标类必须实现某个接口,没有实现接口的类是不能生成代理对象的;
  • 代理的方法必须都声明在接口中,否则,无法代理;
  • 执行速度性能相对cglib较低;

cglib动态代理:基于子类的代理,为被代理的类创建一个子类继承父类,子类重写父类的方法来实现。(参数:被代理的类,methodInterceptor)

  • cglib动态代理需要导入相应jar包
  • 代理的类无需实现接口,是生成了代理真实类的子类,无论代理真实类有没有实现接口都可以使用;
  • 执行速度相对JDK动态代理较高;
  • 代理的对象不能是final关键字修饰的

Joinpoint:连接点。业务层所有的方法。

Pointcut:切入点。 指被增强的方法。

前置通知:切入点之前

后置通知:切入点之后

异常通知:有异常情况下通知

最终通知:finally里的通知

环绕通知:动态代理中invork整个方法。

2、IOC:控制反转。由一个中心化的 Bean 工厂来负责各个 Bean 的实例化和依赖管理。各个 Bean 可以不需要关心各自的复杂的创建过程,达到了很好的解耦效果。

用工厂模式实现的。在spring容器启动的时候,扫描配置文件或者注解将配置的类实例化后放到map里。在用到某个类的实例对象时会直接在map中取出来用。

@Component:用于把当前对象存入Spring容器中。value默认值为当前类首字母小写。

@Controller:一般用在表现层,把当前对象存入Spring容器中。

@Service:一般用在业务层,把当前对象存入Spring容器中。

@Repository:一般用在持久层,把当前对象存入Spring容器中。

@Autowired:自动按照类型注入,只要容器中有唯一的一个bean对象类型和要注入的变量的类型匹配,就可以住入成功。

如果容器中有多个类型一样的实例和要注入的类型匹配,就再按照字段名称匹配。

@Qualifier:和@Autowired配合使用,指定要注入bean的id。value

@Resource:直接按照bean的id注入。name

@Value:用于注入基本类型和String类型。

二、Spring bean的加载过程

图文并茂,揭秘 Spring 的 Bean 的加载过程 - 简书

1、首先获取 BeanName,对传入的 name 进行解析,转化为可以从 Map 中获取到 的BeanDefinition 的 bean name。

2、然后对父类的定义进行合并和覆盖,如果父类还有父类,会进行递归合并,以获取完整的 Bean 定义信息。

从配置文件读取到的 BeanDefinition 是 GenericBeanDefinition。它的记录了一些当前类声明的属性或构造参数,但是对于父类只用了一个 parentName来记录,在判断 parentName 存在的情况下,说明存在父类定义,启动合并。如果父类还有父类,递归调用,继续合并。每次合并完父类定义后,都会调用 RootBeanDefinition.overrideFrom 对父类的定义进行覆盖,获取到当前类能够正确实例化的 全量信息

3、实例化,使用构造或者工厂方法创建 Bean 实例。

4、属性填充,寻找并且注入依赖,依赖的 Bean 还会递归调用 getBean方法获取。

5、初始化,调用自定义的初始化方法。

6、获取最终的 Bean,如果是 FactoryBean 需要调用 getObject 方法,如果需要类型转换调用 TypeConverter 进行转化。

首先在springFramework整个体系当中,我们的一个bean,它是有一个BeanDefinition来构建的,就是BeanDefinition可以理解为SpringBean的定义,(然后我们要理解循环依赖的话,首先我们要讲到我们的SpringBean的生命周期。)SpringBean的生命周期一共大体的分为几步,首先是Spring容器的启动、扫描,根据传入的baenName变成BeanDefinition存到BeanDefinition Map中,然后遍历,遍历完成之后,对BeanDefinition做基本的验证,是否单例、是否原型、是否懒加载等等之类的。验证完成之后的话,Spring就会去在实例化bean之前,会去我们的Spring单例池当中获取一遍,看存不存在,再去看有没有被提前暴露,如果都没有,代码会继续执行创建X对象,利用反射实例化一个对象,如果支持循环依赖,将这个半成品的对象提前暴露出来,然后填充属性。填充属性过程中,发现X依赖Y,就会走Y的生命周期流程。和X一样,首先会对Y做验证,判断Y有没有在单例池当中,如果没有,再去看有没有被提前暴露。如果Y没有被提前暴露,接着往下执行就会把Y给实例化好,之后会对Y做一些初始化工作,包括Y的提前暴露、Y的属性填充。Y属性填充的时候,发现需要填充X,这个时候,他要走一遍获取X的流程。在走X的流程中,会发现X已经被提前暴露,所以他能够拿到我们已经提前暴露好的一个ObjectFactory所产生的一个X对象,这样子就完成了循环依赖。然后完成对X的属性注入,然后进行初始化,会触发一些aware的回调方法,如果有代理生成代理,还会完成一些事件的发布,最后得到一个最终的bean,放到单例池中。

Spring 不支持原型模式的任何循环依赖:是因为原型模式,是用到的时候才会创建,不能提前暴露。

Spring 也不支持单例模式的构造循环依赖

查看源码能证明实例化、属性赋值和初始化这三个生命周期的存在。至于销毁,是在容器关闭时调用的,详见ConfigurableApplicationContext#close()。

三、Spring bean的作用域(scope)

  • singleton(默认):单例,当一个bean被标识为singleton时 候,spring的IOC容器中只会存在一个该bean。只要容器不销毁或退出,该类型的bean的单一实例就会一直存活。
  • prototype:多例,每一次请求(将其注入到另一个bean中,或者以程序的方式调用容器的 getBean()方法)都会产生一个新的bean实例。对象销毁的时候,spring容器不会调用任何方法。因为不是单例,这个类型的对象有很多个,spring容器一旦把这个对象交给请求方之后,就不再管理这个对象了。

request,session和global session类型只实用于web程序,通常是和XmlWebApplicationContext共同使用

  • request:每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效。
  • session:每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效
  • global session:作用域类似于标准的HTTP Session作用域,但是它仅在基于portlet的web应用中才有意义。Portlet规范定义了全局Session的概念,它被所有构成某个 portlet web应用的各种不同的portlet所共享,如果普通的servlet的web 应用中使用了这个scope,容器会把它作为普通的session的scope对待。

四、Spring事务的传播机制

  • REQUIRED(Spring默认的事务传播类型):如果当前没有事务,则自己新建一个事务,如果当前存在事务,则加入这个事务。
  • SUPPORTS:当前存在事务,则加入当前事务,如果当前没有事务,就以非事务方法执行。
  • MANDATORY:当前存在事务,则加入当前事务,如果当前事务不存在,则抛出异常。
  • REQUIRES_NEW:创建一个新事务,如果存在当前事务,则挂起该事务。
  • NOT_SUPPORTED:始终以非事务方式执行,如果当前存在事务,则挂起当前事务。
  • NEVER:不使用事务,如果当前事务存在,则抛出异常。
  • NESTED:如果当前事务存在,则在嵌套事务中执行,否则REQUIRED的操作一样(开启一个事务)

NESTED需要注意两点:

  • 和REQUIRES_NEW的区别
REQUIRES_NEW是新建一个事务并且新开启的这个事务与原有事务无关,而NESTED则是当前存在事务时(我们把当前事务称之为父事务)会开启一个嵌套事务(称之为一个子事务)。
在NESTED情况下父事务回滚时,子事务也会回滚,而在REQUIRES_NEW情况下,原有事务回滚,不会影响新开启的事务。
  • 和REQUIRED的区别
REQUIRED情况下,调用方存在事务时,则被调用方和调用方使用同一事务,那么被调用方出现异常时,由于共用一个事务,所以无论调用方是否catch其异常,事务都会回滚
而在NESTED情况下,被调用方发生异常时,调用方可以catch其异常,这样只有子事务回滚,父事务不受影响

带你读懂Spring 事务——事务的传播机制 - 知乎

事物需要注意的地方:

1、因为spring的事物是用代理模式实现的,如果是一个不带事务的方法调用该类的带事务的方法,直接通过this.xxx()调用,而不生成代理事务,所以事务不起作用。

JDK的动态代理。只有被动态代理直接调用时才会产生事务。在SpringIoC容器中返回的调用的对象是代理对象而不是真实的对象。而这里的this是EmployeeService真实对象而不是代理对象。

2、Spring的事务管理默认只对出现运行期异常(java.lang.RuntimeException及其子类)进行回滚(至于为什么spring要这么设计:因为spring认为Checked的异常属于业务的,coder需要给出解决方案而不应该直接扔该框架),可以添加rollbackfor=Exception.class来表示所有的Exception都回滚。

3、入口的方法必须是public,否则事务不起作用(这一点由Spring的AOP特性决定的,理论上而言,不public也能切入,但spring可能是觉得private自己用的方法,应该自己控制,不应该用事务切进去吧)。另外private 方法, final 方法 和 static 方法不能添加事务,加了也不生效。

【小家java】Spring事务不生效的原因大解读_YourBatman的博客-CSDN博客

五、springboot启动过程

  • 1、加载并解析springboot配置文件:springboot会在classpath中查找application.properties或者application.yml文件,并读取其中的配置信息。
  • 2、加载自动配置类:springboot会自动加载自动配置类,并根据配置文件中的信息自动配置Bean。
  • 3、加载用户定义的Bean:会扫描用户定义的Bean,并将其添加到容器中。
  • 4、启动spring应用上下文:会创建并启动spring应用上下文,报考加载所有的Bean、执行Bean的生命周期方法等。
  • 5、启动web应用上下文:如果是web应用,则会创建并启动web应用上下文,包括加载所有的Servlet、Filter和监听器等。
  • 6、启动嵌入式服务器:如果应用需要启动嵌入式服务器,则会根据配置文件中的信息启动相应的服务器。
  • 7、运行应用程序:当所有的Bean都加载完毕,并且嵌入式服务器也启动成功后,会运行应用程序。

1、创建SpringApplication对象。

2、运行run方法

3、常用注解

@SpringBootApplication 包含@Configuration、@EnableAutoConfiguration、@ComponentScan通常用在主类上。

@Repository 用于标注数据访问组件,即DAO组件。

@Service 用于标注业务层组件。

@RestController 用于标注控制层组件(如struts中的action),包含@Controller和@ResponseBody

@ResponseBody 会直接返回json数据。

@RequestMapping 是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。

六、SpringMvc执行流程

从输入网址到获得页面的过程

  (1). 浏览器查询 DNS,获取域名对应的IP地址:具体过程包括浏览器搜索自身的DNS缓存、搜索操作系统的DNS缓存、读取本地的Host文件和向本地DNS服务器进行查询等。对于向本地DNS服务器进行查询,如果要查询的域名包含在本地配置区域资源中,则返回解析结果给客户机,完成域名解析(此解析具有权威性);如果要查询的域名不由本地DNS服务器区域解析,但该服务器已缓存了此网址映射关系,则调用这个IP地址映射,完成域名解析(此解析不具有权威性)。如果本地域名服务器并未缓存该网址映射关系,那么将根据其设置发起递归查询或者迭代查询;

  (2). 浏览器获得域名对应的IP地址以后,浏览器向服务器请求建立链接,发起三次握手;

  (3). TCP/IP链接建立起来后,浏览器向服务器发送HTTP请求;

  (4). 服务器接收到这个请求,并根据路径参数映射到特定的请求处理器进行处理,并将处理结果及相应的视图返回给浏览器;

  (5). 浏览器解析并渲染视图,若遇到对js文件、css文件及图片等静态资源的引用,则重复上述步骤并向服务器请求这些资源;

  (6). 浏览器根据其请求到的资源、数据渲染页面,最终向用户呈现一个完整的页面。

@RequestMapping:用于处理请求 url 映射的注解,可用于类或方法上。用于类上,则表示类中的所有响应请求的方法都是以该地址作为父路径。

@RequestBody:注解实现接收http请求的json数据,将json转换为java对象。

@ResponseBody:注解实现将conreoller方法返回对象转化为json对象响应给客户。

七、Mybaties的工作原理

mybatis应用程序通过SqlSessionFactoryBuilder从mybatis-config.xml配置文件(也可以用Java文件配置的方式,需要添加@Configuration)来构建SqlSessionFactory(SqlSessionFactory是线程安全的);

然后,SqlSessionFactory的实例直接开启一个SqlSession,再通过SqlSession实例获得Mapper对象并运行Mapper映射的SQL语句,完成对数据库的CRUD和事务提交,之后关闭SqlSession。

说明:SqlSession是单线程对象,因为它是非线程安全的,是持久化操作的独享对象,类似jdbc中的Connection,底层就封装了jdbc连接。

详细流程如下:

1、加载mybatis全局配置文件(数据源、mapper映射文件等),解析配置文件,MyBatis基于XML配置文件生成Configuration,和一个个MappedStatement(包括了参数映射配置、动态SQL语句、结果映射配置),其对应着<select | update | delete | insert>标签项。

2、SqlSessionFactoryBuilder通过Configuration对象生成SqlSessionFactory,用来开启SqlSession。

3、SqlSession对象完成和数据库的交互:

  • 用户程序调用mybatis接口层api(即Mapper接口中的方法)
  • SqlSession通过调用api的Statement ID找到对应的MappedStatement对象
  • 通过Executor(负责动态SQL的生成和查询缓存的维护)将MappedStatement对象进行解析,sql参数转化、动态sql拼接,生成jdbc Statement对象
  • JDBC执行sql。
  • 借助MappedStatement中的结果映射关系,将返回结果转化成HashMap、JavaBean等存储结构并返回。

八、Mybatis缓存

Mybatis提供一级缓存机制和二级环境机制,默认为一级缓存。

  • 一级缓存是SqlSession级别的缓存,每个SqlSession类的实例对象中有一个Map用来存储缓存数据,不同的SqlSession类的实例对象缓存的数据是互不影响的。当在同一个SqlSession中执行两次相同的sql语句时,第一次执行完毕会将数据缓存到内存中,第二次查询不执行sql直接从内存中获取。
  • 二级缓存是mapper级别的缓存,一个Mapper有一个自己的二级缓存区域(按照namespace划分),多个SqlSession类的实例对象可以共用二级缓存,二级缓存是跨SqlSession的。

二级缓存回收策略:

  • LRU (最近最少使用的): 移除最长时间不被使用的对象,这是默认值 。
  • FIFO (先进先出 ): 按对象进入缓存的顺序来移除它们 。
  • SOFT (软引用) : 移除基于垃圾回收器状态和软引用规则的对象 。
  • WEAK (弱引用) : 更积极地移除基于垃圾收集器状态和弱引用规则的对象。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值