Java Web
ShiJh Lv3

从零开始学JAVA web,我学了些什么,这篇文章将会记录这一段学习历程。(暂告一段落)

JavaWeb-Spring

GitHub:Spring学习过程中创建的项目,包含一些例子

一​、🚶JavaSE

  • [x] Java语言基础内容,已经掌握

二、🐴JSP/Servlet

主要以理解Tomcat中Servlet的运行原理为重点

JSP

Jsp本质是servlet,约等于分不清前后端,耦合性太强,已经被时代抛弃,大概了解即可,直接跳过学习。

JSTL

JavaServer Pages Tag Library :JSP标准标签库

用于简化和替换jsp页面上的java 代码

Servlet

  • Servlet:

    注解声明@WebServlet("path")

    tomcat服务器的核心,主要是理解Http协议、HttpServletRequest以及HttpServletResponse原理。req可以进行转发forward(服务器内servlet间转发),resp可以进行重定向redirect(由浏览器发送第二次请求,可跨服务器)

  • Filter

    注解声明@WebFilter("path")

    过滤器,拦截request和response,可以对req和resp进行判断是否放行。

  • Listener

    注解声明@WebListener

    监听器,ServletContextListener监听ServletContext对象的创建(服务器启动)和销毁(服务器关闭),该接口主要用于加载资源和释放资源等操作。除此之外还有其他监听器。

  • ServletContext:

    服务器内的一个全局上下文,可以从servlet或req中获取(单例类)。作用是获取服务器真实路径getRealPath()、设置和获取服务器内共享的数据域set/getAttribute()和获取Content-typegetMimeType(filename),通过配置<context-param>可以用getInitParam()来加载资源文件

  • Cookie:保存于浏览器的数据,用来识别身份,保存sessionid等。可以设置存活周期。

  • Session:保存于服务器的数据,用来记录每个浏览器特定的信息,由一个id对应。可以设置存活周期,服务器关闭后会钝化,再次开启后活化

EL表达式

语法:${expression}

  1. 运算

    算数运算、比较运算、逻辑运算、判空运算empty

  2. 获取值

    从域对象中获取值${域名称.keyName}${keyName}

    域名称 说明
    pageScope 从pageContext获取
    requestScope 从request中获取
    sessionScope 从session中获取
    applicationScope 从ServletContext获取

ExtraDetail

这里记录在学习这个模块的过程中额外学习到的内容。

ProxyMode

代理模式,软件设计模式的一种,通过中间代理执行方法,来达到增强的目的。后续框架中经常使用该模式。

动态代理在Java中使用

class Proxy{
    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfacies, InvocationHandler h){}
}

其中InvocationHandler是抽象类,需实现

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {}

通过在该方法中调用method.invoke(obj,args)来执行被代理的对象的方法。可以修改返回值或者参数等手段来增强该方法,也可以在方法调用前和调用后增加其他类的方法。

实例化的对象即代理对象,通过强制转化为传入的某个接口可以直接调用接口方法,且是通过InvocationHandler执行。

MVC开发模式

  1. M: Model 模型 JavaBean

    完成业务逻辑操作,如查询数据库、封装数据

  2. V: View 视图 jsp

    展示数据

  3. C: Controller 控制器 servlet

    分发工作,获取客户端的输入然后调用模型,将数据交给视图。

三层架构

  1. 界面层(表示层 web)SpringMVC

    用户能够通过界面的组件和服务器进行交互,调用Service完成请求处理,转发jsp页面完成显示。

    • 控制器:servlet,接受用户请求并封装参数信息到Servic层
    • 视图:JSP,接受控制器提供的数据并展示出来给用户
  2. 业务逻辑层 (service) Spring

    处理业务逻辑,组合DAO层的简单方法,组成各种复杂的业务功能。

  3. 数据访问层 (dao) MyBatis

    操作数据存储文件,定义了对数据库最基本的CRUD操作

Bootstrap/JQuery/AJAX

皆为前端框架

  • Boostrap

    提供丰富的html标签和css样式,具有响应式布局,配合JQuery/Vue使用。

  • JQuery

    对js的封装,提供了选择器等许多丰富API来帮助编写js动态生成页面。

  • AJAX

    异步的JavaScript和XML,用于向服务器发送异步请求,可以在JQuery/Vue中便捷使用。

三​、🚲JDBC/SQL

两者是数据库的重要内容,SQL有MySQL、SQLserver等,JDBC则是Java操作数据库SQL的重要API,都应熟练掌握并理解原理。

MySQL

