Spring优点
- 低侵入式设计,代码污染极低
- DI机制将对象之间的依赖关系交给框架处理,减低组件的耦合性
- AOP可以支持通用任务,如安全、事务、日志、权限校验等进行集中管理,从而得到更好的复用
- 对其他框架提供集成支持
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容器
DI(方法)
依赖注入,把Spring容器中的对象注入到一个对象中,降低耦合,减少代码量
1)构造方法注入
2)setter注入
3)接口注入
Spring创建对象
1)无参构造
2)静态工厂创建
3)实例工厂创建
Spring Bean的生命周期
实例化(init-method)
被销毁(destroy-method)
只有作用域Singleton生效
Spring延迟加载(lazy-init)
默认情况下,容器启动之后将所有作用域为单例的bean创建好,有时候我们不需要它提前创建,在bean设置lazy-init="true"后,作用域为单例的bean就不提前创建
Spring自动装配
就是将一个Bean注入到其它的Bean的Property中,默认情况下,容器不会自动装配,需要我们手动设定。Spring 可以通过向Bean Factory中注入的方式来搞定bean之间的依赖关系,达到自动装配的目的
Spring Bean作用域
Singleton:单例模式,在整个spring IOC容器中,使用singleton定义的bean将只有一个实例。
Prototype:多例模式,每次通过容器中的getBean方法获取prototype定义的beans时,都会产生一个新的bean的实例。
Request:对于每次Http请求,使用request定义的bean都会产生一个新的实例,只有在web应用时候,该作用域才会有效。
Session:对于每次Http Session,使用session定义的Bean都将产生一个新的实例。
Globalsession:每个全局的Http Sesisonn,使用session定义的本都将产生一个新的实例。
Spring Bean线程安全性
Spring框架没有对单例的bean进行多线程的封装,需要自己考虑线程安全和并发问题
service和dao没有可变状态,可以看成线程安全
view和model需要自己考虑线程安全问题
Spring注入Java Collection
List标签: 有重复值的list
Set标签:无重复值的set值
Map标签:注入键值对
props标签:注入键值对和字符串类型键值对
Spring框架用到的设计模式
代理模式: AOP使用最多,jdk和cglib
单例模式: Spring Bean默认
工厂模式: BeanFactory用来创建对象的实例
适配器模式: 在AOP中,被代理的类方法前,设置拦截器,从而实现增强
包装器模式: sessionFactory根据用户请求切换数据源
观察者模式: 定义对象间的一对多的依赖关系,当一个对象状态改变,所有依赖它的对象都得到通知并更新(listener,如ApplicationListener)
策略模式: spring实例化对象
模板方法模式: jdbcTemplate
Spring事件处理
ApplicationContext负责管理bean的完整生命周期
Spring提供内置事件:
ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvenet、ContextClosedEvenet
Spring事件处理是单线程的
监听上下文事件
自定义事件
AOP
面向切面编程,针对业务处理的切面进行提取,以达到增强代码,减少重复代码的目的。
场景: 缓存、权限管理、内容传递、错误处理、懒加载、记录跟踪、优化、校准、调试、持久化、资源池、同步管理、事物控制等。
切面(Aspect)
连接点(JoinPoint)
通知(Advice)
切入点(Pointcut)
代理(Proxy)
织入(WeaVing)
AOP原理:
1)JDK动态代理: 只能用于实现接口的类产生代理
2)Cglib代理:针对没有实现接口的类产生代理,应用的是底层字节码增强结束,生成当前类的子类对象
代理模式
代理类代理目标类,目标类的逻辑代码不再修改,仅通过代理对象来实现业务代码的更换
-
静态代理
优点:不修改目标对象功能的前提下,对目标功能进行扩展
缺点:目标类和代理类要实现统一的接口,接口增加方法,要对目标类和所有代理类读增加方法,代价太高 -
动态代理
不需要实现接口,利用JDK的API,动态的在内存构建代理对象,一般需要我们指定代理对象/目标对象实现的接口类型。
而Cglib:在内存中构建子类对象,从而实现对目标对象功能的扩展
SpringMVC
MVC架构: 模型(封装业务逻辑的javabean)、视图(展示数据)、控制器(协调视图和模型,对请求进行分发)
1.视图将请求发给控制器
2.控制器选择对应的模型来处理
3.模型将返回结果交给控制器
4.控制器选择合适的视图来展示处理结果
SpringMVC五大组件
- 前端控制器(DispatcherServlet)
- 映射处理器(HandlerMapping)
- 处理器(Controller)
- 模型和视图(ModelAndView)
- 视图解析器(ViewResolver)
SpringMVC运行原理
1.客户端请求提交到DispatcherServlet 由DispatcherServlet控制器查询一个或多个HandlerMapping找到处理请求的Controller
2.DispatcherServlet将请求提交到Controller
3.Controller调用业务逻辑处理后,返回ModelAndView
4.DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图
5.视图负责将结果显示到客户
SpringMVC Controller
- 单例模式
- 多线程访问有线程安全问题,但是不要使用同步,会影响性能,解决方法是在控制器不能写字段
SpringMVC转发和重定向
- 返回值前面加上"forword"
- 返回值前面加上"redirect"
SpringMVC优点
- 基于View框架的无缝集成,采用IOC便于测试
- 典型的纯MVC架构,Struts不完全是MVC框架
- 与tapestry是纯正的Servlet系统
Spring MVC 比较 Struts2
- Spring MVC 的入口是 Servlet, 而 Struts2 是 Filter
- Spring MVC 会稍微比 Struts2 快些. Spring MVC 是基于方法设计, 而 Sturts2 是基于类, 每次发一次请求都会实例一个 Action
- Spring MVC 使用更加简洁, 开发效率 Spring MVC 确实比 struts2 高;支持 JSR303, 处理 ajax 的请求更方便
- Struts2 的 OGNL 表达式使页面的开发效率相比 Spring MVC 更高些
MyBatis
- MyBatis是半ORM(对象关系映射)框架,内部封装JDBC,开发注重SQL本身,不注重加载驱动、创建连接、创建statment等过程。
- MyBatis可以用XML或注解配置和映射,将POJO映射成数据库中的记录,避免了所有JDBC代码和手动给POJO设置参数以及获取结果集。
MyBatis优点
- 基于SQL,查询灵活。
- SQL写在XML,降低SQL语句与程序代码的耦合度
- XML提供标签,以支持动态SQL语句,提供了代码重用性
- 与JDBC相比,减少了大量冗余代码,不需要手动开关连接,创建statement
- 兼容性强,Mybatis使用JDBC连接数据库
- 与Spring框架很好的集成
- 提供对象关系映射标签,便于维护
MyBatis缺点
- SQL语句编写工作量较大,尤其在字段多、关联表多时,对开发人员编写SQL有一定要求
- SQL语法依赖数据库,移植性比较差,不能随意更换数据库
MyBatis使用场合
- 注重SQL本身,是一个足够灵活的DAO层解决方案
- 对性能要求高,需求多变的项目
MyBatis与Hibernate的区别
- MyBatis半ORM,需要自己写SQL语句。Hibernate是ORM框架,不需要自己写SQL语句。
- MyBatis直接编写原生SQL,可以严格控制性能,灵活度高,适用于需求多变,对关系数据模型要求不高的项目中,数据库无关性较差,多种数据库需要自定义多套SQL映射文件,工作量大。
- Hibernate对象/关系映射能力强,数据库无关性比较好,适用于对关系数据模型要求高的软件,可以节省很多代码,提高效率。
MyBatis种#{}和${}的区别
#{}是预编译,${}是字符串替换
- #{}里的内容会替换掉SQL语句中的占位符?(调用PreparedStatement的set方法赋值)
- ${}里的内容会替换掉SQL语句中的变量值(调用Statement),会出现SQL注入漏洞
MyBatis属性名和字段名不一致解决方案
- 查询时返回字段名的别名
<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>
MyBatis模糊查询like语句
- sql通配符 select * from student where name like #{stringOfFirstname}
- sql通配符衔接 select * from student where name like “%”#{firstname}%"%" 会有注入漏洞
MyBatis Dao接口的使用
- Dao接口名就是Mapper接口名,映射相同Mapper接口名的Mapper文件
- Dao接口的方法名对应Mapper文件的id值,是唯一的,不能重载
- 每个select、insert、update、delete标签都被解析成一个MapperStatement对象
MyBatis Dao接口的原理
运行时使用JDK动态代理为Mapper接口生成代理对象proxy,代理对象会拦截接口方法,然后去执行对应MapperStatement所代表的sql语句,将结果返回。
MyBatis传递多个参数
- 使用参数位置
//DAO层的函数
public User selectUser(String name,String area);
//对应的xml,#{0}代表接收的是dao层中的第一个参数,#{1}代表dao层中第二参数,更多参数一致往后加即可。
<select id="selectUser"resultMap="BaseResultMap">
select * from user where user_name = #{0} and user_area=#{1}
</select>
- 使用@param注解
public interface usermapper {
user selectuser(@param(“username”) string username,@param(“hashedpassword”) string hashedpassword);
}
//然后,就可以在xml像下面这样使用(推荐封装为一个map,作为单个参数传递给mapper):
<select id=”selectuser” resulttype=”user”>
select id, username, hashedpassword
from some_table
where username = #{username}
and hashedpassword = #{hashedpassword}
</select>
- 封装成map
MyBatis的动态SQL原理及标签
原理:根据表达式的值,完成逻辑判断并动态拼接SQL的功能。
9种标签:trim | where | set | foreach | if | choose | when | otherwise | bind
MyBatis的不同xml文件id值能否重复
namespace不同id值可以相同,没有配置namespace则不行
因为namespace+id是Map<String, MapperStatement>的key
MyBatis一对一asscociation
<!--association 一对一关联查询 -->
<select id="getClass" parameterType="int" resultMap="ClassesResultMap">
select * from class c,teacher t where c.teacher_id=t.t_id and c.c_id=#{id}
</select>
<resultMap type="com.lcb.user.Classes" id="ClassesResultMap">
<!-- 实体类的字段名和数据表的字段名映射 -->
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" javaType="com.lcb.user.Teacher">
<id property="id" column="t_id"/>
<result property="name" column="t_name"/>
</association>
</resultMap>
MyBatis一对多collection
<select id="getClass2" parameterType="int" resultMap="ClassesResultMap2">
select * from class c,teacher t,student s where c.teacher_id=t.t_id and c.c_id=s.class_id and c.c_id=#{id}
</select>
<resultMap type="com.lcb.user.Classes" id="ClassesResultMap2">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" javaType="com.lcb.user.Teacher">
<id property="id" column="t_id"/>
<result property="name" column="t_name"/>
</association>
<collection property="student" ofType="com.lcb.user.Student">
<id property="id" column="s_id"/>
<result property="name" column="s_name"/>
</collection>
</resultMap>
MyBatis延迟加载
- 仅支持association和collection关联对象的延迟加载
- 设置lazyLoadingEnabled = true | false
- 原理: 使用cglib创建代理对象,当调用到目标方法时,进入拦截器方法,比如调用a.getB().getName()前,拦截器invoke()方法发现a.getB()为null,就会把查询关联B对象的sql查询上来,把B查询上来并调用a.setB(b),接着调用a.getB().getName()
MyBatis的一级、二级缓存
- 一级缓存:基于PerpetualCache的HashMap本地缓存,存储作用域session,当session flush或者close后,session将cache清空。默认打开一级缓存。
- 二级缓存:也是基于PerpetualCache的HashMap本地缓存,不过存储作用域为Mapper(Namespace),可以自定义存储源,如Ehcache。默认不打开二级缓存,POJO实现Serializable,在配置文件设置cache
- 缓存更新,某个作用域进行C/U/D,默认作用域下的select缓存被clear掉
MyBatis插件运行原理,一级如何编写一个插件
Mybatis仅可以编写针对ParameterHandler、ResultSetHandler、StatementHandler、Executor这4种接口的插件,Mybatis使用JDK的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4种接口对象的方法时,就会进入拦截方法,具体就是InvocationHandler的invoke()方法,当然,只会拦截那些你指定需要拦截的方法。
编写插件:实现Mybatis的Interceptor接口并复写intercept()方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文件中配置你编写的插件。
Hibernate中对象的三种状态
- 临时/瞬时状态 : new出来的对象,没有被持久化,不受session管理
- 持久化状态 : 当session调用save/saveOrUpdate/get/load/list等方法后,或者数据库有对应数据,受session管理,对象属性更改后会反映都数据库
- 游离状态 : 当session关闭后,持久化的对象就变游离状态了,不处于session管理,但数据库有对应记录
Hibernate的三种检索策略优缺点
立即检索:
优点: 对应用程序完全透明,不管对象处于持久化状态,还是游离状态,应用程序都可以方便的从一个对象导航到与它关联的对象;
缺点: 1.select语句太多;2.可能会加载应用程序不需要访问的对象白白浪费许多内存空间;
立即检索:lazy=false;
延迟检索:
优点: 由应用程序决定需要加载哪些对象,可以避免可执行多余的select语句,以及避免加载应用程序不需要访问的对象。因此能提高检索性能,并且能节省内存空间;
缺点: 应用程序如果希望访问游离状态代理类实例,必须保证他在持久化状态时已经被初始化;
延迟加载:lazy=true;
迫切左外连接检索:
优点: 1对应用程序完全透明,不管对象处于持久化状态,还是游离状态,应用程序都可以方便地冲一个对象导航到与它关联的对象。2使用了外连接,select语句数目少;
缺点: 1 可能会加载应用程序不需要访问的对象,白白浪费许多内存空间;2复杂的数据库表连接也会影响检索性能;
预先抓取: fetch=“join”;
Hibernate里面的sorted collection和ordered collection区别
- sorted collection: 是在内存中通过Java比较器进行排序的(可能导致内存溢出,适用于小数据集)
- ordered collection: 是数据库中通过order by 进行排序的(适用于大数据集)
Hibernate缓存机制
- Hibernate一级缓存可以在session范围减少数据库的访问次数,用户不能直接操作缓存内容,可以通过hibernate提供evit/clear方法
- Hibernate二级缓存基于应用程序的缓存,所有的Session都可以使用。二级缓存是可插配的缓存框架,想要使用在hibernate.cfg.xml中配置即可,也可以自己换用其他缓存框架,缓存的对象是常用的类
Hibernate查询方式
- 对象导航查询
- HQL查询(1.属性查询 2.参数查询、命名参数查询 3.关联查询 4.分页查询 5. 统计函数)
- Criteria查询
- SQLQuery本地SQL查询
Hibernate优化
- 数据库结构调整
- HQL优化
- API的正确使用(如根据不同的业务类型选用不同的集合及查询API)
- 主配置参数(日志, 查询缓存, fetch_size, batch_size等)
- 映射文件优化(ID生成策略, 二级缓存, 延迟加载, 关联优化)
- 一级缓存的管理
- 针对二级缓存,还有许多特有的策略
Hibernate的inverse(维护关系)
- inverse默认是false,也就是说关系的两端都来维护关系
- 比如Student和Teacher是多对多,用中间表TeacherStudent表维护。Student这边的inverse=“true”, 那么关系由另一端Teacher维护(也就是说插入Student时候,不会操作TeacherStudent表)
- 多对多双方不能都是inverse=“true”,至少有一方维护
- 如果是一对多关系,inverse="true"只能在一方
数据库条件查询优化
- 建索引 CREATE INDEX ON table_name
- 减少表之间的关联
- 优化sql,尽量让sql很快定位数据,不要让sql做全表查询,应该走索引,把数据量大的表排前面
- 简化查询字段,舍弃没用的字段,尽量返回少量数据
Hibernate的SessionFactory
- 单例数据存储,线程安全
Hibernate查询get(立即查询)和load(懒加载)
- get如果没有找到会返回null, load没有找到会抛出异常
- get: 一级缓存->二级缓存->数据库
- load: 一级缓存->创建代理对象等需要的时候->二级缓存->数据库
Hibernate的persist和save的区别
- persist不保证立即执行,可能要等到flush
- persist不更新缓存
- save,把一个瞬态的实例持久化标识符,及时的产生,它要返回标识符,所以它会立即执行Sql insert
- 使用save()方法保存持久化对象时,返回该持久化对象的标识属性值(即对应记录的主键值)
- 使用persist()方法保存持久化对象时,无返回值
Hibernate主键自动生成策略
- identity 自增长(mysql, db2)
- sequence 自增长(序列,oracle)
- native 自增长(根据底层数据库选择identity或者sequence)
- increment 自增长(会有并发访问的问题, 一般再服务器集群环境使用会存在问题)
- assigned 手动指定主键的值
- uuid 手动指定UUID生成的值
Hibernate的getCurrentSession和openSession
- getCurrentSession绑定当前线程,它是可以交给spring控制,有事务的线程就会绑定工厂里的每个session,不需要我们手动关闭,但是需要我们手动设置绑定事务的机制(jdbc本地的Thread、JTA、SpringSessionContext(默认))
- openSession是开启一个新的session,需要手动开启,提交,关闭
Hibernate的命名SQL查询
- 命名查询指的是用<sql-query>标签在影射文档中定义的SQL查询,可以通过使用Session.getNamedQuery()方法对它进行调用。命名查询使你可以使用你所指定的一个名字拿到某个特定的查询。
- Hibernate中的命名查询可以使用注解来定义,也可以使用我前面提到的xml影射问句来定义。在Hibernate中,@NameQuery用来定义单个的命名查询,@NameQueries用来定义多个命名查询。
Hibernate的实体类一定需要无参构造器
每个Hibernate实体类必须包含一个 无参数的构造器, 这是因为Hibernate框架要使用Reflection API,通过调用Class.newInstance()来创建这些实体类的实例。如果在实体类中找不到无参数的构造器,这个方法就会抛出一个InstantiationException异常。
Hibernate的实体类定义为final类带来的问题
你可以将Hibernate的实体类定义为final类,但这种做法并不好。因为Hibernate会使用代理模式在延迟关联的情况下提高性能,如果你把实体类定义成final类之后,因为 Java不允许对final类进行扩展,所以Hibernate就无法再使用代理了, 如此一来就限制了使用可以提升性能的手段。