Spring

Posted by 盈盈冲哥 on March 5, 2020

Spring

  • 什么是 Spring 框架?

    Spring 是一种轻量级开发框架,旨在提高开发人员的开发效率以及系统的可维护性。Spring 官网:https://spring.io/。

    我们一般说 Spring 框架指的都是 Spring Framework,它是很多模块的集合,使用这些模块可以很方便地协助我们进行开发。这些模块是:核心容器、数据访问/集成、Web、AOP(面向切面编程)、工具、消息和测试模块。比如:Core Container 中的 Core 组件是Spring 所有组件的核心,Beans 组件和 Context 组件是实现IOC和依赖注入的基础,AOP组件用来实现面向切面编程。

    Spring 官网列出的 Spring 的 6 个特征:

    核心技术 :依赖注入(DI),AOP,事件(events),资源,i18n,验证,数据绑定,类型转换,SpEL。 测试 :模拟对象,TestContext框架,Spring MVC 测试,WebTestClient。 数据访问 :事务,DAO支持,JDBC,ORM,编组XML。 Web支持 : Spring MVC和Spring WebFlux Web框架。 集成 :远程处理,JMS,JCA,JMX,电子邮件,任务,调度,缓存。 语言 :Kotlin,Groovy,动态语言。

  • 列举一些重要的Spring模块?

    下图对应的是 Spring4.x 版本。目前最新的5.x版本中 Web 模块的 Portlet 组件已经被废弃掉,同时增加了用于异步响应式处理的 WebFlux 组件。

    img

    • Spring Core: 基础,可以说 Spring 其他所有的功能都需要依赖于该类库。主要提供 IoC 依赖注入功能。
    • Spring Aspects : 该模块为与AspectJ的集成提供支持。
    • Spring AOP :提供了面向切面的编程实现。
    • Spring JDBC : Java数据库连接。
    • Spring JMS :Java消息服务。
    • Spring ORM : 用于支持Hibernate等ORM工具。
    • Spring Web : 为创建Web应用程序提供支持。
    • Spring Test : 提供了对 JUnit 和 TestNG 测试的支持。
  • @RestController vs @Controller

    Controller 返回一个页面

    单独使用 @Controller 不加 @ResponseBody的话一般使用在要返回一个视图的情况,这种情况属于比较传统的Spring MVC 的应用,对应于前后端不分离的情况。

    @RestController 返回JSON 或 XML 形式数据

    但@RestController只返回对象,对象数据直接以 JSON 或 XML 形式写入 HTTP 响应(Response)中,这种情况属于 RESTful Web服务,这也是目前日常开发所接触的最常用的情况(前后端分离)。

    @Controller + @ResponseBody 返回JSON 或 XML 形式数据

    如果你需要在Spring4之前开发 RESTful Web服务的话,你需要使用@Controller 并结合@ResponseBody注解,也就是说 @Controller + @ResponseBody = @RestController(Spring 4 之后新加的注解)。

    @ResponseBody 注解的作用是将 Controller 的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到HTTP 响应(Response)对象的 body 中,通常用来返回 JSON 或者 XML 数据,返回 JSON 数据的情况比较多。

  • Spring IOC & AOP

    谈谈自己对于 Spring IoC 和 AOP 的理解

    IoC

    IoC(Inverse of Control:控制反转)是一种设计思想,就是 将原本在程序中手动创建对象的控制权,交由Spring框架来管理。 IoC 在其他语言中也有应用,并非 Spring 特有。 IoC 容器是 Spring 用来实现 IoC 的载体, IoC 容器实际上就是个Map(key,value),Map 中存放的是各种对象

    将对象之间的相互依赖关系交给 IoC 容器来管理,并由 IoC 容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。 IoC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。 在实际项目中一个 Service 类可能有几百甚至上千个类作为它的底层,假如我们需要实例化这个 Service,你可能要每次都要搞清这个 Service 所有底层类的构造函数,这可能会把人逼疯。如果利用 IoC 的话,你只需要配置好,然后在需要的地方引用就行了,这大大增加了项目的可维护性且降低了开发难度。

    Spring 时代我们一般通过 XML 文件来配置 Bean,后来开发人员觉得 XML 文件来配置不太好,于是 SpringBoot 注解配置就慢慢开始流行起来。

    什么是DI机制?

    依赖注入(Dependecy Injection)和控制反转(Inversion of Control)是同一个概念,具体的讲:当某个角色 需要另外一个角色协助的时候,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在spring中 创建被调用者的工作不再由调用者来完成,因此称为控制反转。创建被调用者的工作由spring来完成,然后注入调用者 因此也称为依赖注入。

    依赖注入可以通过setter方法注入(设值注入)、构造器注入和接口注入三种方式来实现,Spring支持setter注入构造器注入,通常使用构造器注入来注入必须的依赖关系,对于可选的依赖关系,则setter注入是更好的选择,setter注入需要类提供无参构造器或者无参的静态工厂方法来创建对象。

    ioc:控制反转、依赖注入(核心原理:反射机制)(优点:借助于第三方IOC容器实现对象之间的依赖关系解耦)

    aop:分离应用的业务逻辑和系统级服务(如日志、事务管理、安全权限等)(核心原理:代理模式(动态代理))

    代理模式(静态代理、动态代理):(在不修改被代理对象源码的基础上,进行功能增强)

    AOP

    AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。

    Spring AOP就是基于动态代理的,如果要代理的对象,实现了某个接口,那么Spring AOP会使用JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候Spring AOP会使用Cglib ,这时候Spring AOP会使用 Cglib 生成一个被代理对象的子类来作为代理,如下图所示:

    img

    当然你也可以使用 AspectJ ,Spring AOP 已经集成了AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。

    使用 AOP 之后我们可以把一些通用功能抽象出来,在需要用到的地方直接使用即可,这样大大简化了代码量。我们需要增加新功能时也方便,这样也提高了系统扩展性。日志功能、事务管理等等场景都用到了 AOP 。

    Spring AOP 和 AspectJ AOP 有什么区别?

    Spring AOP 属于运行时增强,而 AspectJ 是编译时增强。Spring AOP 基于代理(Proxying),而 AspectJ 基于字节码操作(Bytecode Manipulation)。

    Spring AOP 已经集成了 AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。AspectJ 相比于 Spring AOP 功能更加强大,但是 Spring AOP 相对来说更简单,如果我们的切面比较少,那么两者性能差异不大。但是,当切面太多的话,最好选择 AspectJ ,它比Spring AOP 快很多。

  • Spring如何解决循环依赖(三级缓存)

    https://blog.csdn.net/github_38687585/article/details/82317674

    一、循环依赖的产生和解决的前提

    循环依赖的产生可能有很多种情况,例如:

    1. A的构造方法中依赖了B的实例对象,同时B的构造方法中依赖了A的实例对象
    2. A的构造方法中依赖了B的实例对象,同时B的某个field或者setter需要A的实例对象,以及反之
    3. A的某个field或者setter依赖了B的实例对象,同时B的某个field或者setter依赖了A的实例对象,以及反之

    Spring对于循环依赖的解决不是无条件的,首先前提条件是针对scope单例并且没有显式指明不需要解决循环依赖的对象,而且要求该对象没有被代理过。同时Spring解决循环依赖也不是万能,以上三种情况只能解决两种,第一种在构造方法中相互依赖的情况Spring也无力回天。

    二、Spring循环依赖的理论依据

    Spring循环依赖的理论依据其实是Java基于引用传递,当我们获取到对象的引用时,对象的field或者或属性是可以延后设置的。

    Spring单例对象的初始化其实可以分为三步:(实例化、填充属性、初始化)

    1. createBeanInstance, 实例化,实际上就是调用对应的构造方法构造对象,此时只是调用了构造方法,spring xml中指定的property并没有进
    2. populate:populateBean,填充属性,这步对spring xml中指定的property进行populate
    3. initializeBean,调用spring xml中指定的init方法,或者AfterPropertiesSet方法

    会发生循环依赖的步骤集中在第一步和第二步。

    三、三级缓存

    对于单例对象来说,在Spring的整个容器的生命周期内,有且只存在一个对象,很容易想到这个对象应该存在Cache中,Spring大量运用了Cache的手段,在循环依赖问题的解决过程中甚至使用了“三级缓存”。

    “三级缓存”主要是指

    1
    2
    3
    4
    5
    6
    7
    8
    
    /** Cache of singleton objects: bean name --> bean instance */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
    
    /** Cache of singleton factories: bean name --> ObjectFactory */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
    
    /** Cache of early singleton objects: bean name --> bean instance */
    private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
    

    从字面意思来说:singletonObjects指单例对象的cache,singletonFactories指单例对象工厂的cache,earlySingletonObjects指提前曝光的单例对象的cache。以上三个cache构成了三级缓存,Spring就用这三级缓存巧妙的解决了循环依赖问题。

    什么是三级缓存

    • 第一级缓存:单例缓存池singletonObjects
    • 第二级缓存:早期提前暴露的对象缓存earlySingletonObjects。(属性还没有值,对象也没有被初始化
    • 第三级缓存:singletonFactories单例对象工厂缓存。

    分析getSingleton的整个过程,Spring首先从singletonObjects(一级缓存)中尝试获取,如果获取不到并且对象在创建中,则尝试从earlySingletonObjects(二级缓存)中获取,如果还是获取不到并且允许从singletonFactories通过getObject获取,则通过singletonFactory.getObject()(三级缓存)获取。如果获取到了则将singletonObject放入到earlySingletonObjects,也就是 将三级缓存提升到二级缓存中!

  • 你如何理解AOP中的连接点(Joinpoint)、切点(Pointcut)、增强(Advice)、引介(Introduction)、织入(Weaving)、切面(Aspect)这些概念?

    a. 连接点(Joinpoint):程序执行的某个特定位置(如:某个方法调用前、调用后,方法抛出异常后)。一个类或一段程序代码拥有一些具有边界性质的特定点,这些代码中的特定点就是连接点。Spring仅支持方法的连接点。

    b. 切点(Pointcut):如果连接点相当于数据中的记录,那么切点相当于查询条件,一个切点可以匹配多个连接点。Spring AOP的规则解析引擎负责解析切点所设定的查询条件,找到对应的连接点。

    c. 增强(Advice):增强是织入到目标类连接点上的一段程序代码。Spring提供的增强接口都是带方位名的,如:BeforeAdvice、AfterReturningAdvice、ThrowsAdvice等。*

    d. 引介(Introduction):引介是一种特殊的增强,它为类添加一些属性和方法。这样,即使一个业务类原本没有实现某个接口,通过引介功能,可以动态的为该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。

    e. 织入(Weaving):织入是将增强添加到目标类具体连接点上的过程,AOP有三种织入方式:①编译期织入:需要特殊的Java编译期(例如AspectJ的ajc);②装载期织入:要求使用特殊的类加载器,在装载类的时候对类进行增强;③运行时织入:在运行时为目标类生成代理实现增强。Spring采用了动态代理的方式实现了运行时织入,而AspectJ采用了编译期织入和装载期织入的方式。

    f. 切面(Aspect):切面是由切点和增强(引介)组成的,它包括了对横切关注功能的定义,也包括了对连接点的定义。

  • AOP的原理是什么?

    Java核心面试知识整理

    Spring 提供了两种方式来生成代理对象: JDKProxy 和Cglib,具体使用哪种方式生成由AopProxyFactory 根据AdvisedSupport 对象的配置来决定。默认的策略是如果目标类是接口,则使用JDK 动态代理技术,否则使用Cglib 来生成代理。

    • JDK动态接口代理

      JDK 动态代理主要涉及到java.lang.reflect 包中的两个类:Proxy 和InvocationHandler。InvocationHandler 是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编制在一起。Proxy 利用InvocationHandler 动态创建一个符合某一接口的实例,生成目标类的代理对象。

    • CGLib 动态代理

      CGLib 全称为Code Generation Library,是一个强大的高性能,高质量的代码生成类库,可以在运行期扩展Java 类与实现Java 接口,CGLib 封装了asm,可以再运行期动态生成新的class。和JDK 动态代理相比较:JDK 创建代理有一个限制,就是只能为接口创建代理实例,而对于没有通过接口定义业务方法的类,则可以通过CGLib 创建动态代理。

    https://juejin.im/post/5bf4fc84f265da611b57f906

    AOP简介

    相信大家或多或少的了解过AOP,都知道它是面向切面编程,在网上搜索可以找到很多的解释。这里我用一句话来总结:AOP是能够让我们在不影响原有功能的前提下,为软件横向扩展功能。 那么横向扩展怎么理解呢,我们在WEB项目开发中,通常都遵守三层原则,包括控制层(Controller)->业务层(Service)->数据层(dao),那么从这个结构下来的为纵向,它具体的某一层就是我们所说的横向。我们的AOP就是可以作用于这某一个横向模块当中的所有方法。

    我们在来看一下AOP和OOP的区别:AOP是OOP的补充,当我们需要为多个对象引入一个公共行为,比如日志,操作记录等,就需要在每个对象中引用公共行为,这样程序就产生了大量的重复代码,使用AOP可以完美解决这个问题。

    接下来介绍一下提到AOP就必须要了解的知识点:

    • 切面:拦截器类,其中会定义切点以及通知

    • 切点:具体拦截的某个业务点。

    • 通知:切面当中的方法,声明通知方法在目标业务层的执行位置,通知类型如下:

      1. 前置通知:@Before 在目标业务方法执行之前执行

      2. 后置通知:@After 在目标业务方法执行之后执行

      3. 返回通知:@AfterReturning 在目标业务方法返回结果之后执行

      4. 异常通知:@AfterThrowing 在目标业务方法抛出异常之后

      5. 环绕通知:@Around 功能强大,可代替以上四种通知,还可以控制目标业务方法是否执行以及何时执行

    代码中实现举例

    上面已经大概的介绍了AOP中需要了解的基本知识,也知道了AOP的好处,那怎么在代码中实现呢?给大家举个例子:我们现在有个学校管理系统,已经实现了对老师和学生的增删改,又新来个需求,说是对老师和学生的每次增删改做一个记录,到时候校长可以查看记录的列表。那么问题来了,怎么样处理是最好的解决办法呢?这里我罗列了三种解决办法,我们来看下他的优缺点。

    img

    最简单的就是第一种方法,我们直接在每次的增删改的函数当中直接实现这个记录的方法,这样代码的重复度太高,耦合性太强,不建议使用。

    其次就是我们最长使用的,将记录这个方法抽离出来,其他的增删改调用这个记录函数即可,显然代码重复度降低,但是这样的调用还是没有降低耦合性。

    这个时候我们想一下AOP的定义,再想想我们的场景,其实我们就是要在不改变原来增删改的方法,给这个系统增加记录的方法,而且作用的也是一个层面的方法。这个时候我们就可以采用AOP来实现了。

    我们来看下代码的具体实现:

    1. 首先我定义了一个自定义注解作为切点
      1
      2
      3
      
      @Target(AnnotationTarget.FUNCTION)  //注解作用的范围,这里声明为函数
      @Order(Ordered.HIGHEST_PRECEDENCE)  //声明注解的优先级为最高,假设有多个注解,先执行这个
      annotation class Hanler(val handler: HandlerType)  //自定义注解类,HandlerType是一个枚举类型,里面定义的就是学生和老师的增删改操作,在这里就不展示具体内容了
      
    2. 接下来就是要定义切面类了
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      
      @Aspect   //该注解声明这个类为一个切面类
      @Component
      class HandlerAspect{
        
       @Autowired
       private lateinit var handlerService: HandlerService
        
       @AfterReturning("@annotation(handler)")   //当有函数注释了注解,将会在函数正常返回后在执行我们定义的方法
       fun hanler(hanler: Hanler) {
           handlerService.add(handler.operate.value)   //这里是真正执行记录的方法
       }
      }
      
    3. 最后就是我们本来的业务方法了
      1
      2
      3
      4
      5
      6
      7
      
      /**
      * 删除学生方法
      */
      @Handler(operate= Handler.STUDENT_DELETE)   //当执行到删除学生方法时,切面类就会起作用了,当学生正常删除后就会执行记录方法,我们就可以看到记录方法生成的数据
      fun delete(idString) {
        studentService.delete(id)
      }
      

    AOP实现原理

    我们现在了解了代码中如何实现,那么AOP实现的原理是什么呢?之前看了一个博客说到,提到AOP大家都知道他的实现原理是动态代理,显然我之前就是不知道的,哈哈,但是相信阅读文章的你们一定是知道的。

    讲到动态代理就不得不说代理模式了, 代理模式的定义:给某一个对象提供一个代理,并由代理对象控制对原对象的引用。代理模式包含如下角色:subject:抽象主题角色,是一个接口。该接口是对象和它的代理共用的接口; RealSubject:真实主题角色,是实现抽象主题接口的类; Proxy:代理角色,内部含有对真实对象RealSubject的引用,从而可以操作真实对象。代理对象提供与真实对象相同的接口,以便代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。如下图所示:

    img

    img

    那么代理又分为静态代理和动态代理,这里写两个小的demo,动态代理采用的就是JDK代理。举个例子就是现在一个班上的学生需要交作业,现在由班长代理交作业,那么班长就是代理,学生就是被代理的对象。

    • 静态代理

      首先,我们创建一个Person接口。这个接口就是学生(被代理类),和班长(代理类)的公共接口,他们都有交作业的行为。这样,学生交作业就可以让班长来代理执行。

      1
      2
      3
      4
      5
      6
      7
      8
      
      /**
      * Created by Mapei on 2018/11/7
      * 创建person接口
      */
      public interface Person {
          //交作业
          void giveTask();
      }
      

      Student类实现Person接口,Student可以具体实施交作业这个行为。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      
      /**
      * Created by Mapei on 2018/11/7
      */
      public class Student implements Person {
          private String name;
          public Student(String name) {
              this.name = name;
          }
      
          public void giveTask() {
              System.out.println(name + "交语文作业");
          }
      }
      

      StudentsProxy类,这个类也实现了Person接口,但是还另外持有一个学生类对象,那么他可以代理学生类对象执行交作业的行为。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      
      /**
      * Created by Mapei on 2018/11/7
      * 学生代理类,也实现了Person接口,保存一个学生实体,这样就可以代理学生产生行为
      */
      public class StudentsProxy implements Person{
          //被代理的学生
          Student stu;
      
          public StudentsProxy(Person stu) {
              // 只代理学生对象
              if(stu.getClass() == Student.class) {
                  this.stu = (Student)stu;
              }
          }
      
          //代理交作业,调用被代理学生的交作业的行为
          public void giveTask() {
              stu.giveTask();
          }
      }
      

      下面测试一下,看代理模式如何使用:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      
      /**
      * Created by Mapei on 2018/11/7
      */
      public class StaticProxyTest {
          public static void main(String[] args) {
              //被代理的学生林浅,他的作业上交有代理对象monitor完成
              Person linqian = new Student("林浅");
      
              //生成代理对象,并将林浅传给代理对象
              Person monitor = new StudentsProxy(linqian);
      
              //班长代理交作业
              monitor.giveTask();
          }
      }
      

      这里并没有直接通过林浅(被代理对象)来执行交作业的行为,而是通过班长(代理对象)来代理执行了。这就是代理模式。代理模式就是在访问实际对象时引入一定程度的间接性,这里的间接性就是指不直接调用实际对象的方法,那么我们在代理过程中就可以加上一些其他用途。比如班长在帮林浅交作业的时候想告诉老师最近林浅的进步很大,就可以轻松的通过代理模式办到。在代理类的交作业之前加入方法即可。这个优点就可以运用在spring中的AOP,我们能在一个切点之前执行一些操作,在一个切点之后执行一些操作,这个切点就是一个个方法。这些方法所在类肯定就是被代理了,在代理过程中切入了一些其他操作。

    • 动态代理

      动态代理和静态代理的区别是,静态代理的的代理类是我们自己定义好的,在程序运行之前就已经编译完成,但是动态代理的代理类是在程序运行时创建的。 相比于静态代理,动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。比如我们想在每个代理方法之前都加一个处理方法,我们上面的例子中只有一个代理方法,如果还有很多的代理方法,就太麻烦了,我们来看下动态代理是怎么去实现的。

      首先还是定义一个Person接口:

      1
      2
      3
      4
      5
      6
      7
      8
      
      /**
      * Created by Mapei on 2018/11/7
      * 创建person接口
      */
      public interface Person {
          //交作业
          void giveTask();
      }
      

      接下来是创建需要被代理的实际类,也就是学生类:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      
      /**
      * Created by Mapei on 2018/11/7
      */
      public class Student implements Person {
          private String name;
          public Student(String name) {
              this.name = name;
          }
      
          public void giveTask() {
              System.out.println(name + "交语文作业");
          }
      }
      

      创建StuInvocationHandler类,实现InvocationHandler接口,这个类中持有一个被代理对象的实例target。InvocationHandler中有一个invoke方法,所有执行代理对象的方法都会被替换成执行invoke方法。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      
      /**
      * Created by Mapei on 2018/11/7
      */
      public class StuInvocationHandler<T> implements InvocationHandler {
          //invocationHandler持有的被代理对象
          T target;
      
          public StuInvocationHandler(T target) {
              this.target = target;
          }
      
          /**
          * proxy:代表动态代理对象
          * method:代表正在执行的方法
          * args:代表调用目标方法时传入的实参
          */
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              System.out.println("代理执行" +method.getName() + "方法");
              Object result = method.invoke(target, args);
              return result;
          }
      }
      

      那么接下来我们就可以具体的创建代理对象了。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      
      /**
      * Created by Mapei on 2018/11/7
      * 代理类
      */
      public class ProxyTest {
          public static void main(String[] args) {
      
              //创建一个实例对象,这个对象是被代理的对象
              Person linqian = new Student("林浅");
      
              //创建一个与代理对象相关联的InvocationHandler
              InvocationHandler stuHandler = new StuInvocationHandler<Person>(linqian);
      
              //创建一个代理对象stuProxy来代理linqian,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
              Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);
      
              //代理执行交作业的方法
              stuProxy.giveTask();
          }
      }
      

      我们执行代理测试类,首先我们创建了一个需要被代理的学生林浅,将林浅传入stuHandler中,我们在创建代理对象stuProxy时,将stuHandler作为参数,那么所有执行代理对象的方法都会被替换成执行invoke方法,也就是说,最后执行的是StuInvocationHandler中的invoke方法。所以在看到下面的运行结果也就理所当然了。

  • JDK动态代理为什么必须针对接口

    https://www.cnblogs.com/WeidLang/p/9857495.html

    由于java的单继承,动态生成的代理类已经继承了Proxy类的,就不能再继承其他的类,所以只能靠实现被代理类的接口的形式,故JDK的动态代理必须有接口。

  • aop的应用场景有哪些?

    https://zhuanlan.zhihu.com/p/83204146

    为什么会有面向切面编程(AOP)?

    我们知道Java是一个面向对象(OOP)的语言,但它有一些弊端,比如当我们需要为多个不具有继承关系的对象引入一个公共行为,例如日志、权限验证、事务等功能时,只能在在每个对象里引用公共行为。这样做不便于维护,而且有大量重复代码。AOP的出现弥补了OOP的这点不足。

    Spring AOP 中设计的一些核心知识,面试问题?

    1、能说一下Spring AOP用的是哪种设计模式?

    回答:代理模式。

    2、 能简单聊一下你对代理模式的理解吗?

    记住一些贴近日常的示例方便理解,如买火车票,Windows 里面的快捷方式…

    3、 知道JDK代理和Cglib代理有什么区别?

    我们不需要创建代理类,JDK 在运行时为我们动态的来创建,JDK代理是接口

    若目标类不存在接口,则使用Cglib生成代理

    不管是JDK代理还是Cglib代理本质上都是对字节码进行操作

    4、让你实现一个JDK实现动态代理?你的思路是什么?

    Proxy: 定义一个自己的Proxy类

    InvocationHandler:定义一个自己的InvocationHandler类

    ClassLoad:自定义类加载器(方便加载我们自己指定的路径下面的类)

    SpringAOP的在实际应用中场景有哪些?

    1. Authentication 权限
    2. Caching 缓存
    3. Context passing 内容传递
    4. Error handling 错误处理
    5. Lazy loading 懒加载
    6. Debugging 调试
    7. logging,tracing,profiling and monitoring 记录跟踪 优化 校准
    8. Performance optimization 性能优化
    9. Persistence 持久化
    10. Resource pooling 资源池
    11. Synchronization 同步
    12. Transactions 事务
    13. Logging 日志
  • Spring 中的 bean 的作用域有哪些?

    • singleton : 唯一 bean 实例,Spring 中的 bean 默认都是单例的。
    • prototype : 每次请求都会创建一个新的 bean 实例。

    Spring 2.x中针对WebApplicationContext新增了3个作用域

    • request : 每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。
    • session : 每一次HTTP请求都会产生一个新的 bean,该bean仅在当前 HTTP session 内有效。
    • global-session: 全局session作用域,仅仅在基于portlet的web应用中才有意义,Spring5已经没有了。Portlet是能够生成语义代码(例如:HTML)片段的小型Java Web插件。它们基于portlet容器,可以像servlet一样处理HTTP请求。但是,与 servlet 不同,每个 portlet 都有不同的会话
  • Spring 中的单例 bean 的线程安全问题了解吗?

    大部分时候我们并没有在系统中使用多线程,所以很少有人会关注这个问题。单例 bean 存在线程问题,主要是因为当多个线程操作同一个对象的时候,对这个对象的非静态成员变量的写操作会存在线程安全问题。

    常见的有两种解决办法:

    1. 在Bean对象中尽量避免定义可变的成员变量(不太现实)。

    2. 在类中定义一个ThreadLocal成员变量,将需要的可变成员变量保存在 ThreadLocal 中(推荐的一种方式)。

  • @Component 和 @Bean 的区别是什么?

    作用对象不同: @Component 注解作用于类,而@Bean注解作用于方法。

    @Component通常是通过类路径扫描来自动侦测以及自动装配到Spring容器中(我们可以使用 @ComponentScan 注解定义要扫描的路径从中找出标识了需要装配的类自动装配到 Spring 的 bean 容器中)。@Bean 注解通常是我们在标有该注解的方法中定义产生这个 bean,@Bean告诉了Spring这是某个类的示例,当我需要用它的时候还给我。

    @Bean 注解比 Component 注解的自定义性更强,而且很多地方我们只能通过 @Bean 注解来注册bean。比如当我们引用第三方库中的类需要装配到 Spring容器时,则只能通过 @Bean来实现。

    @Bean注解使用示例:

    1
    2
    3
    4
    5
    6
    7
    
    @Configuration
    public class AppConfig {
        @Bean
        public TransferService transferService() {
            return new TransferServiceImpl();
        }
    }
    

    上面的代码相当于下面的 xml 配置

    1
    2
    3
    
    <beans>
        <bean id="transferService" class="com.acme.TransferServiceImpl"/>
    </beans>
    

    下面这个例子是通过 @Component 无法实现的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    @Bean
    public OneService getService(status) {
        case (status)  {
            when 1:
                    return new serviceImpl1();
            when 2:
                    return new serviceImpl2();
            when 3:
                    return new serviceImpl3();
        }
    }
    
  • 在以前的学习中有使用过Spring里面的注解吗?如果有请谈一下autowired 和resource区别是什么?

    1、共同点

    两者都可以写在字段和setter方法上。两者如果都写在字段上,那么就不需要再写setter方法。

    2、不同点

    (1)@Autowired

    @Autowired为Spring提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired;只按照byType注入。

    @Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如果我们想使用按照名称(byName)来装配,可以结合@Qualifier注解一起使用。

    (2)@Resource

    @Resource默认按照ByName自动注入,由J2EE提供,需要导入包javax.annotation.Resource。@Resource有两个重要的属性:name和type,Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不制定name也不制定type属性,这时将通过反射机制使用byName自动注入策略。

    https://blog.csdn.net/u010648555/article/details/76299467

    Spring常用注解

    一: 组件类注解

    @Component :标准一个普通的spring Bean类。

    @Repository:标注一个DAO组件类。

    @Service:标注一个业务逻辑组件类。

    @Controller:标注一个控制器组件类。

    @Component可以代替@Repository、@Service、@Controller,因为这三个注解是被@Component标注的。

    二:装配bean时常用的注解

    @Autowired:属于Spring 的org.springframework.beans.factory.annotation包下,可用于为类的属性、构造器、方法进行注值

    @Resource:不属于spring的注解,而是来自于JSR-250位于java.annotation包下,使用该annotation为目标bean指定协作者Bean。

    @PostConstruct 和 @PreDestroy 方法 实现初始化和销毁bean之前进行的操作

    相同点

    @Resource的作用相当于@Autowired,均可标注在字段或属性的setter方法上。

    不同点

    a:提供方

    @Autowired是Spring的注解,@Resource是javax.annotation注解,而是来自于JSR-250,J2EE提供,需要JDK1.6及以上。

    b :注入方式

    @Autowired只按照Type 注入;@Resource默认按Name自动注入,也提供按照Type 注入;

    c:属性

    @Autowired注解可用于为类的属性、构造器、方法进行注值。默认情况下,其依赖的对象必须存在(bean可用),如果需要改变这种默认方式,可以设置其required属性为false。 还有一个比较重要的点就是,@Autowired注解默认按照类型装配,如果容器中包含多个同一类型的Bean,那么启动容器时会报找不到指定类型bean的异常,解决办法是结合@Qualifier注解进行限定,指定注入的bean名称。

    @Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如果我们想使用按照名称(byName)来装配,可以结合@Qualifier注解一起使用。

    @Resource有两个中重要的属性:name和type。name属性指定byName,如果没有指定name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象。 需要注意的是,@Resource如果没有指定name属性,并且按照默认的名称仍然找不到依赖对象时, @Resource注解会回退到按类型装配。但一旦指定了name属性,就只能按名称装配了。

    @Resource默认按照ByName自动注入,由J2EE提供,需要导入包javax.annotation.Resource。@Resource有两个重要的属性:name和type,Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不制定name也不制定type属性,这时将通过反射机制使用byName自动注入策略。

    d:

    @Resource注解的使用性更为灵活,可指定名称,也可以指定类型 ;@Autowired注解进行装配容易抛出异常,特别是装配的bean类型有多个的时候,而解决的办法是需要在增加@Qualifier进行限定。

    三:@Component vs @Configuration and @Bean

    @Component可以替代 @Configuration注解

    Bean注解主要用于方法上,有点类似于工厂方法,当使用了@Bean注解,我们可以连续使用多种定义bean时用到的注解,譬如用@Qualifier注解定义工厂方法的名称,用@Scope注解定义该bean的作用域范围,譬如是singleton还是prototype等。

    Spring 中新的 Java 配置支持的核心就是@Configuration 注解的类。这些类主要包括 @Bean 注解的方法来为 Spring 的 IoC 容器管理的对象定义实例,配置和初始化逻辑。

    使用@Configuration 来注解类表示类可以被 Spring 的 IoC 容器所使用,作为 bean 定义的资源。

    1
    2
    3
    4
    5
    6
    7
    
    @Configuration
    public class AppConfig {
        @Bean
        public MyService myService() {
            return new MyServiceImpl();
        }
    }
    

    这和 Spring 的 XML 文件中的非常类似

    1
    2
    3
    
    <beans>
      <bean id="myService" class="com.acme.services.MyServiceImpl"/>
    </beans>
    

    四:spring MVC模块注解

    @RestController = @Controller + @ResponseBody

    @RequestMapping, @PostMapping, @GetMapping

    @RequestBody, @RequestParam, @PathVariable

    http://tengj.top/2016/04/28/javareflect/

    Java反射机制是指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

    用一句话总结就是反射可以实现在运行时可以知道任意一个类的属性和方法。

    理解Class类和类类型

    类是java.lang.Class类的实例对象,而Class是所有类的类。

    1
    2
    3
    4
    5
    6
    
    Class c1 = Code.class;
    //这说明任何一个类都有一个隐含的静态成员变量class,这种方式是通过获取类的静态成员变量class得到的
    Class c2 = code1.getClass();
    //code1是Code的一个对象,这种方式是通过一个类的对象的getClass()方法获得的
    Class c3 = Class.forName("com.trigl.reflect.Code");
    //这种方法是Class类调用forName方法,通过一个类的全量限定名获得
    

    这里,c1、c2、c3都是Class的对象,他们是完全一样的,而且有个学名,叫做Code的类类型(class type)。

    Java反射相关操作

    获取成员方法Method

    1
    2
    
    public Method getDeclaredMethod(String name, Class<?>... parameterTypes) // 得到该类所有的方法,不包括父类的
    public Method getMethod(String name, Class<?>... parameterTypes) // 得到该类所有的public方法,包括父类的
    

    获取成员变量Field

    1
    2
    
    public Field getDeclaredField(String name) // 获得该类自身声明的所有变量,不包括其父类的变量
    public Field getField(String name) // 获得该类自所有的public成员变量,包括其父类变量
    

    获取构造函数Constructor

    1
    2
    
    public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) //  获得该类所有的构造器,不包括其父类的构造器
    public Constructor<T> getConstructor(Class<?>... parameterTypes) // 获得该类所以public构造器,包括父类
    

    https://snailclimb.gitee.io/javaguide/#/docs/java/basic/reflection

    静态编译和动态编译

    静态编译:在编译时确定类型,绑定对象 动态编译:运行时确定类型,绑定对象

    反射机制优缺点

    优点: 运行期类型的判断,动态加载类,提高代码灵活度。

    缺点: 1,性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的 java 代码要慢很多。2,安全问题,让我们可以动态操作改变类的属性同时也增加了类的安全隐患。

    反射的应用场景

    反射是框架设计的灵魂。

    在我们平时的项目开发过程中,基本上很少会直接使用到反射机制,但这不能说明反射机制没有用,实际上有很多设计、开发都与反射机制有关,例如模块化的开发,通过反射去调用对应的字节码;动态代理设计模式也采用了反射机制,还有我们日常使用的 Spring/Hibernate 等框架也大量使用到了反射机制。

    举例:

    我们在使用 JDBC 连接数据库时使用 Class.forName()通过反射加载数据库的驱动程序; Spring 框架的 IOC(动态加载管理 Bean)创建对象以及 AOP(动态代理)功能都和反射有联系; 动态配置实例的属性; ……

  • Spring中自动装配的方式

    • no:不进行自动装配,手动设置Bean的依赖关系。
    • byName:根据Bean的名字进行自动装配。
    • byType:根据Bean的类型进行自动装配。
    • constructor:类似于byType,不过是应用于构造器的参数,如果正好有一个Bean与构造器的参数类型相同则可以自动装配,否则会导致错误。
    • autodetect:如果有默认的构造器,则通过constructor的方式进行自动装配,否则使用byType的方式进行自动装配。
  • Spring 中的 bean 生命周期?

    实例化bean、设置属性(依赖注入)、注入aware接口、执行postProcessBeforeInitialization() 方法、调用初始化方法、执行postProcessAfterInitialization() 方法、调用销毁方法

    • Bean 容器找到配置文件中 Spring Bean 的定义。
    • Bean 容器利用 Java Reflection API 创建一个Bean的实例
    • 如果涉及到一些属性值 利用 set()方法设置一些属性值。
    • 如果 Bean 实现了 BeanNameAware 接口,调用 setBeanName()方法,传入Bean的名字。
    • 如果 Bean 实现了 BeanClassLoaderAware 接口,调用 setBeanClassLoader()方法,传入 ClassLoader对象的实例。
    • 与上面的类似,如果实现了其他 *.Aware接口,就调用相应的方法。
    • 如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcessor 对象,执行postProcessBeforeInitialization() 方法
    • 如果Bean实现了InitializingBean接口,执行afterPropertiesSet()方法。
    • 如果 Bean 在配置文件中的定义包含 init-method 属性,执行指定的方法。
    • 如果有和加载这个 Bean的 Spring 容器相关的 BeanPostProcessor 对象,执行postProcessAfterInitialization() 方法
    • 当要销毁 Bean 的时候,如果 Bean 实现了 DisposableBean 接口,执行 destroy() 方法。
    • 当要销毁 Bean 的时候,如果 Bean 在配置文件中的定义包含 destroy-method 属性,执行指定的方法。

    https://www.zhihu.com/question/38597960

    对于普通的Java对象,当new的时候创建对象,当它没有任何引用的时候被垃圾回收机制回收。而由Spring IoC容器托管的对象,它们的生命周期完全由容器控制。Spring中每个Bean的生命周期如下:

    img

    1. 实例化Bean

      对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。

      对于ApplicationContext容器,当容器启动结束后,便实例化所有的bean。

      容器通过获取BeanDefinition对象中的信息进行实例化。并且这一步仅仅是简单的实例化,并未进行依赖注入。

      实例化对象被包装在BeanWrapper对象中,BeanWrapper提供了设置对象属性的接口,从而避免了使用反射机制设置属性。

    2. 设置对象属性(依赖注入)

      实例化后的对象被封装在BeanWrapper对象中,并且此时对象仍然是一个原生的状态,并没有进行依赖注入。

      紧接着,Spring根据BeanDefinition中的信息进行依赖注入。

      并且通过BeanWrapper提供的设置属性的接口完成依赖注入。

    3. 注入Aware接口

      紧接着,Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给bean。

    4. BeanPostProcessor

      当经过上述几个步骤后,bean对象已经被正确构造,但如果你想要对象被使用前再进行一些自定义的处理,就可以通过BeanPostProcessor接口实现。

      该接口提供了两个函数:

      • postProcessBeforeInitialzation( Object bean, String beanName )

        当前正在初始化的bean对象会被传递进来,我们就可以对这个bean作任何处理。

        这个函数会先于InitialzationBean执行,因此称为前置处理。 所有Aware接口的注入就是在这一步完成的。

      • postProcessAfterInitialzation( Object bean, String beanName )

        当前正在初始化的bean对象会被传递进来,我们就可以对这个bean作任何处理。

        这个函数会在InitialzationBean完成后执行,因此称为后置处理。

    5. InitializingBean与init-method

      当BeanPostProcessor的前置处理完成后就会进入本阶段。

      InitializingBean接口只有一个函数:

      • afterPropertiesSet()

        这一阶段也可以在bean正式构造完成前增加我们自定义的逻辑,但它与前置处理不同,由于该函数并不会把当前bean对象传进来,因此在这一步没办法处理对象本身,只能增加一些额外的逻辑。 若要使用它,我们需要让bean实现该接口,并把要增加的逻辑写在该函数中。然后Spring会在前置处理完成后检测当前bean是否实现了该接口,并执行afterPropertiesSet函数。

        当然,Spring为了降低对客户代码的侵入性,给bean的配置提供了init-method属性,该属性指定了在这一阶段需要执行的函数名。Spring便会在初始化阶段执行我们设置的函数。init-method本质上仍然使用了InitializingBean接口。

    6. DisposableBean和destroy-method

      和init-method一样,通过给destroy-method指定函数,就可以在bean销毁前执行指定的逻辑。

  • Spring中BeanFactory和ApplicationContext的区别

    BeanFactory是Spring里面最低层的接口,提供了最简单的容器的功能,只提供了实例化对象和拿对象的功能。

    ApplicationContext应用上下文,继承BeanFactory接口,它是Spring的一各更高级的容器,提供了更多的有用的功能。如国际化,访问资源,载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,消息发送、响应机制,AOP等。

    BeanFactory在启动的时候不会去实例化Bean,中有从容器中拿Bean的时候才会去实例化。ApplicationContext在启动的时候就把所有的Bean全部实例化了。它还可以为Bean配置lazy-init=true来让Bean延迟实例化

  • Spring MVC

    说说自己对于 Spring MVC 了解?

    谈到这个问题,我们不得不提提之前 Model1 和 Model2 这两个没有 Spring MVC 的时代。

    Model1 时代 : 很多学 Java 后端比较晚的朋友可能并没有接触过 Model1 模式下的 JavaWeb 应用开发。在 Model1 模式下,整个 Web 应用几乎全部用 JSP 页面组成,只用少量的 JavaBean 来处理数据库连接、访问等操作。这个模式下 JSP 即是控制层又是表现层。显而易见,这种模式存在很多问题。比如①将控制逻辑和表现逻辑混杂在一起,导致代码重用率极低;②前端和后端相互依赖,难以进行测试并且开发效率极低;

    Model2 时代 :学过 Servlet 并做过相关 Demo 的朋友应该了解“Java Bean(Model)+ JSP(View)+Servlet(Controller) ”这种开发模式,这就是早期的 JavaWeb MVC 开发模式。Model:系统涉及的数据,也就是 dao 和 bean。View:展示模型中的数据,只是用来展示。Controller:处理用户请求都发送给 ,返回数据给 JSP 并展示给用户。

    Model2 模式下还存在很多问题,Model2的抽象和封装程度还远远不够,使用Model2进行开发时不可避免地会重复造轮子,这就大大降低了程序的可维护性和复用性。于是很多JavaWeb开发相关的 MVC 框架应运而生比如Struts2,但是 Struts2 比较笨重。随着 Spring 轻量级开发框架的流行,Spring 生态圈出现了 Spring MVC 框架, Spring MVC 是当前最优秀的 MVC 框架。相比于 Struts2 , Spring MVC 使用更加简单和方便,开发效率更高,并且 Spring MVC 运行速度更快。

    MVC 是一种设计模式,Spring MVC 是一款很优秀的 MVC 框架。Spring MVC 可以帮助我们进行更简洁的Web层的开发,并且它天生与 Spring 框架集成。Spring MVC 下我们一般把后端项目分为 Service层(处理业务)、Dao层(数据库操作)、Entity层(实体类)、Controller层(控制层,返回数据给前台页面)。

    Spring MVC 的简单原理图如下:

    img

    SpringMVC 工作原理了解吗?

    原理如下图所示:

    img

    上图的一个笔误的小问题:Spring MVC 的入口函数也就是前端控制器 DispatcherServlet 的作用是接收请求,响应结果。

    流程说明(重要):

    1. 客户端(浏览器)发送请求,直接请求到 DispatcherServlet(前端控制器)。
    2. DispatcherServlet(前端控制器) 根据请求信息调用 HandlerMapping(处理器映射器),解析请求对应的 Handler。
    3. 解析到对应的 Handler(也就是我们平常说的 Controller 控制器)后,开始由 HandlerAdapter 适配器处理。
    4. HandlerAdapter(处理器适配器) 会根据 Handler来调用真正的处理器开处理请求,并处理相应的业务逻辑。
    5. 处理器处理完业务后,会返回一个 ModelAndView 对象,Model 是返回的数据对象,View 是个逻辑上的 View。
    6. ViewResolver(视图解析器) 会根据逻辑 View 查找实际的 View。
    7. DispaterServlet(前端控制器) 把返回的 Model 传给 View(视图渲染)。
    8. 把 View 返回给请求者(浏览器)

    浏览器请求DispatcherServlet,DispatcherServlet根据请求调用HandlerMapping,解析到对应的Handler。HandlerAdapter根据Handler调用controller,返回ModelAndView,包括数据对象Model和逻辑View。ViewResolver根据逻辑View查找实际的View。DispatcherServlet把Model传给View渲染,并返回给浏览器。

  • Tomcat & Servlet

    https://blog.csdn.net/qq_22638399/article/details/81506448

    spring boot 项目部署到服务器 两种方式

    https://zhuanlan.zhihu.com/p/33612564

    Tomcat+Servlet面试题

    Servlet生命周期可分为5个步骤

    • 加载Servlet。当Tomcat第一次访问Servlet的时候,Tomcat会负责创建Servlet的实例
    • 初始化。当Servlet被实例化后,Tomcat会调用init()方法初始化这个对象
    • 处理服务。当浏览器访问Servlet的时候,Servlet 会调用service()方法处理请求
    • 销毁。当Tomcat关闭时或者检测到Servlet要从Tomcat删除的时候会自动调用destroy()方法,让该实例释放掉所占的资源。一个Servlet如果长时间不被使用的话,也会被Tomcat自动销毁
    • 卸载。当Servlet调用完destroy()方法后,等待垃圾回收。如果有需要再次使用这个Servlet,会重新调用init()方法进行初始化操作。

    https://www.zhihu.com/question/32212996

    tomcat 与 nginx,apache的区别是什么?

    https://www.cnblogs.com/haolnu/p/7294533.html

    SpringMvc和servlet简单对比介绍

    Spring主要也是通过DispatcherServlet实现了Servlet这个接口,又叫前端控制器,来自前端的请求会先到达这里,它负责到后台去匹配合适的handler。DispatcherServlet的主要工作流程如下:

    前端请求到达DispatcherServlet。

    前端控制器请求HandlerMappering 查找Handler。

    如果查找到存在的处理器,进一步去调用service和dao层

    返回结果再到controller层,渲染具体的视图,返回结果给页面。

  • Spring 框架中用到了哪些设计模式?

    • 工厂设计模式 : Spring使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。
    • 代理设计模式 : Spring AOP 功能的实现。
    • 单例设计模式 : Spring 中的 Bean 默认都是单例的。
    • 模板方法模式 : Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
    • 包装器设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
    • 观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
    • 适配器模式 :Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller。
    • ……
  • 事务几种实现方式

    https://blog.csdn.net/chinacr07/article/details/78817449

    (1)编程式事务管理对基于 POJO 的应用来说是唯一选择。我们需要在代码中调用beginTransaction()、commit()、rollback()等事务管理相关的方法,这就是编程式事务管理。

    (2)基于 TransactionProxyFactoryBean的声明式事务管理

    (3)基于 @Transactional 的声明式事务管理

    (4)基于Aspectj AOP配置事务

  • Spring 事务

    • Spring 管理事务的方式有几种?
      1. 编程式事务,在代码中硬编码。(不推荐使用)
      2. 声明式事务,在配置文件中配置(推荐使用)

      声明式事务又分为两种:

      1. 基于XML的声明式事务
      2. 基于注解的声明式事务
    • Spring 事务中的隔离级别有哪几种?

      TransactionDefinition 接口中定义了五个表示隔离级别的常量:

      • TransactionDefinition.ISOLATION_DEFAULT: 使用后端数据库默认的隔离级别,Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别.
      • TransactionDefinition.ISOLATION_READ_UNCOMMITTED: 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
      • TransactionDefinition.ISOLATION_READ_COMMITTED: 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
      • TransactionDefinition.ISOLATION_REPEATABLE_READ: 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
      • TransactionDefinition.ISOLATION_SERIALIZABLE: 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
    • 事务的传播特性

    https://blog.csdn.net/chinacr07/article/details/78817449

    事务传播行为就是多个事务方法调用时,如何定义方法间事务的传播。Spring定义了7中传播行为:

    (1)propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是Spring默认的选择。

    (2)propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。

    (3)propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。

    (4)propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。

    (5)propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

    (6)propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。

    (7)propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作。

    • @Transactional(rollbackFor = Exception.class)注解了解吗?

      我们知道:Exception分为运行时异常RuntimeException和非运行时异常。事务管理对于企业应用来说是至关重要的,即使出现异常情况,它也可以保证数据的一致性。

      当@Transactional注解作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。如果类或者方法加了这个注解,那么这个类里面的方法抛出异常,就会回滚,数据库里面的数据也会回滚。

      在@Transactional注解中如果不配置rollbackFor属性,那么事物只会在遇到RuntimeException的时候才会回滚,加上rollbackFor=Exception.class,可以让事物在遇到非运行时异常时也回滚。

    • spring中声明式事务实现原理

      https://blog.csdn.net/qq_20597727/article/details/84868035

      首先,对于spring中aop实现原理有了解的话,应该知道想要对一个方法进行代理的话,肯定需要定义切点。在@Transactional的实现中,同样如此,spring为我们定义了以 @Transactional 注解为植入点的切点,这样才能知道@Transactional注解标注的方法需要被代理。

      有了切面定义之后,在spring的bean的初始化过程中,就需要对实例化的bean进行代理,并且生成代理对象。

      生成代理对象的代理逻辑中,进行方法调用时,需要先获取切面逻辑,@Transactional注解的切面逻辑类似于@Around,在spring中是实现一种类似代理逻辑。

    • Spring加载过程

      • 刷新预处理
      • 将Bean的配置信息解析,注册到BeanFactory
      • 设置bean的类加载器
      • 如果有第三方想再bean加载注册完成后,初始化前做点什么(例如修改属性的值,修改bean的scope为单例或者多例。),提供了相应的模板方法,后面还调用了这个方法的实现,并且把这些个实现类注册到对应的容器中
      • 初始化当前的事件广播器
      • 初始化所有的bean
      • 广播applicationcontext初始化完成。