学会数据库增删查改等操作,熟练运用操控数据库的高级技巧,重点理解数据库原理,学会优化数据库。

事务

基本介绍

事务指的是一个包含多个步骤的业务操作,如果这个操作被事务管理,则要么同时成功,要么同时失败。事务可以提供回滚操作,撤销出错的影响。

  • START TRANSACTION 开启事务管理
  • ROLLBACK 回滚
  • COMMIT 提交事务

SELECT @@autocommit 查看是否为自动提交,修改后可以改变默认管理方式

四大特征
  • 原子性:数据库操作中不可分割的最小单位,要么同时成功,要么同时失败
  • 持久性:事务提交或回滚后,数据会持久化保存在数据库中
  • 隔离性:多个事务之间相互独立
  • 一致性:事务操作前后,数据总量不变
隔离级别

多个事务操作同一批数据会引发一些问题,通过设置隔离级别可以解决这些问题。

  • 脏读:一个事务,读取到另一个事务中没有提交的数据。
  • 不可重复读:同一个事务中,两次读取到的数据不一样(可能被另一个事务修改了)MySql-MVCC原理
  • 幻读:一个事务(同一个read view)在前后两次查询同一范围的时候,后一次查询看到了前一次查询没有看到的行。
级别 说明 安全性
read uncommitted 读未提交,允许读取未提交的数据 0
read committed 当前读、读已提交,只有已经提交的数据才能被读取 1
repeatable read 可重复读,使一个事务周期内的数据不受其他事务影响 2
serializable 串行化,锁定一张表,相当于单线程执行事务 3

从小到大安全性越来越高,效率越来越低!不同的数据库隔离级别原理不同,如MySql的MMVC,因此能解决的问题也不同

通过SELECT @@tx_isolation查询隔离级别,SET GLOBAL TRANSACTION ISOLATION LEVEL 级别名设置隔离级别

JDBC

JDBC是Java用来操作数据库的接口,具体实现由不同数据库公司实现,他的实现类被称为**“驱动”(Driver)**。

常见驱动类:

  • MySql:“com.mysql.cj.jdbc.Driver”
  • SqlServer:“com.microsoft.sqlserver.jdbc.SQLServerDriver”

使用方法

  1. 注册驱动

    /*
     通过该方法把类加载到内存
     驱动类来自每个不同的数据库公司,以下是mysql的驱动类
     该方法执行时,类的静态代码块会被执行,Driver会使用registerDriver方法注册DriverManager
     mysql5以上的版本可以省略这个注册过程
    */
    Class.forName("com.mysql.cj.jdbc.Driver");
  2. 获取数据库连接对象

    import java.sql.*;
    /*
     url: 数据库地址 jdbc:mysql://域名:端口/dbName
     user: 管理员用户名
     pwd: 管理员密码
    */
    Connection conn = DriverManager.getConnection(url,user,pwd);
  3. 定义sql语句String

  4. 获取执行sql对象

    import java.sql.*;
    //satement 可以执行sql语句
    Statement stat = conn.createStatement();
  5. 执行sql获取返回结果

    //sqlStr 为数据库执行语句,如DML(insert、update、delete)、DDL(create、alter、drop)
    //返回影响的行数:可以通过这个值判断DML语句是否执行成功
    stat.excuteUpdate(sqlStr);
  6. 释放资源

    //执行对象释放
    stat.close();
    //数据库连接释放
    conn.close();
    //结果对象释放
    resultSet.close();

常用对象详解

Connection:

  • 获取sql对象

    createStatement()preparedStatement(sqlStr)

  • 管理事务

    关闭自动提交事务的功能即可开启手动事务管理

    开启:setAutoCommit(false)、提交:commit()、回滚:rollback()

Statement:

  • excuteUpdate(sql):执行DML(insert、update、delete)、DDL(create、alter、drop),返回影响的行数
  • executeQuery(sql):执行DQL(select)语句,返回ResultSet

ResultSet:

  • next():移动游标到下一行,游标初始于第0行(表头),如果下一行没有数据则返回false

  • getXxx(int/str)

    获取当前行的数据,Xxx为数据类型名,返回类型为Xxx

    方法指定参数为int时,表示columIndex获取第几列的数据

    方法指定参数为str时,表示columName获取名为str的列的数据

PreparedStatement:

  • 解决SQL注入问题

    某些sql特殊关键字参与字符串拼接会造成安全性问题,如WHERE后存在恒等式则表示没有约束作用

  • 预编译sql

    sql语句的参数使用占位符,使用setXxx(pos, value)方法赋值,该方法传入占位符的位置(第几个)和值来进行sql参数补充,value类型为Xxx

  • excuteUpdate()

  • executeQuery()

