1、JDBC基本概念
JDBC是独立于特定数据库管理系统,通用的SQL数据库存取和操作的公共接口(一组API),定义了用来访问数据库的标准Java类库,可以更方便标准的访问数据库资源。为开发者屏蔽细节。
JDBC提供用于数据库擦欧总的接口,开发者只需面向接口编程。
2、数据库持久化
将内存中的数据保存到硬盘中,持久化过程通过各种关系数据库完成。
(1)MyBatis是什么?
a、MyBatis 是持久层框架,一个半 ORM(对象关系映射)框架,它内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。程序员直接编写原生态sql,可以严格控制sql执行性能,灵活度高。
b、MyBatis 可以使用 XML 或注解来配置和映射原生信息,将 POJO映射成数据库中的记录,避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
c、通过xml 文件或注解的方式将要执行的各种 statement 配置起来,并通过java对象和 statement中sql的动态参数进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射为java对象并返回。(从执行sql到返回result的过程)
(2)ORM对象关系映射是什么?
ORM(Object Relational Mapping),对象关系映射,是一种为了解决关系型数据库数据与简单Java对象的映射关系的技术。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系型数据库中。
(3)为什么说Mybatis是半自动ORM映射工具?
Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。
Mybatis在查询关联对象或关联集合对象时,需要手动编写sql来完成,所以,称之为半自动ORM映射工具。
(4)传统JDBC开发存在的问题及Mybatis如何解决的
JDBC问题 | Mybatis解决 |
---|---|
频繁创建数据库连接对象、释放,造成系统资源浪费,影响系统性能。 | 在mybatis-config.xml中配置数据链接池,使用连接池管理数据库连接 |
Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码 | 将Sql语句配置在Xmapper.xml文件中与java代码分离 |
向sql语句传参数麻烦 | Mybatis自动将java对象映射至sql语句 |
对结果集解析麻烦,sql变化导致解析代码变化 | Mybatis自动将sql执行结果映射至java对象 |
(5)Mybatis优缺点
优点:
a、基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用;
b、与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接
c、很好的与各种数据库兼容(因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持)
d、提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护
e、能够与Spring很好的集成
缺点:
a、SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求;
b、SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库
(6)Mybatis使用场景
MyBatis专注于SQL本身,是一个足够灵活的DAO层解决方案。
对性能的要求很高,或者需求变化较多的项目,如互联网项目,MyBatis将是不错的选择
(7)Hibernate 和 MyBatis 的区别
框架 | MyBatis | Hibernate |
---|---|---|
相同点 | 对jdbc的封装,持久层的框架,用于dao层的开发 | 对jdbc的封装,持久层的框架,用于dao层的开发 |
映射关系 | 半自动映射的框架,配置Java对象与sql语句执行结果的对应关系,多表关联关系配置简单 | 全表映射、全自动映射的框架,配置Java对象与数据库表的对应关系,多表关联关系配置复杂 |
SQL优化和移植性 | 需要手动编写 SQL。直接使用SQL语句操作数据库,数据库移植性差,但sql语句优化容易 | 对SQL语句封装,数据库移植性好,SQL语句优化困难 |
开发难易程度和应用场景 | 轻量级框架,适合于需求变化频繁,大型的项目,比如:互联网电子商务系统 | 重量级框架,适合于需求相对稳定,中小型的项目,比如:办公自动化系统 |
1)读取 MyBatis 配置文件:mybatis-config.xml 为 MyBatis 的全局配置文件,配置了 MyBatis 的运行环境等信息,例如数据库连接信息。
2)加载映射文件:映射文件即 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句,需要在 MyBatis 配置文件 mybatis-config.xml 中加载。mybatis-config.xml 配置文件可以加载多个映射文件,每个文件对应数据库中的一张表
3)构造会话工厂:通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory。
4)创建会话对象:由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法。
5)Executor 执行器:MyBatis 底层定义了一个 Executor 接口来操作数据库,它将根据 SqlSession 传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护
6)MappedStatement 对象:在 Executor 接口的执行方法中有一个 MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的 SQL 语句的 id、参数等信息。
7)输入参数映射:输入参数类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型。输入参数映射过程类似于 JDBC 对 preparedStatement 对象设置参数的过程
8)输出结果映射:输出结果类型可以是 Map、 List 等集合类型,也可以是基本数据类型和 POJO 类型。输出结果映射过程类似于 JDBC 对结果集的解析过程
1)API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。
2)数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。主要的目的是根据调用的请求完成一次数据库操作。
3)基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑
MyBatis的初始化,会从mybatis-config.xml配置文件,解析构造成Configuration类。
(1)加载配置:配置来源于Xml配置文件和注解,将SQL的配置信息加载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。
(2)SQL解析:当API接口层接收到调用请求时,会接收到传入SQL的ID和传入对象(可以是Map、JavaBean或者基本数据类型),Mybatis会根据SQL的ID找到对应的MappedStatement,然后根据传入参数对象对MappedStatement进行解析,解析后可以得到最终要执行的SQL语句和参数。
(3)SQL执行:将最终得到的SQL和参数拿到数据库进行执行,得到操作数据库的结果。
(4)结果映射:将操作数据库的结果按照映射的配置进行转换,可以转换成HashMap、JavaBean或者基本数据类型,并将最终结果返回。
SQL 预编译:数据库驱动在发送 SQL 语句和参数给 DBMS 之前对 SQL 语句进行编译,这样 DBMS 执行 SQL 时,就不需要重新编译。
预编译阶段可以优化 SQL 的执行,防止sql注入。预编译之后的 SQL 多数情况下可以直接执行,DBMS 不需要再次编译,越复杂的SQL,编译的复杂度将越大,预编译阶段可以合并多次操作为一个操作。同时预编译语句对象可以重复利用。把一个 SQL 预编译后产生的 PreparedStatement 对象缓存下来,下次对于同一个SQL,可以直接使用这个缓存的 PreparedState 对象。Mybatis默认情况下,将对所有的 SQL 进行预编译。
Executor执行器 | SimpleExecutor | ReuseExecutor | BatchExecutor |
---|---|---|---|
区别 | 每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象 | 执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map |
执行update,将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),缓存多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理 |
在Mybatis配置文件中,在设置(settings)可以指定默认的ExecutorType执行器类型,也可以手动给DefaultSqlSessionFactory的创建SqlSession的方法传递ExecutorType类型参数,如SqlSession openSession(ExecutorType execType)
#{} | ${} |
---|---|
占位符,预编译处理 | 拼接符,没有预编译处理 |
传入参数以字符串传入,将SQL中的 #{}替换为?号,调用PreparedStatement的set方法来赋值 | 传入参数以原值传入,把 ${}替换成变量的值 |
#{} 对应的变量自动加上单引号 ‘’ | ${} 对应的变量不会加上单引号 ‘' |
防止SQL注入 | 不能防止SQL 注入 |
变量替换是在DBMS 中 | 变量替换是在DBMS 外 |
1)’%${question}%’ 可能引起SQL注入,不推荐
2)"%"#{question}"%" 注意:因为#{…}解析成sql语句时候,会自动加单引号’ ',所以这里 % 需要使用双引号" ",不然会查不到任何结果。
3)CONCAT(’%’,#{question},’%’) 使用CONCAT()函数
4)使用bind标签
<select id="listUserLikeUsername" resultType="com.jourwon.pojo.User">
<bind name="pattern" value="'%' + username + '%'" />
select id,sex,age,username,password from person where username LIKE #{pattern}
</select>
1、基本概念
Spring是一个轻量级Java开发框架,解决企业级应用开发的业务逻辑层和其他各层的耦合问题。它是一个分层的JavaSE/JavaEE full-stack(一站式)轻量级开源框架,为开发Java应用程序提供全面的基础架构支持。容器框架。
两个核心特性:依赖注入(IOC)和面向切面编程(AOP)
特点:
1)方便解耦,简化开发:Spring就是一个大工厂,可以将所有对象的创建和依赖关系的维护,交给Spring管理;
2)AOP编程的支持:Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能;
3)声明式事务的支持:只需要通过配置就可以完成对事务的管理,而无需手动编程;
4)方便程序的测试;
5)方便集成各种优秀框架;
6)降低JavaEE API的使用难度。
2、Spring模块划分图
主要由以下几个模块组成:
Spring Core:核心类库,提供IOC服务;
Spring Context:提供框架式的Bean访问方式,以及企业级功能(JNDI、定时任务等)
Spring AOP:AOP服务
Spring DAO:对JDBC的抽象,简化了数据访问异常的处理
Spring ORM:对现有的ORM框架的支持
Spring Web:提供了基本的面向Web的综合特性,例如多方文件上传
Spring MVC:提供面向Web应用的Model-View-Controller实现
控制反转IOC是一种设计思想,就是将原本在程序中手动创建对象的控制权,交由给Spring框架来管理,并由容器根据配置文件去创建实例和管理各个实例之间的依赖关系。IOC容器是Spring用来实现IOC的载体,IOC容器实际上就是一个Map(key, value),Map中存放的是各种对象。
将对象之间的相互依赖关系交给IOC容器来管理,并由IOC容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。
IOC容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的,大大增加项目的可维护性且降低开发难度。
DI依赖注入:和控制反转是同一个概念的不同角度的描述,即 应用程序在运行时依赖IoC容器来动态注入对象需要的外部资源
IoC的注入方式: 构造器注入、setter方法注入、根据注解注入
1)控制反转(IoC)有什么作用
IOC思想基于IOC容器,IOC容器底层就是对象
a) 管理对象的创建和依赖关系的维护;
b) 解耦,由容器去维护具体的对象;
c) 托管了类的产生过程.
2) 控制反转 IoC 的实现机制:工厂模式、反射
3)IOC容器实现的两种方式:BeanFactory 和 ApplicationContext有什么区别?
BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。其中ApplicationContext是BeanFactory的子接口,提供更强大的功能。
区别 | BeanFactory | ApplicationContext |
---|---|---|
依赖关系 | Spring里面最底层的接口,包含了各种Bean的定义 | BeanFactory的子接口,提供了更完整的框架功能 |
加载方式 | 延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。不能立即发现配置问题 | 在容器启动时,一次性创建了所有的Bean对象。可以立刻发现配置错误,有利于检查所依赖属性是否注入。 |
创建方式 | 编程的方式被创建 | 以声明的方式创建,如使用ContextLoade |
注册方式 | 手动注册 | 自动注册 |
实质 | 低级容器,可认为是HashMap,Key 是 BeanName,Value 是 Bean 实例 | 高级容器,继承了多个接口,比 BeanFactory 有更多的功能 |
IOC实现 | 1、加载配置文件,解析成 BeanDefinition 放在 Map 里;2、完成依赖注入:调用 getBean 的时候,从 BeanDefinition 所属的 Map 里,拿出Class对象进行实例化,如果有依赖关系,将递归调用getBean 方法 | 不仅是 IoC,还支持不同信息源头、BeanFactory 工具类、层级容器、访问文件资源、事件发布通知、接口回调 |
4)ApplicationContext实现类
1、FileSystemXmlApplicationContext:绝对路径,全路径名
2、ClassPathXmlApplicationContext:相对路径
5)IOC 的Bean管理及操作方式
两类Bean管理:
a、Spring创建对象;
b、Spring注入属性
操作方式:
a、基于Xml配置文件实现;
b、基于注解实现。
控制反转IoC可以用不同的方式来实现,主要有两种:依赖注入和依赖查找。
依赖注入:相对于IoC而言,依赖注入(DI)更加准确地描述了IoC的设计理念。所谓依赖注入,即组件之间的依赖关系由容器在应用系统运行期来决定,也就是由容器动态地将某种依赖关系的目标对象实例注入到应用系统中的各个关联的组件之中。组件不做定位查询,只提供普通的Java方法让容器去决定依赖关系。
依赖注入的基本原则:应用组件不应该负责查找资源或者其他依赖的协作对象。配置对象的工作应该由IoC容器负责,“查找资源”的逻辑应该从应用组件的代码中抽取出来,交给IoC容器负责。容器全权负责组件的装配,它会把符合依赖关系的对象通过属性(JavaBean中的setter)或者是构造器传递给需要的对象。
依赖注入优势:
a、查找定位操作与应用代码完全无关。
b、不依赖于容器的API,可以很容易地在任何容器以外使用应用对象。
c、不需要特殊的接口,绝大多数对象可以做到完全不必依赖容器
1)使用set方法基于Xml注入属性
2)使用有参构造方法基于Xml注入属性
先创建类,并创建有参构造方法。在配置文件中通过constructor-arg标签实现参数配置。
构造器依赖注入和 Setter方法注入的区别:
最好的解决方案是用构造器参数实现强制依赖,setter方法实现可选依赖。
1)开启注解装配
注解装配在默认情况下是不开启的,为了使用注解装配,我们必须在Spring配置文件中配置
元素
2)基于注解实现创建对象
注解 | @Component | @Controller | @Repository | @Service |
---|---|---|---|---|
通用注解,可标注任意类为Spring组件 | 对应Spring MVC的控制层,即Controller层,主要用于接受用户请求并调用Service层的方法返回数据给前端页面 | 对应持久层,即Dao层,主要用于数据库相关操作 | 对应服务层,即Service层,主要涉及一些复杂的逻辑,需要用到Dao层(注入) |
3)@Component和@Bean的区别是什么
注解 | @Component | @Bean |
---|---|---|
作用对象不同 | 类 | 方法 |
作用 | 通过类路径扫描来自动侦测以及自动装配到Spring容器中 | 在标有该注解的方法中定义产生这个bean,告诉Spring这是某个类的实例,当我需要用它的时候还给我 |
应用范围 | 自定义性弱 | 自定义性更强,很多地方只能通过@Bean注解来注册bean |
4)基于注解实现属性注入
注解 | @Autowired | @Resource | @Qualifier |
---|---|---|---|
作用 | 默认是按照类型装配注入的,默认情况下要求依赖对象必须存在(可以设置required属性为false)。提供了更细粒度的控制,包括在何处以及如何完成自动装配 | 按照名称装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入。 | 类型、名称注入均可 |
适用范围 | 构造函数、成员变量、Setter方法 | 创建多个相同类型的 bean ,使用 @Qualifier 注解和 @Autowired一起使用 来指定应该装配哪个bean消除歧义 |
5)@Required 注解有什么作用
这个注解表明bean的属性必须在配置的时候设置,通过一个bean定义的显式的属性值或通过自动装配,若@Required注解的bean属性未被设置,容器将抛出BeanInitializationException
beans 是那些形成Spring应用的主干的java对象。它们被Spring IOC容器初始化,装配,和管理。这些beans通过容器中配置的元数据创建。
1) Spring Bean 定义 包含什么?
一个Spring Bean 的定义包含容器必知的所有配置元数据,包括如何创建一个bean,它的生命周期详情及它的依赖
2)Bean的类型?
普通Bean:在配置文件中定义Bean的类型就是返回类型
工厂Bean:在配置文件中定义Bean的类型可以和返回类型不一样,实现接口FactoryBean。
3)Spring基于xml注入bean的几种方式?
Set方法注入;
构造器注入:①通过index设置参数的位置;②通过type设置参数类型;
静态工厂注入;
实例工厂
在Spring里,通过bean 定义中的scope属性来给bean定义声明一个作用域。
区别 | 单实例 | 多实例 | ||
---|---|---|---|---|
属性值 | singleton | prototype | request | session |
作用域 | 唯一Bean实例,默认 | 每次生产一个新的bean实例 | 仅在当前HTTP request内有效 | 仅在当前HTTP session内有效 |
创建时间 | 加载配置文件时,就创建单实例对象 | 在调用getBean方法时才创建多实例对象 | 每一次HTTP请求都会产生一个新的bean | 每一次HTTP请求都会产生一个新的bean |
bean 默认作用域是Singleton。使用 prototype 作用域需要慎重的思考,因为频繁创建和销毁 bean 会带来很大的性能开销。
单例bean存在线程问题,主要是因为当多个线程操作同一个对象的时候,对这个对象的非静态成员变量的写操作会存在线程安全问题。spring 框架并没有对单例 bean 进行多线程的封装处理。
无状态的bean是线程安全的,有状态bean线程不安全。
有两种常见的解决方案:
1.在bean对象中尽量避免定义可变的成员变量(不太现实)。
2.在类中定义一个ThreadLocal成员变量保存可变成员变量(推荐的一种方式)
3、改变 bean 的作用域,把“singleton”变更为“prototype”,这样请求 bean 相当于 new Bean(),可以保证线程安全。
只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域,因为Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理,解决线程安全问题。
ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了“时间换空间”的方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要排队。而ThreadLocal采用了“空间换时间”的方式。
ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。
(1)实例化Bean:
对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean.
(2)设置对象属性(依赖注入):
实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息 以及 通过BeanWrapper提供的设置属性的接口完成依赖注入。
(3)处理Aware接口:
Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给Bean
a、如果Bean实现了BeanNameAware接口,调用setBeanName()方法,传入Bean的名字;
b、 如果Bean实现了BeanClassLoaderAware接口,调用setBeanClassLoader()方法,传入ClassLoader对象的实例;
c、如果Bean实现了BeanFactoryAware接口,调用setBeanFacotory()方法,传入ClassLoader对象的实例;
d、如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文
(4)BeanPostProcessor:
如果想对Bean进行一些自定义的处理,那么可以让Bean实现BeanPostProcessor接口,那将会调用postProcessBeforeInitialization(Object obj, String s)方法.
(5)InitializingBean 与 init-method
如果Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法。
(6)BeanPostProcessor
如果Bean实现BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法。
此时Bean已经被正确创建并且可以使用。
(7)DisposableBean:
当Bean不再需要时,会经过清理阶段,如果Bean实现DisposableBean这个接口,会调用其实现的destroy() 方法
(8)destroy-method:
如果Bean在配置文件中的定义包含destroy-method属性,会自动调用其配置的销毁方法。
装配:或bean 装配是指在Spring 容器中把bean组装到一起,前提是容器需要知道bean的依赖关系,如何通过依赖注入来把它们装配到一起。
自动装配:根据指定的装配规则(属性名称或属性类型),Spring自动将匹配的属性值进行注入,容器不需要手动配置,能通过Bean工厂自动处理bean之间的协作。Spring可以通过向Bean Factory中注入的方式自动搞定bean之间的依赖关系。
在spring中,对象无需自己查找或创建与其关联的其他对象,由容器负责把需要相互协作的对象引用赋予各个对象,使用标签属性autowire配置自动装载模式。
属性值:
属性值 | 含义 |
---|---|
byName | 通过bean的名称进行自动装配,注入Bean的id和类属性名称一致,就进行自动装配 |
byType | 通过参数的数据类型进行自动装配。此时相同类型的Bean不能定义多个,否则会出错 |
constructor | 利用构造函数进行装配,并且构造函数的参数通过byType进行装配 |
autodetect | 自动探测,如果有构造方法,通过 construct的方式自动装配,否则使用 byType的方式自动装配 |
使用@Autowired注解自动装配的过程是怎样的?
在使用@Autowired注解之前需要在Spring配置文件进行配置,
在启动spring IoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean。
如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据;
如果查询的结果不止一个,那么@Autowired会根据名称来查找;
如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。
自动装配有哪些局限性?
1)重写:仍需用Xml和注解配置来定义依赖,意味着总要重写自动装配。
2)基本数据类型:不能自动装配简单的属性,如基本数据类型,String字符串
3)模糊特性:自动装配不如显式装配精确,如果有可能,建议使用显式装配
AOP减少系统中的重复代码,降低了模块间的耦合度**,同时提高了系统的可维护性和可重用性,提高了开发效率,可用于权限认证、日志、事务处理。
通俗理解:不改变源代码,在主干功能里添加新功能,实现功能增强。
1)Spring AOP and AspectJ AOP 有什么区别?AOP 有哪些实现方式?
AOP实现的关键在于 代理模式,AOP代理主要分为静态代理和动态代理。
AspectJ 是独立于Spring的框架。Spring基于Aspect J实现AOP,实现方式为Xml配置文件和注解。
代理 | 静态代理 | 动态代理 |
---|---|---|
代表 | AspectJ | Spring AOP,JDK动态代理和CGlib动态代理 |
基础 | 基于字节码 | 基于代理 |
生成时期 | 在编译阶段生成AOP代理类,也称为编译时增强。在编译阶段将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象 | 运行时增强。不修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,包含了目标对象的全部方法,并且在特定的切点做增强处理,并回调原对象的方法 |
静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。
动态代理 | JDK动态代理 | CGLIB动态代理 |
---|---|---|
接口 | 只提供接口的代理,不支持类的代理 | 没有接口情况 |
方式 | 创建接口实现类的代理对象 | 创建子类的代理对象 |
核心 | InvocationHandler接口和Proxy类 | 通过继承的方式做的动态代理 |
3)JDK动态代理
使用Proxy类中的方法newProxyInstance方法:
InvocationHandler 的 invoke(Object proxy,Method method,Object[],args):proxy是最终生成的代理实例; method 是被代理目标实例的某个具体方法; args是被代理目标实例某个方法的具体入参, 在方法反射调用时使用。
通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy利用 InvocationHandler接口动态创建一个符合某一接口的的实例, 生成目标类的代理对象。
4)CGLIB动态代理
如果代理类没有实现 InvocationHandler 接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
5)Spring AOP里面的术语
a、连接点(Join point):可以被增强的方法。 连接点是在应用执行过程中能够插入切面的一个点。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
b、切入点(Pointcut):实际被增强的方法。切点的定义会匹配通知所要织入的一个或多个连接点。通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。
切入点表达式:对某个类中的某个方法进行增强。
语法结构:execution([权限修饰符][返回类型][类全路径][方法名称][参数列表])
相同切入点提取:
c、通知(增强Advice):实际增强的逻辑部分。切面的工作。可分为前置通知@Before、后置通知@AfterReturning、环绕通知@Around、异常通知@AfterThrowing、最终通知@After(finally肯定会执行)。
d、切面(Aspect):将通知应用到切入点的过程。通知和切点的结合。通知和切点共同定义了切面的全部内容。 在Spring AOP中,切面可以使用通用类(基于模式的风格) 或者在普通类中以 @AspectJ 注解来实现。
什么是切面 Aspect?
既包含了横切逻辑的定义, 也包括了连接点的定义。Spring AOP 就是负责实施切面的框架, 它将切面所定义的横切逻辑编织到切面所指定的连接点中.
AOP 的工作重心在于如何将增强编织目标对象的连接点上, 这里包含两个工作:
如何通过 pointcut 和 advice 定位到特定的 joinpoint 上;
如何在 advice 中编写切面代码。
e、引入(Introduction):引入允许向现有类添加新方法或属性。
f、目标对象(Target Object): 被一个或者多个切面(aspect)所通知(advise)的对象。它通常是一个代理对象。也有人把它叫做被通知(adviced)对象。 Spring AOP是动态代理,则这个对象永远是一个被代理(proxied)对象
g、织入(Weaving):织入是把切面应用到目标对象并创建新的代理对象的过程。在目标对象的生命周期里有多少个点可以进行织入:
编译期:切面在目标类编译时被织入。AspectJ的织入编译器是以这种方式织入切面的。
类加载期:切面在目标类加载到JVM时被织入。需要特殊的类加载器,它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5的加载时织入就支持以这种方式织入切面。
运行期:切面在应用运行的某个时刻被织入。在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。SpringAOP就是以这种方式织入切面。
多个增强类对同一个增强方法进行增强,设置增强优先级,通过添加@Order(int),值越小优先级越高。
事务是数据库操作的基本单元,逻辑上的一组操作,要么都成功,如果有一个操作失败,则所有操作都失败。
事务特性(ACID):原子性、一致性、隔离性、操作性。
在Spring中进行声明式事务管理,底层使用AOP原理。
1)事务管理的方式
事务管理方式 | 编程式事务管理 | 声明式事务管理 |
---|---|---|
方式 | 在代码中硬编码 | 本质是通过AOP,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,即在目标方法开始之前加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务 |
优点 | 不推荐 | 可以将业务代码和事务管理分离,分为基于XML的声明式事务和基于注解的声明式事务 |
2)Spring事务的实现方式和实现原理
Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。真正的数据库层的事务提交和回滚是通过binlog或者redo log实现的。
spring 有五大隔离级别,默认值为 ISOLATION_DEFAULT(使用数据库的设置),其他四个隔离级别和数据库的隔离级别一致:
隔离级别 | 含义 |
---|---|
ISOLATION_DEFAULT | 用底层数据库的设置隔离级别 |
ISOLATION_READ_UNCOMMITTED | 未提交读,最低隔离级别,事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读) |
ISOLATION_READ_COMMITTED | 读已提交,一个事务提交后才能被其他事务读取到(会造成幻读、不可重复读) |
ISOLATION_REPEATABLE_READ | 可重复读,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据(会造成幻读),MySQL 的默认级别 |
ISOLATION_SERIALIZABLE | 序列化,最高最可靠的隔离级别,该隔离级别能防止脏读、不可重复读、幻读 |
PS:
脏读 :表示一个事务能够读取另一个事务中还未提交的数据。
不可重复读 :是指在一个事务内,多次读同一数据。
幻读 :指同一个事务内多次查询返回的结果集不一样。
设计模式 | 应用场景 |
---|---|
工厂模式 | 通过BeanFactory和ApplicationContext创建bean对象 |
代理模式 | AOP功能的实现,基于动态代理,JDK动态代理和CGLIB动态代理 |
单例模式 | bean默认都是单例的 |
模板模式 | jdbcTemplate、hibernateTemplate等以Template结尾的对数据库操作的类 |
包装器模式 | 项目需要连接多个数据库,不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源 |
观察者模式 | 事件驱动模型 |
适配器模式 | AOP的增强或通知(Advice)使用到了适配器模式、Spring MVC中也是用到了适配器模式适配Controller |
spring事务的传播行为:当多个事务同时存在的时候,spring如何处理这些事务的行为
在TransactionDefinition接口中定义了八个表示事务传播行为的常量。
支持当前事务的情况:(当前存在事务,则加入该事务)
常量 | 含义 |
---|---|
PROPAGATION_REQUIRED | 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务 |
PROPAGATION_SUPPORTS | 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行 |
PROPAGATION_MANDATORY | 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性) |
不支持当前事务的情况:(当前存在事务,则将事务挂起)
常量 | 含义 |
---|---|
PROPAGATION_REQUIRES_NEW | 创建一个新的事务,如果当前存在事务,则把当前事务挂起 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式运行,如果当前存在事务,则把当前事务挂起 |
PROPAGATION_NEVER | 以非事务方式运行,如果当前存在事务,则抛出异常 |
其他情况:
TransactionDefinition.PROPAGATION_NESTED: 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED
事件 | 触发 |
---|---|
上下文更新事件(ContextRefreshedEvent) | 调用ConfigurableApplicationContext 接口中的refresh()方法时被触发 |
上下文开始事件(ContextStartedEvent) | 当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件 |
上下文停止事件(ContextStoppedEvent) | 当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件 |
上下文关闭事件(ContextClosedEvent) | 当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁 |
请求处理事件(RequestHandledEvent) | Web应用中,当一个http请求(request)结束触发该事件。如果一个bean实现了ApplicationListener接口,当一个ApplicationEvent 被发布以后,bean会自动被通知 |
Spring 通过提供ORM模块,支持我们在直接JDBC之上使用一个对象/关系映射映射(ORM)工具,Spring 支持集成主流的ORM框架,如Hiberate,JDO和 iBATIS,JPA,TopLink,JDO,OJB 。Spring的事务管理同样支持以上所有ORM框架及JDBC。
在Spring框架中更有效地使用JDBC----JdbcTemplate模块
1、Spring DAO的作用?
Spring DAO(数据访问对象) 使得 JDBC,Hibernate 或 JDO 这样的数据访问技术更容易以一种统一的方式工作。这使得用户容易在持久性技术之间切换。还允许在编写代码时,无需考虑捕获每种技术不同的异常。
2、spring JDBC API 中存在哪些类?
JdbcTemplate、SimpleJdbcTemplate、NamedParameterJdbcTemplate、SimpleJdbcInsert、SimpleJdbcCall
3、Spring通过什么方式访问Hibernate?
使用 Hibernate 模板和回调进行控制反转;
扩展 HibernateDAOSupport 并应用 AOP 拦截器节点。
4、如何通过HibernateDaoSupport将Spring和Hibernate结合起来?
用Spring的 SessionFactory 调用 LocalSessionFactory。集成过程分三步:
1.配置the Hibernate SessionFactory
2.继承HibernateDaoSupport实现一个DAO
3.在AOP支持的事务中装配