0%

复习笔记 - Spring

Spring

Spring核心是什么

spring包含众多模块,如:core,Testing,Data Access, Web Servlet等。其中core是整个Spring框加的核心模块。其中最核心的部分是IOC和AOP。

  • IOC(Inversion of control)是控制反转的意思,是一种面向对象编程的设计思想。IOC可以帮我们维护对象和对象之间的依赖关系,降低对象之间的耦合度。
  • DI(dependency injection)是依赖注入的意思。它是IOC实现的实现方式。就是说IOC是通过DI实现的。实现依赖注入的关键是IOC容器,它的本质是一个工厂。
  • AOP(aspect oriented programing)是面向切面编程的意思。在Aop思想下,我们可以将解决共性需求的代码独立出来,然后通过配置的方式,声明这些代码在什么地方调用,什么时机调用。当满足调用条件时,AOP会将该业务代码织入到我们指定的位置,从而统一解决了问题,又不需要修改这一批组件的代码。

Spring5模块

Spring的IOC容器

spring主要提供两种类型的容器:BeanFactoryApplicationContext

  • BeanFactory:是基础类型的IoC容器,提供完整的IoC服务支持。如果没有特殊指定,默认采用延迟初始化策略懒汉式)。只有当客户端对象需要访问容器中的某个受管对象的时候,才对该受管对象进行初始化以及依赖注入操作。

  • ApplicationContext:它是在BeanFactory的基础上构建的,是相对比较高级的容器实现,除了拥有BeanFactory的所有支持,ApplicationContext还提供了其他高级特性,

    ApplicationContext所管理的对象,在该类型容器启动之后,默认全部初始化并绑定完成(饿汉式)。所以,相对于BeanFactory来说,ApplicationContext要求更多的系统资源,同时,因为在启动时就完成所有初始化,容 器启动时间较之BeanFactory也会长一些。

BeanFactory

  • BeanFactory是一个类工厂,与传统类工厂不同的是,BeanFactory是类的通用工厂,它可以创建并管理各种类的对象。这些可被创建和管理的对象本身没有什么特别之处,仅是一个POJO,Spring称这些被创建和管理的Java对象为Bean。
  • BeanFactory是Spring容器的顶层接口。最主要的方法就是getBean(String beanName),该方法从容器中返回特定名称的bean。

Spring如何管理Bean

Spring通过IOC容器来管理Bean,我们可以通过xml配置或注解配置来指导IOC容器对Bean的管理。因为注解配置比XML配置方便很多,所以更多的使用注解配置的方式。

管理Bean的注解

  • @ComponentScan用于声明扫描策略,通过它的声明,容器就知道要扫描哪些包下带有声明的类,也可以知道哪些类是被排除在外的。
  • @Component@Service@Repository@Controller用于声明Bean,他们的作用一样。被这些注解声明的类就可以被容器扫描并创建。
  • Autowired@Qualifier用于注入Bean,即告诉容器应该为当前属性注入哪个Bean。其中@Autowired是按照Bean类型进行匹配的。如果这个属性的类型有多个bean,就可以通过@Qualifieer指定bean的名称,以消除歧义。
  • @Scope用于声明Bean的作用域。默认情况下Bean是单例的,即在整个容器中这个类型只有一个实例。可以通过@Scope注解指定prototype值将其声明为多例的,
  • @PostConstruct@PreDestroy用于声明Bean的生命周期。其中,被@PostConstruct修饰的方法将在Bean实例化后被调用,@PreDestroy修饰的方法将在容器销毁前被调用。

Bean的作用域

默认情况下:Bean在Spring容器中是单例的,我们可以通过@Scope注解修改bean的作用域,

类型 说明
singleton 单例:在Spring容器中仅存在一个实例,即bean以单例的形式存在。
prototype 多例:每次调用getBean()时,都会执行new操作,返回一个新的实例。
request 每次HTTP请求都会创建一个新的Bean。
session 同一个HTTP Session共享一个Bean,不同的HTTP Session使用不同的Bean。
globalSession 同一个全局的Session共享一个Bean,一般用于Portlet环境。