事务管理

事务指的是一个包含多个步骤的业务操作,如果这个操作被事务管理,则要么同时成功,要么同时失败。

使用方法
try {
    conn.setAutoCommit(false); //open
    // TODO: do sql
    conn.commit(); //commit if no exception occur
} catch (Exception e){
    conn.rollback(); //rollback if has any exception 
}

rollback()执行时,刚刚执行的sql操作都会撤销。

数据库连接池

优化Connection操作,提高数据库访问性能

原理

使用连接对象容器管理Connection,从容器中获取,归还到容器中去,由容器管理是否断开连接。

实现方法

*Interface DataSource:*由Driver实现,Druid性能较高

  1. C3P0:ComboPooledDataSource

    使用classpath:c3p0.properties/c3p0-config.xml配置参数

  2. Druid:DruidDataSourceFactory

    使用druid.properties配置参数,需要手动加载。通常封装成工具类静态加载配置文件。

Spring JdbcTemplete

由Spring封装的JDBCUtils,简化操作。详细见下文

Junit4

Junit单元测试属于白盒测试的一种。

注解

注解 说明
@Before 在所有测试方法前执行
@After 在所有测试方法后执行,无论是否存在异常
@Test 标注方法为测试方法

测试方法

所有测试方法都是无参的,测试方法内最好不要直接输出数值,应该尽量使用assert断言结果于预期是否相符合。对于测试期间出现的异常应该尽量捕获并写入文件中,标明异常方法和异常原因。

Redis/Jedis

非关系型数据库,有缓存功能。。。

四、🚗SSM

JavaWeb框架集合之一,由Spring+SpringMVC+Mybatis组成,适用于轻量级微服务开发,要深入理解原理。

Spring

Spring框架属于Service层框架,但是也管理了Web层和DAO层

IOC

ioc容器

Bean容器,spring在内部创建Java类,我们或Spring可以从容器中取出Bean

XML式依赖注入

<bean id="" class=""></bean>对象的属性进行配置

分别为:对象注入ref、普通数据类型注入value、集合类型

  • 集合类型:配置文件使用<list><value><map><entry key="" value-ref=""><props><prop key="">组合使用

  • 构造方法:有参构造,配置文件中使用<constructor-arg name="" ref=""/>注入

  • set方法:配置文件中使用<property name="" ref=""/>子标签标签设置属性值注入

模块开发

<import resource="applicationContext-xxx.xml" />
注解式依赖注入
  1. 原始注解

    使用前须在配置文件中配置组件扫描,指定需要注解开发的包

    <context:component-scan base-package="">
        <!--用于排除不需要被扫描的类-->
    	<context:exclude-filter type="" expression="" />
    </context:component-scan>
    注解 说明
    @Component 类上实例化Bean,代替Bean标签
    @Controller web层类上实例化Bean
    @Respository dao层上实例化Bean
    @Service service层上实例化Bean
    @Autowired 依赖注入标签,加在成员变量/setter/构造方法
    @Qualifier 结合Autowired一起使用根据名称进行依赖注入
    @Resource 相当于前两者的结合,是javaEE的注解,不是Spring的注解
    @Value 注入普通属性(int等)
    @PostConstruct 声明Bean的初始化方法
    @PreDestroy 声明Bean的销毁方法
    @Scope 标志Bean的范围,singleton/prototype
  2. 新注解

    可以完全替代xml配置文件

    注解 说明
    @Configuration 指定类为配置类,创建容器时从该类上加载注解
    @ComponentScan 指定要扫描的包,同<context:componnet-scan>,使用excludeFilters排除不需要被扫描的类
    @Bean 标注方法的返回值储存于容器中
    @PropertySource 用于加载.properties文件的配置
    @Import 用于导入其他配置类

AOP

概念

面向切面编程,是动态代理的规范化编程,动态代理如何增强方法见上文。SpringAOP底层使用的动态代理为以下两种:

  1. Java原生接口代理

    通过接口反射出代理对象,使用的是 ProxyMode

  2. cglib子类代理

    外部的工具库,已经集成在springframework-core中,使用的是Enhancer类。通过指定父类反射出代理对象。

根据目标对象[1]是否有接口来选择其中一个方法。

XML配置动态代理