SpringBoot

  • SpringBoot介绍

    https://github.com/Snailclimb/springboot-guide/blob/master/docs/start/springboot-introduction.md#%E4%B8%80-springboot%E4%BB%8B%E7%BB%8D

    我们知道Spring是重量级企业开发框架 Enterprise JavaBean(EJB) 的替代品,Spring为企业级Java开发提供了一种相对简单的方法,通过 依赖注入 和 面向切面编程 ,用简单的 Java对象(Plain Old Java Object,POJO) 实现了EJB的功能

    虽然Spring的组件代码是轻量级的,但它的配置却是重量级的(需要大量XML配置) 。Spring 2.5引入了基于注解的组件扫描,这消除了大量针对应用程序自身组件的显式XML配置。Spring 3.0引入了基于Java的配置,这是一种类型安全的可重构配置方式,可以代替XML。

    尽管如此,我们依旧没能逃脱配置的魔爪。开启某些Spring特性时,比如事务管理和Spring MVC,还是需要用XML或Java进行显式配置。启用第三方库时也需要显式配置,比如基于Thymeleaf的Web视图。配置Servlet和过滤器(比如Spring的DispatcherServlet)同样需要在web.xml或Servlet初始化代码里进行显式配置。组件扫描减少了配置量,Java配置让它看上去简洁不少,但Spring还是需要不少配置。

    光配置这些XML文件都够我们头疼的了,占用了我们大部分时间和精力。除此之外,相关库的依赖非常让人头疼,不同库之间的版本冲突也非常常见。

    从本质上来说,Spring Boot就是Spring,它做了那些没有它你自己也会去做的Spring Bean配置。

  • Spring Boot 项目结构分析

    https://github.com/Snailclimb/springboot-guide/blob/master/docs/basis/sringboot-restful-web-service.md

    1. Application.java是项目的启动类
    2. domain目录主要用于实体(Entity)与数据访问层(Repository)
    3. service 层主要是业务类代码
    4. controller 负责页面访问控制
    5. config 目录主要放一些配置类
  • 开发 RestFul Web 服务

    https://github.com/Snailclimb/springboot-guide/blob/master/docs/basis/sringboot-restful-web-service.md

    RESTful Web 服务介绍

    RESTful Web 服务与传统的 MVC 开发一个关键区别是返回给客户端的内容的创建方式:传统的 MVC 模式开发会直接返回给客户端一个视图,但是 RESTful Web 服务一般会将返回的数据以 JSON 的形式返回,这也就是现在所推崇的前后端分离开发。

    1. @RestController 将返回的对象数据直接以 JSON 或 XML 形式写入 HTTP 响应(Response)中。 绝大部分情况下都是直接以 JSON 形式返回给客户端,很少的情况下才会以 XML 形式返回。转换成 XML 形式还需要额为的工作,上面代码中演示的直接就是将对象数据直接以 JSON 形式写入 HTTP 响应(Response)中。关于@Controller和@RestController 的对比,我会在下一篇文章中单独介绍到(@Controller +@ResponseBody= @RestController)。
    2. @RequestMapping :上面的示例中没有指定 GET 与 PUT、POST 等,因为@RequestMapping默认映射所有HTTP Action,你可以使用@RequestMapping(method=ActionType)来缩小这个映射。
    3. @PostMapping实际上就等价于 @RequestMapping(method = RequestMethod.POST),同样的 @DeleteMapping ,@GetMapping也都一样,常用的 HTTP Action 都有一个这种形式的注解所对应。
    4. @PathVariable :取url地址中的参数。@RequestParam url的查询参数值。
    5. @RequestBody:可以将 HttpRequest body 中的 JSON 类型数据反序列化为合适的 Java 类型。
    6. ResponseEntity: 表示整个HTTP Response:状态码,标头和正文内容。我们可以使用它来自定义HTTP Response 的内容。

    @RestController = @Controller + @ResponseBody

    @RequestMapping, @PostMapping, @GetMapping

    @RequestBody, @RequestParam

  • 面试题

    什么是 Spring Boot?

    首先,重要的是要理解 Spring Boot 并不是一个框架,它是一种创建独立应用程序的更简单方法,只需要很少或没有配置(相比于 Spring 来说)。Spring Boot最好的特性之一是它利用现有的 Spring 项目和第三方项目来开发适合生产的应用程序。

    说出使用Spring Boot的主要优点

    1. 开发基于 Spring 的应用程序很容易。
    2. Spring Boot 项目所需的开发或工程时间明显减少,通常会提高整体生产力。
    3. Spring Boot不需要编写大量样板代码、XML配置和注释。
    4. Spring引导应用程序可以很容易地与Spring生态系统集成,如Spring JDBC、Spring ORM、Spring Data、Spring Security等。
    5. Spring Boot遵循“固执己见的默认配置”,以减少开发工作(默认配置可以修改)。
    6. Spring Boot 应用程序提供嵌入式HTTP服务器,如Tomcat和Jetty,可以轻松地开发和测试web应用程序。(这点很赞!普通运行Java程序的方式就能运行基于Spring Boot web 项目,省事很多)
    7. Spring Boot提供命令行接口(CLI)工具,用于开发和测试Spring Boot应用程序,如Java或Groovy。
    8. Spring Boot提供了多种插件,可以使用内置工具(如Maven和Gradle)开发和测试Spring Boot应用程序。

    什么是 Spring Boot Starters?

    Spring Boot Starters 是一系列依赖关系的集合,因为它的存在,项目的依赖之间的关系对我们来说变的更加简单了。举个例子:在没有Spring Boot Starters之前,我们开发REST服务或Web应用程序时; 我们需要使用像Spring MVC,Tomcat和Jackson这样的库,这些依赖我们需要手动一个一个添加。但是,有了 Spring Boot Starters 我们只需要一个只需添加一个spring-boot-starter-web一个依赖就可以了,这个依赖包含的字依赖中包含了我们开发REST 服务需要的所有依赖。

    1
    2
    3
    4
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    

    如何在Spring Boot应用程序中使用Jetty而不是Tomcat?

    Spring Boot Web starter使用Tomcat作为默认的嵌入式servlet容器, 如果你想使用 Jetty 的话只需要修改pom.xml(Maven)或者build.gradle(Gradle)就可以了。

    介绍一下@SpringBootApplication注解

    大概可以把 @SpringBootApplication 看作是 @Configuration@EnableAutoConfiguration@ComponentScan 注解的集合。根据 SpringBoot官网,这三个注解的作用分别是:

    • @EnableAutoConfiguration:启用 SpringBoot 的自动配置机制
    • @ComponentScan扫描被@Component (@Service,@Controller)注解的bean,注解默认会扫描该类所在的包下所有的类。
    • @Configuration:允许在上下文中注册额外的bean或导入其他配置类

    (重要)Spring Boot 的自动配置是如何实现的?

    这个是因为@SpringBootApplication 注解的原因。

    Spring Boot支持哪些嵌入式web容器?

    Spring Boot支持以下嵌入式servlet容器:

    Name Servlet Version
    Tomcat 9.0 4.0
    Jetty 9.4 3.1
    Undertow 2.0 4.0

    您还可以将Spring引导应用程序部署到任何Servlet 3.1+兼容的 Web 容器中。

    这就是你为什么可以通过直接像运行 普通 Java 项目一样运行 SpringBoot 项目。这样的确省事了很多,方便了我们进行开发,降低了学习难度。

    什么是Spring Security ?

    Spring Security 应该属于 Spring 全家桶中学习曲线比较陡峭的几个模块之一,下面我将从起源和定义这两个方面来简单介绍一下它。

    • 起源: Spring Security 实际上起源于 Acegi Security,这个框架能为基于 Spring 的企业应用提供强大而灵活安全访问控制解决方案,并且框架这个充分利用 Spring 的 IoC 和 AOP 功能,提供声明式安全访问控制的功能。后面,随着这个项目发展, Acegi Security 成为了Spring官方子项目,后来被命名为 “Spring Security”。
    • 定义:Spring Security 是一个功能强大且高度可以定制的框架,侧重于为Java 应用程序提供身份验证和授权。——官方介绍
  • SpringBoot 配置文件详解(告别XML)

    https://www.jianshu.com/p/60b34464ca58

    application.yml: 运行端口、数据库连接池、shiro(cookie记住登录信息时间、Session会话超时时间、无需认证(登录)可访问的请求资源路径)

  • spring定时任务详解(@Scheduled注解)

    https://blog.csdn.net/qq_33556185/article/details/51852537

  • Tomcat性能优化

    https://blog.csdn.net/lexang1/article/details/77849485