Bean的生命周期

image-20230411154650926

image-20230411154803434

@Autowired@Resource的区别

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

Spring 中默认提供的单例是线程安全的吗?

不是。

Spring容器本身并没有提供Bean的线程安全策略。如果单例的Bean是一个无状态的Bean,即线程中的操作不会对Bean的成员执行查询以外的操作,那么这个单例的Bean是线程安全的。比如,Controller、Service、DAO这样的组件,通常都是单例且线程安全的。如果单例的Bean是一个有状态的Bean,则可以采用ThreadLocal对状态数据做线程隔离,来保证线程安全。

Spring Aop

AOP(Aspect Oriented Programming)是面向切面编程,它是一种编程思想。是面向对象编程(OOP)的一种补充,面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面。所谓切面,相当于应用对象间的横切点。我们可以将其单独抽象为单独的模块。

AOP术语

  • 连接点:对应的是具体被拦截的对象,因为Spring只能支持方法,所以被拦截的对象往往就是指特定的方法。Aop将通过动态代理技术把它织入对应的流程中。【类里面哪些方法可以被增强,这些方法称为连接点】
  • 切点:有时候,我们的切面不单单应用于单个方法,也可能是多个类的不同方法,这时,可以通过正则式和指示器规则去定义,从而适配连接点。切点就是提供这样一个功能的概念。【实际被真正增强的方法被称为切入点】
  • 通知:就是按照约定的流程下的方法,分为前置通知,后置通知,环绕通知,事后返回通知和异常通知。【实际增强的逻辑部分称为通知。】
  • 切面:是一个可以定义切点、各类通知和引入的内容,SpringAOP将通过它的信息来增强Bean的功能或者将对应的方法织入流程。【把通知应用到切入点过程】

Spring的底层实现方式

  • 使用JDK动态代理实现:这是Java提供的动态代理技术,可以在运行时创建接口的代理实例。Spring AOP默认采用这种方式,在接口的代理实例中织入代码。
    • JDK的动态代理是通过反射类Proxy以及InvocationHandler回调接口实现的。只能对实现了接口的类生成代理,而不是针对类,该目标类型实现的接口都将被代理。可以看【Java设计模式 - 代理模式】
  • 使用CGlib实现:采用底层的字节码技术,在运行时创建子类代理的实例。当目标对象不存在接口时,Spring AOP就会采用这种方式,在子类实例中织入代码。
    • CGLIB是一个强大、高性能的字节码生成库。使用CGLib实现动态代理,完全不受代理类必须实现接口的限制,而且CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类

Spring AOP不能对哪些类进行增强

  • Spring AOP只能对IOC容器中的Bean进行增强,对于不受容器管理的对象不能增强。
  • 由于CGLib采用动态创建子类的方式生成代理对象,所以不能对final修饰的类进行代理。

Spring如何管理事务

Spring为事务管理提供了一致的编程模板,在高层次上建立了统一的事务抽象。也就是说,不管是选择MyBatis、Hibernate、JPA还是Spring JDBC,Spring都可以让用户以统一的编程模型进行事务管理。

Spring支持两种事务编程模型:

  • Spring MVC

什么是MVC

MVC是一种设计模式,在这种设计模式下软件被分为三层:即Model(模型),View(视图),Controller(控制器)。

  • Model代表的是数据
  • View代表的是用户界面
  • Controller代表的是数据的处理逻辑。是Model和View这两层的桥梁。

将软件分层的好处是,可以将对象之间的耦合度降低,便于代码的维护。

Dao层是做什么的

DAO是Data Access Object的缩写。即数据访问对象,在项目中通常作为独立的一层。专门用于访问数据库。

这一层的具体实现技术有很多,常用的有Spring JDBC、Hibernate、JPA、MyBatis等,在Spring框架下无论采用哪一种技术访问数据库,它的编程模式都是统一的。

SpringMVC的执行流程