maven导入依赖aspectjweaverspring-aop

  1. 配置目标对象和切面对象Bean

    切面类和目标类都需要配置为Bean才能被Spring从IOC容器中取出来

  2. 配置织入(前置、后置……)

    使用命名空间xmlns:aop

    <aop:config>
     	<!--声明切面类-->   
    	<aop:aspect ref="beanName">
        	<!--配置切面:切点+通知-->
            <!--类似的还有aop:after等等-->
            <aop:before method="adviceName" pointcut="execution('方法声明')" />
        </aop:aspect>
    </aop:config>

    切面标签

    名称 标签 说明
    前置通知 aop:before
    后置通知 aop:after-returning
    环绕通知 aop:around 前后都执行,比前置更前,比后置更后
    异常抛出通知 aop:throwing 出现异常时执行
    最终通知 aop:after 最后执行,无论是否抛出异常

    ℹ️环绕通知方法需要携带参数ProceedingJoinPoint表示连接点[2]对象,要在方法中执行该对象的proceed()方法并return该方法的返回值。

    ⚠️环绕通知方法的返回值都为Object,表示被增强的方法会返回一个对象或者NULL,因此需要注意被增强的方法不能返回booleanint等基础类型,需要用对应的包装类替换!

  3. 切点表达式

    execution([修饰符] 返回值 包名.类名.方法(参数))

    • 修饰符可以省略(public、private……)

    • 可以用通配符(*)代表任意

    • 参数可以用..表示任意个数,任意类型的参数列表(可变参数)

    ex:

    execution(* cn.test.*.*(..)) //cn.test包下任意类的任意方法

    **表达式抽取:**替换point-cutpoint-cut-ref,引用<aop:pointcut id="" expression=""/>标签设定好的表达式id

    args()

    限制并自动抽取切点的方法参数,可以使用..表示缺省,一般配合通知方法获取方法参数,如:

    @Before("pointCut() && args(.., name)")
    public void before(String name) {}
    //表示pointCut限制下 要求“存在”参数【String name】
注解配置动态代理

在配置文件中添加<aop:aspect-autoproxy />自动代理aop

或者在配置类上用@EnableAspectJAutoProxy标注

注解 说明
@Aspect 标注为切面类
@Before 标志为前置通知,参数为execution表达式
@AfterReturning 标志为后置通知,参数为execution表达式
@Around 标志为环绕通知,参数为execution表达式
@AfterThrowing 标志为异常抛出通知,参数为execution表达式
@After 标志为最终通知,参数为execution表达式

**切点[3]表达式抽取:**在切面类中定义空实现方法,在方法上用@PointCut(’表达式‘)标注,引用时按照className.methodName()来描述

public class Aspect {
    @PointCut("excution(*.cn.shijh.*.*(..))")
    public void myPointCut(){}

    @Before("Aspect.myPointCut()")
    public void beforeEnhance(JoinPoint jp)    
}
// 所有通知方法都能使用JoinPoint作为参数,Spring自动注入

Spring事务控制

底层基于AOP实现事务管理,一般在Service层进行。

切点:Service方法

通知:事务管理


⚠️ ⚠️ ⚠️ 关于这个问题的详细说明

Spring事务管理与AOP之间存在神坑,被环绕通知的方法是无法进行事务控制的!!!!坑了我两三个小时!原因是事务管理本身就是环绕通知!!解决办法:设定Aspect和advisor的Order,且事务控制的Order要比环绕通知的大,否则无法完成事务回滚


编程式控制
  1. PlatfromTransactionManager

spring事务管理器的接口,依据不同Dao层技术使用不同的实现类。

  • DataSourceTransactionManager
 Jdbc or MyBatis 对应的实现类。
  1. TransacitonDefinition

事务的定义对象,封装事务的隔离级别、传播行为、超时时间等属性。

  • 隔离级别

    ISOLATION_DEFAULT:数据库默认级别

    ISOLATION_READ_UNCOMMITTED

    ISOLATION_READ_COMMITTED

    ISOLATION_REPEATABLE_READ

    ISOLATION_SERIALIZABLE

  • 传播行为

    REQUIRED:默认值,防止嵌套的两个事务发生,后一个事务会加入到前一个事务中。

    SUPPORTS:后一个事务将不会开启

    MANDATORY:强制加进入前一个事务中,没有前一个事务就报错

  1. TransactionStatus

    事务的运行状态对象。是否完成、是否回滚等状态。

XML声明式控制
<!--配置事务管理的增强方法-->
<tx:advice id="" transaction-manager="">
	<tx:attributes>
        <!--name可以使用通配符‘*’和字符串组合使用
			如‘update*’表示以update开头的所有方法名-->
    	<tx:method name="methodName" isolation="" propogation=""/>
    </tx:attributes>
