Java框架常见知识点

框架

1.mybatis的一级缓存和二级缓存

一级缓存是SqlSession级别的缓存,只要SqlSession没有flush或close,它就存在。
虽然在上面的代码中我们查询了两次,但最后只执行了一次数据库操作,这就是Mybatis提供给我们的一级缓存在起作用了。因为一级缓存的存在,导致第二次查询id为41的记录时,并没有发出sql语句从数据库中查询数据,而是从一级缓存中查询。
简单地来说就是,当用户查询相同参数下的同一条SQL语句两次,第二次的查询结果会在第一次的一级缓存中查询
一级缓存是SqlSession范围的缓存,当调用SqlSession的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存
二级缓存是mapper映射级别的缓存,多个SqlSession去操作同一个Mapper映射的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的

SqlSession是Mybatis最重要的构建之一,可以简单的认为Mybatis一系列的配置目的是生成类似 JDBC生成的Connection对象的SqlSession对象,这样才能与数据库开启“沟通”,通过SqlSession可以实现增删改查(当然现在更加推荐是使用Mapper接口形式),那么它是如何执行实现的,这就是本篇博文所介绍的东西,其中会涉及到简单的源码讲解。

2.mysql的哪些操作会刷新缓存

修改、添加、删除方法的提交(commit)都会刷新缓存

3.java的反射机制

反射是Java的特征之一,是一种间接操作目标对象的机制,核心是JVM在运行的时候才动态加载类,并且对于任意一个类,都能够知道这个类的所有属性和方法,调用方法/访问属性,不需要提前在编译期知道运行的对象是谁,他允许运行中的Java程序获取类的信息,并且可以操作类或对象内部属性。程序中对象的类型一般都是在编译期就确定下来的,而当我们的程序在运行时,可能需要动态的加载一些类,这些类因为之前用不到,所以没有加载到jvm。这时,使用Java反射机制可以在运行期动态的创建对象并调用其属性,它是在运行时根据需要才加载。
在这里插入图片描述
反射就是把java类中的各种成分映射成一个个的Java对象
例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。
(其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述)
如图是类的正常加载过程:反射的原理在与class对象。
熟悉一下加载的时候:Class对象的由来是将class文件读入内存,并为之创建一个Class对象。
将类的各个组成部分封装为其他对象,这就是反射机制。
好处:

  • 可以在程序运行过程中,操作这些对象。
  • 可以解耦,提高程序的可扩展性。

获取Class对象的方式
1. Class.forName(“全类名”):将字节码文件加载进内存,返回Class对象
多用于配置文件,将类名定义在配置文件中。读取文件,加载类
2. 类名.class:通过类名的属性class获取
多用于参数的传递
3. 对象.getClass():getClass()方法在Object类中定义着。
多用于对象的获取字节码的方式
结论:
同一个字节码文件( .class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。
一般都第一种,一个字符串可以传入也可写在配置文件中等多种方法

Spring框架的七大模块

1. Spring Core
框架的最基础部分,提供 IoC 容器,对 bean 进行管理。
2.Spring Context
基于 bean,提供上下文信息,扩展出JNDI、EJB、电子邮件、国际化、校验和调度等功能。
3.Spring DAO
提供了JDBC的抽象层,它可消除冗长的JDBC编码和解析数据库厂商特有的错误代码,还提供了声明性事务管理方法。
4.Spring ORM
提供了常用的“对象/关系”映射APIs的集成层。 其中包括JPA、JDO、Hibernate、MyBatis 等。
5.Spring AOP
提供了符合AOP Alliance规范的面向方面的编程实现。
6.Spring Web
提供了基础的 Web 开发的上下文信息,可与其他 web 进行集成。
7.Spring Web MVC
提供了 Web 应用的 Model-View-Controller 全功能实现。

4.Spring AOP

AOP:全称是Aspect Oriented Programming即:面向切面编程。
简单的说它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对我们的已有方法进行增强

静态代理
创建一个接口,然后创建被代理的类实现该接口并且实现该接口中的抽象方法。之后再创建一个代理类,同时使其也实现这个接口。在代理类中持有一个被代理对象的引用,而后在代理类方法中调用该对象的方法。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
使用静态代理很容易就完成了对一个类的代理操作。但是静态代理的缺点也暴露了出来:由于代理只能为一个类服务,如果需要代理的类很多,那么就需要编写大量的代理类,比较繁琐。

基于JDK的动态代理
基于JDK的动态代理就需要知道两个类:1.InvocationHandler(接口)、2.Proxy(类)
还要知道JDK是基于接口的动态代理
下面我们实际用代码来讲解这两个类的实际作用
1.第一步,创建一个接口

public interface Subject {
    void hello(String param);
}

2.第二步,实现接口

public class SubjectImpl implements Subject {
    @Override
    public void hello(String param) {
        System.out.println("hello  " + param);
    }
}

3.第三步,创建SubjectImpl的代理类

public class SubjectProxy implements InvocationHandler {
    private Subject subject;

    public SubjectProxy(Subject subject) {
        this.subject = subject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("--------------begin-------------");
        Object invoke = method.invoke(subject, args);
        System.out.println("--------------end-------------");
        return invoke;
    }
}

invoke方法的说明:
在这里插入图片描述

4.编写代理类实际的调用,利用Proxy类创建代理之后的Subject类。

public class Main {

    public static void main(String[] args) {
        Subject subject = new SubjectImpl();
        InvocationHandler subjectProxy = new SubjectProxy(subject);
        Subject proxyInstance = (Subject) Proxy.newProxyInstance(subjectProxy.getClass().getClassLoader(), subject.getClass().getInterfaces(), subjectProxy);
        proxyInstance.hello("world");
    }

}

在这里插入图片描述

输出:

--------------begin-------------
hello  world
--------------end-------------

看这个结果,实际上在Subject类中只会输出一条hello world,但是在被代理之后,实际调用的方法是SubjectProxy的invoke方法,这样可以在不修改业务类的情况下对业务类增加一些日志等其他操作,甚至可以直接修改有返回值方法的返回值。
可以看出静态代理和动态代理的区别就是:静态代理是一开始就把被代理类的对象注入到代理类的方法中。而动态代理则是在要使用的时候在Test类中再创建实例,把实例再注入到方法中,然后用反射机制去调用代理类的方法。

AOP的使用方法可看:
https://zhuanlan.zhihu.com/p/103236714

5.SpringMVC的执行流程:

整体流程
在这里插入图片描述
具体步骤:

  • 首先用户发送请求到前端控制器,前端控制器根据请求信息(如
    URL)来决定选择哪一个页面控制器进行处理并把请求委托给它,即以前的控制器的控制逻辑部分;图中的 1、2 步骤;
  • 页面控制器接收到请求后,进行功能处理,首先需要收集和绑定请求参数到一个对象,这个对象在 Spring Web MVC
    中叫命令对象,并进行验证,然后将命令对象委托给业务对象进行处理;处理完毕后返回一个
    ModelAndView(模型数据和逻辑视图名);图中的 3、4、5 步骤;
  • 前端控制器收回控制权,然后根据返回的逻辑视图名,选择相应的视图进行渲染,并把模型数据传入以便视图渲染;图中的步骤 6、7;
    前端控制器再次收回控制权,将响应返回给用户,图中的步骤 8;至此整个结束。

核心流程
在这里插入图片描述
具体步骤:
第一步:发起请求到前端控制器(DispatcherServlet)
第二步:前端控制器请求HandlerMapping查找 Handler (可以根据xml配置、注解进行查找)
第三步:处理器映射器HandlerMapping向前端控制器返回Handler,HandlerMapping会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象,多个HandlerInterceptor拦截器对象),通过这种策略模式,很容易添加新的映射策略
第四步:前端控制器调用处理器适配器去执行Handler
第五步:处理器适配器HandlerAdapter将会根据适配的结果去执行Handler
第六步:Handler执行完成给适配器返回ModelAndView
第七步:处理器适配器向前端控制器返回ModelAndView (ModelAndView是springmvc框架的一个底层对象,包括 Model和view)
第八步:前端控制器请求视图解析器去进行视图解析 (根据逻辑视图名解析成真正的视图(jsp)),通过这种策略很容易更换其他视图技术,只需要更改视图解析器即可
第九步:视图解析器向前端控制器返回View
第十步:前端控制器进行视图渲染 (视图渲染将模型数据(在ModelAndView对象中)填充到request域)
第十一步:前端控制器向用户响应结果
下面我们对出现的一些组件进行详细的介绍:
(1) 前端控制器DispatcherServlet(不需要程序员开发)。
  作用:接收请求,响应结果,相当于转发器,中央处理器。有了DispatcherServlet减少了其它组件之间的耦合度。