image-20230413091342491

  1. 整个过程开始于客户端发出的一个Http请求,Web应用服务器接收到这个请求。如果匹配DispatcherServlet(前端控制器)的请求映射路径,则Web容器将这个请求转交给DispatcherServlet处理。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <!-- 配置springMVC的前端控制器,对浏览器发送的请求进行统一处理 -->
    <servlet>
    <servlet-name>springMVC</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    </servlet>
    <servlet-mapping>
    <servlet-name>springMVC</servlet-name>
    <!--
    设置springMVC的核心控制器所能处理的请求的请求路径
    /所匹配的请求可以是/login或.html或.js或.css方式的请求路径
    但是/不能匹配.jsp请求路径的请求
    -->
    <url-pattern>/</url-pattern>
    </servlet-mapping>
  2. 由DispatcherServlet请求一个或多个HandlerMapping(处理器映射器),并返回一个执行链(Handler Execution Chain);

  3. DispatcherServlet将HandlerMapping返回的执行链中的Handler信息发送给HandlerAdapter(处理器适配器);

  4. HandlerAdapter根据Handler信息找到并执行相应的Handler(常见为Controller)

  5. Handler执行完毕后会返回给HandlerAdapter一个ModelAndView对象,(SpringMVC的底层对象,包括Model数据模型和View视图模型)

  6. HandlerAdapter接收到ModelAndView对象后,将其返回给DispatchServlet;

  7. DispatcherServlet 接收到 ModelAndView 对象后,会请求 ViewResolver(视图解析器)对视图进行解析;

  8. ViewResolver 根据 View 信息匹配到相应的视图结果,并返回给 DispatcherServlet;

  9. DispatcherServlet 接收到具体的 View 视图后,进行视图渲染,将 Model 中的模型数据填充到 View 视图中的 request 域,生成最终的 View(视图);

  10. 视图负责将结果显示到浏览器(客户端)

快速记忆技巧

  • DispatchServlet(核心前端控制器)捕获请求。
  • 通过HandlerMapping(处理器映射器)查找Handler(处理器)
  • 执行Handler返回ModelAndView对象由HandlerAdapter(处理器适配器)交给前端控制器
  • 前端控制器将ModelAndView传给ViewReslover(视图解析器)解析后返回具体的View
  • 前端控制器对View进行渲染(将模型数据Model填充到视图中)返回给用户。

SpringMVC接口

pring MVC 涉及到的组件有 DispatcherServlet(前端控制器)、HandlerMapping(处理器映射器)、HandlerAdapter(处理器适配器)、Handler(处理器)、ViewResolver(视图解析器)和 View(视图)。下面对各个组件的功能说明如下。

  • DispatchServlet【前端控制器】:(不需要程序员开发)

DispatcherServlet 是前端控制器,从上图可以看出,Spring MVC 的所有请求都要经过 DispatcherServlet 来统一分发。

DispatcherServlet 相当于一个转发器或中央处理器,控制整个流程的执行,对各个组件进行统一调度,以降低组件之间的耦合性,有利于组件之间的拓展。

  • Handler Mapping【处理器映射器】:(不需要程序员开发)

HandlerMapping 是处理器映射器,其作用是根据请求的 URL 路径,通过注解或者 XML 配置,寻找匹配的处理器(Handler)信息。

  • HandlerAdapter【处理器适配器】:(不需要程序员开发)

HandlerAdapter 是处理器适配器,其作用是根据映射器找到的处理器(Handler)信息,按照特定规则执行相关的处理器(Handler)

  • Handler【处理器】(Controller,需要程序员开发)

Handler 是处理器,和 Java Servlet 扮演的角色一致。其作用是执行相关的请求处理逻辑,并返回相应的数据和视图信息,将其封装至 ModelAndView 对象中。

  • View Resolver【视图解析器】(不需要程序员开发)

View Resolver 是视图解析器,其作用是进行解析操作,通过 ModelAndView 对象中的 View 信息将逻辑视图名解析成真正的视图 View(如通过一个 JSP 路径返回一个真正的 JSP 页面)。

  • View(需要程序原开发jsp)