</tx:advice>
<!--transactionManager需要注入到IOC容器中-->
<!--配置增强方法的织入,使用aop-->
<aop:config>
    <!--事务管理增强专用标签-->
	<aop:advisor advice-ref="tx:advice" pointcut="execution"/>	
</aop:config>
标签 说明
tx:advice 配置事务管理的通知,需指定transactionManager
tx:attributes 配置事务的属性,具体由多个tx:method配置
tx:method 指定一个方法名,配置一个业务方法的属性,包括隔离级别等
aop:advisor 用于事务管理的aop增强方法,引用配置好的tx:advice作为通知

配置事务属性时,只需指定方法名,不需要指定包名类名等,具体位置在织入时配置

注解声明式控制
注解 说明
@EnableTransactionManagement 在配置类中标注,表示开启spring事务管理
@Transactional 在方法或者类上标注,表示类下的方法都会被spring事务进行管理,可以加参数来配置事务的属性

使用注解配置时,driver会由spring自动配置,只需要将transactionManager放到IOC容器中

Spring-Junit

通过注解集成Junit

注解 说明
@RunWith 指定使用Spring内置的测试类
@ContextConfiguration 指定Spring配置文件、配置类
@AutoWired 从IOC容器中注入测试对象

Spring-JdbcTemplate

JDBC API的封装,使调用更加简单

maven需要导入依赖spring-jdbcspring-tx

常用方法

  1. setDataSource():设置连接池对象

  2. update()query():执行sql语句

  3. RowMapper<T>可以自动完成数据封装的接口,是query()的方法参数,对应实现类有 BeanPropertyRowMapper<T>,需要构造参数:

    new BeanPropertyRowMapper<JavaBean>(JavaBean.class)

    query()将返回List<T>类型的集合

  4. queryForObject()query()一样,但是只返回一个对象

  5. 查询简单对象只需要传入Class<T>

可以使用IOC容器来创建jdbcTemplate、dataSource对象,完成参数的注入。

Spring-web

用于集成web环境的maven依赖spring-web,提供获取应用上下文(ApplicationContext)的工具类和监听器。获取的原理是在服务器启动时创建上下文并放入ServletContext域中,即可在服务器共享。方便在 Servlet中获取应用上下文,且可以避免多次创建带来的性能开销。

  1. web.xml中配置监听器ContextLoaderListener

  2. 配置<context-param>指定applicationContext.xml/.class的路径

    <context-param>
    	<param-name>contextConfigLocation</param-name>
        <param-value>xx.xx.xx.ClassName</param-value>
    </context-param>
    <!--如果使用类配置则一定要指定下面的参数-->
    <context-param>
    	<param-name>contextClass</param-name>
        <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
    </context-param>
  3. 使用WebApplicationContextUtils获取上下文

SpringMVC

Spring+SpringMvc Demo

属于web层框架,提供轻量级的MVC开发 ,核心是用servlet实现的前端控制器。该控制器执行servlet的共有行为,并调用特有行为servlet,称为 Controller

组件解析

原理图解

组件详解
  • HandlerMapping

  • HandlerAdaptor

    执行对应的Controller,并把Controller的返回值包装成ModelAndView对象。

  • ModelAndView

    • setViewName()设置页面跳转的页面名称
    • addObject()设置request域中的参数
    • new ModelAndView(JsonView, ModelMap)用一个Map和JsonView对象创建Json数据视图
  • ViewResolver

    参考地址

    视图解析器,SpringMvc内部有很多解析器,默认使用的 InternalResourceViewResolver。主要作用是把一个ModelAndView解析为一个真正的视图。

    其中一种视图解析使用了一种拼接url的方式,内部规定了前缀(prefix)、后缀(suffix),与视图的名称,如"index",拼接后就得到视图URL了。url方式可以在ViewName中加上前缀指定,有"redirect:“和"forward:”,表示重定向和转发。视图解析器会把这些包装成View对象返回前端控制器。

  • View

    由解析器产生的对象,分为URL(jsp, jstl)、文档(excel, pdf)、报表、JSON。

XML配置

  1. web.xml中配置前端控制器DispatcherServlet

    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--设定SpringMVC配置文件的地址 使用类配置时要指定contextClass类型,同spring-web-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>配置文件地址</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
  2. 使用独立的spring-mvc.xml配置SpringMVC的各种属性

    • 配置组件,ViewResolver、HandleAdapter等

      <bean>标签配置,同Spring一样

  3. <mvc:annotation-driven> 自动配置一些组件的属性