(2) 处理器映射器HandlerMapping(不需要程序员开发)。
  作用:根据请求的url查找Handler。
(3) 处理器适配器HandlerAdapter(不需要程序员开发)。
  作用:按照特定规则(HandlerAdapter要求的规则)去执行Handler。
(4) 处理器Handler(需要程序员开发)。
  注意:编写Handler时按照HandlerAdapter的要求去做,这样适配器才可以去正确执行Handler
(5) 视图解析器ViewResolver(不需要程序员开发)。
  作用:进行视图解析,根据逻辑视图名解析成真正的视图(view)
(6) 视图View(需要程序员开发jsp)。
  注意:View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf…)
ps:不需要程序员开发的,需要程序员自己做一下配置即可。
可以总结出:需要我们开发的工作只有处理器 Handler 的编写以及视图比如JSP页面的编写。可能你还对诸如前端控制器、处理器映射器等等名词不太理解,那么接下来我们对其进行详细的介绍。

Mybatis执行流程:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

读取MyBatis的核心配置文件。mybatis-config.xml为MyBatis的全局配置文件,用于配置数据库连接、属性、类型别名、类型处理器、插件、环境配置、映射器(mapper.xml)等信息

加载映射文件。映射文件即SQL映射文件,该文件中配置了操作数据库的SQL语句,映射文件是在mybatis-config.xml中加载;可以加载多个映射文件。常见的配置的方式有两种,一种是package扫描包,一种是mapper找到配置文件的位置

构造会话工厂获取SqlSessionFactory。这个过程其实是用建造者设计模式使用SqlSessionFactoryBuilder对象构建的,SqlSessionFactory的最佳作用域是应用作用域。

创建会话对象SqlSession。由会话工厂创建SqlSession对象,对象中包含了执行SQL语句的所有方法,每个线程都应该有它自己的 SqlSession 实例。SqlSession的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域

获取接口代理对象MapperProxy,根据Mapper接口类获取实现类(动态代理生成的对象)

Executor执行器(执行SQL)。是MyBatis的核心,负责SQL语句的生成和查询缓存的维护,它将根据SqlSession传递的参数动态地生成需要执行的SQL语句,同时负责查询缓存的维护,这才是内部真正对数据库进行操作的操作者,他才是真正的干事的

输入参数映射。输入参数类型可以是基本数据类型

另外就是StatementHandler,该类是Statment处理器,封装了对数据库各种操作方法,使用时候,就调用其中的一些方法

最后就是结果集处理器(ResultSetHandler),这个处理器的作用就是对结果进行处理并返回的,封装结果集。可以封装成多种类型可以是基本数据类型,也可以是Map、List、POJO类型复杂数据类型。封装结果集的过程就和JDBC封装结果集是一样的。也有两个常用的属性resultType和resultMap

6.Mybatis的拦截器有哪几种

拦截器的一个作用就是我们可以拦截某些方法的调用,我们可以选择在这些被拦截的方法执行前后加上某些逻辑,也可以在执行这些被拦截的方法时执行自己的逻辑而不再执行被拦截的方法。Mybatis拦截器设计的一个初衷就是为了供用户在某些时候可以实现自己的逻辑而不必去动Mybatis固有的逻辑。打个比方,对于Executor,Mybatis中有几种实现:BatchExecutor、ReuseExecutor、SimpleExecutor和CachingExecutor。这个时候如果你觉得这几种实现对于Executor接口的query方法都不能满足你的要求,那怎么办呢?是要去改源码吗?当然不。我们可以建立一个Mybatis拦截器用于拦截Executor接口的query方法,在拦截之后实现自己的query方法逻辑,之后可以选择是否继续执行原来的query方法。
简单地说拦截器就是可以实现自己拦截mybatis本身存在的方法,然后换成自己实现的。这样子既不会修改mybatis的源码,也可以实现自己的方法。
Mybatis拦截器并不是每个对象里面的方法都可以被拦截的。Mybatis拦截器只能拦截Executor、ParameterHandler、StatementHandler、ResultSetHandler四个对象里面的方法。

7.Spring中Bean的生命周期

1.实例化 Instantiation
2.属性赋值 Populate
3.初始化 Initialization
4.销毁 Destruction
实例化 -> 属性赋值 -> 初始化 -> 运行 -> 销毁
主要逻辑都在doCreate()方法中,逻辑很清晰,就是顺序调用以下三个方法,这三个方法与三个生命周期阶段一一对应,非常重要,在后续扩展接口分析中也会涉及。
1.createBeanInstance() -> 实例化
2.populateBean() -> 属性赋值
3.initializeBean() -> 初始化
在传统的Java应用中,bean的生命周期很简单,使用Java关键字 new 进行Bean 的实例化,然后该Bean 就能够使用了。一旦bean不再被使用,则由Java自动进行垃圾回收。
相比之下,Spring管理Bean的生命周期就复杂多了,正确理解Bean 的生命周期非常重要,因为Spring对Bean的管理可扩展性非常强,下面展示了一个Bean的构造过程
在这里插入图片描述