View是视图,其本身是一个接口,实现类支持不同的View类型(JSP,FreeMarket,Excel)

SpringMVC的注解

@RequestMapping

作用:用来处理请求地址映射的,也就是说将其中的处理器方法映射到url路径上。将请求 和 处理请求的控制器方法关联起来。

属性:

  • method:指定请求的method类型,比如:get和post
  • value必须设置,请求的实际地址。有多个地址时需要使用{}。
  • params:指定request中一定要有的参数值,它才会使用该方法处理请求。
1
2
3
4
5
6
7
8
@RequestMapping(
value = {"/testRequestMapping", "/test"},
method = {RequetMethod.POST, RequestMethod.GET},
param = {"username", "password!=123456"} // 必须有username,并且password不能是123456
)
public String success() {
return "success";
}

@RequestParam

作用:将请求参数绑定到你的控制器的方法参数上,是Spring MVC中的接收普通参数的注解。

属性:

  • value:请求参数中的名称
  • required:请求参数是否必须提供参数,默认是true,表示必须提供。
1
2
3
4
5
6
7
@RequestMapping("/testParams")
public String testParams(
@RequestParam("user_name") String username, // user_name 和 username相对应
@RequestParam("password") String password) {
System.out.println(username + " : " + password);
return "success";
}

@RequestBody

作用:如果作用在方法上,就表示该方法的返回结果是直接按写入的Http responsebody中(一般在异步获取数据时使用的注解)。也可以用来接收参数

属性:required,是否必须有请求体。它的默认值是true,在使用该注解时,值得注意的当为true时get的请求方式是报错的,如果你取值为false的话,get的请求是null。

1
2
3
4
5
6
@RequestMapping("/test")
public class test {
public void testMethod(@RequestBody String str) {
System.out.println("测试");
}
}

@RequestParam@RequestBody的区别

  • RequestParam接收的参数是来自requestHeader中,即请求头,通常用于get请求。请求路径中的参数

  • 当我们使用@RequestBody接收数据的时候,前端的请求方法要使用post方式来进行提交;

  • RequestBody注解接收的参数是来自requestBody中的,即请求体;一般用于处理非Content-Type:application/x-www-form-urlencoded编码格式的数据,比如application/json、application/xml等类型的数据;

@PathVariable

作用:该注解是用于绑定url中的占位符,但是注意,spring3.0以后,url才开始支持占位符的,它是Spring MVC支持的rest风格url的一个重要的标志。

1
2
3
4
5
@RequestMapping("/testPath/{id}/{name}")
public String testPath(@PathVariable("id") Integer id, @PathVariable("name") String name) {
System.out.println(id + " " + name);
return "success";
}

MyBatis

MyBatis输入输出支持的类型有哪些

MyBatis支持多种输入输出类型,包括:

  • 简单类型:如整数,小数,字符串等。
  • 集合类型:如List,Map等。
  • 自定义的JavaBean。

其中,简单类型,其数值直接映射到参数上,对于Map或JavaBean则将其属性按照名称映射到参数上。

MyBatis里如何实现一对多关联查询

一对多映射有两种配置方式,都是使用collection标签实现的,在此之前,为了能够存储一对多数据,需要在主表对应的实体类中增加集合属性,用于封装子表对应的实体类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 部门实体类
public class Dept { // 主表
private Integer did;
private String deptName;
private List<Emp> emps; // 子表
//...构造器、get、set方法等
}

// 员工实体类
//员工实体类
public class Emp {
private Integer empId;
private String empName;
private Integer age;
private String sex;
//一个员工只属于一个部门,多个员工可以同属于一个部门
private Dept dept;
}

嵌套查询(分步查询)

  1. 通过select标签定义查询主表的SQL,返回结果通过resultMap进行映射。
  2. 在resultMap中,除了映射主表属性,还要通过collection标签映射子表属性,该标签需要包含如下内容:
    • 通过property属性指定子表属性名;
    • 通过javaType属性指定封装子表属性的集合类型;
    • 通过ofType属性指定子表的实体类型;
    • 通过select属性指定查询子表所依赖的SQL,这个SQL需单独定义,内部包含查询子表的语句。