注解配置

Spring新注解也通用于SpringMVC

注解 说明 参数
@EnableWebMvc 启用mvc注解驱动
@RequestMapping 请求映射地址,用于类或方法中 value:路径;method:请求方式;params:限定请求参数
@GetMapping Request的细分 value:路径
@PostMapping 同上 同上
@ResponseBody 标注返回值到content中
@RequestParam 取request的参数 value:所需参数的名称; require:强制要求?;defaultValue:默认值
@RequestBody 标注方法参数上,注入JSON请求参数
@PathVariable 标注方法参数上,用于restful风格的参数 value;require;defaultValue; 同RequestParam
@RequstHeader 获取请求头数据 value; require;

WebMvcConfigurer

Mvc配置类需要实现的接口,用于配置更多SpringMvc的属性,如资源解析、拦截器等。

注解配置web.xml

配置web.xml的类需要实现接口AbstractAnnotationConfigDispatcherServletInitializer,并实现其中方法,Spring会自动查找该实现类来代替web.xml

/**
 * 指定 ContextLoaderListener 所需的配置类
 */
@Override
protected Class<?>[] getRootConfigClasses() {}

/**
 * 指定mvc的核心配置类
 */
@Override
protected Class<?>[] getServletConfigClasses() {}

/**
 * 等同于设置 DispatcherServlet 的 url-pattern
 */
@Override
protected String[] getServletMappings() {}

/**
 * 配置过滤器
 */
@Override
protected Filter[] getServletFilters() {
    return new Filter[]{
        new CharacterEncodingFilter("UTF-8"),
    };
}

数据响应

服务的的数据响应分为页面跳转和回写数据。

页面跳转
  1. 直接返回字符串,如页面的名称"index",默认为转发

  2. 返回ModelAndView对象

    通过setViewName()指定页面名称,addObject()保存数据供JSP调用

    ModelAndView对象可以放在形参中,Spring会自动注入一个空的对象。

回写数据
  1. ModelAndView

    Json数据内容由LinkedHashMap转化而来,Json转化驱动可以是jackjsonfastjson,需要添加maven依赖。

    return new ModelAndView(new FastJsonJsonView(), map);
  2. 字符串

    方法上标注@ResponseBody表示返回值直接写入response的content。Json也可以作为字符串直接返回,使用json驱动转化。

  3. 对象或集合

    通过配置消息转换器,可以直接返回集合或者对象自动转化成Json字符串,同时要在方法上标注@ResponseBody

    • XML

      配置RequestMappingHandlerAdapterbean标签,把Json转化驱动注入messageConverters中。

    • 注解、类配置

      在Mvc配置类中继承WebMvcConfigurer接口,实现以下方法

      @Override
      public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
          // 此处配置FastJson
          FastJsonConfig config = new FastJsonConfig();
          FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
          config.setSerializerFeatures(
              SerializerFeature.DisableCircularReferenceDetect,
              SerializerFeature.WriteMapNullValue,
              SerializerFeature.WriteNullStringAsEmpty
          );
          converter.setFastJsonConfig(config);
          converters.add(converter);
      }

    *返回Map集合时,最好使用LinkedHashMap来保证Json内数据顺序不会乱掉

获得请求参数
  1. spring自动注入方法的参数中

    限制:基本类型、数组和 Javabea(POJO)。集合对象(List等)需要包装到Bean中才能自动注入。当方法参数名与请求参数名一致时不需要加@RequestParam

  2. @RequestBody直接接收request的参数数据

    注释在参数上,一般用于非表单的数据,如application/jsonapplication/xml

    用Json格式的数组作为请求参数,可以通过该标签直接注入到集合类型的方法参数中。

  3. 获得Restful风格的请求参数

    使用@PathVariable获取,具体使用如下

    @RequsetMapping("/api/{id}/{name}")
    public void testController(@PathVariable("id") int id, @PathVariable("name") Stirng name) {}
  4. 获取请求头

    使用@RequstHeader(name)在方法参数中注入。

    @CookieValue直接获取Cookie数据。

文件上传

Post Content类型为 Multipart/form-data时,需要借助第三方工具简化获取参数的过程。

导入maven依赖 commons-iocommons-fileupload

SpringMvc获取到上传的文件时,会打包成MulitpartFile对象,因此用于获取文件的Controller方法需要有此类型的参数。