http://www.spring4all.com/article/246

SpringBoot文档

SpringDataJPA

  • JPA和MyBatis的区别

    https://blog.csdn.net/yunzhonmghe/article/details/78069449

    对于数据的操作,hibernate是面向对象的,而MyBatis是面向关系的。

    https://blog.csdn.net/chehec2010/article/details/91195770

    Mybatis可以进行更细致的SQL优化,查询必要的字段,但是需要维护SQL和查询结果集的映射。数据库的移植性较差,针对不同的数据库编写不同的SQL。

    Hibernate对数据库提供了较为完整的封装,封装了基本的DAO层操作,有较好的数据库移植性。但是学习周期长,开发难度大于Mybatis。

    https://blog.csdn.net/u013905744/article/details/90812229

    1. spring data jpa实现了jpa(java persistence api)功能,即可以实现pojo转换为关系型数据库记录的功能,通俗来讲就是可以不写任何的建表sql语句了。jpa是spring data jpa功能的一个子集。而mybatis并没有jpa功能,建表语句还是要自己写的。

    2. spring data jpa是全自动框架,不需要写任何sql。而mybatis是半自动框架,需要自己写sql,mybatis-plus为mybatis赋能,使其也可以基本上不需要写任何模板sql。

  • MyBatis与Hibernate有哪些不同?

    https://mp.weixin.qq.com/s?__biz=MzI4Njc5NjM1NQ==&mid=2247483810&idx=2&sn=ecf3d5f70b2973a85b4bb3329a9c8524&chksm=ebd63e8edca1b798fa1be1cd673642855927c2313ea6a7a1a102d30f31ca106eb9765dfac7e1&scene=21#wechat_redirect

    1、Mybatis和hibernate不同,它不完全是一个ORM框架,因为MyBatis需要程序员自己编写Sql语句,不过mybatis可以通过XML或注解方式灵活配置要运行的sql语句,并将java对象和sql语句映射生成最终执行的sql,最后将sql执行的结果再映射生成java对象。

    2、Mybatis学习门槛低,简单易学,程序员直接编写原生态sql,可严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,例如互联网软件、企业运营类软件等,因为这类软件需求变化频繁,一但需求变化要求成果输出迅速。但是灵活的前提是mybatis无法做到数据库无关性,如果需要实现支持多种数据库的软件则需要自定义多套sql映射文件,工作量大。

    3、Hibernate对象/关系映射能力强,数据库无关性好对于关系模型要求高的软件(例如需求固定的定制化软件)如果用hibernate开发可以节省很多代码,提高效率。但是Hibernate的缺点是学习门槛高,要精通门槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡,以及怎样用好Hibernate需要具有很强的经验和能力才行。

    总之,按照用户的需求在有限的资源环境下只要能做出维护性、扩展性良好的软件架构都是好架构,所以框架只有适合才是最好。

  • 多表联查

    关系映射@OneToOne, @OneToMany, @ManyToOne

    https://blog.wuwii.com/jpa-query-muti.html

    https://blog.csdn.net/johnf_nash/article/details/80642581

  • Spring Boot JPA 基础:常见操作解析

    https://github.com/Snailclimb/springboot-guide/blob/master/docs/basis/springboot-jpa.md

    实体类:为这个类添加了 @Entity 注解代表它是数据库持久化类

    创建操作数据库的 Repository 接口:首先这个接口加了 @Repository 注解,代表它和数据库操作有关。另外,它继承了 JpaRepository<Person, Long>接口

  • JPA 中非常重要的连表查询就是这么简单

    https://github.com/Snailclimb/springboot-guide/blob/master/docs/basis/springboot-jpa-lianbiao.md

    连表查询

    我们需要创建一个包含我们需要的 Person 信息的 DTO 对象,我们简单第将其命名为 UserDTO,用于保存和传输我们想要的信息。

    sql 语句和我们平时写的没啥区别,差别比较大的就是里面有一个 new 对象的操作。

    分页操作

    为了实现分页,我们在@Query注解中还添加了 countQuery 属性。

    传入Pageable pageable,返回Page