第一步查询主表

1
2
3
4
5
6
7
8
9
10
11
<resultMap id="DeptAndEmpByStepOneResultMap" type="Dept">
<id property="did" column="did"></id>
<result property="deptName" column="dept_name"></result>
<collection property="emps"
select="com.atguigu.mybatis.mapper.EmpMapper.getDeptAndEmpByStepTwo"
column="did"></collection>
</resultMap>
<!--Dept getDeptAndEmpByStepOne(@Param("did") Integer did);-->
<select id="getDeptAndEmpByStepOne" resultMap="DeptAndEmpByStepOneResultMap">
select * from t_dept where did = #{did}
</select>

第二步在主表的查询结果中查询子表

1
2
3
4
<!--com.atguigu.mybatis.mapper.EmpMapper.xml -->
<select id = "getDeptAndEmpByStepTwo" resultType="Emp">
select * from t_emp where did = #{did}
</select>

连接查询(更常用)

  1. 通过select标签定义关联查询主表和子表的SQL,返回结果通过resultMap标签进行映射
  2. 在resultMap标签中,除了映射主表属性,还要通过collection标签映射子表属性,该标签需要包含如下内容。
    • 通过property属性指定子表属性名
    • 通过ofType属性指定子表的实体类型。
    • 通过result子标签定义子表字段和属性的映射关系。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<resultMap id="DeptAndEmpResultMap" type="Dept">
<id property="did" column="did"></id>
<result property="deptName" column="dept_name"></result>
<collection property="emps" ofType="Emp"> <!-- 映射子表属性 -->
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
</collection>
</resultMap>
<!--Dept getDeptAndEmp(@Param("did") Integer did);-->
<select id="getDeptAndEmp" resultMap="DeptAndEmpResultMap">
select * from t_dept left join t_emp on t_dept.did = t_emp.did where t_dept.did = #{did}
</select>

MyBatis的xml文件和Mapper接口是怎么绑定的?

是通过xml文件中<mapper>标签的namespace属性进行绑定的,即namespace属性的值需要配置成接口的全限定名,MyBatis内部就会通过这个值将这个接口与这个xml关联起来。

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xzt.mybatis.mapper.UserMapper"> <!-- 全类名 -->
<!--int insertUser();-->
<insert id="insertUser"> <!-- 方法名 -->
insert into t_user values(null,'张三','123',23,'女')
</insert>
</mapper>

MyBatis获取参数的两种方式

MyBatis 获取参数值的两种方式:${}#{}(更推荐)

区别

  • ${} 的本质就是字符串拼接,#{} 的本质就是占位符赋值
  • ${} 使用字符串拼接的方式拼接 sql,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引号;但是 #{} 使用占位符赋值的方式拼接 sql,此时为字符串类型或日期类型的字段进行赋值时,可以自动添加单引号
  • 使用#{}设置参数时,MyBatis会创建预编译的SQL语句,然后在执行SQL时MyBatis会为预编译SQL中的占位符(?)赋值。预编译的SQL语句执行效率高,并且可以防止注入攻击。
  • 使用${}设置参数时,MyBatis只是创建普通的SQL语句,然后在执行SQL语句时MyBatis将参数直接拼入到SQL里。这种方式在效率、安全性上均不如前者,但是可以解决一些特殊情况下的问题。例如,在一些动态表格(根据不同的条件产生不同的动态列)中,我们要传递SQL的列名,根据某些列进行排序,或者传递列名给SQL都是比较常见的场景,这就无法使用预编译的方式了。
1
2
3
4
5
6
7
8
9
<!--User getUserByUsername(String username);-->
<select id="getUserByUsername" resultType="User">
select * from t_user where username = #{username}
</select>

<!--User getUserByUsername(String username);-->
<select id="getUserByUsername" resultType="User">
select * from t_user where username = '${username}'
</select>

MyBatis分页和自己写的分页哪个效率更高

自己写的分页效率高

MyBatis缓存机制

正在加载今日诗词....