Bean 的生命周期
如上图所示,Bean 的生命周期还是比较复杂的,下面来对上图每一个步骤做文字描述:
Spring启动,查找并加载需要被Spring管理的bean,进行Bean的实例化
Bean实例化后对将Bean的引入和值注入到Bean的属性中
如果Bean实现了BeanNameAware接口的话,Spring将Bean的Id传递给setBeanName()方法
如果Bean实现了BeanFactoryAware接口的话,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入
如果Bean实现了ApplicationContextAware接口的话,Spring将调用Bean的setApplicationContext()方法,将bean所在应用上下文引用传入进来。
如果Bean实现了BeanPostProcessor接口,Spring就将调用他们的postProcessBeforeInitialization()方法。
如果Bean 实现了InitializingBean接口,Spring将调用他们的afterPropertiesSet()方法。类似的,如果bean使用init-method声明了初始化方法,该方法也会被调用
如果Bean 实现了BeanPostProcessor接口,Spring就将调用他们的postProcessAfterInitialization()方法。
此时,Bean已经准备就绪,可以被应用程序使用了。他们将一直驻留在应用上下文中,直到应用上下文被销毁。
如果bean实现了DisposableBean接口,Spring将调用它的destory()接口方法,同样,如果bean使用了destory-method 声明销毁方法,该方法也会被调用。
一般来说,只需要回答:实例化 -> 属性赋值 -> 初始化 -> 运行 -> 销毁

AOP发生在SpringBean的哪个阶段

AOP发生在初始化方法initializeBean()执行后
第一阶段 createBeanInstance()方法实例化bean
顺便提一下 为了解决循环依赖问题向缓存暴露bean工厂
第二阶段 populateBean() 属性注入
第三阶段 initializeBean() 方法 进入初始化阶段
第四阶段 销毁

8.Spring Bean是线程安全的吗

不是线程安全的
Spring容器中的Bean是否线程安全,容器本身并没有提供Bean的线程安全策略,Spring中的Bean默认是单例模式的,框架并没有对bean进行多线程的封装处理。因此可以说Spring容器中的Bean本身不具备线程安全的特性,但是具体还是要结合具体scope的Bean去研究。

9.Spring常用注解

@Component,@Service,@Repository,@Controller,@Autowired,@Resource,@Bean,@Value
1.声明bean的注解
@Component 组件,实现bean的注入
@Service 用于标注服务层,主要用来进行业务的逻辑处理
@Repository 用于标注数据访问层,也可以说用于标注数据访问组件,即DAO组件
@Controller 在展现层使用,控制器的声明(C)
2.注入bean的注解
@Autowired:由Spring提供,自动装配,自动按照类型注入,只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
@Inject:由JSR-330提供
@Resource:@Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入,而@Resource默认按 byName自动注入罢了。@Resource有两个属性是比较重要的,分是name和type,Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略
都可以注解在set方法和属性上,推荐注解在属性上(一目了然,少写代码)。
3.java配置类相关注解
@Configuration 声明当前类为配置类,相当于xml形式的Spring配置(类上)
@Bean Spring的@Bean注解用于告诉方法,产生一个Bean对象,然后这个Bean对象交给Spring管理。Spring只会调用一次,将这个Bean对象放在自己的IOC容器中。通俗的将就是在服务启动时,实例化一个对象放到ioc容器中,在需要初始化的实例,方法,内容时使用
@Configuration 声明当前类为配置类,其中内部组合了@Component注解,表明这个类是一个bean(类上)
@Value:声明当前变量的属性值
@Transactional:声明当前类或者方法为事务
@ComponentScan 用于对Component进行扫描,相当于xml中的(类上)
@WishlyConfiguration 为@Configuration与@ComponentScan的组合注解,可以替代这两个注解
4.切面(AOP)相关注解
Spring支持AspectJ的注解式切面编程。
@Aspect 声明一个切面(类上)
使用@After、@Before、@Around定义建言(advice),可直接将拦截规则(切点)作为参数。
@After 在方法执行之后执行(方法上)
@Before 在方法执行之前执行(方法上)
@Around 在方法执行之前与之后执行(方法上)
@PointCut 声明切点
在java配置类中使用@EnableAspectJAutoProxy注解开启Spring对AspectJ代理的支持(类上)
5.@Bean的属性支持
@Scope 设置Spring容器如何新建Bean实例(方法上,得有@Bean)
其设置类型包括:
Singleton (单例,一个Spring容器中只有一个bean实例,默认模式),
Protetype (每次调用新建一个bean),
Request (web项目中,给每个http request新建一个bean),
Session (web项目中,给每个http session新建一个bean),
GlobalSession(给每一个 global http session新建一个Bean实例)
@StepScope 在Spring Batch中还有涉及
@PostConstruct 由JSR-250提供,在构造函数执行完之后执行,等价于xml配置文件中bean的initMethod
@PreDestory 由JSR-250提供,在Bean销毁之前执行,等价于xml配置文件中bean的destroyMethod
6.@Value注解
@Value 为属性注入值(属性上)

@RestController和@Controller的区别

相当于@Controller+@ResponseBody两个注解的结合,返回json数据不需要在方法前面加@ResponseBody注解了,但使用@RestController这个注解,就不能返回jsp,html页面,视图解析器无法解析jsp,html页面
@responseBody注解的作用是将controller的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到response对象的body区,通常用来返回JSON数据或者是XML数据。也就是返回json结果数据

10.Mybatis常用注解:

@Insert
@Update
@Delete
@Select

可在注解里面写sql,例如:
在这里插入图片描述

11.SpringMVC常用注解:

@Controller
@Controller注解在类上,表明这个类是Spring MVC里的Controller,将其声明为Spring的一个Bean,Dispatch Servlet会自动扫描注解了此注解的类,并将Web请求映射到注解了@RequestMapping的方法上,需要注意的是,在Spring MVC声明控制器Bean的时候,只能使用@Controller。
@RequestMapping
@RequestMapping注解是用来映射Web请求(访问路径和参数)、处理类和方法的。它可以注解在类和方法上也就是定义controller接口路径。注解在方法上的@RequestMapping路径会继承注解在类上的路径,@RequestMapping支持Servlet的request和response作为参数,也支持对它们的媒体类型进行配置。
@ResponseBody
@ResponseBody支持将返回值放在response体内,而不是返回一个页面。我们很多机遇Ajax的程序,可以以此注解返回数据而不是返回页面;此注解可以放在返回值或者方法上。
@RequestBody
@RequestBody允许request的参数在request体中,而不是在直接链接在地址后面。此注解放在参数前。
@PathVariable
@PathVariable 用来接收路径参数,如/news/001,可接收001作为参数,此注解放置在参数前。
@RestController
@RestController是一个组合注解,组合了@Controller和@ResponseBody,意味着当只开发一个和页面交互数据的控制的时候,需要使用此注解。 若没有此注解,要想实现上述功能,则需自己在代码中加@Controller和@ResponseBody两个注解。

