一、Mybatis
1. 什么是Mybatis?
Mybatis 是一个基于java的持久层框架,它内部封装了jdbc,使开发者只需要关注sql语句本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。
Mybatis是一个半ORM(对象关系映射)框架, 采用ORM思想解决了实体对象和数据库映射的问题,对jdbc进行了封装,屏蔽了jdbc api底层访问细节,使我们不用与jdbc api打交道,就可以完成对数据库的持久化操作
Mybatis通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射为java对象并返回。(从执行sql到返回result的过程)
JDBC:
JDBC本质:其实是官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。
JDBC是接口,而JDBC驱动才是接口的实现,没有驱动无法完成数据库连接!每个数据库厂商都有自己的驱动,用来连接自己公司的数据库。
ORM简介:
对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。
2. Mybatis 的优缺点
Mybaits的优点:
(1)基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用。
(2)与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接;
(3)很好的与各种数据库兼容(因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持)。
(4)能够与Spring很好的集成;
(5)提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护。
MyBatis框架的缺点:
(1)SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求。
(2)SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
MyBatis框架适用场合:
(1)MyBatis专注于SQL本身,是一个足够灵活的DAO层解决方案。
(2)对性能的要求很高,或者需求变化较多的项目,如互联网项目,MyBatis将是不错的选择。
3. MyBatis与Hibernate有哪些不同?
Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。
1). 两者最大的区别:
- 针对简单逻辑,Hibernate和MyBatis都有相应的代码生成工具,可以生成简单基本的DAO层方法。
- 针对高级查询,Hibernate是一个完全的ORM框架,而Mybatis是半自动ORM映射工具。 Mybatis需要手动编写SQL语句,以及ResultMap。而Hibernate有良好的映射机制,开发者无需关心SQL的生成与结果映射,可以更专注于业务流程。
2). 开发难度对比
- Hibernate的开发难度要大于Mybatis。主要由于Hibernate比较复杂、庞大,学习周期较长。
- 而Mybatis则相对简单一些,并且Mybatis主要依赖于sql的书写,让开发者感觉更熟悉。
3).数据库扩展性比较
- Mybatis由于所有SQL都是依赖数据库书写的,不同的数据库需要写不同SQ,所以扩展性,迁移性比较差。
- Hibernate与数据库具体的关联都在XML中,所以它对具体是用什么数据库并不是很关心。
适用场景:
Mybatis直接编写原生态sql,可以严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,因为这类软件需求变化频繁,一但需求变化要求迅速输出成果。但是灵活的前提是mybatis无法做到数据库无关性,如果需要实现支持多种数据库的软件,则需要自定义多套sql映射文件,工作量大。
Hibernate对象/关系映射能力强,数据库无关性好,适用于对于关系模型要求高的软件,如果用hibernate开发可以节省很多代码,提高效率。
4. 为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里?
Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。而Mybatis在查询关联对象或关联集合对象时,需要手动编写sql来完成,所以,称之为半自动ORM映射工具。
5. #{}和${}的区别是什么?
#{}是预编译处理,${}是字符串替换。
Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
Mybatis在处理${}时,就是把${}替换成变量的值。
使用#{}可以有效的防止SQL注入,提高系统安全性。
6. 当实体类中的属性名和表中的字段名不一样 ,怎么办 ?
第1种: 通过在查询的sql语句中定义字段名的别名,让字段名的别名和实体类的属性名一致。
<select id=”selectorder” parametertype=”int” resultetype=”me.gacl.domain.order”>
select order_id id, order_no orderno ,order_price price form orders where order_id=#{id};
</select>
###第2种: 通过<resultMap>来映射字段名和实体类属性名的一一对应的关系。
<select id="getOrder" parameterType="int" resultMap="orderresultmap">
select * from orders where order_id=#{id}
</select>
<resultMap type=”me.gacl.domain.order” id=”orderresultmap”>
<!–用id属性来映射主键字段–>
<id property=”id” column=”order_id”>
<!–用result属性来映射非主键字段,property为实体类属性名,column为数据表中的属性–>
<result property = “orderno” column =”order_no”/>
<result property=”price” column=”order_price” />
</reslutMap>
7. 模糊查询like语句该怎么写?
第1种:在Java代码中添加sql通配符。
string wildcardname = “%smi%”;
list<name> names = mapper.selectlike(wildcardname);
<select id=”selectlike”>
select * from foo where bar like #{value}
</select>
第2种:在sql语句中拼接通配符,会引起sql注入
string wildcardname = “smi”;
list<name> names = mapper.selectlike(wildcardname);
<select id=”selectlike”>
select * from foo where bar like "%"${value}"%"
</select>
Mybatis的核心组件有:SqlSessionFactory、SqlSession、Mapper、TypeHandler。
8. MyBatis常用对象SqlSessionFactory和SqlSession介绍和运用
SqlSessionFactory是通过SqlSessionFactoryBuilder的build方法创建的
而build方法创建的是一个SqlSessionFactory的实现类,叫DefaultSqlSessionFactory
然后这个实现类主要用的设计模式是建造者(build)模式,而里面最终要达到的一个目的是为了创建出DefaultSqlSession,这个是SqlSession的实现类。
前面说了SqlSession也是同一个接口,那么SqlSession=SqlSessionFactory.openSession()就相当于SqlSession=DefaultSqlSession这个实现类。
这个实现类可以进行增删查改以及事务操作等, DefaultSqlSession是通过调用Executor执行器进行这些操作的。
Executor执行器其实也是一个接口,而在解析配置文件的时候已经解析出来SQL,那么根据一路传过来的SQL以及参数等信息, Executor再调度StatementHandler等对象对数据库进行增删查改操作。
从 XML 中构建 SqlSessionFactory
//1. 读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2. 获取SqlFactorySession
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
XML 配置文件中包含了对 MyBatis 系统的核心设置,包括获取数据库连接实例的数据(DataSource)以及决定事务作用域和控制方式的事务管理器(TransactionManager)。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- mybatis的主配置文件 -->
<configuration>
<!-- 配置环境 -->
<environments default="mysql">
<!-- 配置mysql的环境-->
<environment id="mysql">
<!-- 配置事务的类型-->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源(连接池) -->
<dataSource type="POOLED">
<!-- 配置连接数据库的4个基本信息 -->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/db_mybatis?useUnicode=true&characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
</dataSource>
</environment>
</environments>
<!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 -->
<mappers>
<mapper resource="bjtu/dao/UserDao.xml"/>
</mappers>
</configuration>
XML 头部的声明,它用来验证 XML 文档的正确性。environment 元素体中包含了事务管理和连接池的配置。mappers 元素则包含了一组映射器(mapper),这些映射器的 XML 映射文件包含了 SQL 代码和映射定义信息。
从 SqlSessionFactory 中获取 SqlSession
从SqlSessionFactory获得 SqlSession 的实例。SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。
//1. 读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2. 获取SqlFactorySession
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3. 获取SqlSession对象
sqlSession = factory.openSession();
//4. 获取dao的代理对象
userDao = sqlSession.getMapper(UserDao.class);
9. 映射配置文件
在mapper映射文件中,主要包含如下配置元素:
- mapper元素: 该元素是最顶层的配置元素,其属性namespace指向IDao类的全限定类名,即:包路径+类名。 在mapper元素下面,包含如下子元素:resultMap元素,select元素,insert元素,update元素,delete元素。
- resultMap元素: 建立数据库表的字段名与pojo类的数据字段之间的映射关系。 当pojo类的数据字段与数据库表不一致的时候,或者承载复杂查询结果的时候,使用resultMap配置; ResultMap是Mybatis中最重要最强大的元素,使用ResultMap可以解决两大问题:
- POJO属性名和表结构字段名不一致的问题(有些情况下也不是标准的驼峰格式)
- 完成高级查询,比如说,一对一、一对多、多对多。
- select元素: 用来映射select语句。
- insert元素:用来映射insert语句。
- update元素: 用来映射update语句。
delete元素: 用来映射delete语句。
<?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="bjtu.dao.UserDao"> <!--配置查询结果的列名与实体类的属性名的对应关系--> <resultMap id="userMap" type="bjtu.domain.user"> <!-- 主键字段的对应--> <id property="userId" column="id"></id> <!-- 非主键字段的对应--> <result property="userName" column="username"></result> <result property="userBirthday" column="birthday"></result> <result property="userSex" column="sex"></result> <result property="userAddress" column="address"></result> </resultMap> <!--配置查询所有--> <select id="findAll" resultMap="userMap"> select * from user </select> <!--插入保存用户--> <insert id="saveUser" parameterType="bjtu.domain.user"> <!--配置插入数据后,获取新增数据的id--> <selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER"> select last_insert_id() </selectKey> insert into user(username,address,sex,birthday) values(#{username},#{address},#{sex},#{birthday}) </insert> <!--更新用户信息--> <update id="updateUser" parameterType="bjtu.domain.user"> update user set username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id=#{id} </update> <!--删除用户--> <delete id="deleteUser" parameterType="int"> delete from user where id=#{uid} </delete> <!--按ID查询一个用户的信息--> <select id="findById" parameterType="int" resultMap="userMap"> select * from user where id=#{uid} </select> <!--按名称模糊查询用户信息--> <select id="findByName" parameterType="string" resultMap="userMap"> select * from user where username like #{name} </select> <!--利用聚合函数查询总记录条数--> <select id="findTotal" resultType="int"> select count(id) from user </select> </mapper>
resultMap内的元素:
- constructor - 用于在实例化类时,注入结果到构造方法中
- idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
- arg - 将被注入到构造方法的一个普通结果
- id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
- result – 注入到字段或 JavaBean 属性的普通结果
- association – 一个复杂类型的关联;许多结果将包装成这种类型
嵌套结果映射 – 关联可以指定为一个 resultMap 元素,或者引用一个 - collection – 一个复杂类型的集合嵌套结果映射 – 集合可以指定为一个 resultMap 元素,或者引用一个
- discriminator – 使用结果值来决定使用哪个 resultMap.
10. Mybatis的缓存
Mybatis中的延迟加载
问题:在一对多中,当我们有一个用户,它有100个账户。
在查询用户的时候,要不要把关联的账户查出来?
在查询账户的时候,要不要把关联的用户查出来?
在查询用户时,用户下的账户信息应该是,什么时候使用,什么时候查询的。
在查询账户时,账户的所属用户信息应该是随着账户查询时一起查询出来。
什么是延迟加载?
在真正使用数据时才发起查询,不用的时候不查询。按需加载(懒加载)
什么是立即加载?
不管用不用,只要一调用方法,马上发起查询。
在对应的四种表关系中:一对多,多对一,一对一,多对多。 一对多,多对多:通常情况下我们都是采用延迟加载; 多对一,一对一:通常情况下我们都是采用立即加载。
Mybatis中的缓存
什么是缓存
存在于内存中的临时数据。
为什么使用缓存
减少和数据库的交互次数,提高执行效率。
什么样的数据能使用缓存,什么样的数据不能使用
* 适用于缓存: - 经常查询并且不经常改变的。 - 数据的正确与否对最终结果影响不大的。 * 不适用于缓存: - 经常改变的数据 - 数据的正确与否对最终结果影响很大的。 - 例如:商品的库存,银行的汇率,股市的牌价。
Mybatis中的一级缓存和二级缓存
一级缓存:
- 它指的是Mybatis中SqlSession对象的缓存。
- 当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供一块区域中。该区域的结构是一个Map。当我们再次查询同样的数据,mybatis会先去sqlsession中查询是否有,有的话直接拿出来用。
- 当SqlSession对象消失时,mybatis的一级缓存也就消失了。
- 如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
二级缓存:
- 它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
- 二级缓存的使用步骤:
- 第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
- 第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
- 第三步:让当前的操作支持二级缓存(在select标签中配置)
11. Mybatis中的注解开发
这几年来注解开发越来越流行,Mybatis也可以使用注解开发方式,这样我们就可以减少编写Mapper(即UserDap.xml、AccountDao.xml等配置文件)映射文件了。在Mybatis中只能使用映射配置文件和注解配置一种方式,所以使用注解配置时,应将映射配置文件移除。
1) . 单表的CUDA操作
首先针对单表的CUDA操作,只需要在UserDao接口文件的相关方法上添加配置即可。Mybatis中有四种SQL相关的语句配置:
- @Insert: 实现新增
- @Update: 实现更新
- @Delete: 实现删除
- @Select: 实现查询
以User类的接口UserDao为例,程序改写如下:
public interface UserDao {
/*
查询所有用户,同时获取用户下所有的账户信息
*/
@Select("select * from user")
@Results(id = "userMap", value={
@Result(id = true, column = "id", property = "id"),
@Result(column = "username", property = "username"),
@Result(column = "address", property = "address"),
@Result(column = "sex", property = "sex"),
@Result(column = "birthday", property = "birthday"),
})
List<User> findAll();
/**
* 按ID查询用户信息
* @param userID
*/
@Select("select * from user where id = #{uid}")
@ResultMap(value = {"userMap"})
User findById(Integer userID);
/**
* 插入保存user
* @param u
*/
@Insert("insert into user(username,address,sex,birthday) values(#{username},#{address},#{sex},#{birthday})")
@ResultMap(value = {"userMap"})
void saveUser(User u);
/**
* 更新信息
* @param u
*/
@Update("update user set username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id=#{id}")
@ResultMap(value = {"userMap"})
void updateUser(User u);
/**
* 删除用户
* @param userID
*/
@Delete("delete from user where id = #{id}")
@ResultMap(value = {"userMap"})
void deleteUser(Integer userID);
/**
* 按名称模糊查询用户信息
*/
@Select("select * from user where username like #{username}" )
@ResultMap(value = {"userMap"})
//@Select("select * from user where username like '%${value}%'" )
List<User> findByName(String username);
/**
* 聚合函数:查询总记录条数
*/
@Select("select count(id) from user")
@ResultMap(value = {"userMap"})
int findTotal();
}
- 只需要在方法之上添加 @Select(“SQL语句”),@Insert(“SQL语句”),@Update(“SQL语句”),@Delect(“SQL语句”)。
- 另外,若User类中的属性与数据库中user表的列名不一致,需要增加 @Results() 注解配置,类似于UserDao.xml中的
配置。
2). 多表操作配置
一对一配置
一对多配置
12. 通常一个Xml映射文件,都会写一个Dao接口与之对应,请问,这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗?
Dao接口即Mapper接口。接口的全限定类名,就是映射文件中的namespace的值;接口的方法名,就是映射文件中Mapper的Statement的id值;接口方法内的参数,就是传递给sql的参数。
Mapper接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为key值,可唯一定位一个MapperStatement。在Mybatis中,每一<select>、<insert>、<update>、<delete>标签,都会被解析为一个MapperStatement对象。
举例:com.mybatis3.mappers.StudentDao.findStudentById,可以唯一找到namespace为com.mybatis3.mappers.StudentDao下面 id 为 findStudentById 的 MapperStatement。
Mapper接口里的方法,是不能重载的,因为是使用 全限名+方法名 的保存和寻找策略。Mapper 接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Mapper接口生成代理对象proxy,代理对象会拦截接口方法,转而执行MapperStatement所代表的sql,然后将sql执行结果返回。
13. Mybatis动态sql有什么用?执行原理?有哪些动态sql?
Mybatis动态sql可以在Xml映射文件内,以标签的形式编写动态sql,执行原理是根据表达式的值完成逻辑判断并动态拼接sql的功能。
Mybatis提供了9种动态sql标签:trim | where | set | foreach | if | choose | when | otherwise | bind。
14. MyBatis实现一对一有几种方式? 具体怎么操作的?
有联合查询和嵌套查询:
- 联合查询: 是几个表联合查询,只查询一次, 通过在resultMap里面配置association节点配置一对一的类就可以完成;
- 嵌套查询: 是先查一个表,根据这个表里面的结果的 外键id,去再另外一个表里面查询数据,也是通过association配置,但另外一个表的查询通过select属性配置。
15、MyBatis实现一对多有几种方式,怎么操作的?
有联合查询和嵌套查询:
- 联合查询: 是几个表联合查询,只查询一次,通过在resultMap里面的collection节点配置一对多的类就可以完成;
- 嵌套查询: 是先查一个表,根据这个表里面的 结果的外键id,去再另外一个表里面查询数据,也是通过配置collection,但另外一个表的查询通过select节点配置。
二、 Spring
1. Spring框架是什么?
Spring是分层的Java SE/EE应用 full-stack轻量级开源框架,以IoC(Inverse Of Control:反转控制)和AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层Spring MVC和持久层Spring JDBC以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE企业应用开源框架。
2. Spring的优势
1. **方便解耦,简化开发:** 通过Spring提供的IoC容器,可以将对象间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
2. **AOP编程的支持:** 通过Spring的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付。
3. **声明式事务的支持:** 可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,支持将一些通用任务,如安全、事务、日志、权限等进行集中式管理,从而提供更好的复用, 提高开发效率和质量。
4. **方便程序的测试:** 可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情
5. **方便集成各种优秀框架:** Spring可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的直接支持。
6. **降低JavaEE API的使用难度:** Spring对JavaEE API(如JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些API的使用难度大为降低。
3. 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实现。
Spring体系结构
4、 Spring的IOC
IOC就是控制反转,是指创建对象的控制权的转移,以前创建对象的主动权和时机是由自己把控的,而现在这种权力转移到Spring容器中,并由容器根据配置文件去创建实例和管理各个实例之间的依赖关系,对象与对象之间松散耦合,也利于功能的复用。DI依赖注入,和控制反转是同一个概念的不同角度的描述,即 应用程序在运行时依赖IoC容器来动态注入对象需要的外部资源。
最直观的表达就是,IOC让对象的创建不用去new了,可以由spring自动生产,使用java的反射机制,根据配置文件在运行时动态的去创建对象以及管理对象,并调用对象的方法的。
5. BeanFactory和ApplicationContext有什么区别
Spring使用工厂模式可以通过 BeanFactory 或 ApplicationContext 创建 bean 对象。 BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。其中ApplicationContext是BeanFactory的子接口。
1)BeanFactory:是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能, 所以一般开发人员使用ApplicationContext会更多.
- ①继承MessageSource,因此支持国际化。
- ②统一的资源文件访问方式。
- ③提供在监听器中注册bean的事件。
- ④同时加载多个配置文件。
- ⑤载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。
2)Bean’对象实例加载机制
①*BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,我们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。
②ApplicationContext是立即加载,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。 ApplicationContext启动后预载入所有的单实例Bean,通过预载入单实例bean ,确保当你需要的时候,你就不用等待,因为它们已经创建好了。
③相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。
ApplicationContext的三个实现类:
- ClassPathXmlApplication:把上下文文件当成类路径资源。
- FileSystemXmlApplication:从文件系统中的 XML 文件载入上下文定义信息。
- XmlWebApplicationContext:从Web系统中的XML文件载入上下文定义信息。
6. Spring中的依赖注入
* 依赖注入:
Dependency Injection
* IOC的作用:
降低程序间的耦合(依赖关系)
* 依赖关系的管理:
以后都交给spring来维护
在当前类需要用到其他类的对象,由spring为我们提供,我们只需要在配置文件中说明
* 依赖关系的维护:
就称之为依赖注入。
*依赖注入:
1. 能注入的数据:有三类
1) 基本类型和String
2) 其他bean类型(在配置文件中或者注解配置过的bean)
3) 复杂类型/集合类型
2. 注入的方式:有三种
1) 第一种:使用构造函数提供
2) 第二种:使用set方法提供
3) 第三种:使用注解提供
1) 构造函数注入
1 |
|
- 使用的标签:constructor-arg
标签出现的位置:bean标签的内部
标签中的属性 type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型 index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始 name:用于指定给构造函数中指定名称的参数赋值 常用的 =============以上三个用于指定给构造函数中哪个参数赋值=============================== value:用于提供基本类型和String类型的数据 ref:用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象 优势: 在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功。 弊端: 改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供。 --> <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"> <constructor-arg name="name" value="泰斯特"></constructor-arg> <constructor-arg name="age" value="18"></constructor-arg> <constructor-arg name="birthday" ref="now"></constructor-arg> </bean> <!-- 配置一个日期对象 --> <bean id="now" class="java.util.Date"></bean>
2) set方法注入
1 |
|
- 涉及的标签:property
出现的位置:bean标签的内部
标签的属性 name:用于指定注入时所调用的set方法名称 value:用于提供基本类型和String类型的数据 ref:用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象 优势: 创建对象时没有明确的限制,可以直接使用默认构造函数 弊端: 如果有某个成员必须有值,则获取对象是有可能set方法没有执行。 --> <bean id="accountService2" class="com.itheima.service.impl.AccountServiceImpl2"> <property name="name" value="TEST" ></property> <property name="age" value="21"></property> <property name="birthday" ref="now"></property> </bean>
3)注入集合
顾名思义,就是给类中的集合成员传值,它用的也是set方法注入的方式,只不过变量的数据类型都是集合。我们这里介绍注入数组,List,Set,Map,Properties。具体代码如下:
1 | public class AccountServiceImpl implements IAccountService { |
复杂类型的注入/集合类型的注入
*用于给List结构集合注入的标签:
list array set
* 用于个Map结构集合注入的标签:
map props
结构相同,标签可以互换
1 |
|
7. Bean生命周期
单例对象: singleton
- 出生:当容器创建时对象出生
- 活着:只要容器还在,对象一直或者
- 死亡:容器销毁,对象消亡
总结:单例对象的生命周期和容器相同
多例对象: prototype
- 出生: 使用对象时spring框架为我们创建
- 活着:对象只要是在使用过程中就一直活着
- 死亡:当对象长时间不用且没有其它对象引用时,由java的垃圾回收机制回收
8. Bean作用域?默认什么级别?是否线程安全?Spring如何保障线程安全的?
Bean作用域
取值: 常用的就是单例的和多例的
- singleton: 单例的(默认值)-一个应用只有一个对象的实例。它的作用范围就是整个引用。
- prototype: 多例的-每次访问对象时,都会重新创建对象实例。
- request: 作用于web应用的请求范围,每⼀次HTTP请求都会产⽣⼀个新的bean,该bean仅在当前HTTP request内有效。
- session: 作用于web应用的会话范围,在一次 HTTP session 中,容器将返回同一个实例。
- global-session: 作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session
bean标签的scope属性:
作用:用于指定bean的作用范围
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl" scope="prototype"></bean>-->
9、 Spring的AOP
OOP面向对象,允许开发者定义纵向的关系,但并适用于定义横向的关系,导致了大量代码的重复,而不利于各个模块的重用。
AOP,一般称为面向切面,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理。
可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术
AOP的作用:
在程序运行期间,使用动态代理的技术不修改源码对已有方法进行增强。AOP的优势:
- 减少重复代码
- 提高开发效率
- 维护方便
10、动态代理
AOP实现的关键在于 代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。
(1)AspectJ是静态代理的增强,所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强,他会在编译阶段将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。
(2)Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:
①JDK动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy类,InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy利用 InvocationHandler动态创建一个符合某一接口的的实例, 生成目标类的代理对象。
②如果代理类没有实现 InvocationHandler 接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
(3)静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。
11、 AOP相关术语
- Joinpoint(连接点):
- 所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。
- Pointcut(切入点):
- 所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。
- Advice(通知/增强):
- 所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。
- 通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
- Target(目标对象):
- 代理的目标对象。
- Weaving(织入):
- 是指把增强应用到目标对象来创建新的代理对象的过程。 spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
- Proxy(代理):
- 一个类被AOP织入增强后,就产生一个结果代理类。
- Aspect(切面):
- 是切入点和通知(引介)的结合
Spring框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。
12、 Spring中的通知类型
1)在aop:aspect标签的内部使用对应标签来配置通知的类型
aop:before:表示配置前置通知
method属性:用于指定Logger类中哪个方法是前置通知
pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中哪些方法增强
ponitcut-ref: 用于指定切入点的表达式的引用,该引用使用aop:pointcut配置
通知类型:
* aop:before
- 作用: 用于配置前置通知。指定增强的方法在切入点方法之前执行
- 执行时间:切入点方法执行之前执行
* aop:after-returning
- 用于配置后置通知
- 切入点方法正常执行之后。它和异常通知只能有一个执行
* aop:after-throwing
- 用于配置异常通知
- 执行时间:切入点方法执行产生异常后执行。它和后置通知只能执行一个
* aop:after
- 用于配置最终通知
- 执行时间:无论切入点方法执行时是否有异常,它都会在其后面执行。
2)使用aop:pointcut配置切入点表达式
1) 作用: 用于配置切入点表达式。就是指定对哪些类的哪些方法进行增强。
2) 属性:
id属性: 用于指定表达式的唯一标识
expression属性: 用于指定表达式内容
此标签写在aop:aspect标签内部只能当前切面使用。 它还可以写在aop:aspect外面,此时就变成了所有切面可用
3)切入点表达式的写法:
关键字:execution(表达式)
表达式:
访问修饰符 返回值 包名.包名.包名...类名.方法名(参数列表)
* 标准的表达式写法:
public void com.itheima.service.impl.AccountServiceImpl.saveAccount()
* 访问修饰符可以省略
void com.itheima.service.impl.AccountServiceImpl.saveAccount()
* 返回值可以使用通配符,表示任意返回值
* com.itheima.service.impl.AccountServiceImpl.saveAccount()
* 包名可以使用通配符,表示任意包。但是有几级包,就需要写几个*.
* *.*.*.*.AccountServiceImpl.saveAccount())
* 包名可以使用..表示当前包及其子包
* *..AccountServiceImpl.saveAccount()
* 类名和方法名都可以使用*来实现通配
* *..*.*()
* 参数列表:
可以直接写数据类型:
基本类型直接写名称 int
引用类型写包名.类名的方式 java.lang.String
可以使用通配符表示任意类型,但是必须有参数
可以使用..表示有无参数均可,有参数可以是任意类型
* 全通配写法:
* *..*.*(..)
* 实际开发中切入点表达式的通常写法:
切到业务层实现类下的所有方法
* com.itheima.service.impl.*.*(..)
bean.xml配置文件的配置:
> {% codeblock lang:java %}
>
> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
> xmlns:aop="http://www.springframework.org/schema/aop"
> xsi:schemaLocation="http://www.springframework.org/schema/beans
> http://www.springframework.org/schema/beans/spring-beans.xsd
> http://www.springframework.org/schema/aop
> http://www.springframework.org/schema/aop/spring-aop.xsd">
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
> {% endcodeblock %}
13、 Spring 框架中都用到了哪些设计模式?
(1)工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;
(2)单例模式:Bean默认为单例模式。
(3)代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;
(4)模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。
(5)观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现–ApplicationListener。
三、SpringMVC
1. 什么是Spring MVC ?简单介绍下你对springMVC的理解?
Spring MVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架,通过把Model,View,Controller分离,将web层进行职责解耦,把复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。
2、Springmvc的优点:
清晰的角色分配,分工明确:前端控制器(dispatcherServlet) , 请求到处理器映射(handlerMapping), 处理器适配器(HandlerAdapter), 视图解析器(ViewResolver)。
可以支持各种视图技术,而不仅仅局限于JSP;
与Spring框架集成(如IoC容器、AOP等);
支持各种请求资源的映射策略。
3. SpringMVC和Struts2的联系与区别
共同点:
它们都是表现层框架,都是基于MVC模型编写的。 它们的底层都离不开原始ServletAPI。 它们处理请求的机制都是一个核心控制器。
区别:
SpringMVC 的入口是一个 servlet 即前端控制器(DispatchServlet),而 struts2 入口是一个 filter 过虑器(StrutsPrepareAndExecuteFilter)
springmvc是基于方法开发(一个url对应一个方法),请求参数传递到方法的形参,可以设计为单例或多例(建议单例),struts2是基于类开发,传递参数是通过类的属性,只能设计为多例, Struts2每次执行都会创建一个动作类。所以Spring MVC 会稍微比 Struts2 快些。
Struts采用值栈存储请求和响应的数据,通过OGNL存取数据,springmvc通过参数解析器是将request请求内容解析,并给方法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据通过reques域传输到页面。Jsp视图解析器默认使用jstl。
4. Spring MVC的主要组件?
(1)前端控制器 DispatcherServlet(不需要程序员开发)
作用:接收请求、响应结果,相当于转发器、中央处理器,有了DispatcherServlet 就减少了其它组件之间的耦合度。
(2)处理器映射器HandlerMapping(不需要程序员开发)
作用:根据请求的URL来查找Handler处理器
(3)处理器适配器HandlerAdapter
注意:在编写Handler的时候要按照HandlerAdapter要求的规则去编写,这样适配器HandlerAdapter才可以正确的去执行Handler。
(4)处理器Handler(需要程序员开发)
它就是我们开发中要编写的具体业务控制器。由DispatcherServlet把用户请求转发到Handler。由Handler对具体的用户请求进行处理。
(5)视图解析器 ViewResolver(不需要程序员开发)
作用:进行视图的解析,负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户
(6)视图View(需要程序员开发jsp)
View是一个接口, 它的实现类支持不同的视图类型(jsp,freemarker,pdf等等)
5. SpringMVC的流程?
- 1) 用户向服务端发送一次请求,这个请求会先到前端控制器DispatcherServlet(也叫中央控制器)。
- 2) DispatcherServlet接收到请求后会调用HandlerMapping处理器映射器。由此得知,该请求该由哪个Controller来处理(并未调用Controller,只是得知)
- 3)HandlerMapping 处理器映射器根据请求url找到具体的处理器Handler,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet;
- 4)DispatcherServlet调用HandlerAdapter处理器适配器,告诉处理器适配器应该要去执行哪个Controller
- 5)HandlerAdapter处理器适配器去执行Controller并得到ModelAndView(数据和视图),并层层返回给DispatcherServlet
- 6)DispatcherServlet将ModelAndView交给ViewReslover视图解析器解析,然后返回真正的视图。
- 7)DispatcherServlet将模型数据填充到视图中
- 8)DispatcherServlet将结果响应给用户
6. RequestMapping注解
- RequestMapping注解的作用是建立请求URL和处理方法之间的对应关系
- RequestMapping注解可以作用在方法和类上
- 作用在类上:第一级的访问目录
- 作用在方法上:第二级的访问目录
- 细节:路径可以不编写 / 表示应用的根目录开始
- 细节:${ pageContext.request.contextPath }也可以省略不写,但是路径上不能写 /
- RequestMapping的属性
- path 指定请求路径的url
- value value属性和path属性是一样的
- mthod 指定该方法的请求方式 Get和Post,method=RequestMethod.GET
- params 指定限制请求参数的条件
- headers 发送的请求中必须包含的请求头
7. SpringMVC常用的注解有哪些?
@RequestMapping:用于处理请求 url 映射的注解,可用于类或方法上。 用于类上,则表示类中的所有响应请求的方法都是以该地址作为父路径。
@RequestBody: 注解实现接收http请求的json数据,将json转换为java对象。
@ResponseBody: 注解实现将conreoller方法返回对象转化为json对象响应给客户。
8. 请求参数的绑定
表单中请求参数都是基于key=value的。 SpringMVC绑定请求参数的过程是通过把表单提交请求参数,作为控制器中方法参数进行绑定的。
支持的数据类型:
- 基本类型参数: 包括基本类型和String类型
- POJO类型参数: 包括实体类,以及关联的实体类
- 数组和集合类型参数: 包括List结构和Map结构的集合(包括数组)
9. 如何解决POST请求中文乱码问题,GET的又如何处理呢?
POST请求
在web.xml中配置一个CharacterEncodingFilter过滤器,设置成utf-8;
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Get请求
GET请求的编码问题,要改tomcat的server.xml配置文件。
<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
改为: <Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443" useBodyEncodingForURI="true"/>
如果遇到ajax请求仍然乱码,请把:
useBodyEncodingForURI="true"改为URIEncoding="UTF-8" 即可。
10、 常用的注解
RequestParam注解
- 作用:把请求中的指定名称的参数传递给控制器中的形参赋值
属性
value:请求参数中的名称
required:请求参数中是否必须提供此参数,默认值是true,必须提供/** * 接收请求 * @return */ @RequestMapping(path="/hello") public String sayHello(@RequestParam(value="username",required=false)String name) { System.out.println("aaaa"); System.out.println(name); return "success"; }
RequestBody注解
作用:用于获取请求体的内容(注意:get方法不可以)ResponseBody
作用:将controller方法返回对象转换为json响应给客户端PathVariable注解
作用:拥有绑定url中的占位符的
11、转发与重定向
请求次数:重定向是浏览器向服务器发送一个请求并收到响应后再次向一个新地址发出请求,转发是服务器收到请求后为了完成响应跳转到一个新的地址;重定向至少请求两次,转发请求一次;
地址栏不同:重定向地址栏会发生变化,转发地址栏不会发生变化;
是否共享数据:重定向两次请求不共享数据,转发一次请求共享数据(在request级别使用信息共享,使用重定向必然出错);
跳转限制:重定向可以跳转到任意URL,转发只能跳转本站点资源;
发生行为不同:重定向是客户端行为,转发是服务器端行为;