1.Spring
Spring是一系列框架
Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架。
Spring核心,SpringMVC,Spring Boot
Spring核心:DI,IOC,AOP,容器context
SpringMVC代替了Servlet (SSM,Spring+SpringMVC+MyBatis)
(SSH , Spring+Struts+Hibernate)
SpringMVC目前技术比较成熟
2. 核心
容器,DI,IOC,AOP
Spring是一个容器,它里面有很多对象(框架自身的对象和我们的业务对象)
SSM或SSH组合中,Spring作为一个容器给其他框架提供对象。Spring在框架组合中起的是整合作用。
DI 叫依赖注入 DI Dependency Injection
一种设计思想,面向接口开发编程。在代码中通过接口定义一个变量,而不给变量赋值,在程序运行过程中,通过框架给这个变量赋值,让变量可以工作,如果变量没有通过框架赋值,则这个变量是空指针,无法正常工作,依赖框架给他注入实例。
IOC 控制反转 Inversion of Control
配合DI,程序的运行状态和结果通过框架来框架来控制,正常情况下是程序自己控制自己的流程,现在控制交给由外部的框架来决定,就是控制反转。
3.Spring 思想
三大思想:控制反转IOC 依赖注入DI 和面向切面AOP
1.控制反转
1.控制反转是一种设计模式
2.控制反转的主要形式有两种:依赖查找和依赖注入
依赖查找:容器提供回调接口和上下文件给组件。EJB和Apache Avalon 都用这种方式。 这样一来,组件就必须使用容器提供的API来查找资源和协作对象,仅有的控制反转只体现在哪些回调方法上。
依赖注入:组件不做定位查询,只提供普通的Java方法让容器去决定依赖关系。
把每个Servlet的访问路径具体绑定起来,当我访问每个路径,就会对应的某个方法上。
作为一个容器,是要控制组件。
4. 技术
Lombock,Logback
1.Lombock
1.1 引入依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
1.2 引入作用
Lombock 提高开发效率,自动生成Get/Set等方法。安装一个插件,这个插件自动生成class文件内容。
@Data
public class College {
/**
* 主键
**/
private int id;
/**
* 名称
**/
private String name;
/**
* 编码
**/
private String code;
}
给类添加lombok
@Date注解 自动生成 equals、 canEqual 、hashCode、get/set、ToString
@AllArgsConstructor 生成带有参数的构造函数 public College(int id, String name, String code)
@ToString(exclude = {"code"}) 生成ToString 并且除了code这个属性
在日常开发中,我们尽量少直接使用@Data注解
而是换成@Setter、@Getter、@NoArgsConstructor、@AllArgsConstructor、@ToString即可
2. Logback
2.1 引入依赖
负责记录日志的一个插件
<!-- logback 依赖 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3<ersion>
</dependency>
2.2 作用
2.3 logback对比log4j的优点
- 更快的实现:Logback的内核重写了,不仅性能提升了,初始化内存加载也更小了。
- Logback-classic非常自然实现了SLF4j:Logback-classic实现了SLF4j。切换到log4j或者其他,只需要提供成另一个jar包就OK,根本不需要去动那些通过SLF4JAPI实现的代码。
自动重新加载配置文件,当配置文件修改了,Logback-classic能自动重新加载配置文件。 - Lilith是log事件的观察者,和log4j的chainsaw类似。而lilith还能处理大数量的log数据 。
- 在谨慎模式下,多个FileAppender实例跑在多个JVM下,能 够安全地写道同一个日志文件。FileAppender和它的子类包括 RollingFileAppender能够非常友好地从I/O异常中恢复。
- 配置文件可以处理不同的情况,一个配置文件就可以适应多个环境(开发,测试,生产)。
- Filters(过滤器),可以对特定的的业务设置不同的日志级别。要实现这个功能只需加4行XML配置。
- SiftingAppender(一个非常多功能的Appender):它可以用来分割日志文件根据任何一个给定的运行参数。
- 自动压缩已经打出来的log:RollingFileAppender在产生新文件的时候,会自动异步压缩已经打出来的日志文件。
- 堆栈树带有包版本:Logback在打出堆栈树日志时,会带上包的数据。
- 自动去除旧的日志文件:通过设置TimeBasedRollingPolicy或者 SizeAndTimeBasedFNATP的maxHistory属性,你可以控制已经产生日志文件的最大数量。
5. 容器 context
Spring有两种配置方式 一种是xml 一种是注解
1. 配置方式
-
xml
在resources下新建applicationContext.xml文件
-
注解
打开注解的开关,指明框架要扫描的包
Spring的注解很多,@Component组件注解是一个帆帆的总称,可以代替别的一些注解,比如:@Controller,@Service等注解
ApplicationContext.xml配置
<!--打开注解扫描开关-->
<context:annotation-config/>
<!-- 扫描路径-->
<context:component-scan base-package="com.ychs"/>
测试代码
public static void main(String[] args) {
String config = "_applicationContext.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(config);
College college = (College) context.getBean("college");
logger.debug("college=" + college);
}
2. 引入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.0</version>
</dependency>
6. AOP 面向切面编程
Aspect Oriented Programming 面向切面编程
目标是通过定义一组共同的接口和组件来促进AOP的使用以及不同的AOP实现之间的互用性
面向切面是一个概念,通常用来为许多其他的类提供相同的服务,而且是固定的服务,是独立的。提升了代码的复用性,减少了代码的耦合度,减轻程序员的工作负担,把程序的重心放在了核心的逻辑上。
它是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。
应用场景有日志记录,性能统计,安全控制,事务处理,异常处理等。
eg:
1.系统没有任何日志的记录,现在给每个业务层的方法添加日志的记录。
解决方案
建一个类,再给类中通过AOP的技术实现统一日志的记录。
原理:Spring 把切面中新加的功能自动加到原有的业务类中,生成一个新的代理对象,这个代理对象是两个类的功能的总和。
2.给系统中的业务类增加统计的方法,那个用户在什么时间,操作了什么方法,参数是什么,结果是什么?
前提 拿到需要的数据
AOP可以做很多事,在获取程序的工作进程后,自己设计任何场景。
日志,统计,审核,安全控制,事务处理(SSM整合),异常处理等。这些附加的功能就是在AOP的切面类中完成。切面是一个自定义的类。
1.AOP的配置
引入依赖
<!-- 1.引入Lombok 自动生成class文件内容
<scope>provided</scope>
只在项目开发中使用它,在发布时候不需要它 scope
-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
<!-- 2. 引入Logback 记录日志-->
<!-- logback 依赖 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- 3. 引入context 容器-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.0</version>
</dependency>
<!-- 4.导入aop依赖-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.9</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
<scope>runtime</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
xml配置
- aspet 切面
- pointcut 切点(织入点的集合,是一个表达式)
- joinpoint 织入点 (具体的某个具体的业务方法,如:add,search…)
- advice 通知:就是AOP附加功能的方法 MuAop类的before /after方法
- adviser 顾问:是advice方法执行的时机(前,后,环绕,异常后)
- adviser 为after时,业务方法除了异常依然执行advice。
- 当adviser为after-returning时 除了异常则不在向下运行
- 当adviser为after-throwing时 当发生异常时 才会执行
- 当adviser为around时,在业务方法前后都能做处理
- Spring通过切点(表达式的信息)获取到所有的织入点信息,进而监听织入点的执行情况,把切面中的附加功能加入到这些织入点中。
<!-- AOP的配置-->
<aop:config>
<!-- 指定AOP的切面对象 AOP的切面-->
<aop:aspect ref="myAOP">
<!-- 切点-->
<!-- 1. 第一个* 代表任意返回值
2. 第二个* 代表任意类名
3. 第三个* 任意方法明
4. (..)两点代表任意参数-->
<aop:pointcut id="pc" expression="execution(* com.ychs.Service.*.*(..))"/>
</aop:aspect>
</aop:config>
@Service //注解 会自动把当前类名首字母小写,作为容器中的名称
// 也可以通过@Service(value="xxx")来手动指定名称
advice 通知:就是before /after方法
* 描述: 切面类
*
* @author yk
* @version 1.0
* @date
*/
public class MyAop {
private Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 在业务方法执行前打印日志
**/
private void before(){
logger.debug("service method before...");
}
public void after() {
logger.debug("service method after....");
}
}
注解配置
<!--打开注解扫描开关-->
<context:annotation-config/>
<!-- 扫描路径-->
<context:component-scan base-package="com.ychs"/>
<!-- Aop 开关-->
<aop:aspectj-autoproxy/>
LogAop方法
//添加组件
@Component
//配置切面
@Aspect
public class LogAop {
private Logger logger = LoggerFactory.getLogger(this.getClass());
// 配置织点
@Pointcut(value = "execution(* com.ychs.Service.*.*(..))")
public void pc(){}
// 通知
/**
* 在业务方法执行前打印日志
**/
//配置 方法
@Before("pc()")
private void before(){
logger.debug("service method before...");
}
@After("pc()")
public void after() {
logger.debug("service method after....");
}
@AfterReturning("pc()")
public void afterReturn(){
logger.debug("service method afterReturn ");
}
@AfterThrowing("pc()")
public void afterThrow() throws Throwable {
logger.debug("service method afterThrow ");
}
/**
* 在业务方法的前后做处理
* @Param [joinPoint] 是具体业务方法被封装后的对象
* @return java.lang.Object
**/
@Around("pc()")
public Object around(ProceedingJoinPoint joinPoint) {
String className = joinPoint.getTarget().getClass().getName();
String method = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
logger.debug("enter" + className + "." + method);
logger.debug("args=" + Arrays.toString(args));
try {
Result result = (Result) joinPoint.proceed(args);
logger.debug("result=" + result);
return result;
} catch (Throwable throwable) {
logger.error("occur exception;", throwable);
return new Result(null,"proceed failure");
}
}
}
7.SpringMVC
他有一个核心的Servlet,所有请求都会进入这个核心Servlet。根据请求的路径,核心Servlet把请求分发到不同的Controller方法中,Controller类似于Servlet。一个路径对应一个方法。
核心Servlet是Spring实现的一个类,是框架的类,需要配置在web.xml中。
需要有一个以核心Servlet命名的Spring配置文件。
SpringMVC对Controller方法返回的字符串做相应处理。Controller方法返回的字符串是jsp的名称或另一个请求的路径。
Controller方法会自动把请求的参数打包成一个bean对象。
SpringMVC可以完成后台数据的校验,文件的上传下载,国际化的显示,REST风格的处理,统一异常的处理。
1. 添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>Spring02</groupId>
<artifactId>Spring02</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>Spring02 Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<spring-version>5.3.0</spring-version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- Lombok依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
<!-- Logback依赖-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- SpringMVC依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring-version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.0</version>
</dependency>
</dependencies>
<build>
<finalName>Spring02</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
2. 在WEB-INF下创建 mvc-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 打开注解开关-->
<mvc:annotation-driven/>
<!-- 解决返回名称的是否解决方案-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 指定 jsp的路径-->
<property name="prefix" value="/WEB-INF/page"/>
<!-- 视图的后缀-->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
凡是WEB-INF的页面 都不允许直接访问,重定向也不行,只能转发。
3. web.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>mvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
8.静态资源
resources目录,存放静态资源 如css,js,jsp,gif,ico等等。
在SpringMVC中配置此目录,这样对该目录请求不会进入核心Servlet。登录过滤器就只要能看路径带有resources就放行
9.传参
以college为例
1.首先需要控制器 + 实体类(使用lomback中@Date可以自动生成)
@Controller
@RequestMapping("/college")
public class CollegeController {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@PostMapping("/add")
public String add(College college){
logger.debug("enter add");
logger.debug("college=" + college);
return "collegeManager";
}
}
2.在WEB-INF中配置mvc-servlet.xml 添加jsp的指定路径 访问时只需要通过控制器访问页面的名字即可
!-- 打开注解开关-->
<mvc:annotation-driven/>
<contex:component-scan base-package="com.ychs"/>
<!-- 配置静态资源路径-->
<mvc:resources mapping="/resources/**"
location="/resources/"/>
<!-- 解决返回名称的是否解决方案-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 指定 jsp的路径-->
<property name="prefix" value="/WEB-INF/page/"/>
<!-- 视图的后缀-->
<property name="suffix" value=".jsp"/>
</bean>
3.静态资源不可以放入动态加载中,所以修改路径 api/init
4.优化:在多页面下,我们需要一个类似于接口的页面 用来将需要的文件引入,避免重复多次修改
方法:先创建一个head.jsp 添加引入的路径
在其他页面中引入<%@include file=“head.jsp” %>
10. Page页面传参(modifyPage)
1. 方法
首先 要注意:
1. 修改页面 需要两个方法 第一个时modifyPage 进入修改页面
2. 当进入修改页面后,会给前端jsp页面返回参数(通过Map map)也可以通过modle回传
3. 然后在进入 modify方法,进行修改页面
代码如下:
CollegeController.java
// 在管理界面 通过get方式 进行传送 进入修改界面
@GetMapping("/modifyPage")
public String modifyPage(int id , Map map){
logger.debug("id=" + id);
College college = service.searchById(id);
logger.debug("college=" + college);
if (college != null) {
// 通过map把college带到修改页面,类似于request.setAttribute
map.put("college", college);
return "collegeModify";
}else {
return "error";
}
}
// 进入修改页面后 通过post方式传参 进行修改
@PostMapping("/modify")
public String modify(College college){
logger.debug("enter college modify" );
logger.debug("college=" + college);
boolean result = service.modify(college);
if (result){
return "collegeManager";
}else {
return "error";
}
}
collegeManager.jsp
content: 'college/modifyPage?id='+data.id,
该段代码意思是 进入modifyPage 的方法 并传送id controller 进入后 返回collegeModify页面
collegeModify.jsp
使用Spring回传时 需要过滤器 filter=“formFilter”
// html
<form class="layui-form" action="college/modify" method="post" lay-filter="formFilter">
// js
form.val('formFilter',{
id:'${college.id}',
name:'${college.name}',
code:'${college.code}'
})
注意:
id为隐藏域中也需要书写,因为 回传时 传回 如果不拿到,则 id会默认为0
<input type="hidden" name="id" >
11. 后端验证
主要使用 hibernate-validator 和 javax.validation这两个进行后端验证
1. Maven依赖
<!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.2.0.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
2. 注解意思
Spring的相关注解
注解 | 类型 | 描述 |
---|---|---|
@AssertFalse | Boolean,boolean | 验证注解的元素值是false |
@AssertTrue | Boolean,boolean | 验证注解的元素值是true |
@NotNull | 任意类型 | 验证注解的元素值不是null |
@Null | 任意类型 | 验证注解的元素值是null |
@Min(value=值) | 任何Number或CharSequence(存储的是数字)子类型 | 验证注解的元素值大于等于@Min指定的value值 |
@Max(value=值) | 和@Min要求一样 | 验证注解的元素值小于等于@Max指定的value值 |
@DecimalMin(value=值) | 和@Min要求一样 | 验证注解的元素值大于等于@ DecimalMin指定的value值 |
@DecimalMax(value=值) | 和@Min要求一样 | 验证注解的元素值小于等于@ DecimalMax指定的value值 |
@Digits(integer=整数位数, fraction=小数位数) | 和@Min要求一样 | 验证注解的元素值的整数位数和小数位数上限 |
@Size(min=下限, max=上限) | 字符串、Collection、Map、数组等 | 元素值的在min和max(包含)指定区间之内,如字符长度、集合大小 |
@Past | Date,Calendar;Time类库的日期类型 | 验证注解的元素值(日期类型)比当前时间早 |
@Future | 与@Past要求一样 | 验证注解的元素值(日期类型)比当前时间晚 |
@NotBlank | CharSequence子类型 | 验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的首位空格 |
@Length(min=下限, max=上限) | CharSequence子类型 | 验证注解的元素值长度在min和max区间内 |
@NotEmpty | 值不为null且不为空 | (字符串长度不为0、集合大小不为0) |
@Range(min=最小值, max=最大值) | 原子类型和包装类型 | 验证注解的元素值在最小值和最大值之间 |
@Email(regexp=正则表达式,flag=标志的模式) | CharSequence子类型(如String) | 验证注解的元素值是Email,也可以通过regexp和flag指定自定义的email格式 |
@Pattern(regexp=正则表达式,flag=标志的模式) | String,任何子类型验证 | 注解的元素值与指定的正则表达式匹配 |
@Valid | 任何非原子类型 | 指定递归验证关联的对象,如用户对象中有个地址对象属性,如果想在验证用户对象时一起验证地址对象的话,在地址对象上加@Valid注解即可级联验证 |
3. 注解添加方式 是 添加到entiey/类中 用于进行后端验证
package com.ychs.entity;
import lombok.Data;
import org.hibernate.validator.constraints.Range;
import javax.validation.constraints.*;
@Data
public class College {
/**
* 编号
**/
private int id;
/**
* 编码
**/
@Range(min = 1,max = 20,message = "[1-20]之间的一个数字")
private int code;
/**
* 姓名
**/
// @Min(value = 2,message="最小2位,最大20位") 对数字值的校验
// @Max(value = 20,message = "最小2位,最大20位")
@NotEmpty(message = "名称不能为空")
@Size(min = 2,max = 20,message = "最小2位,最大20位")
private String name;
}
// jsp 返回错误信息
<span class="errorMsg">${name}</span>
// CollegeController
@PostMapping("/add")
public String add(@Validated College college, BindingResult br, Map map){
// 测试异常处理机制
// int num = 1/0;
logger.debug("enter add");
// 校验不通过 会有错误信息
if (br.hasFieldErrors()){
// 字段校验出错
List<FieldError> errors = br.getFieldErrors();
for (FieldError err : errors){
// 把字段校验错误带到页面
if (err.getDefaultMessage().contains("NumberFormatException")){
map.put(err.getField(),"必须是有效数字");
map.put(err.getField()+"value",err.getRejectedValue());
}else {
map.put(err.getField(),err.getDefaultMessage());
}
map.put("college",college);
}
return "collegeAdd";
}
logger.debug("college=" + college);
boolean result = service.add(college);
if (result){
return "collegeManager";
}else{
return "error";
}
}
注意
(@Validated College college, BindingResult br,Map map) 注解 与 BindingResult br 必须连续使用才有作用
12. 标准的后端 增删改查
13. 面试题
1.拦截器与过滤器的区别,在理解/ 和 /* 的区别
过滤器:
依赖于servlet容器。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能子啊容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作,获取我们想要获取的数据,比如:在过滤器中修改字符编码,在过滤器中修改HttpServletRequest的一些参数,包括:过滤低俗文字、危险字符等。
拦截器:
依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。在实现上基于Java的反射机制,属于切面编程(AOP)的一种运用,由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入DI进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。
但是缺点是只能对controller请求进行拦截,对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理。
配置web.xml,/和/*的区别:上面说的只能拦截controller,所以jsp路径不能一起拦截,会认为它是controller,就出错了。/不会匹配到.jsp,只针对我们编写的请求;即:.jsp不会进入spring的DispatcherServlet类。/会匹配 *.jsp,会出现返回jsp视图时再次进入Spring的DispatcherServlet类,导致找不到对应的controller所以报404错
2. 重定向的作用
在进入主页面之前,它会先进行一次判断,判断session中是否存在有数据,如果有,证明该用户已经登录过了,如果没有,说明还没有登录,它便会立马重定向回到登录页面,直到把登录的信息填写完整才能进入主页面。
就目前我知道的重定向的方法就有两种,redirect()和redirect To Action(),一种是上图所示红色下划线标明的那种重定向回完整路径的方法,另一种便是下面这种仅仅重定向回指定的某个Action方法,该种方法一般只能在同一个控制器下才能实现重定向。
3. post 和 get的区别
GET和POST的相同点:
GET和POST是HTTP请求的两种方法,
GET和POST都是TCP链接
标准答案”:
-
GET在浏览器回退时是无害的,而POST会再次提交请求。
-
GET产生的URL地址可以被Bookmark,而POST不可以。
-
GET请求会被浏览器主动cache(缓存),而POST不会,除非手动设置。
-
GET请求只能进行url编码,而POST支持多种编码方式。
-
GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
-
GET请求在URL中传送的参数是有长度限制的,而POST么有。
-
对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
-
GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
-
GET参数通过URL传递,POST放在Request body中。
-
GET和POST都是TCP链接。GET产生一个TCP数据包;而POST会产生两个TCP数据包。
强烈推荐:原文链接:https://blog.csdn.net/weixin_44251129/article/details/89522484
4. ajax 无法测试转发 和重定向
5.restController 和 Controller的区别
14. SpringMVC 的思想
现在MVC框架已经有很多了,Struts、Webwork,新兴的MVC框架有Spring MVC、JSF等等,本文主要介绍Spring MVC的设计理念
Struts1.0是基于webwork的MVC框架,里面有很多类的继承关系,耦合性太高,之后推出了struts2.0,但是这个MVC框架对于视图的解析还是存在一些小的不妥之处,所以产生了Spring MVC
SpringMVC的请求相应步骤如下
1、用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获
2、DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回
3、DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(…)方法)
4、提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作
HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
1.数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
2.数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
3.数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
5、Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象
6、根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet
7、ViewResolver 结合Model和View,来渲染视图
8、将渲染结果返回给客户端
原文链接:https://blog.csdn.net/qq_38449518/article/details/81545578
15. 自定义异常MyHandler 和 Spring异常
1.MyHandler 自定义异常
@Component
public class MyHandler implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
ModelAndView view = new ModelAndView("error");
httpServletRequest.setAttribute("exception",e);
return view;
}
}
2. Spring自带异常
注意 配置 Spring时 要以 bean位一组
//在 mvc-servlet.xml配置
!-- Spring 自动处理机制-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!-- 起名字 e-->
<property name="exceptionAttribute" value="e"></property>
<property name="exceptionMappings">
<props>
<prop key="java.lang.Exception">error</prop>
</props>
</property>
</bean>
注意
error 为自己创建的jsp页面 通过名字.exception 捕获异常
服务器发生异常:${e.message}
自定义异常 速度 快于 Spring 自带异常
@Component 和@Controller的区别
@Component 是一个通用的Spring容器管理的单例bean组件。
而 @Repository, @Service, @Controller 就是针对不同的使用场景所采取的特定功能化的注解组件。
注解的含义:
@Component
最普通的组件,可以被注入到spring容器进行管理
@Repository
作用于持久层
@Service
作用于业务逻辑层
@Controlle
作用于表现层(spring-mvc的注解)
16. 国际化
配置的思想是 改变SessionLocaleResolver的语言 来达到国际化的标准
方法
-
配置Spring mvc-servlet.xml
-
在 java/resources中创建 Resource Bundle
-
添加语言 zh(中国) jpn(日本)ko(韩国)en(英语)
-
添加链接 该链接是改变语言后重新跳转该页面
<a href="forward?pageName=collegeAdd&&locale=zh">中国</a>
-
// mvc-servlet.xml
<!-- 国际化开始-->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="defaultEncoding" value="UTF-8"/>
<property name="basename" value="message"/>
<!-- 在code找不到对应的资源名称时,显示code的字面值-->
<property name="useCodeAsDefaultMessage" value="true"/>
</bean>
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"/>
<!-- 拦截器-->
<mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"></bean>
</mvc:interceptors>
<!-- 国际化结束-->
17. 上传
纯Servlet流上传: 把文件作为二进制流 打包在文件中 ,找到文件位置开始读取文件信息。
一般使用第三方架包进行上传。
-
第三方jar,commons-io,commons-fileuoload,可以引用jar包,也可以引用Maven依赖。
-
Spring配置,用到了Spring的上传类
-
JSP的表单数据打包方式,enctyp的属性,method以post方式提交
-
Controller方法的实现
1. 依赖
<!-- 上传依赖的架包-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3</version>
</dependency>
2. mvc-servlet.xml
<!-- 上传 依赖第三方-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 醉倒上传文件大小,字节 100M-->
<property name="maxUploadSize" value="104857600"></property>
<!-- 每一个文件大小 单文件最大 1m-->
<property name="maxUploadSizePerFile" value="1048576"/>
<!-- 缓存-->
<property name="maxInMemorySize" value="4096"></property>
<property name="defaultEncoding" value="UTF-8"></property>
</bean>
3.修改CollegeController
@Resource(name = "collegeService")
private IBaseService<College> service;
private MultipartFile headImg;
private Logger logger = LoggerFactory.getLogger(this.getClass());
// @Validated 前台提交数据 要符合我的校验规则
// BindingResult br 对象紧挨着 校验规则 如果中间插入参数 则失去效果
@PostMapping("/add")
public String add(@Validated College college, BindingResult br, Map map,
HttpServletRequest request, MultipartFile headImg){
// 测试异常处理机制
// int num = 1/0;
logger.debug("enter add");
// 校验不通过 会有错误信息
if (br.hasFieldErrors()){
// 字段校验出错
List<FieldError> errors = br.getFieldErrors();
for (FieldError err : errors){
// 把字段校验错误带到页面
if (err.getDefaultMessage().contains("NumberFormatException")){
map.put(err.getField(),"必须是有效数字");
map.put(err.getField()+"value",err.getRejectedValue());
}else {
map.put(err.getField(),err.getDefaultMessage());
}
}
map.put("college",college);
return "collegeAdd";
}
logger.debug("college=" + college);
// 获取files的真实路径
String path = request.getServletContext().getRealPath("files");
File pathFile = new File(path);
if (!pathFile.exists()){
// 如果上传目录不存在,则创建
// mkdirs mkdir区别是,前者可以创建多层目录,后者只可以创建一层目录
pathFile.mkdirs();
}// 获取原始文件的名称
String fileName = headImg.getOriginalFilename();
logger.debug("path=" + path );
logger.debug("fileName=" + fileName);
try {
// 最后要有原始的文件名称
headImg.transferTo(new File(path + File.separator + fileName));
} catch (IOException e) {
logger.debug("上传失败",e);
}
boolean result = service.add(college);
if (result){
return "collegeManager";
}else{
return "error";
}
}
4. 修改error.jsp 提示最大传输字节
<%--页面加载完后 在初始化--%>
<body onload="init()">
服务器发生异常:<span id="errMsg">${exception.message}</span>
<hr/>
服务器发生异常:${e.message}
<script>
function init(){
let errMsg = '${exception.message}';
if (errMsg.includes('FileSizeLimitException')){
let size = errMsg.split(" ")[4];
document.getElementById("errMsg").innerText="上传文件不能超过"+size+"字节"
}else{
document.getElementById("errMsg").innerText=errMsg;
}
}
</script>
</body>
5.修改Tomcat配置
在conf/server.xml 中 设置上传限制 maxSwallowSize="-1"
<Connector port="80" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
maxSwallowSize="-1" />