12.Spring AOP的实现原理?(基于动态代理模式,如果目标类实现了接口,那么使用基于接口的动态代理,否则使用基于子类/cglib的动态代理)

AOP(面向切面)是一种编程范式,提供从另一个角度来考虑程序结构以完善面向对象编程(OOP)。
AOP为开发者提供了一种描述横切关注点的机制,并能够自动将横切关注点织入到面向对象的软件系统中,从而实现了横切关注点的模块化。
AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任,例如事务处理、日志管理、权限控制等,封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性此外,也可利用AOP进行方法增强

使用AOP的好处

  • 降低模块的耦合度
  • 使系统容易扩展
  • 提高代码复用性
  • 不用再去修改一些已经确定的代码

基于动态代理模式,如果目标类实现了接口,那么使用基于接口的动态代理,否则使用基于子类/cglib的动态代理

IOC(控制反转)

IOC(控制反转)就是依赖倒置原则的一种代码设计思路。就是把原先在代码里面需要实现的对象创建、对象之间的依赖,反转给容器来帮忙实现。
Spring IOC容器通过xml,注解等其它方式配置类及类之间的依赖关系,完成了对象的创建和依赖的管理注入。实现IOC的主要设计模式是工厂模式

●谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对 象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。  
●为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了

使用IOC的好处

  • 集中管理,实现类的可配置和易管理
  • 降低了类与类之间的耦合度

Spring AOP 的具体加载步骤?
spring AOP的使用,分三个步骤,记住这三个步骤,AOP就不会有问题:

  1. 确定目标对象(target—>bean) 通俗的来讲就是“哪个方法需要增强,你就把他交给spring
  2. 编写Advice通知方法 (增强代码) 就是写增强代码
  3. 配置切入点和切面 第三点的作用就是:让你的增强代码作用于你要增强的目标对象上

连接点(Joinpoint) 程序执行的某个特定位置,如某个方法调用前,调用后,方法抛出异常后,这些代码中的特定点称为连接点。简单来说,就是在哪加入你的逻辑增强
连接点表示具体要拦截的方法,上面切点是定义一个范围,而连接点是具体到某个方法
切点(PointCut) 每个程序的连接点有多个,如何定位到某个感兴趣的连接点,就需要通过切点来定位。比如,连接点–数据库的记录,切点–查询条件
切点用于来限定Spring-AOP启动的范围,通常我们采用表达式的方式来设置,所以关键词是范围
增强(Advice) 增强是织入到目标类连接点上的一段程序代码。在Spring中,像BeforeAdvice等还带有方位信息
通知是直译过来的结果,我个人感觉叫做“业务增强”更合适 对照代码就是拦截器定义的相关方法,通知分为如下几种:
前置通知(before):在执行业务代码前做些操作,比如获取连接对象
后置通知(after):在执行业务代码后做些操作,无论是否发生异常,它都会执行,比如关闭连接对象
异常通知(afterThrowing):在执行业务代码后出现异常,需要做的操作,比如回滚事务
返回通知(afterReturning),在执行业务代码后无异常,会执行的操作
环绕通知(around),这个目前跟我们谈论的事务没有对应的操作,所以暂时不谈
目标对象(Target) 需要被加强的业务对象
织入(Weaving) 织入就是将增强添加到对目标类具体连接点上的过程。
织入是一个形象的说法,具体来说,就是生成代理对象并将切面内容融入到业务流程的过程。
代理类(Proxy) 一个类被AOP织入增强后,就产生了一个代理类。
切面(Aspect) 切面由切点和增强组成,它既包括了横切逻辑的定义,也包括了连接点的定义,SpringAOP就是将切面所定义的横切逻辑织入到切面所制定的连接点中。
比如上文讨论的数据库事务,这个数据库事务代码贯穿了我们的整个代码,我们就可以这个叫做切面。 SpringAOP将切面定义的内容织入到我们的代码中,从而实现前后的控制逻辑。 比如我们常写的拦截器Interceptor,这就是一个切面类

13. AOP和OOP的区别,分别适用什么场景?

OOP是面向对象编程,核心思想是将客观存在的不同事物抽象成相互独立的类,然后把与事物相关的属性和行为封装到类里,并通过继承和多态来定义类彼此间的关系,最后通过操作类的实例来完成实际业务逻辑的功能需求。
AOP是面向切面编程,核心思想是将业务逻辑中与类不相关的通用功能切面式的提取分离出来,让多个类共享一个行为,一旦这个行为发生改变,不必修改类,而只需要修改这个行为即可。

OOP与AOP的区别:

  • 面向目标不同:简单来说OOP是面向名词领域,AOP面向动词领域。
  • 思想结构不同:OOP是纵向结构,AOP是横向结构。
  • 注重方面不同:OOP注重业务逻辑单元的划分,AOP偏重业务处理过程的某个步骤或阶段。

OOP面向对象编程,针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。而AOP则是针对业务处理过程中的切面进行提取,它所面对的是处理过程的某个步骤或阶段,以获得逻辑过程的中各部分之间低耦合的隔离效果。这两种设计思想在目标上有着本质的差异。

14.Spring Bean的作用域,Spring Bean的生命周期(Bean的实例化—初始化Bean—使用Bean—Bean的销毁)**

在Spring中,那些组成应用程序的主体及由Spring IoC容器所管理的对象,被称之为bean。简单地讲,bean就是由IoC容器初始化、装配及管理的对象(例如JavaBean),除此之外,bean就与应用程序中的其他对象没有什么区别了。而bean的定义以及bean相互间的依赖关系将通过配置元数据来描述。
  Spring中的bean默认都是单例的,对于Web应用来说,Web容器对于每个用户请求都创建一个单独的Sevlet线程来处理请求,引入Spring框架之后,每个Action都是单例的,那么对于Spring托管的单例Service Bean,Spring的单例是基于BeanFactory也就是Spring容器的,单例Bean在此容器内只有一个,Java的单例是基于JVM,每个JVM内只有一个实例。