http://www.spring4all.com/article/500

SpringDataJpa文档

慕课网教程 SpringData

1) 创建Maven项目

maven工程的目录结构

添加依赖

1
2
3
4
5
6
7
8
9
10
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.38</version>
  </dependency>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.10</version>
  </dependency>

2) 数据表准备

create database spring_data;

create table student( id int not null auto_increment, name varchar(20) not null, age int not null, primary key(id) )

insert into student (name, age) values (“zhangsan”, 20); insert into student (name, age) values (“lisi”, 21); insert into student (name, age) values (“wangwu”, 22);

3) 开发JDBCUtil工具类

获取Connection,关闭Connection、Statement、ResultSet

注意事项:配置的属性放在配置文件中,然后通过代码的方式将配置文件中的数据加载进来即可。

4) 建立对象模型,DAO

使用Spring jdbc的方式操作数据库

1) 添加依赖

org.springframework spring-jdbc 5.2.2.RELEASE org.springframework spring-context 5.2.2.RELEASE

2) 配置beans.xml

3) 开发spring jdbc版本的query和save方法

传统方式访问数据库

弊端分析

  • DAO有很多代码
  • DAOImpl有很多重复的代码
  • 开发分页还要做其他的封装

慕课网MyBatis教程

CREATE TABLE tb_area ( area_id int(2) NOT NULL AUTO_INCREMENT, area_name varchar(200) NOT NULL, priority int(2) NOT NULL DEFAULT ‘0’, create_time datetime DEFAULT NULL, last_edit_time datetime DEFAULT NULL, PRIMARY KEY(area_id), UNIQUE KEY UK_AREA(area_name), ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

慕课网事务管理教程

事务指的是逻辑上的一组操作,这组操作要么全部成功,要么全部失败。

事务的特性

  • 原子性:原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
  • 一致性:一致性指事务前后数据的完整性必须保持一致。
  • 隔离性:隔离性指多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务所干扰,多个并发事务之间数据要相互隔离。
  • 持久性:隔离性指多个用户并发访问数据库时,一个用户的事务不能被其他回用户的事务所干扰,多个并发事务之间数据要相互隔离。

Spring事务管理高层结构主要包括3个接口

  • PlatformTransactionManager 事务管理器

    • DataSourceTransactionManager: 使用Spring JDBC或iBatis进行持久化数据使用
    • HibernateTransactionManager: 使用Hibernate3.0版本进行初九话数据时使用
  • TransactionDefinition 事务定义信息(隔离、传播、超时、只读)

    • 脏读:一个事务读取了另一个事务改写但还未提交的数据,如果这些数据被回滚,则读到的数据是无效的。
    • 不可重复读:在同一事务中,多次读取同一数据返回的结果有所不同。
    • 幻读:一个事务读取了几行记录后,另一个事务插入一些记录,幻读就发生了。在后来的查询中,第一个事务就会发现有些原来没有的记录。

    • DEFAULT:使用后端数据库默认的隔离级别(spring中的选择项)
    • READ_UNCOMMITED
    • READ_COMMITED
    • REPEATABLE_READ
    • SERIALIZABLE

    事务的传播行为

    Web层->业务层->持久层

    DAO1 { xxx() {} }

    DAO2 { yyy() {} }

    Service1 { aaa() { DAO1.xxx(); DAO2.yyy(); } }

    Service2 { bbb() {

    } }

    出现复杂情况:

    • 调用Service1.aaa()和Service2.bbb()才能完成一个业务
    • 事务的传播行为:解决业务层方法之间的相互调用的问题

    事务的传播行为(七种)

    • PROPAGATION_REQUIRED:支持当前事务,如果不存在就新建一个
    • PROPAGATION_SUPPORTS:支持当前事务,如果不存在,就不使用事务
    • PROPAGATION_MANDATORY:支持当前事务,如果不存在,抛出异常
    • PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果有事务存在,挂起当前事务
    • PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果有事务存在,挂起当前事务
    • PROPAGATION_NEVER:以非事务方式运行,如果有事务存在,抛出异常
    • PROPAGATION_NESTED:如果当前事务存在,则嵌套事务执行
  • TransactionStatus 事务具体运行状态

CREATE TABLE account ( id int(11) NOT NULL AUTO_INCREMENT, name varchar(20) NOT NULL, money double DEFAULT NULL, PRIMARY KEY (id) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; INSERT INTO account VALUES (‘1’, ‘aaa’, ‘1000’); INSERT INTO account VALUES (‘2’, ‘bbb’, ‘1000’); INSERT INTO account VALUES (‘3’, ‘ccc’, ‘1000’);

总结

Spring将事务管理分成了两类:

  • 编程式事务管理:手动编写代码进行事务管理(很少使用)
  • 声明式事务管理

    • 基于TransactionProxyFactoryBean的方式:需要为每个进行事务管理的类配置一个TransactionProxyFactoryBean进行增强(很少使用)
    • 基于AspectJ的XML方式:一旦配置好之后,类上不需要添加任何东西(经常使用)
    • 基于注解方式:配置简单,需要在业务类上添加一个@Transactional的注解(经常使用)

慕课网Spring AOP教程

使用方式

  • XML配置
  • 注解方式

  • Wildcards通配符

      • 匹配任意数量的字符
      • 匹配指定类及其子类
    • .. 一般用于匹配任意数的子包或参数
  • Operators运算符

    • && 与操作符
    •   或操作符
    • ! 非操作符
  • designators指示器

    • 匹配方法 execution()

MyBatis

mybatis interview

mybatis缓存机制详解

  • #{}和${}的区别是什么?

    ${}是 Properties 文件中的变量占位符,它可以用于标签属性值和 sql 内部,属于静态文本替换,比如${driver}会被静态替换为com.mysql.jdbc.Driver。

    #{}是 sql 的参数占位符,Mybatis 会将 sql 中的#{}替换为?号,在 sql 执行前会使用 PreparedStatement 的参数设置方法,按序给 sql 的?号占位符设置参数值,比如 ps.setInt(0, parameterValue),#{item.name} 的取值方式为使用反射从参数对象中获取 item 对象的 name 属性值,相当于 param.getItem().getName()。

  • mybatis缓存

    mybatis提供了缓存机制减轻数据库压力,提高数据库性能

    mybatis的缓存分为两级:一级缓存、二级缓存

    一级缓存是SqlSession级别的缓存,缓存的数据只在SqlSession内有效

    mybatis的一级缓存是SqlSession级别的缓存,在操作数据库的时候需要先创建SqlSession会话对象,在对象中有一个HashMap用于存储缓存数据,此HashMap是当前会话对象私有的,别的SqlSession会话对象无法访问。

    二级缓存是mapper级别的缓存,同一个namespace公用这一个缓存,所以对SqlSession是共享的

    二级缓存是mapper级别的缓存,也就是同一个namespace的mapper.xml,当多个SqlSession使用同一个Mapper操作数据库的时候,得到的数据会缓存在同一个二级缓存区域

  • Xml 映射文件中,除了常见的 select insert updae delete 标签之外,还有哪些标签?

    还有很多其他的标签,,加上动态 sql 的 9 个标签,trim|where|set|foreach|if|choose|when|otherwise|bind等,其中为 sql 片段标签,通过标签引入 sql 片段,为不支持自增的主键生成策略标签。

  • 最佳实践中,通常一个 Xml 映射文件,都会写一个 Dao 接口与之对应,请问,这个 Dao 接口的工作原理是什么?Dao 接口里的方法,参数不同时,方法能重载吗?

    Dao 接口,就是人们常说的 Mapper接口,接口的全限名,就是映射文件中的 namespace 的值,接口的方法名,就是映射文件中MappedStatement的 id 值,接口方法内的参数,就是传递给 sql 的参数。Mapper接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为 key 值,可唯一定位一个MappedStatement,举例:com.mybatis3.mappers.StudentDao.findStudentById,可以唯一找到 namespace 为com.mybatis3.mappers.StudentDao下面id = findStudentById的MappedStatement。在 Mybatis 中,每一个

    Dao 接口里的方法,是不能重载的,因为是全限名+方法名的保存和寻找策略。

    Dao 接口的工作原理是 JDK 动态代理,Mybatis 运行时会使用 JDK 动态代理为 Dao 接口生成代理 proxy 对象,代理对象 proxy 会拦截接口方法,转而执行MappedStatement所代表的 sql,然后将 sql 执行结果返回。

  • mybatis和hibernate, jdbc的区别;mybatis解决了什么问题,mybatis有什么缺点

    JDBC、MyBatis、Hibernate的区别

    JDBC

    1.使用JDBC编程需要链接数据库,注册驱动和数据库信息。

    2.操作Connection,打开Statement对象。

    3.通过Statement执行SQL语句,返回结果放到ResultSet对象。

    4.使用ResultSet读取数据。

    5.关闭数据库相关的资源。

    JDBC缺点:

    工作量比较大,需要连接,然后处理jdbc底层事务,处理数据类型,还需要操作Connection,Statement对象和ResultSet对象去拿数据并关闭他们

    没有使用框架的时候 sql语句是和java语句一起写在dao层 耦合度高,维护不易而且实际开发中sql是会变的,需要频繁修改 当你要替换某个sql代码的时候,需要对整个项目 进行操作,极不方便。

    JDBC优点:

    接近底层,理论上效率最高

    MyBatis

    半自动化的持久层框架 半自动 轻量级

    1.SQLSessionFactoryBuilder(构造器):它会根据配置信息或者代码生成SqlSessionFactory。

    2.SqlSessionFactory(工厂接口):依靠工厂生成SqlSession。

    3.SqlSession(会话):是一个既可以发送SQL去执行并且返回结果,也可以获取Mapper接口。

    4.SQL Mapper:是由一个JAVA接口和XML文件(或注解)构成,需要给出对应的SQL和映射规则。SQL是由Mapper发送出去,并且返回结果。

    Mybatis的优点:

    1、易于上手和掌握,提供了数据库查询的自动对象绑定功能,而且延续了很好的SQL使用经验,对于没有那么高的对象模型要求的项目来说,相当完美。

    2、sql写在xml里,便于统一管理和优化, 解除sql与程序代码的耦合。

    3、提供映射标签,支持对象与数据库的orm字段关系映射

    4、 提供对象关系映射标签,支持对象关系组建维护

    5、提供xml标签,支持编写动态sql。

    6、速度相对于Hibernate的速度较快

    Mybatis的缺点:

    1、关联表多时,字段多的时候,sql工作量很大。

    2、sql依赖于数据库,导致数据库移植性差。

    3、由于xml里标签id必须唯一,导致DAO中方法不支持方法重载。

    4、对象关系映射标签和字段映射标签仅仅是对映射关系的描述,具体实现仍然依赖于sql。

    5、DAO层过于简单,对象组装的工作量较大。

    6、不支持级联更新、级联删除。

    7、Mybatis的日志除了基本记录功能外,其它功能薄弱很多。

    8、编写动态sql时,不方便调试,尤其逻辑复杂时。

    9、提供的写动态sql的xml标签功能简单,编写动态sql仍然受限,且可读性低。

    Hibernate

    Hibernate的优点:

    1、hibernate是全自动,hibernate完全可以通过对象关系模型实现对数据库的操作,拥有完整的JavaBean对象与数据库的映射结构来自动生成sql。

    2、功能强大,数据库无关性好,O/R映射能力强,需要写的代码很少,开发速度很快。

    3、有更好的二级缓存机制,可以使用第三方缓存。

    4、数据库移植性良好。

    5、hibernate拥有完整的日志系统,hibernate日志系统非常健全,涉及广泛,包括sql记录、关系异常、优化警告、缓存提示、脏数据警告等

    Hibernate的缺点:

    1、学习门槛高,精通门槛更高,程序员如何设计O/R映射,在性能和对象模型之间如何取得平衡,以及怎样用好Hibernate方面需要的经验和能力都很强才行

    2、hibernate的sql很多都是自动生成的,无法直接维护sql;虽然有hql查询,但功能还是不及sql强大,见到报表等变态需求时,hql查询要虚,也就是说hql查询是有局限的;hibernate虽然也支持原生sql查询,但开发模式上却与orm不同,需要转换思维,因此使用上有些不方便。总之写sql的灵活度上hibernate不及mybatis。

  • mapper中的sql语句怎么转化成代码中的sql的