MultipartFile

  • getOriginFilename()获取文件名
  • transferTo(File)通过IO保存本地或其他服务器
自定义类型转换器

SpringMvc从Request中获取参数时会使用Converter进行类型转化来匹配Controller方法中的参数类型。

实现 Convert<S,D> 接口,并配置到SpringMvc中。接口泛型分别为转换前类型转换后类型

<bean id="" class="org.springframework.context.support.ConversionServiceFactoryBean">
	<property name="converters">
    	<list>
        	<bean id="" class=""></bean>
        </list>
    </property>
</bean>
<mvc:annotation-driven conversion-service=""/>

静态资源访问

当 spring-mvc 的 dispatcher-servlet 路径配置为/,访问资源时也会去寻找Handler,无法访问到资源文件。解决方法有两种,第一种是配置资源文件的映射,第二种是交由tomcat来寻找资源文件。

  • XML配置

    <!--1.配置资源文件的映射-->
    <mvc:resource mapping="url" location="path" />
    <!--2.启用默认servlet-handler管理资源文件-->
    <mvc:default-servlet-handler />
  • 注解、类配置

    在Mvc配置类中继承WebMvcConfigurer接口,实现以下方法

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

Session管理

SpringMvc可以使用注解对Session进行一些简单的管理操作,如向session域中存入数据等。

  1. 方法一:

    在Controller方法中使用HttpSession对象作为参数,Spring会自动注入。HttpSession能够同时完成添加、删除、查询Attribute的功能。

  2. 方法二:

    在Controller类上使用@SessionAttributes并指定参数为该类需要使用的AttributeNames,然后方法上的参数就可以使用@ModelAttribute(name)注入Session域的数据。在方法参数中使用Model类型,Spring会把该类的所有Session注入到Model对象中,因此也能够获取Session中的值,同时能够向Session中添加值。需要注意的是,使用该方法清除Session时,需要使用SessionStatus(作为方法参数,spring自动注入)的SetComplete()来清除,并且只会清除@SessionAttributes中标记的键值对。

Spring官方文档中提出,对于用户登录验证的session最好使用方法一,实际上我们会使用安全框架来完成这个功能,如 Shiro

拦截器 Interceptor

与Filter过滤器类似,用于预处理和后处理,原理为AOP只对控制器方法进行拦截,不拦截资源访问

自定义拦截器

实现HandlerInterceptor接口,并进行配置。

  • 类配置

    @Override
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(interceptor).addPathPatterns("/");
    }
  • xml配置

    <mvc:interceptors>
    	<mvc:interceptor>
            <mvc:mapping path=""/>
        	<bean/>
        </mvc:interceptor>
    </mvc:interceptors>
  • HandlerInterceptor

    // 执行前拦截
    public boolean preHandle(req, resp, handler);	
    // 执行后,返回前拦截
    public void postHandle(req,resp, handler, modelAndView);
    // 流程完毕后拦截
    public void afterCompletion(req, resp, handler, exception)
    1. preHandle返回为 True 时,后续操作才能执行。
    2. Handler为控制器方法执行对象(Method)
拦截器执行顺序

多个拦截器执行时,并非串行,而是嵌套的,也就是说拦截器执行链最后执行的方法时第一个拦截器的afterCompletion。执行的顺序与配置的顺序有关,先配置先执行。

异常处理机制

把异常处理抽取出来,通过异常处理组件统一解决,在SpringMvc中异常处理可以由DispatcherServlet调用响应组件处理。

异常处理器

HandlerExceptionResolver

  • SimpleMappingExceptionResolver

    框架自带的实现类。Mapping——发生异常时跳转到异常视图,映射关系通过配置完成。

    <bean class="org.xx.x.SimpleMappingExceptionResolver">
    	<property name="defalutErrorView" vlaue=""/>
        <property name="exceptionMappings">
        	<map>
            	<entry key="" value=""/>
            </map>
        </property>
    </bean>
自定义异常处理器
  • 异常处理方法

    使用注解@ExceptionHanlder标注在方法上时,该方法为所在类的异常处理方法。

  • 异常处理类

    使用注解@ControllerAdvice标注在类上时,该类为全局的异常处理类。类内的方法使用@ExceptionHandler标注的方法将用于处理发生的异常。

注解 说明 参数
@ExceptionHanlder 用于方法上,异常处理方法 可以指定异常类,表示只处理个别异常
@ControllerAdvice 用于类上,全局的异常处理类

*异常处理方法,会被当做一个Controller处理,因此其返回值最终会包装成ModelAndView对象,所以可以使用 @ResponseBody注解