bean的作用域
singleton(默认),prototype,request,session,globalSession
  创建一个bean定义,其实质是用该bean定义对应的类来创建真正实例的“配方”。把bean定义看成一个配方很有意义,它与class很类似,只根据一张“处方”就可以创建多个实例。不仅可以控制注入到对象中的各种依赖和配置值,还可以控制该对象的作用域。这样可以灵活选择所建对象的作用域,而不必在Java Class级定义作用域。Spring Framework支持五种作用域,分别阐述如下表。
在这里插入图片描述
servlet的生命周期
servlet的生命周期就是从servlet出现到销毁的全过程。主要分为以下几个阶段:
加载类—>实例化(为对象分配空间)—>初始化(为对象的属性赋值)—>请求处理(服务阶段)—>销毁

15.单例作用域和原型作用域的区别?

其中比较常用的是singleton和prototype两种作用域对于singleton作用域的Bean,每次请求该Bean都将获得相同的实例。容器负责跟踪Bean实例的状态,负责维护Bean实例的生命周期行为;如果一个Bean被设置成prototype作用域,程序每次请求该id的Bean,Spring都会新建一个Bean实例,然后返回给程序。在这种情况下,Spring容器仅仅使用new 关键字创建Bean实例,一旦创建成功,容器不在跟踪实例,也不会维护Bean实例的状态。

如果不指定Bean的作用域,Spring默认使用singleton作用域。Java在创建Java实例时,需要进行内存申请;销毁实例时,需要完成垃圾回收,这些工作都会导致系统开销的增加。因此,prototype作用域Bean的创建、销毁代价比较大。而singleton作用域的Bean实例一旦创建成功,可以重复使用。因此,除非必要,否则尽量避免将Bean被设置成prototype作用域。

Spring容器创建对象的两种时机,各有什么优缺点?(一是Spring容器启动时创建Bean,二是调用getBean方法时创建)
其实就是预加载和懒加载

  1. spring默认创建对象是在启动spring容器的时候
  2. 这里还有另外一种情况,就是在我们调用这个对象的时候,才新建对象。我们要在配置文件中,对应类的属性里面添加一个属性: lazy-init
    该属性的默认值为default。相当于false。
<bean class="cn.ansel.domain.helloSpring" id="hello" lazy-init="true"></bean>
是先启动了spring容器,然后就是我们调用该类的时候,spring容器才帮我们创建对象

在第一种情况下可以在启动 spring 容器时,检查 spring 容器配置文件的正确性,如果再结合Tomcat。
如果 spring 容器不能正常启动,整体 Tomcat 就不能启动。但是缺点是把一些 bean 过早存放在内存中,如果数据大,对内存是一个消耗
在第二种情况下,可以减少内存消耗,但是不容易发现错误

17.如果要给每个bean都加一个字段属性,如何实现?

Java Bean 的遍历,利用Java的反射原理。Bean的属性添加属性值,使用Java的自定义注解。

18.Mybatis如何实现批量插入?

  1. 通过insert标签加上foreach标签,可以实现Mybatis批量插入的功能
  2. 另一种,可以用mysql支持的批量插入语句,这种方式相比起来,更高效
insert into table ([列名],[列名])
VALUES
([列值],[列值])),
([列值],[列值])),
([列值],[列值]));

批量的好处:可以避免程序和数据库建立多次连接,从而增加服务器负荷。
使用批量插入执行的SQL语句应该等价于:

 insert into table (id, name,sex,address)
 values
 (?,?,?,?),(?,?,?,?),(?,?,?,?),(?,?,?,?)

19.Mybatis的xml文件中,sql语句可以使用直接使用大于号小于号吗?应该用什么符号代替?

一、使用<![CDATA[ ]]>符号
  例如:<![CDATA[ c.COUPON_MININUM <= #{COUPON_MININUM} ]]>
  因为在XML中不允许出现“<”等符号,在经过“<![CDATA[ ]]>”处理的大于小于符就不会被解析
二、使用XML转义字符
在这里插入图片描述

20.spring的事务传播特性

事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播,spring支持7种事务传播行为
propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择
propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作
Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。假设 ServiveX#methodX() 都工作在事务环境下(即都被 Spring 事务增强了),假设程序中存在如下的调用链:Service1#method1()->Service2#method2()->Service3#method3(),那么这 3 个服务类的 3 个方法通过 Spring 的事务传播机制都工作在同一个事务中
Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况

21.Java自定义的元注解

Java中有四种元注解:
@Retention:表示该注解可以保存的时间范围(也就是保存的时间)
@Inherited:允许子类继承父类中的注解
@Documented:即拥有这个注解的元素可以被javadoc此类的工具文档化
@Target:表示该注解用于什么地方

22.Spring IOC依赖注入的几种方式

构造方法注入,set注入,@Autowired注解自动注入,接口注入(已废弃)

23.MyBatis 中 # 和 $ 的区别?

#{}是预编译处理,
$ {}是字符串替换。

Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
Mybatis会把这个参数认为是一个字符串,并自动加上’ ’
Mybatis在处理$ {}时,就是把${}替换成变量的值。

使用#{}可以有效的防止SQL注入,提高系统安全性

SQL注入的原理
SQL 注入的原理主要有以下 4 点:
1)恶意拼接查询
我们知道,SQL 语句可以查询、插入、更新和删除数据,且使用分号来分隔不同的命令。例如:

SELECT * FROM users WHERE user_id = $user_id

其中,user_id 是传入的参数,如果传入的参数值为“1234; DELETE FROM users”,那么最终的查询语句会变为:

SELECT * FROM users WHERE user_id = 1234; DELETE FROM users

如果以上语句执行,则会删除 users 表中的所有数据。
2)利用注释执行非法命令
SQL 语句中可以插入注释。例如:

SELECT COUNT(*) AS ‘num’ FROM game_score WHERE game_id=24411 AND
version=$version

如果 version 包含了恶意的字符串’-1’ OR 3 AND SLEEP(500)–,那么最终查询语句会变为:

SELECT COUNT(*) AS ‘num’ FROM game_score WHERE game_id=24411 AND
version=‘-1’ OR 3 AND SLEEP(500)–

以上恶意查询只是想耗尽系统资源,SLEEP(500) 将导致 SQL 语句一直运行。如果其中添加了修改、删除数据的恶意指令,那么将会造成更大的破坏。
3)传入非法参数
SQL 语句中传入的字符串参数是用单引号引起来的,如果字符串本身包含单引号而没有被处理,那么可能会篡改原本 SQL 语句的作用。 例如:

SELECT * FROM user_name WHERE user_name = $user_name

如果 user_name 传入参数值为 G’chen,那么最终的查询语句会变为:

SELECT * FROM user_name WHERE user_name =‘G’chen’

