Java 主流框架
框架的概念: 封装
最核心的实现: 反射和动态代理
反射
(1)反射的概念
Java反射就是在运行状态中,对于任意一个类,都可以知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的方法和属性,改变其属性值。
Student student = new Student();
student.name
student.study();
(2)反射能做什么?
反射的机制允许程序在运行时取得任何一个已经名称的类的内部信息,包括其modifiers(访问修饰符),field(字段属性),mothods(方法),constructor(构造方法),并可以在运行时改变属性值或调用方法。
(3)反射的优缺点
更灵活的编写代码,代码在运行时才进行装配,无需组件有源代码中链接的关系,降低了代码的耦合度。
使用反射的时候注意有可能会造成资源的消耗。有可能造成性能降低。反射可以访问私有变量,可能存在不安全的变量 setAccessiable(true) , 可以访问私有变量
(4)反射的原理
在jdk1.8之前,方法区中会记录类的基本信息(类的访问控制符,类名,属性名,属性类型,属性访问空字符,方法名,方法参数,方法返回值等),在jdk1.8之后没有方法区,转移到元空间中。
(5)获取Class对象的三种方式
//获取反射对象的三种方式
Class c1 = Class.forName("org.lanqiao.test.Test");//第一种方式
Class c2 = Test.class; //第二种方式
Test t = new Test();
Class c3 = t.getClass();//第三种方式
//可以通过反射对象操作该类的属性和方法
Fields[] fields = c1.getFields(); //获取Test这个类的所有属性还包括父类的public的属性
Fields[] fields = c1.getDeclareFields();//可以当前类的所有属性,包括私有属性
fields[0].getName();//获取属性的名字
fields[0].getType();//获取返回值类型
fields[0].getModifiers();//获取属性的访问修饰符
//获取构造方法
Contructor con = c1.getConstructor(int.class,String.class,int.class);
Contructor con2 = c1.getConstructor();
Test test = (Test)con.newInstance(1,"john",21);
Test test2 = (Test)con2.newInstance();
//获取普通方法
Method[] ms = c1.getMethods();
Method method = c1.getMethod("getName");
method.invoke();//调用方法
(6)反射的应用
通用性框架(灵活性,不能写死)
最典型Spring的IOC,把创建对象的权限全权交给Spring容器
<bean id="test" class="org.lanqiao.test.Test">
一、mybatis框架(替代和简化JDBC的操作)
1.mybatis框架的概念 ORM框架概念 主流的orm框架
核心就是替代jdbc
orm 对象关系型映射,让程序员操作对象的时候等同于操作数据库中的表
2.mybatis的优缺点
3.mybatis和hibernate的区别
4.mybatis的核心api和原理、源码解析(重点孙奇)
SqlSessionFactory 创建sqlsession实例,读取mybatis的一些配置信息到Configuration
SqlSession jdbc的Connection,顶层的api,完成增删改查的操作
Executor
以下几个api都是处理sql的操作的
StatementHandler(代表sql语句操作)
ParameterHandler(处理sql语句的参数)
ResultSetHandler(处理返回值)
5.手写一个简单的orm框架(基于动态代理和反射) (孙奇)
6.mybatis的#{}和${}的区别
可以动态的按年龄、成绩、学号等进行排序
select * from student order by #{} //不能使用这种
select * from student order by ${} //可以使用,但可能会造成sql注入的问题
7.mybatis的动态sql的含义< if > < where > < set >等
动态sql,拼接sql,最终为了拼出来一个sql语句
8.mybatis的一级缓存和二级缓存的概念和区别
缓存的概念:非常非常的重要,现在非常多的项目普遍使用,提升用户的访问速度,减轻服务器的压力,避免了过多的访问数据库(非常重的IO操作)
mybatis的缓存作用就是减少访问数据库,提升查询效率
- mybatis的一级缓存:
作用域(有效范围): 同一个sqlsession中
针对: 查询操作
默认: 开启
写入时间: 查询后自动写入缓存
只要有了第一次查询的结果会自动写入缓存,下次查询的时候直接从缓存中获取结果,不需要到数据中再次查询
清除一级缓存:
(1) sqlSession.close() //手动清除
(2) sqlSession.clearCache() //手动清除
(3) 对缓存的记录执行了insert、update、delete等DML操作,都会自动清除一级缓存
SqlSession sqlSession = sqlSessionFacotry.openSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
StudentDao studentDao2 = sqlSession.getMapper(StudentDao.class);
Student student = studentDao.selectByID(1);//第一次查询id是1的,会查询数据库,会自动的写入一级缓存
Student student2 = studentDao2.selectByID(1); //由于studentDao和studentDao2都是同一个SqlSession创建出来的,所以共用一个一级缓存,第二次查询id为1的,直接可以从一级缓存中获取,不需要查询数据库
SqlSession sqlSession = sqlSessionFacotry.openSession();
SqlSession sqlSession2 = sqlSessionFacotry.openSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
StudentDao studentDao2 = sqlSession2.getMapper(StudentDao.class);
Student student = studentDao.selectByID(1);//第一次查询id是1的,会查询数据库,会自动的写入一级缓存
Student student2 = studentDao2.selectByID(1); //由于studentDao和studentDao2不是同一个SqlSession创建出来的,所以不会共用同一个一级缓存,第二次查询id为1的,不从一级缓存中获取,只能查询数据库
-
mybatis的二级缓存
作用域(有效范围): 全局,可以跨sqlSession,同一个namespace中
针对: 查询
默认: 关闭
写入时间: 查询完毕,而且要关闭sqlSession的时候才会写入缓存
如何开启二级缓存?
在sql的映射文件中
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- namespace:命名空间,随便写,一般保证命名空间唯一,用动态代理必需写接口的全限定名 --> <mapper namespace="table1"> <!--在本namespace中开启二级缓存--> <cache/> <select id="selectByID" resultType="org.lanqiao.Entity.Student"> select * from student where id = #{id} </select> </mapper>
SqlSession sqlSession = sqlSessionFacotry.openSession(); SqlSession sqlSession2 = sqlSessionFacotry.openSession(); StudentDao studentDao = sqlSession.getMapper(StudentDao.class); StudentDao studentDao2 = sqlSession2.getMapper(StudentDao.class); Student student = studentDao.selectByID(1);//第一次查询id是1的,会查询数据库,会自动的写入一级缓存 Student student2 = studentDao2.selectByID(1); //由于studentDao和studentDao2不是同一个SqlSession创建出来的,所以不会共用同一个一级缓存,第二次查询id为1的,不从一级缓存中获取,但是由于在同一个namespace中,可以使用二级缓存,不需要查询数据库,但一定要注意(上边的一定要记得先关闭sqlSession才会写入到二级缓存)
9.分页插件pagehelper,以及mysql和oracle分页sql语句区别
(1)分页的概念(应用的非常多)
逻辑分页 (假分页)
(查询所有的记录,在前端进行分页处理,会比较占用内存,占用带宽,数据量比较小,查询速度比较快)
RowBounds 实现逻辑分页 select * from student (所有的学生都会查询出来)
物理分页(真分页),主要使用这种方式 (很多大的数据量并不是从数据库中查询出来的,而是elasticsearch)
会根据指定页码和每页显示的数据查询数据库 page=2 pageSize=10 ,要查询数据库(11-20)
(2)mysql和oracle的分页区别
mysql:
select * from student limit start , pageSize
start(起始记录数) = (currentPage(当前页码)- 1)*pageSize(每页记录数)
oracle:(没有limit关键字,使用伪列rownum)
select s. * ,rownum rn from (select t.* , rownum rn from student t where rn<20) s where rn>10
select s. * ,rownum rn from (select t.* , rownum rn from student t where rn>10) s where rn<20
上边的2种写法结果一样,但在数据量非常大的情况下,SQL1效率要远远比SQL2要高(去掉了大部分记录)
(3)分页插件pagehelper
原理:mybatis+pagehelper实现的分页
使用mybatis的插件拦截器,Excutor调用拦截器,执行pagehelper实现分页的效果
使用:
第一步、导入pagehelper maven依赖(下载对应的jar包)
第二步、mybatis的配置文件配置plugin
第三步、在业务层中可以直接使用:
PageHelper.startPage(start,size);
二、Spring框架
1.Spring框架的概念
粘合剂,整合非常多非常多的框架,解耦(降低耦合度)===》 扩展性非常好
2.优缺点
3.Spring的核心概念
4.Spring IOC和DI概念(重点的重点)
IOC : inverse of control 控制反转,把之前程序员在当前程序使用new创建对象和管理对象的权限交给spring容器来负责对象的创建和管理
public class Test{
Human human = new Student();//直接由程序员在Test类中直接使用new创建对象,强依赖(耦合)关系
}
public class Test{
@Autowried
Human human; //使用spring ioc容器,Student对象是由spring容器来创建的,可以注入student,也可以注入Teacher
}
BeanFactory就是spring ioc容器,程序员在使用ioc容器使用的ApplicationContext
在spring当中,ioc的使用方式有几种
-
使用xml的方式
<!--把Student这个对象交由ioc容器进行管理--> <bean id="student" class="org.lanqiao.entity.Student"/>
-
使用注解的方式
前提:要扫描这个类所在的包
@Component @Service @Controller @Mapper @Repoistory //告知被注解的类交由ioc容器管理
package org.lanqiao.service; @Service //<bean id="myService" class="org.lanqiao.service.MyService"/> public class MyService{ }
-
使用javaconfig配置类@Bean
@Configuration public class MyConfig{ @Bean //把第三方的类加载到ioc容器中 public Student student(){ return new Student(); } }
DI: dependence injection 依赖注入
ioc是负责创建bean,管理bean,di就是负责给这个bean的属性赋值
package org.lanqiao.entity;
@Component//交给ioc容器负责创建管理
public class Student{
private int sno;
private String name;
private Teacher teacher;
public Student(){
}
public Student(String name){
this.name = name;
}
public Student(int sno ,String name){
this.sno = sno;
this.name = name;
}
public void setSno(int sno){
this.sno = sno;
}
}
di注入的方式:
-
属性注入 setXXX()注入
<bean id="student" class="org.lanqiao.entity.Student"> <!--属性注入,Student类中必须给sno属性提供setXXX方法--> <property name="sno" value="11"></property> </bean>
-
构造器注入
<bean id="student" class="org.lanqiao.entity.Student"> <!--属性注入,Student类中必须给sno属性提供setXXX方法--> <constructor-arg index="0" value="10"></constructor-arg> <constructor-arg index="1" value="张三"></constructor-arg> </bean> <bean id="student" class="org.lanqiao.entity.Student"> <!--属性注入,Student类中必须给sno属性提供setXXX方法--> <constructor-arg type="java.lang.Integer" value="10"></constructor-arg> <constructor-arg type="java.lang.String" value="张三"></constructor-arg> </bean>
-
工厂方式注入
-
引用注入
<bean id="teacher" class="org.lanqiao.entity.Teacher">
<bean id="student" class="org.lanqiao.entity.Student">
<!--属性注入,Student类中必须给sno属性提供setXXX方法-->
<property name="teacher" ref="teacher"></property>
</bean>
-
注解方式注入
public class Test{ @Autowired Student student; @Resource Teacher teacher; }
@Autowird与@Resource注解都可以完成di依赖注入,他们之间的区别?
5.Spring AOP概念(重点的重点)
OOP: object oriented programming
AOP : aspect oriented programming面向切面编程
一般把软件中非业务功能抛离出来做成切面,利用预编译方式和运行期的动态代理把切面切入到业务逻辑中,从而降低了业务逻辑和非业务逻辑之间的耦合度。
public class StudentService{
public int addStudent(Student student){
//1.需要进行权限的判定,这个方法当前的操作者有无这个权限
//3.启动事务
//4.添加学生基本信息(基本信息表) 业务功能
//5.添加学生详细信息(详细信息表) 业务功能
//6.关闭事务
//7.操作日志,做一些关闭或者异常处理
//8.会把什么时间,哪个人添加的哪个学生(比如学号)会记录到日志中
}
}
由于业务功能中存在非常多的非业务操作(权限),极大的增加了程序员的负担,对程序员有比较高的要求,把非业务功能都可以抽离出来,可以动态的加载到业务功能中去
(1)通知(Advice)
就是你想要的功能,比如 安全,事务,日志,性能测试,缓存机制等。需要先定义好,然后在想用的地方用一下。
(2)连接点(JoinPoint)
是spring允许你使用通知的地方,基本每个方法的前,后(两者都有也行),或抛出异常时都可以是连接点,spring只支持方法连接点.和方法有关的前前后后(抛出异常),都是连接点。
(3)切入点(Pointcut)
上面说的连接点的基础上,来定义切入点,你的一个类里,有15个方法,那就有几十个连接点,但是你并不想在所有方法附近都使用通知(使用叫织入),你只想让其中的几个,在调用这几个方法之前,之后或者抛出异常时干点什么,那么就用切点来定义这几个方法,让切点来筛选连接点,选中那几个你想要的方法。
(4)切面(Aspect)
切面是通知和切入点的结合。通知说明了干什么和什么时候干(什么时候通过方法名中的before,after,around等就能知道),而切入点说明了在哪干(指定到底是哪个方法),这就是一个完整的切面定义。
(5)引入(introduction)
允许向现有的类添加新方法属性。就是把切面(也就是新方法属性:通知定义的)用到目标类中
(6)目标(target)
引入中所提到的目标类,也就是要被通知的对象,也就是真正的业务逻辑,可以被织入切面。而自己专注于业务本身的逻辑。
(7)代理(proxy)
怎么实现整套aop机制的,都是通过代理
(8)织入(weaving)
把切面应用到的目标对象来创建新的代理对象的过程。spring采用的是运行时
关键就是:切点定义了哪些连接点会得到通知
6.Spring源码解析以及手写IOC(孙奇的重点)
BeanFactory等理解
Bean的生命周期
默认: bean的创建 – 》 bean的di注入(属性赋值)–》bean的初始化init-method --》bean的运行 --》bean的销毁destroy-method
bean的创建 – 》 bean的di注入(属性赋值)
—》XXXAware组件(可以获取到各种各种对象或者值)
```java
public class MyBean implements BeanNameAware{
private String beanId;
//可以获取或感知到bean的id的值
public void setBeanName(String beanName){
this.beanId = beanName;
}
}
```
<bean id="myBean01" class="org.lanqiao.bean.Mybean"></bean>
—》BeanPostProcessor(可以自定义在bean初始化前后处理一些功能)
public class MyBean implements BeanPostProcessor{
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//在bean初始化前要做什么。。。。。。
return bean;
}
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
//在bean初始化之后要做什么。。。。。。
return bean;
}
}
—》初始化bean,调用自定义init()
<bean id="" class="" init-method="aaa"/>
—>后置处理器BeanPostProcessor
—》使用bean 从容器中getBean()获取bean或者是使用@Autowired注解获取bean
—》假如实现了DisposableBean接口,调用接口的方法destory()
–>执行自定义的destroy
<bean id="" class="" destroy-method="bbb"/>
7.静态代理和动态代理的区别
动态代理 cglib和jdk动态代理区别
8.Spring aop的应用 - 声明式事务
事务:一组DML的操作
1.事务有四个特性:ACID
l 原子性(Atomicity):事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。
l 一致性(Consistency):一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏。
l 隔离性(Isolation):可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。
l 持久性(Durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。
2.事务的五个维度
事务属性可以理解成事务的一些基本配置,描述了事务策略如何应用到方法上。事务属性包含了5个方面
事务传播行为(事务嵌套当中)
事务的第一个方面是传播行为(propagation behavior)。当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。Spring定义了七种传播行为:
传播行为 | 含义 |
---|---|
PROPAGATION_REQUIRED | 表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务 |
PROPAGATION_SUPPORTS | 表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行 |
PROPAGATION_MANDATORY | 表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常 |
PROPAGATION_REQUIRED_NEW | 表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager |
PROPAGATION_NOT_SUPPORTED | 表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager |
PROPAGATION_NEVER | 表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常 |
PROPAGATION_NESTED | 表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。注意各厂商对这种传播行为的支持是有所差异的。可以参考资源管理器的文档来确认它们是否支持嵌套事务 |
public void a(){
insertTable1();
insertTable2();
}
public void b(){
insertTable3();
a();
}
事务隔离级别
事务的第二个维度就是隔离级别(isolation level)。隔离级别定义了一个事务可能受其他并发事务影响的程度。
**(1) 并发事务引起的问题 **
在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务。并发虽然是必须的,但可能会导致一下的问题。
l 脏读(Dirty reads)——脏读发生在一个事务读取了另一个事务改写但尚未提交的数据时。如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的。
解决办法:把数据库的事务隔离级别调整到READ_COMMITTED
l 不可重复读(Nonrepeatable read)——不可重复读发生在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间进行了更新。
解决 办法:把数据库的事务隔离级别调整到REPEATABLE_READ
l 幻读(Phantom read)——幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录。
解决办法:把数据库的事务隔离级别调整到SERIALIZABLE_READ
不可重复读与幻读的区别
不可重复读的重点是修改:
在同一事务当中, 你读取过的数据, 再次读取出来发现值不一样了
例如:在事务1中,Mary 读取了自己的工资为1000,操作并没有完成
con1 = getConnection(); select salary from employee empId =“Mary”;
在事务2中,这时财务人员修改了Mary的工资为2000,并提交了事务.
con2 = getConnection(); update employee set salary = 2000; con2.commit();
在事务1中,Mary 再次读取自己的工资时,工资变为了2000
//con1 select salary from employee empId =“Mary”;
在一个事务中前后两次读取的结果并不一致,导致了不可重复读。
幻读的重点在于新增或者删除:
在同一事务当中, 第1次和第2次读出来的记录数不一样
例如:目前工资为1000的员工有10人。事务1,读取所有工资为1000的员工。
con1 = getConnection(); Select * from employee where salary =1000;
共读取10条记录
这时另一个事务向employee表插入了一条员工记录,工资也为1000
con2 = getConnection(); Insert into employee(empId,salary) values(“Lili”,1000); con2.commit();
事务1再次读取所有工资为1000的员工
//con1 select * from employee where salary =1000;
共读取到了11条记录,这就产生了幻像读。
从总的结果来看, 似乎不可重复读和幻读都表现为两次读取的结果不一致。但如果你从控制的角度来看, 两者的区别就比较大。
对于前者, 只需要锁住满足条件的记录。
对于后者, 要锁住满足条件及其相近的记录。
(2)隔离级别
隔离级别 | 含义 |
---|---|
ISOLATION_DEFAULT | 使用后端数据库默认的隔离级别 |
ISOLATION_READ_UNCOMMITTED | 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读 |
ISOLATION_READ_COMMITTED | 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生 |
ISOLATION_REPEATABLE_READ | 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生 |
ISOLATION_SERIALIZABLE | 最高的隔离级别,完全服从ACID的隔离级别,确保阻止脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的 |
只读
事务的第三个特性是它是否为只读事务。如果事务只对后端的数据库进行该操作,数据库可以利用事务的只读特性来进行一些特定的优化。通过将事务设置为只读,你就可以给数据库一个机会,让它应用它认为合适的优化措施。
事务超时
为了使应用程序很好地运行,事务不能运行太长的时间。因为事务可能涉及对后端数据库的锁定,所以长时间的事务会不必要的占用数据库资源。事务超时就是事务的一个定时器,在特定时间内事务如果没有执行完毕,那么就会自动回滚,而不是一直等待其结束。
回滚规则
事务五边形的最后一个方面是一组规则,这些规则定义了哪些异常会导致事务回滚而哪些不会。默认情况下,事务只有遇到运行期异常时才会回滚,而在遇到检查型异常时不会回滚(这一行为与EJB的回滚行为是一致的)
但是你可以声明事务在遇到特定的检查型异常时像遇到运行期异常那样回滚。同样,你还可以声明事务遇到特定的异常不回滚,即使这些异常是运行期异常。
9.事务的概念(重点)
(1)事务的五大维度(传播行为、隔离规则、回滚规则、超时、只读)
(2)事务的传播机制的概念及常见的传播机制
(3)事务的隔离级别
(4)并发事务带来的问题
脏读、不可重复读、幻读概念和区别
解决方案:主要是通过设置不同的隔离级别
三、SpringMVC框架
1.SpringMVC的概念
2.SpringMVC的执行过程和原理(重点)
核心类:DispatcherServlet(需要在web.xml配置这个类,拦截所有需要后台处理的请求)
其他类: Controller 、 Handler 、 HandlerMapping、 HandlerAdapter 、 ModelAndView、ViewResolver等
以添加学生为例:
视图层 getStudent.html
<form action="/student/getStudent.do" method="get">
</form>
web.xml配置
<!-- 加载springmvc的核心控制类 DispatcherServlet-->
<servlet>
<servlet-name>ds</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!-- 加载一个springmvc的配置文件 -->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>ds</servlet-name>
<!-- 拦截所有的serlvet请求 -->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
DispatcherServlet会拦截/student/getStudent.do这个请求
@Controller
public class MyController{
@GetMapping("/student/getStudent")
public ModelAndView getStudent(Integer id){
ModelAndView mav = new ModelAndView();
mav.addObject("student",new Student(id,"张三"));
mav.addView("student_show");
return mav;
}
@GetMapping("/teacher/getTeacher")
public Teacher getTeacher(Integer id){
return new Teacher(id,"历史");
}
}
第一步、用户的请求/student/getStudent.do被DispatcherServlet拦截
第二步、会根据请求的url调用HandlerMapping(使用@RequestMapping,@GetMapping,@PostMapping等)
url地址与getMapping进行匹配,查询对应的Handler就是getStudent()这个方法,把这个方法返回
给DispatcherServlet
第三步、会去通过HandlerApater去调用Handler执行getStudent()方法
第四步、getStudent()会有返回值ModelAndView(返回值和返回的视图)
第五步、会根据返回ModelAndView的view值找到对应得视图解析器
springmvc得配置文件中:
<!-- 视图转换器 -->
<bean id="jspViewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/"/>
<property name="suffix" value=".jsp"/>
</bean>
给view中得字符串加上前缀和后缀student_show后变成 /WEB-INF/student_show.jsp
第五步、通过视图解析器解析后得地址找到对应得视图界面student_show.jsp(该界面可以获取到ModelAndView中model的数据),通过数据渲染返回给用户,用户可以通过浏览器显示出来
3.Spring和SpringMVC常用的注解
@Autowired @Resource @Controller @Service @Mapper @Respoistory @RequestMapping,@ResponseBody等
4.SpringMVC的拦截器和过滤器的区别
过滤器与拦截器的区别
过滤器,是在java web中,你传入的request,response提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet或者struts的 action进行业务逻辑,比如过滤掉非法url(不是login.do的地址请求,如果用户没有登陆都过滤掉),或者在传入servlet或者 struts的action前统一设置字符集,或者去除掉一些非法字符
拦截器,是在面向切面编程的就是在你的service或者一个方法,前调用一个方法,或者在方法后调用一个方法比如动态代理就是拦截器的简单实现,在你调用方法前打印出字符串(或者做其它业务逻辑的操作),也可以在你调用方法后打印出字符串,甚至在你抛出异常的时候做业务逻辑的操作。
拦截器interceptor与过滤器filter的区别 :
- 拦截器是基于java的反射机制的,而过滤器是基于函数回调。
- 拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
- 拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
- 拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
- 在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次
- 拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。
执行顺序 :过滤前 - 拦截前 - Action处理 - 拦截后 - 过滤后。过滤是一个横向的过程,首先把客户端提交的内容进行过滤(例如未登录用户不能访问内部页面的处理);过滤通过后,拦截器将检查用户提交数据的验证,做一些前期的数据处理,接着把处理后的数据发给对应的Action;Action处理完成返回后,拦截器还可以做其他过程(还没想到要做啥),再向上返回到过滤器的后续操作。
拦截器与过滤器使用场景:
SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。
1、日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等。
2、权限检查:如登录检测,进入处理器检测检测是否登录,如果没有直接返回到登录页面;
3、性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如apache可以自动记录);
4、通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个处理器都需要的即可使用拦截器实现。
5、OpenSessionInView:如hibernate,在进入处理器打开Session,在完成后关闭Session。
拦截器是AOP的一种实现,底层通过动态代理模式完成。