在Junit中使用MockMvc

由于SpringMvc面向web层,因此无法直接调用Controller组件进行测试。

使用方法
  1. 在Junit测试类上注解@WebAppConfiguration引入Web层配置文件
  2. 使用IOC注入WebApplicationContext对象
  3. 使用MockMvcBuilders创建MockMvc对象,需要传入WebApplicationContext初始化
  4. 使用MockMvc进行测试
MockMvc

MockMvc主要使用perform()方法向Controller发出请求,无需启动Tomcat服务器。perfrom方法的参数由MockMvcRequestBuilders创建,并返回一个ResultActions对象。

  • MockMvcRequestBuilders:创建一个模拟请求,有get或post类型,能够设置请求体和请求头参数
  • ResultActions:通常使用andDo方法输出请求和响应的所有信息,执行andReturn返回MvcResult结果对象
  • MvcResult:结果对象,包含request和response信息

测试样例:

@Test
public void test3() throws Exception{
    MvcResult res = mockMvc.perform(
        MockMvcRequestBuilders
        .post("/user/login")
        .contentType(MediaType.APPLICATION_FORM_URLENCODED)
        .characterEncoding("UTF-8")
        .param("userName", "zhangsan")
        .param("password", "123")
    ).andDo(MockMvcResultHandlers.print()).andReturn();
    System.out.println(res.getResponse().getContentAsString());
}

Mybatis-Spring

前置-关于Mybatis的详细内容:Mybatis原始教程

Maven:mybatis-spring

IOC注入

将Mybatis的SqlSessionFactory等对象配置到SpringIOC容器中。

  1. 配置SqlSessionFactory,主要是配置其数据源dataSource和核心配置文件configLocation(非必须)

  2. mapper组件扫描

    注解方式:

    • @MapperScan(basepackage="")

    简洁方式:

    • <mybatis:scan base-package="" />,这个方式不需要额外注解,但是也支持配置自定义注解来限制其扫描范围,指定annotation为自定义的注解即可。以上两种方法将映射器(DAO)z注入到SpringIOC时,默认使用首字母小写作为Bean的名称,可以在自定义注解中设置org.springframework.stereotype.Component 这样的注解既可以作为标记,也可以作为一个名字提供器来使用了。

    经典方式(不推荐使用了):

    • <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
      	<property name="basePackage" value="xx.xx"/>
      </bean>
      

配置完毕后即可使用依赖注入直接创建DAO/Mapper接口对象。

事务控制

配置事务管理器transactionManager和事务增强,同样可以使用注解,同上文。配置完成后就可以像使用JdbcTempelate一样进行事务管理。

ExtraDetail

Properties/ResourceBundle

properties是一种键值对key=value储存文件,Java中自带的Properties类可以简单的读写properties文件,但是ResourceBundle类更好的支持了不同的语言,比前者要好一点。

Restful

Restful是一种软件架构、设计风格,提供了一些原则和约束,可以帮助软件设计变得更加简介有层次,更易于实现缓存机制等。Restful风格表现为“url+请求方式”来表现一次HTTP请求,特点是请求参数用/接在url后面,而不再使用?来表示请求参数。如:http://url/api?id=1使用restful风格后应该是http://url/api/1

Restful规定请求方式具有如下几个含义:

  • GET 表示获取
  • POST 表示新建
  • PUT 表示更新
  • DELETE 表示删除

在前端中,form表单形式的请求是不支持PUT/DELETE的,需要使用隐藏的参数配合后端Filter完成请求映射,在spring-mvc使用自带的HiddenHttpMethodFilter来完成这个功能,通过指定隐藏参数的名自动从前端请求中获取Method值,但是前端显式的Method必须是"POST"形式。

FastJson

由阿里巴巴开发的Json转化驱动。其中FastJsonHttpMessageConverter可以用于SpringMvc的消息转换器中。

  • @JSONType: 可以标注在类上,通过指定orders参数可以决定转化该类时解析属性的顺序。
  • @JSONFiled: 标注在成员变量上,可以指定转化后名称以及转化顺序
  • @ResponseJSONP:标注在Controller类或方法上,并配置Bean:JsonpResponseAdvice,可实现注解方法返回Jsonp格式的数据,格式头取决于前端参数 callback,也可通过注解参数重新指定。(中文乱码问题)

  1. 被代理的类 ↩︎

  2. 目标对象被增强的方法 ↩︎

  3. pointCut 目标对象的方法 ↩︎

 评论