一般情况下,以上语句会执行出错,这样的语句风险比较小。虽然没有语法错误,但可能会恶意产生 SQL 语句,并且以一种你不期望的方式运行。
4)添加额外条件
在 SQL 语句中添加一些额外条件,以此来改变执行行为。条件一般为真值表达式。例如:

UPDATE users SET userpass=' u s e r p a s s ′ W H E R E u s e r i d = userpass' WHERE user_id= userpassWHEREuserid=user_id;

如果 user_id 被传入恶意的字符串“1234 OR TRUE”,那么最终的 SQL 语句会变为:

UPDATE users SET userpass= ‘123456’ WHERE user_id=1234 OR TRUE;

这将更改所有用户的密码。

24.什么是 SQL 注入?

sql注入是一种将sql代码添加到输入参数中,传递到sql服务器解析并执行的一种攻击手法,是针对程序员编写时的疏忽,通过SQL语句,实现无账号登录,甚至篡改数据库

25.MVC是什么?为什么这么设计?

M:model,分装数据相关;
V:view,视图层;
C:controller,控制层,
一般处理逻辑,作为连接 M 和 C 的“桥梁”。这么设计使得相关模块分层,增加了程序的可拓展性和维护性

26.项目中如何配置事务

事务管理是应用系统开发中必不可少的一部分。Spring 为事务管理提供了丰富的功能支持。Spring 事务管理分为 编程式声明式 的两种方式。

编程式:指的是通过编码方式实现事务
比如java代码使用jdbc进行数据库操作而没有引入其他框架时,就要通过手动编写事务进行处理。

声明式:基于 AOP, 将具体业务逻辑与事务处理解耦

声明式事务管理使业务代码逻辑不受污染, 因此在实际使用中声明式事务用的比较多
声明式事务有两种方式配置

1.基于XML配置
2.基于 @Transactional 注解

一、SSM
配置:
spring-mybatis.xml

<!-- 配置事务管理器 -->
<bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- 注入数据库连接池 -->
    <property name="dataSource" ref="dataSource"/>
</bean>

<!-- 使用基于注解方式配置事务 -->
<tx:annotation-driven transaction-manager="transactionManager"/>

使用:
在这里插入图片描述
只需在 Service 的类或方法上面写 @Transactional 注解即可, @Transactional 中的属性,下面会详细讲解。

二、SpringBoot
开启事务:
Application 类上面打个 @EnableTransactionManagement 注解即可。如下:
在这里插入图片描述
@EnableTransactionManagement有两个属性可以看看:

  • proxyTargetClass 默认false(标准的 JDK 基于接口的代理)

    该属性用于控制代理是基于接口的还是基于类被创建 设置为 true 表示使用基于子类实现的代理(CGLIB),设置为 false
    表示使用基于接口实现的代理

  • mode 默认PROXY

    该属性表示是使用哪种事务切面,有 PROXY 和 ASPECTJ,想要更深入了解,可以看看源码

使用也是在 Service 上面加 @Transactional 注解即可

在使用 @Transactional 注解的时候,需要注意的一些问题
1、默认配置下 Spring 只会回滚运行时、未检查异常(继承自 RuntimeException 的异常)或者 Error,但是我们可以配置 rollbackFor 来指定我们要处理的异常

2、 @Transactional 注解只能应用到 public 可见度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错, 但是这个被注解的方法将不会展示已配置的事务设置。

3、 @Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上,但是Spring团队建议在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上(因为 Spring 的声明式事务是默认基于 SpringAOP 实现,而 SpringAOP 默认是使用 java 动态代理实现(基于接口实现),如果此时将代理改为cglib(基于子类实现),在接口上面加注解就没用了,所以为了保持兼容注解最好都写到实现类方法上)

4、如果 @Transactional 注解被写在 Service 的类上面,则表示类中所有的方法都被事务管理,但是有些查询方法是不需要事务管理,那么可以这样做(为什么这样,看完下面就明白了):

/**
 1. 获取用户列表
 2. <br/>
 3. 查询不需要事务
 */
@Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
public PageInfo<User> listUsers(Page<User> page) {
    PageHelper.startPage(page.getPageNum(), page.getPageSize());
    List<User> users = userMapper.listUsers();
    return new PageInfo<>(users);
}

5、有些时候我们需要手动回滚事务,那么,有两种方法:

  1. service层处理事务,那么service中的方法中不做异常捕获,或者在catch语句中最后增加throw new
    RuntimeExcetpion()语句,以便让Aop捕获异常再去回滚,并且在service上层(webservice客户端,view层Controller)要继续捕获这个异常并处理
  2. 在service层方法的catch语句中增加:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();语句,手动回滚,这样上层就无需去处理异常(现在项目的做法)

@Transactional 中的属性:
1、value、transactionManager
这两个作用一样,当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器
2、propagation
事务的传播行为,默认值为 Propagation.REQUIRED
3、isolation
事务的隔离级别,默认值为 Isolation.DEFAULT
4、timeout
事务的超时时间,默认值为-1,表示事务超时将依赖于底层事务系统
如果超过该时间限制但事务还没有完成,则自动回滚事务。
5、readOnly
指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。
6、rollbackFor
需要触发回滚的异常定义,可定义多个,默认任何RuntimeException都将导致事务回滚,而任何Checked Exception将不导致事务回滚
7、noRollbackFor
抛出指定的异常类型,不回滚事务,也可以指定多个异常类型

@Transactional注解可以作用于哪些地方?

@Transactional 可以作用在接口、类、类方法

作用于类:当把@Transactional 注解放在类上时,表示所有该类的public方法都配置相同的事务属性信息
作用于方法:当类配置了@Transactional,方法也配置了@Transactional,方法的事务会覆盖类的事务配置信息
作用于接口:不推荐这种使用方法,因为一旦标注在Interface上并且配置了Spring AOP 使用CGLib动态代理,将会导致@Transactional注解失效

27.@Transactional这个注解,底层做了些什么?

主要是利用动态代理模式通过反射获取bean的注解信息,利用AOP对编程式事务进行封装实现

@Transactional注解失效的情况

1、@Transactional 应用在非 public 修饰的方法上

如果Transactional注解应用在非public修饰的方法上,Transactional将会失效。

注意:protected、private修饰的方法上使用 @Transactional 注解,虽然事务无效,但不会有任何报错,这是我们很容犯错的一点。

2、@Transactional 注解属性 propagation 设置错误

这种失效是由于配置错误,若是错误的配置以下三种 propagation,事务将不会发生回滚。

TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。

TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。

3、@Transactional 注解属性 rollbackFor 设置错误

4、同一个类中方法调用,导致@Transactional失效

开发中避免不了会对同一个类里面的方法调用,比如有一个类Test,它的一个方法A,A再调用本类的方法B(不论方法B是用public还是private修饰),但方法A没有声明注解事务,而B方法有。则外部调用方法A之后,方法B的事务是不会起作用的。这也是经常犯错误的一个地方。

5、异常被你的 catch“吃了”导致@Transactional失效

这种情况是最常见的一种@Transactional注解失效场景,
如果B方法内部抛了异常,而A方法此时try catch了B方法的异常,那这个事务还能正常回滚吗?
答案:不能!

6、数据库引擎不支持事务

这种情况出现的概率并不高,事务能否生效数据库引擎是否支持事务是关键。常用的MySQL数据库默认使用支持事务的innodb引擎。一旦数据库引擎切换成不支持事务的myisam,那事务就从根本上失效了。

28.spring 容器的启动加载流程

  1. 首先解析 spring.xml 配置文件,把其中 解析为 BeanDefinition, 存入beanFactory,把 BeanDefinition中的定义读取到一个map集合中管理起来了,但还没有创建 bean 的单例对象
 <bean id="" class="" init-method="" destroy-method="" scope="" lazy-init="">
  1. 执行beanFactory的后处理器
  2. 接下来 由 beanFactory 创建每个类对应的单例对象, 利用了反射根据类名创建每个类的实例对象(构造,初始化方法)
  3. 执行 bean 的后处理器, 它其中有两个方法,会在 bean 的初始化方法前后被调用 会把这些结果 存入 beanFactory 的
    singletonObjects 这样一个map集合里
    userService -> userService对象
  4. 执行依赖注入 主要为了解决循环引用问题
  5. 容器.getBean(“bean id”) 根据bean 的id,把bean id当做key,进入 singletonObjects
    获取值 返回的结果就是 userSerivce对象

29.注解的底层实现原理

注解的底层也是使用反射实现的,我们可以自定义一个注解来体会下。注解和接口有点类似,不过申明注解类需要加上@interface,注解类里面,只支持基本类型、String及枚举类型,里面所有属性被定义成方法,并允许提供默认值
注解利用元注解来定义

@Target — —注解用于什么地方

ANNOTATION_TYPE,//给注解注解(这貌似把自己不当类来看)
ElementType.FIELD  //注解作用于变量
ElementType.METHOD //注解作用于方法
ElementType.PARAMETER //注解作用于参数
ElementType.CONSTRUCTOR //注解作用于构造方法
ElementType.LOCAL_VARIABLE //注解作用于局部变量
ElementType.PACKAGE //注解作用于包

@Retention — —描述注解保留的时间范围

SOURCE, //源码状态运行,
 CLASS, //编译类文件时运行
 RUNTIME //运行时运行

@Documented — — 生成说明文档,添加类的解释

表示注解会被包含在javaapi文档中
当一个注解被@Documented元注解所修饰时,那么无论在哪里使用这个注	解,都会被Javadoc工具文档化。

@Inherited — —允许子类继承父类中的注解
允许子类继承父类的注解。
用于描述某个被标注的类型可被继承的,如果一个使用了@Inherited修饰的annotation类型类型被用于一个class,则这个annotation将被用于该class类的子类。

自定义实现运行时注解

   @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface TestAnnonation {
       String name() default "";
       int Id()  default 0;
    }

注解关键字时@interface,然后上面标注为元注解,表示只能修饰方法并且加载到虚拟机中,里面时这个注解所具有的属性,name, id,我们在给方法加注解的时候设置相应的值。

@TestAnnonation(name = "android" , Id = 1)
private void testAnno(){
}

上面我们在一个方法上面添加注解,然后我们通过下面的方法将这个注解打印出来

30.Mapper里的方法可以重载吗?

不可以,因为mybatis查找mapper内的方法是靠方法名,和参数无关

31.Restful风格接口

Restful风格的API是一种软件架构风格,设计风格而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制

在Restful风格中,用户请求的url使用同一个url而用请求方式:get,post,delete,put…等方式对请求的处理方法进行区分,这样可以在前后台分离式的开发中使得前端开发人员不会对请求的资源地址产生混淆和大量的检查方法名的麻烦,形成一个统一的接口

规定如下
GET(SELECT):从服务器查询,可以在服务器通过请求的参数区分查询的方式。
POST(CREATE):在服务器新建一个资源,调用insert操作。
PUT(UPDATE):在服务器更新资源,调用update操作。
PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。(目前jdk7未实现,tomcat7也不行)。
DELETE(DELETE):从服务器删除资源,调用delete语句。

SpringBoot

在使用传统的Spring去做Java EE(Java Enterprise Edition)开发中,大量的 XML 文件存在于项目之中,导致JavaEE项目变得慢慢笨重起来,,繁琐的配置和整合第三方框架的配置,导致了开发和部署效率的降低。

Spring Boot 并不是用来替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具。同时它集成了大量常用的第三方库配置,Spring Boot应用中这些第三方库几乎可以是零配置的开箱即用(out-of-the-box),大部分的 Spring Boot 应用都只需要非常少量的配置代码(基于 Java 的配置),开发者能够更加专注于业务逻辑

SpringBoot的优点
优点
快速构建项目。
对主流开发框架的无配置集成。
项目可独立运行,无须外部依赖Servlet容器。
提供运行时的应用监控。
极大地提高了开发、部署效率。
简化配置:部署配置方面,原来 Spring 有多个 xml 和 properties配置,在 Spring Boot 中只需要个 application.yml即可

简化部署:在使用 Spring 时,项目部署时需要我们在服务器上部署 tomcat,然后把项目打成 war 包扔到 tomcat里,在使用 Spring Boot 后,我们不需要在服务器上去部署 tomcat,因为 Spring Boot 内嵌了 tomcat,我们只需要将项目打成 jar 包,使用 java -jar xxx.jar一键式启动项目。
另外,也降低对运行环境的基本要求,环境变量中有JDK即可

提供 starter 简化 Maven 配置
Spring 提供了一系列的 starter pom 来简化 Maven 的依赖加载,例如,当你使用了spring-boot-starter-web 时,Spring Boot 这个 starter-web 已经包含了多个依赖,包括之前在 Spring 工程中需要导入的依赖。

Spring Cloud常用组件:

服务发现——Eureka
实现服务治理(服务注册与发现)

客服端负载均衡——Ribbon
主要提供客户侧的软件负载均衡算法。

断路器——Hystrix
断路器,保护系统,控制故障范围。

服务网关——Zuul
过滤、安全、监控、限流、路由等作用

分布式配置——Spring Cloud Config
SpringCloud Config提供服务器端和客户端。服务器存储后端的默认实现使用git,因此它轻松支持标签版本的配置环境,以及可以访问用于管理内容的各种工具,配置管理

Mybatis的命名空间:

就是给mapper文件命名唯一的id,然后可以根据这个命名去找到相应的sql的id方法
当你的namespace绑定接口后,你可以不用写接口实现类,mybatis会通过该绑定自动帮你找到对应要执行的SQL语句

1.定义mapper接口,面向接口编程。

2.在大型项目中,可能存在大量的SQL语句,这时候为每个SQL语句起一个唯一的标识(ID)就变得并不容易了。为了解决这个问题,在MyBatis中,可以为每个映射文件起一个唯一的命名空间,这样定义在这个映射文件中的每个SQL语句就成了定义在这个命名空间中的一个ID。只要我们能够保证每个命名空间中这个ID是唯一的,即使在不同映射文件中的语句ID相同,也不会再产生冲突了。

hibernate懒加载

面试题: get、load方法区别?

get: 及时加载,只要调用get方法立刻向数据库查询

load:默认使用懒加载,当用到数据的时候才向数据库查询。

懒加载:(lazy)

概念:当用到数据的时候才向数据库查询,这就是hibernate的懒加载特性。

               目的:提供程序执行效率!

lazy 值

     true   使用懒加载

     false   关闭懒加载

     extra   (在集合数据懒加载时候提升效率)

在真正使用数据的时候才向数据库发送查询的sql;

如果调用集合的size()/isEmpty()方法,只是统计,不真正查询数据!

懒加载异常

n Session关闭后,不能使用懒加载数据!

n 如果session关闭后,使用懒加载数据报错:

org.hibernate.LazyInitializationException: could not initialize proxy - no Session

               如何解决session关闭后不能使用懒加载数据的问题?

                        // 方式1: 先使用一下数据

               //dept.getDeptName();

              // 方式2:强迫代理对象初始化

               Hibernate.initialize(dept);

              // 方式3:关闭懒加载

                        设置lazy=false;

               // 方式4: 在使用数据之后,再关闭session!

项目配置网关gateway:

gateway:
  routes:
    - id: order
      predicates:
        - Path=/order/**
      uri: lb:http://order
    - id: terminal
      predicates:
        - Path=/terminal/**
      filters:
        - StripPrefix=1
      uri: lb:ws://terminal
discovery:
  client:
    simple:
      instances:
        order:
          - uri: ${ORDER_WEB_URL:http://10.10.3.46:3520}
        terminal:
          - uri: ${ORDER_TERMINAL_URL:ws://127.0.1.123:3749}

代码里获取配置中心的值

1.@value注解
2.@ConfigurationProperties注解
3.配置Config类

@Component
@RefreshScope
public class Config {
// 这里使用到的是ConfigurableApplicationContext 来获取配置中心到值。
    @Autowired
    private ConfigurableApplicationContext configurableApplicationContext;
	//这个变量最好和nacos里需要获取到一致。方便识别
    public String maxloglength;
	//get和set方法
    public void setMaxloglength(String maxloglength) {
        this.maxloglength = maxloglength;
    }
	//get方法处理一下
    public String getMaxloglength() {
    //这里的xx.xx是你配置中心要获取到值
    /*例如你的nacos需要获取的是这样到
    *monthTime:
    *  maxloglength: 9000
    * 那么这个地方就是monthTime.maxloglength
    */
        return maxloglength = configurableApplicationContext.getEnvironment().getProperty("xx.xx");
    }
}


spring如何解决循环依赖

什么是循环依赖:循环依赖就是循环引用,俗称“套娃”,比如beanA需要引用BeanB,BeanB需要引用BeanA,形成循环关系;
一个项目,随着业务的扩展,功能的迭代,必定会存在某些类和类之间的相互调用,比如 serviceA 调用了serviceB 的某个方法,同时 serviceB 也调用了serviceA 中的某个方法,从而形成了一种循环依赖的关系。
在这里插入图片描述

假如 Spring 容器启动后,先会实例化 A,但在 A 中又注入了 B,然后就会去实例化 B,但在实例化 B 的时候又发现 B 中注入了 A,于是又继续循环,后果就是导致程序 OOM

spring bean发生循环依赖有三种形式:

1、互相依赖:
eg:a和b相互依赖形成循环依赖。如下图:
在这里插入图片描述

2、三者之间及其以上的依赖:
eg:多个bean之间形成循环依赖。如下图:
在这里插入图片描述

3、自我依赖
eg:a依赖a形成循环依赖。 如下图:
在这里插入图片描述
spring解决循环依赖的方式
利用缓存机制解决循环依赖问题,spring设计了三级缓存解决循环依赖的问题,分别是一级缓存:singletonObjects;二级缓存:earlySingletonObjects;三级缓存:singletonFactories;

名称描述
singletonObjects一级缓存,用于存放完全初始化好的 Bean
earlySingletonObjects二级缓存,存放提前暴露的Bean,Bean 是不完整的,未完成属性注入和执行初始化方法
singletonFactories三级缓存,单例Bean工厂,二级缓存中存储的就是从这个工厂中获取到的对象

三级缓存之间逐级取,流程如下:

1、getBean()获取实例,Spring首先从一级缓存singletonObjects中获取;
2、如果获取不到,就从二级缓存earlySingletonObjects中获取,如果还是获取不到意味着bean没有实例化;
3、这时singletonFactories通过getObject()获取,就从三级缓存singletonFactory.getObject()(三级缓存)获取;(代理也是从三级缓存生产的)
4、如果从三级缓存中获取到就从singletonFactories中移除,并放入earlySingletonObjects中。其实也就是从三级缓存移动到了二级缓存;
5、这个bean存在会等待下一次轮寻的时候去赋值(解析@Autowared,@Resource)注解等,属性赋值完成后,将bean存入一级缓存;

三级循环调用
在这里插入图片描述
Spring解决了单例情况下的循环依赖,在不考虑AOP的情况下,大致步骤如下:

A 实例化时依赖 B,于是 A 先放入三级缓存,然后去实例化 B;

B 进行实例化,把自己放到三级缓存中,然后发现又依赖于 A,于是先去查找缓存,但一级二级都没有,在三级缓存中找到了。

然后把三级缓存里面的 A 放到二级缓存,然后删除三级缓存中的 A;
然后 B 注入半成品的实例 A 完成实例化,并放到一级缓存中;

然后回到 A,因为 B 已经实例化完成并存在于一级缓存中,所以直接从一级缓存中拿取,然后注入B,完成实例化,再将自己添加到一级缓存中。
参考:https://blog.csdn.net/qq_41907991/article/details/107164508

springcloud五大组件

1、Eureka实现服务治理;
2、Ribbon主要提供客户侧的软件负载均衡算法;
3、Hystrix断路器,保护系统,控制故障范围;
4、Zuul,api网关,路由,负载均衡等多种作用;
5、Config配置管理。

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值