Spring
Spring IOC
1.Spring 框架介绍 :Spring IOC、DI和AOP等核心概念
2.Spring IOC容器:Spring实例化与管理对象
3.集合对象注入:注入List、Set、Map集合对象
4.底层原理:Spring Bean的生命周期
5.注解与Java Config:Spring注解分类和常用注解应用
Spring AOP
1.理解AOP及名词:Spring AOP开发与配置流程
2.五种通知类型:Spring 五种通知类型与应用场景
3.切点表达式:PointCut切点表达式的语法规则及应用
4.代理模式:JDK动态代理和CGLib代理的执行过程
Spring JDBC
1.Spring JDBC: Spring JDBC的环境配置
2.RestTemplate: 基于RestTemplate 实现SQL处理
3.配置声明式事务:声明式事务的配置过程
4.事务传播行为介绍:讲解常用事务传播行为的用途
5.声明式事务注解形式:基于注解使用声明式事务
Spring MVC
1.Spring MVC 入门:Spring MVC 开发流程与环境配置
2.接收Web数据:Spring MVC 参数接受与数据绑定
3.URL Mapping:讲解URL绑定过程
4.中文乱码问题:解决请求与响应中文乱码
5.拦截器:Spring MVC拦截器的使用
Restful开发风格
1.Restful风格介绍:介绍Restful开发规范
2.Restful开发实战:实例讲解Restful在Spring MVC 中的实现
3.JSON 序列化:通过响应输出数据
4.Restful的跨域问题:分析跨域问题的来源与解决方法
第一部分 Spring IoC
一、Spring 快速入门
1.IoC控制反转
IoC控制反转,全称Inverse of Control,是一种设计理念
由代理人来创建与管理对象,消费者通过代理人来获取对象
IoC的目的是降低对象之间直接耦合
加入IoC容器将对象统一管理,让对象关联变为弱耦合
2.DI依赖注入
IoC是设计理念,是现代程序设计遵循的标准,是宏观目标
DI(Dependency Injection)是具体技术实现,是微观实现
DI在java中利用反射技术实现对象注入(Injection)
3.Spring(官网)
Spring 可从狭义与广义两个角度看待
狭义的Spring是指Spring框架(Spring Fremework)
Spring框架是企业开发复杂性的一站式解决方案
Spring框架的核心是IoC容器与AOP面向切面编程
Spring IoC负责创建与管理对象,并在此基础上扩展功能
广义的Spring是指Spring生态体系
Microservices、Reactive、Cloud、Web apps、Serverless、Event Driven、Batch
4.传统开发方式
对象直接引用导致对象硬关联,程序难以扩展维护
5.Sring IoC容器
IoC容器是Spring生态的地基,用于创建与管理对象依赖
6.Sring IoC容器职责
对象的控制权交由第三方统一管理(IoC控制反转)
利用java反射技术实现运行时对象创建与关联(DI依赖注入)
基于配置提高应用程序的可维护性与扩展
7. XML管理对象Bean
1)基于XML配置Bean
-
基于构造方法对象实例化
<bean id="apple2" class="com.xiaofeng.spring.ioc.entity.Apple"> <constructor-arg name="color" value="红色"></constructor-arg> <constructor-arg name="title" value="红富士"></constructor-arg> <constructor-arg name="origin" value="欧洲"></constructor-arg> </bean>
-
基于静态工厂实例化
静态工厂通过静态方法创建对象,隐藏创建对象的细节
<bean id="apple4" class="com.xiaofeng.spring.ioc.factory.AppleStaticFactory" factory-method="createSweetApple"/>
public static Apple createSweetApple(){
Apple apple = new Apple();
apple.setTitle("红富士");
apple.setColor("红色");
apple.setOrigin("欧洲");
return apple;
}
-
基于工厂实例方法实例化
工厂实例方法创建对象是指IoC容器对工厂类进行实例化并调用对应的实例方法创建对象的过程
<bean id="factoryInstance" class="com.xiaofeng.spring.ioc.factory.AppleFactoryInstance"/> <bean id="apple5" class="com.xiaofeng.spring.ioc.factory.AppleStaticFactory" factory-bean="factoryInstance" factory-method="createSweetApple"/> public static Apple createSweetApple(){ Apple apple = new Apple(); apple.setTitle("红富士"); apple.setColor("红色"); apple.setOrigin("欧洲"); return apple; }
2)基于注解配置Bean
3)基于java代码配置Bean
二、Spring XML配置
applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
maven方式获取spring
<repositories>
<repository>
<id>aliyun</id>
<name>aliyun</name>
<url>https://maven.aliyun.com/reoository/public</url>
</repository>
</repositories>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<bean id="sweetApple" class="com.xiaofeng.spring.ioc.entity.Apple">
<property name="title" value="红富士"></property>
<property name="color" value="红色"></property>
<property name="origin" value="欧洲"></property>
</bean>
创建IoC容器并根据配置文件创建对象
ApplicationContext context=new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
三、对象实例化配置
从IoC容器获取Bean
Apple sweetApple1 = context.getBean("sweetApple",Apple.class);
或
Apple sweetApple2=(Apple)context.getBean("sweetApple");
bean中id与name属性相同点
bean id与name都是设置对象在IoC容器中唯一标识
两者在同一配置文件中都不允许出现重复
两者允许在多个配置文件中出现重复,新对象覆盖旧对象
bean中id与name属性不同点
id要求更为严格,一次只能定义一个对象标识(推荐)
name更为宽松,一次允许定义多个对象标识
tips:id与name的命名要求有意义,按驼峰命名书写
没有id与name的bean默认使用类名全称作为标识
路径表达式
加载单个文件
ApplicationContext context=new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
加载多个文件
String[] configLocations=new String[]{"classpath:applicationContext.xml","classpath:applicationContext.xml"};
ApplicationContext context=new ClassPathXmlApplicationContext(configLocations);
表达式实例 | 说明 |
---|---|
classpath:config.xml | 扫描classpath根路径(不包含jar)的config.xml |
classpath:com/xiaofeng/config.xml | 扫描classpath下(不包含jar)com.xiaofeng包中的config.xml |
classpath*:com/xiaofeng/config.xml | 扫描classpath下(包含jar)com.xiaofeng包中的config.xml |
classpath:config-*.xml | 扫描classpath根路径下所有以config-开头的XML文件 |
classpath:com/**/config.xml | 扫描com包下(包含任何子包)的config.xml |
file:c:/config.xml | 扫描c盘根路径config.xml |
四、依赖注入配置
依赖注入是指运行时将容器内对象利用反射赋给其他对象的操作
1.基于setter方法注入对象
<bean id="sweetApple" class="com.xiaofeng.spring.ioc.entity.Apple">
<property name="title" value="红富士"></property>
<property name="color" value="红色"></property>
<property name="origin" value="欧洲"></property>
</bean>
<bean id="lily" class="com.xiaofeng.spring.ioc.entity.Child">
<property name="name" value="红富士"></property>
<property name="apple" ref="sweetApple"></property>
</bean>
2.基于构造方法注入对象
<bean id="sourApple" class="com.xiaofeng.spring.ioc.entity.Apple">
<property name="title" value="青苹果"></property>
<property name="color" value="青色"></property>
<property name="origin" value="中亚"></property>
</bean>
<bean id="andy" class="com.xiaofeng.spring.ioc.entity.Child">
<constructor-arg name="name" value="andy"></constructor-arg>
<constructor-arg name="apple" ref="sourApple"></constructor-arg>
</bean>
3.注入集合对象
1)注入List
<bean id="" class="">
<property name="someList">
<list>
<value>v1</value>
<ref bean="beanId"></ref>
</list>
</property>
</bean>
2)注入Set
<bean id="" class="">
<property name="someSet">
<set>
<value>v1</value>
<ref bean="beanId"></ref>
</set>
</property>
</bean>
3)注入Map
<bean id="" class="">
<property name="someMap">
<map>
<entry key="k1" value="v1"></entry>
<entry key="k2" value-ref="beanId"></entry>
</map>
</property>
</bean>
4.注入Properties
<bean id="" class="">
<property name="someProperties">
<props>
<prop key="k1">v1</prop>
<prop key="k2">v2</prop>
</props>
</property>
</bean>
4.查看容器内对象
//获取容器内所有beanId数组
String[] beanNames= context.getBeanDefinitionNames();
5.bean scope属性
bean scope属性用于决定对象何时被创建与作用范围
bean scope配置将影响容器内对象的数量
bean scope默认值为singleton(单例),指全局共享同一个对象实例
base scope属性清单
scope属性 | 说明 |
---|---|
singleton | 单例(默认值),每一个容器有且只有唯一的实例,实例被全局共享 |
prototype | 多例,每次使用时都是创建一个实例 |
request | web环境下,每一次独立请求存在唯一实例 |
session | web环境下,每一个session存在唯一实例 |
application | web环境下,ServletXContext存在唯一实例 |
websocket | 每一次WebSocket连接中存在唯一实例 |
singleton在容器是单例多线程执行的,存在线程安全风险
prototype在容器是多实例,占用更多的资源,不存在线程安全问题
singleton与prototype对比
singleton | prototype | |
---|---|---|
对象数量 | 全局唯一 | 存在多个 |
实例化时机 | IoC容器启动时 | getBean()或对象注入时 |
线程安全问题 | 存在 | 不存在 |
执行效率 | 高 | 低 |
6.bean生命周期
//释放资源
((ClassPathXmlApplicationContext) context).registerShutdownHook();
五、注解与Java Config
(1)基于注解配置IoC容器
1.基于注解的优势
摆脱繁琐的XML形式的bean与依赖注入
基于”声明式“的原则,更适合轻量级的现代企业应用
让代码的可读性变得更好,研发人员拥有更好的开发体验
2.三类注解
组件类型注解----声明当前类的功能与职责
自动装配注解---根据属性特征自动注入对象
元数据注解----更细化的辅助IoC容器管理对象的注解
1)四种组件类型注解
注解 | 说明 |
---|---|
@Component | 组件注解,通用注解,被该注解描述的类将被IoC容器管理并实例化 |
@Controller | 语义注解,说明当前类是MVC应用中的控制类 |
@Service | 语义注解,说明当前类是Service业务服务类 |
@Repository | 语义注解,说明当前类用于业务持久层,通常描述对应Dao类 |
开启组件扫描
<!--XML配置开启组件扫描,才能使用主键-->
<context:component-scan base-package=" ">
<!--符合条件的不会被扫描-->
<!--正则表达式-->
<context:exclude-filter type="regex" expression=".exl.*"/>
</context:component-scan>
注解applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--XML配置开启组件扫描,才能使用主键-->
<context:component-scan base-package=" ">
<context:annotation-config/>
</beans>
在IoC容器初始化时自动扫描四种组件类型注解并完成实例化
组件类型注解默认beanId为类名首字母小写
2)两类自动装配注解
分类 | 注解 | 说明 |
---|---|---|
按类型装配 | @Autowired | 按容器内对象类型动态注入属性,由Spring机构提供 |
@Inject | 基于JSR-330(Dependency Injection for Java)标准,其他同@Autowired,但不支持required属性 | |
按名称装配 | @Named | 与@Inject配合使用,JSR-330规范,按属性名自动装配属性 |
@Resource | 基于JSR-250规范,优先按名称,再按类型智能匹配 |
@Autowired
如果装配注解放在set方法上,则自动按类型/名称对set方法参数进行注入
如果装配注解放在属性上,spring IoC容器会自动通过反射技术将属性private修饰符自动改为public,直接赋值,不在执行set方法
@Resource
@Resource设置name属性,则按name在IoC容器中将Bean注入
@Resource 未设置name属性
以属性名作为bean name在IoC容器中匹配bean,如有匹配则注入
按属性名未匹配,则按类型进行匹配,同@Autowired,需加入@Primary解决类型冲突
使用建议:在使用@Resource对象时推荐设置name或保证属性名与bean名称一致
3)元数据注解
注解 | 说明 |
---|---|
@Primary | 按类型装配时出现多个相同类型对象,拥有此注解对象优先被注入 |
@PostConstruct | 描述方法,相当于XML中init-method配置的注解版本 |
@PreDestory | 描述方法,相当于XML中destory-method配置的注解版本 |
@Scope | 设置对象的Scope属性 |
@Vaule | 为属性注入静态数据 |
配置文件
<context:property-placeholder location="classpath:config.properties" />
@Value("${metaData}")//读取config.properties的metaData属性值
private String metaData;
(2)基于java Config配置IoC容器
1.基于java config的优势
完全摆脱XML的束缚,使用独立Java类管理对象和依赖
注解配置相对分散,利用java config可对配置集中管理
可以在编译时进行依赖检查,不容易出错
2.java config核心注解
注解 | 说明 |
---|---|
@Configuration | 描述类,说明当前类是java config配置类,完全替代XML文件 |
@Bean | 描述方法,方法返回对象将被IoC容器管理,beanId默认为方法名 |
@ImportResource | 描述类,加载静态文件,可使用@value注解获取 |
@ComponentScan | 描述类,同XML的< context:compoment-scan >标签 |
//基于java Config配置IoC容器的初始化
ApplicationContext context=new AnnotationConfigApplicationContext(Config.class);
@ComponentScan(basePackages="com.xiaofeng")
六、Spring 单元测试
(1)Spring Test测试模块
Spring Test是Spring中用于测试的模块
Spring Test对Junit单元测试框架有良好的整合
通过Spring Test可在JUnit在单元测试时自动初始化IoC容器
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
(2)Spring与JUnit整合过程
Maven工程依赖spring-test
利用@RunWith与@ContextConfiguration描述测试用例类
//将JUnit4的执行权交由Spring Test,在测试用例执行前自动初始化IoC容器
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
测试用例类从容器类获取对象完成测试用例的执行
(3)JUnit4依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
第二部分 Spring AOP --面向切面编程
一、介绍Spring AOP与相关概念名词
(1)Spring中的可插拔组件技术
Spring AOP-----------Aspect Orinented Programming 面向切面编程
AOP 的做法是将通用、与业务无关的功能抽象封装为切面类
切面可配置在目标方法的执行前、后运行,真正做到即插即用
在不修改源码的情况下对程序行为进行扩展
(2)AOP 底层依赖(aspectjweaver)
<!--aspectjweaver是Spring AOP的底层依赖-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
(3)AOP applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
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 https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
<!-- AOP配置 -->
<bean id="methodAspect" class="com.imooc.spring.aop.aspect.MethodAspect">
</bean>
<aop:config>
<!-- pointcut 切点,使用execution表达式描述切面的作用范围 -->
<!--execution(public * com.imooc..*.*(..)) 说明切面作用在com,imooc包下的所有类的所有方法 -->
<aop:pointcut id="pointcut" expression="execution(public * com.imooc..*.*(..))"></aop:pointcut>
<aop:aspect ref="methodAspect">
<!-- before通知,代表在目标方法运行前先执行 methodAspect.printExecutionTime()-->
<aop:before method="printExecutionTime" pointcut-ref="pointcut"/>
<aop:after method="doAfter" pointcut-ref="pointcut"></aop:after>
<aop:after-returning method="doAfterReturning" returning="ret" pointcut-ref="pointcut"></aop:after-returning>
<aop:after-throwing method="doAfterThrowing" throwing="th" pointcut-ref="pointcut"></aop:after-throwing>
</aop:aspect>
</aop:config>
切面类
public class MethodAspect {
//切面方法
//JoinPoint 连接点:通过连接点可以获取目标类/方法的信息
public void printExecutionTime(JoinPoint joinPoint){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String now=sdf.format(new Date());
String className=joinPoint.getTarget().getClass().getName();//获取目标类的名称
String methodName=joinPoint.getSignature().getName();//获取目标方法名称
System.out.println("----->"+now+":"+className+"."+methodName);
Object[] args= joinPoint.getArgs();//获取目标对象方法参数
System.out.println("---->参数个数:"+args.length);
for (Object arg:args){
System.out.println("---->参数:"+arg);
}
}
}
(4)Spring AOP 与Aspectj的关系
Eclipse Aspectj,一种基于java平台的面向切面编程的语言
Spring AOP 使用AspectJWeaver实现类与方法匹配
String AOP利用代理模式实现对象运行时功能扩展
(5)几个关键概念
注解 | 说明 |
---|---|
Aspect | 切面,具体的可插拔组件功能类,通常一个切面只实现一个通用功能 |
Target Class/Method | 目标类,目标方法,指真正要执行与业务相关的方法 |
PointCut | 切入点,使用execution表达式说明切面要作用在系统哪些类上 |
JoinPoint | 连接点,切面运行过程中是包含了目标类/方法元数据的对象 |
Advice | 通知,说明具体的切面的执行时机,Spring包含了五种不同类型的通知 |
二、Spring AOP开发与配置流程
(1)AOP配置过程
1.依赖AspectJ
2.实现切面类/方法
3.配置Aspect Bean
4.定义JoinPoint
5.配置Advice
(2)JoinPoint 核心方法
注解 | 说明 |
---|---|
Object getTarget() | 获取IoC容器内目标对象 |
Signature getSignature() | 获取目标方法 |
Object[ ] getArgs() | 获取目标对象方法参数 |
(3)PointCut切点表达式
public void com.imooc.service.UserService.createUser(形参1,形参2,...)
execution((public可省略) * com.imooc .. * . * ( .. ) )
* -通配符 .. -包通配符 (..) -参数通配符
三、Spring 五种通知类型与应用场景
(1)五种通知类型
注解 | 说明 |
---|---|
Before Advice | 前置通知,目标方法在运行前执行 |
After Returning Advice | 返回后通知,目标方法返回数据后执行 |
After Throwing Advice | 异常通知,目标方法抛出异常后执行 |
After Advice | 后置通知,目标方法运行后执行 |
Around Advice | 最强大通知(环绕通知),自定义通知执行时机,可决定目标方法是否运行 |
特殊的“通知”--引介增强
引介增强(IntroductionInterceptor)是对类的增强,而非方法
引介增强允许在运行时为目标类增加新属性或方法
引介增强允许在运行时改变类的行为,让类随运行环境动态变更
环绕通知
public class MethodChecker {
//ProceedingJoinPoint是JoinPoint的升级版,在原有功能外,还可以控制目标方法是否执行
public Object check(ProceedingJoinPoint pjp) throws Throwable {
try {
long startTime=new Date().getTime();
Object ret= pjp.proceed();//执行目标方法
long endTime=new Date().getTime();
long duration=endTime-startTime;//执行时长
if(duration>1000){
String className=pjp.getTarget().getClass().getName();
String methodName=pjp.getSignature().getName();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:SS sss");
String now=sdf.format(new Date());
System.out.println("====="+now+":"+className+"."+methodName+"("+duration+"ms)");
}
return ret;
} catch (Throwable throwable) {
System.out.println("Exception message:"+throwable.getMessage());
throw throwable;
}
}
}
<bean id="methodChecker" class="com.imooc.spring.aop.aspect.MethodChecker"></bean>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.imooc..*.*(..))"></aop:pointcut>
<aop:aspect ref="methodChecker">
<aop:around method="check" pointcut-ref="pointcut"></aop:around>
</aop:aspect>
</aop:config>
(2)利用注解配置Spring AOP
applicationContext.xml配置
<!-- 初始化IoC容器 -->
<context:component-scan base-package="com.imooc"/>
<!-- 启用Spring AOP注解模式 -->
<aop:aspectj-autoproxy/>
切面类配置
@Component//标记当前类为组件
@Aspect//说明当前类是切面类
public class MethodChecker {
//环绕通知,参数为pointcut切点表达式
@Around("execution(* com.imooc..*Service.*(..))")
public Object check(ProceedingJoinPoint pjp) throws Throwable {}
}
四、AOP中的代理模式应用
(1)Spring AOP实现原理
Spring基于代理模式实现功能动态扩展,包含两种形式:
1.目标类拥有接口,通过JDK动态代理实现功能扩展
2.目标类没有接口,通过CGLib组件实现功能扩展
(2)代理模式与静态代理
代理模式通过代理对象对原对象的实现功能扩展
静态代理是指必须手动创建代理类的代理模式使用方式
(3)AOP底层原理--JDK动态代理
/**
* InvocationHandler是jdk提供的反射类,用于在jdk动态代理中对目标方法进行增强
* InvocationHandler实现类与切面类的环绕通知类似
*/
public class ProxyInvocationHandler implements InvocationHandler {
private Object target;//目标对象
private ProxyInvocationHandler(Object target){
this.target=target;
}
/**
* 在invoke()方法对目标方法进行增强
* @param proxy 代理类对象
* @param method 目标对象方法
* @param args 目标对象实参
* @return 目标方法运行后的返回值
* @throws Throwable 目标方法抛出的异常
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object ret= method.invoke(target,args);//调用目标方法,ProxyJoinPoint.proceed()
return ret;
}
public static void main(String[] args) {
//动态代理,必须实现接口才可以运行
UserService userService=new UserServiceImpl();
ProxyInvocationHandler invocationHandler = new ProxyInvocationHandler(userService);
//动态创建代理类
UserService userServiceProxy=(UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),userService.getClass().getInterfaces(),invocationHandler);
userServiceProxy.createUser();
}
}
(4)JDK动态代理解析
百度 等会再写
(5)AOP底层原理--CGLib
CGLib是运行时字节码增强技术(Code Generation Library)
Spring AOP扩展无接口类时使用CGLib
AOP会运行时生成目标继承类字节码的方式进行行为扩展
第三部分 Spring JDBC与事务管理
一、Spring JDBC与JdbcTemplate对象
(1)Spring JDBC
spring JDBC 是Spring框架用于处理关系型数据库的模块
Spring JDBC 对JDBC API 进行封装,极大简化开发工作量
jdbcTemplate 是Spring JDBC 的核心类,提供数据CRUD方法
(2)Spring JDBC的使用步骤
Maven工程引入依赖spring-jdbc
applicationContext.xml配置DataSource数据源
在Dao注入jdbcTemplate对象,实现数据CRUD
(3)Spring JDBC 配置过程
1.Maven工程引入依赖spring-jdbc
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
</dependencies>
2.applicationContext.xml配置DataSource数据源
<!-- 数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/imooc?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true"/>
<property name="username" value="dev"/>
<property name="password" value="xiaofeng"/>
</bean>
<!-- jdbcTemplate提供数据CRUD的API -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
3.在Dao注入jdbcTemplate对象,实现数据CRUD
<bean id="employeeDao" class="com.xiaofeng.spring.jdbc.dao.EmployeeDao">
<!-- 为Dao注入jdbcTemplate对象 -->
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
(4)JdbcTemplate的数据查询方法
public class EmployeeDao {
private JdbcTemplate jdbcTemplate;
public Employee findById(Integer eno){
String sql="select * from employee where eno=?";
//查询单条数据
Employee employee=jdbcTemplate.queryForObject(sql,new Object[]{eno},new BeanPropertyRowMapper<Employee>(Employee.class));
return employee;
}
public List<Employee> findByDname(String dname){
String sql="select * from employee where dname=?";
//查询复合数据
List<Employee> list= jdbcTemplate.query(sql,new Object[]{dname},new BeanPropertyRowMapper<Employee>(Employee.class));
return list;
}
public List<Map<String,Object>> findMapByDname(String dname){
String sql="select eno as empno,salary as s from employee where dname=?";
//将查询结果作为Map进行封装
List<Map<String,Object>> maps=jdbcTemplate.queryForList(sql,new Object[]{dname});
return maps;
}
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
(5)JdbcTemplate的数据写入方法
//新增
public void insert(Employee employee){
String sql="insert into employee(eno,ename,salary,dname,hiredate) values(?,?,?,?,?)";
jdbcTemplate.update(sql,new Object[]{
employee.getEno(),employee.getEname(),employee.getSalary(),employee.getDname(),employee.getHiredate()
});
}
//修改
public int update(Employee employee){
String sql= "UPDATE employee SET ename = ?, salary = ?, dname = ?, hiredate = ? WHERE eno= ?";
int count= jdbcTemplate.update(sql,new Object[]{
employee.getEname(),employee.getSalary(),employee.getDname(),employee.getHiredate(),employee.getEno()
});
return count;
}
//删除
public int delete(Integer eno){
String sql="delete from employee where eno=?";
int count=jdbcTemplate.update(sql,new Object[]{eno});
return count;
}
二、声明式事务配置方式
(1)什么是事务
事务是以可靠的、一致的方式、访问和操作数据库的程序单元
说人话:要么把事情做完,要么什么也不做,不要做一半
事务依赖于数据库实现,MySQL通过事务区作为数据缓存地带
(2)编程式事务
编程式事务是指通过代码手动提交回滚事务的事务控制方法
Spring JDBC 通过TransactionManger事务管理器实现事务控制
事务管理器提供commit/rollback方法进行事务提交与回滚
1)applicationContext.xml配置
<bean id="employeeService" class="com.xiaofeng.spring.jdbc.service.EmployeeService">
<property name="employeeDao" ref="employeeDao"/>
<property name="transactionManger" ref="transactionManger"/>
</bean>
<!-- 事务管理器 -->
<bean id="transactionManger" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
2)具体实现
public class EmployeeService {
private DataSourceTransactionManager transactionManger;
public DataSourceTransactionManager getTransactionManger() {
return transactionManger;
}
public void setTransactionManger(DataSourceTransactionManager transactionManger) {
this.transactionManger = transactionManger;
}
public void batchImport(){
//定义了事务默认的标准配置
TransactionDefinition definition=new DefaultTransactionDefinition();
//开始一个事务,返回事务状态,事务状态说明当前事务的执行阶段
TransactionStatus status= transactionManger.getTransaction(definition);
try {
for (int i = 1; i <=10 ; i++) {
// if (i==3){
// throw new RuntimeException("意外之外的异常");
// }
Employee employee=new Employee();
employee.setEno(8000+i);
employee.setEname("员工"+i);
employee.setSalary(3000f);
employee.setDname("产品1部");
employee.setHiredate(new Date());
employeeDao.insert(employee);
}
//提交事务
transactionManger.commit(status);
} catch (RuntimeException e) {
//回滚事务
transactionManger.rollback(status);
e.printStackTrace();
}
}
}
三、声明式事务七种事务传播行为
(1)声明式事务
声明式事务指在不修改源码的情况下通过配置形式自动实现事务控制,声明式事务的本质就是AOP环绕通知
当目标方法执行成功时,自动提交事务
当目标方法抛出运行时异常时,自动回滚事务
(2)applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
(3)配置过程
1)配置transactionManger事务管理器
<!-- 1.事务管理器,用于创建事务/提交、回滚 -->
<bean id="transactionManger" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
2)配置事务通知与事务属性
<!-- 2.事务通知配置,决定那些方法使用事务,那些方法不使用事务 -->
<tx:advice id="txAdvce" transaction-manager="transactionManger">
<tx:attributes>
<!-- 目标方法名为batchImport时,启用声明式事务,成功提交,运行时异常回滚-->
<tx:method name="batchImport" propagation="REQUIRED"/>
<tx:method name="batch*t" propagation="REQUIRED"/>
<!-- 设置所有findXXX方法不需要使用事务-->
<tx:method name="find*" propagation="NOT_SUPPORTED" read-only="true"/>
<tx:method name="get*" propagation="NOT_SUPPORTED" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
3)为事务通知绑定PointCut切点
<!-- 定义声明式事务的作用范围 -->
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.imooc..*Service.*(..))"/>
<aop:advisor advice-ref="txAdvce" pointcut-ref="pointcut"/>
</aop:config>
(4)事务传播行为
事务传播行为是指多个拥有事务的方法嵌套调用时的事务控制方式
XML:
<tx:method name="..." propagation="REQUIRED"/>
注解
//放在类上,将声明式事务配置应用于当前类的所有方法,默认事务传播为REQUIRED
@Transactional(propagation = Propagation.REQUIRED)
@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly = true)//查询方法
@Transactional(propagation= Propagation.REQUIRES_NEW)
1)事务传播行为七种类型
事务传播类型 | 说明 |
---|---|
PROPAGATION_REQUIRED(默认) | 如果当前没有事务,就新建一个事务,如果已经存在一个事务,加入到这个事务中。这是最常见的选择 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行 |
PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常 |
PROPAGATION_REQUIREDS_NEW | 新建事务,如果当前存在事务,把当前事务挂起 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起 |
PROPAGATION_NEVER | 以非事务方式执行操作,如果当前存在事务,则抛出异常 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作 |
2)注解配置声明式事务
<context:component-scan base-package="com.imooc"/>
<!-- 数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/imooc?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true"/>
<property name="username" value="dev"/>
<property name="password" value="xiaofeng"/>
</bean>
<!-- jdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--事务管理器-->
<bean id="transactionManger" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 启用注解形式声明式事务 -->
<tx:annotation-driven transaction-manager="transactionManger"/>
第四部分Spring MVC
一、Spring MVC入门
(1)MVC是什么------架构模式
Spring MVC 是Spring体系的轻量级Web MVC 框架
Spring MVC 的核心是Controller控制器,用于处理请求,产生响应
Spring MVC 基于Spriong IoC容器运行,所有的对象被IoC管理
(2)Spring 5.x版本变化
Spring 5.x最低要求JDK8与J2EE 7(Servlet3.1/Tomcat 8.5+)
Spring 5.x支持JDK8/9,可以使用新特性
Spring 5.x最重要的支持响应式编程
(3)IDEA环境下创建Maven WebApp
见idea环境下创建MavenWebApp.docx
(4)Spring MVC 环境配置
1)Maven依赖spring-webmvc
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
2)web.xml配置DispatcherServlet
<!--DispatchServlet -->
<servlet>
<servlet-name>springmvc</servlet-name>
<!-- DispatcherServlet是Spring MVC最核心的对象
DispatcherServlet用于拦截Http请求
并根据请求的URL调用与之对应的Controller方法,来完成Http的请求的处理
-->
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- applicationContext.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
<!--
在web应用启动时自动创建Spring IoC容器,
并初始化DispatcherServlet
-->
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!--"/"代表拦截所有的请求-->
<url-pattern>/</url-pattern>
</servlet-mapping>
3)配置applicationContext.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--
context:component-scan标签作用
在Spring IoC初始化过程中,自动创建并管理com.xiaofeng.springmvc及子包中
拥有以下注解的对象。
@Repository dao类
@Service service类
@Controller controller类
@Component 通用类
-->
<context:component-scan base-package="com.xiaofeng.springmvc"/>
<!--启用Spring MVC的注解开发模式-->
<mvc:annotation-driven/>
<!--将图片/css/js等静态资源排除在外,可提高执行效率-->
<mvc:default-servlet-handler/>
</beans>
4)开发Controller控制器
@Controller
public class TestController {
@GetMapping("/t")//localhost/t
@ResponseBody//直接向响应输出字符串数据,不跳转页面
public String test(){
return "Hello Spring MVC";
}
}
(5)Spring MVC处理流程
二、Spring MVC 数据绑定
(1)URL Mapping(URL映射)
URL Mapping指URL与Controller方法绑定
通过URL与方法绑定,Spring MVC便可通过Tomcat对外暴露服务
(2)URL Mapping注解
@RequestMapping//通用绑定
@GetMapping//绑定Get请求
@PostMapping//绑定Post请求
(3)Controller方法参数接收请求参数
1)使用Controller方法接收
html
<form action="/m1" method="post">
<input type="text" name="username"/>
<br/>
<input type="text" name="password"/>
<br/>
<input type="submit" value="提交">
</form>
控制器
@Controller
public class Test1Controller {
@PostMapping("/m1")//绑定post请求
@ResponseBody//直接向响应输出字符串数据,不跳转页面
public String post(String username,String password){
System.out.println(username+":"+password);
return username+":"+password;
}
@GetMapping("/g")
@ResponseBody
public String getMapping(@RequestParam("manager_name") String managerName) {
System.out.println("managerName:"+managerName);
return "This is get method";
}
}
2)使用Java Bean 接收
<form action="/um/p1" method="post">
<input type="text" name="username"/>
<br/>
<input type="text" name="password"/>
<br/>
<input type="submit" value="提交">
</form>
//实体类
public class User {
private String username;
private Long password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Long getPassword() {
return password;
}
public void setPassword(Long password) {
this.password = password;
}
}
//控制器
@PostMapping("/p1")
@ResponseBody
public String postMapping1(User user){
System.out.println(user.getUsername()+":"+user.getPassword());
return "This is post method";
}
3)接收表单复杂数据
利用数组或者List接收请求中的复合数据
@PostMapping("/apply")
@ResponseBody
public String apply(@RequestParam(value = "n",defaultValue = "ANON") String name, String course, Integer[] purpose){
System.out.println(name);
System.out.println(course);
for (Integer p : purpose) {
System.out.println(p);
}
return "SUCCESS";
}
@PostMapping("/apply")
@ResponseBody
//利用@ResquestParam为参数设置默认值
public String apply(String name, String course, @RequestParam List<Integer> purpose){
System.out.println(name);
System.out.println(course);
for (Integer p : purpose) {
System.out.println(p);
}
return "SUCCESS";
}
利用@ResquestParam为参数设置默认值
使用Map对象接收请求参数及注意事项
@PostMapping("/apply")
@ResponseBody
//利用@ResquestParam为参数设置默认值
public String apply(@RequestParam Map map){
System.out.println(map);
return "SUCCESS";
}
(4)关联数据赋值
(5)日期类型转换
@DateTimeFormat(pattern = "yyyy-MM-dd")
/**
* 日期转换器类
*/
public class MyDateConverter implements Converter<String, Date> {
public Date convert(String s) {
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
try {
Date d=sdf.parse(s);
return d;
} catch (ParseException e) {
return null;
}
}
}
<!--启用Spring MVC的注解开发模式-->
<!--启用转换器服务-->
<mvc:annotation-driven conversion-service="converterService"/>
<!-- 日期转换器-->
<bean id="converterService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.xiaofeng.springmvc.converter.MyDateConverter"/>
</set>
</property>
</bean>
(6)解决中文乱码
(1)Web应用的中文乱码由来
Tomcat默认使用字符集ISO-8859-1,属于西欧字符集
解决乱码的核心思路是将ISO-8859-1转换为UTF-8
Controller中请求与响应都需要设置UTF-8字符集
(2)中文乱码的配置
Get请求乱码:server.xml增加URIEncoding属性
<!--Tomcat8以后不用配置URIEncoding属性,默认就是UTF-8,在Tomcat7中需要配置URIEncoding属性-->
<Connector connectionTimeout="20000" port="80" protocol="HTTP/1.1" redirectPort="8443" URIEncoding="UTF-8"/>
Post请求乱码:web.xml配置CharacterEncodingFilter
<filter>
<filter-name>characterFilter</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>characterFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Response响应乱码:Spring配置StringHttpMessageConverter
<mvc:annotation-driven conversion-service="converterService">
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<!--response.setContentType=("text/html;charset=utf-8")-->
<value>text/html;charset=utf-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
(7)响应输出结果
响应中产生结果
(1)@ResponseBody//产生响应文本
(2) ModelAndView//利用模板引擎渲染输出
1)@ResponseBody
@ResponseBody直接产生响应体的数据,过程不涉及任何视图
@ResponseBody可产生标准字符串/JSON/XML等格式数据
@ResponseBody被StringHttpMessageConverter所影响
2)ModelAndView
ModelAndView对象是指“模型(数据)与视图(界面)“对象
通过ModelAndView可将包含数据对象与模板引擎进行绑定
Spring MVC 中默认的View是JSP,也可配置其他模板引擎
@GetMapping("/view")
public ModelAndView showView(Integer userId){
ModelAndView mav=new ModelAndView("/view.jsp");
//ModelAndView mav = new ModelAndView();
//mav.setViewName("/view.jsp");
User user=new User();
if (userId==1){
user.setUsername("lily");
}else if(userId==2){
user.setUsername("andy");
}
mav.addObject("u",user);
return mav;
}
3)ModelAndView对象核心用法
mav.addObject()方法设置的属性默认存放在当前请求中
默认ModelAndView使用请求转发(forward)至页面
重定向使用new ModelAndView("redirect:/index.jsp")
//String 与ModelMap
//Controller方法返回String的情况
//1.方法被@ResponseBody描述,SpringMVC 直接响应String字符串本身
//2.方法不存在@ResponseBody,则SpringMVC处理String指代的视图(页面)
@GetMapping("/xxxx")
//@ResponseBody
public String showView1(Integer userId, ModelMap modelMap){
String view="/view.jsp";
User user = new User();
if (userId == 1) {
user.setUsername("lily");
} else if (userId == 2) {
user.setUsername("andy");
}
modelMap.addAttribute("u",user);
return view;
}
(8)Spring MVC 整合Freemarker
1)pom.xml引入依赖
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.29</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
2)启用Freemarker模板引擎
//applicationContext.xml
<bean id="ViewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<!--设置响应输出,并解决中文乱码-->
<property name="contentType" value="text/html;charset=utf-8"/>
<!--指定Freemarker模板文件扩展名-->
<property name="suffix" value=".ftl"/>
</bean>
3)配置Freemarker
//applicationContext.xml
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<!--设置模板保存的目录-->
<property name="templateLoaderPath" value="/WEB-INF/ftl"/>
<!--其他模板引擎设置-->
<property name="freemarkerSettings" >
<props>
<!--设置Freemarker脚本与数据渲染时使用的字符集-->
<prop key="defaultEncoding">Utf-8</prop>
</props>
</property>
</bean>
控制器
@Controller
@RequestMapping("/fm")
public class FreemarkerController {
@GetMapping("/test")
public ModelAndView showTest(){
ModelAndView mav=new ModelAndView("/test");
User user=new User();
user.setUsername("andy");
mav.addObject("u",user);
return mav;
}
}
三、Restful开发风格
(1)REST与RESTful
REST:表现层状态转换,资源在网络中以某种表现形式进行状态转移
RESTful是基于REST理念的一套开发风格,是具体的开发规则
(2)RESTful传输数据
前后端分离
(3)RESTful开发规范
使用URL作为用户交互入口
明确的语义规范(GET | POST | PUT | DELETE)
只返回数据(JSON | XML),不包含任何展现
(4)RESTful命名要求
URI | 说明 | 修改建议 |
---|---|---|
GET/articles?au=lily | 正确用法 | |
GET/a/1 | URI必须具有语义 | GET/student/1 |
POST/createArticle/1 | URI必须使用名词 | POST/article/1 |
GET/articles/author/1 | URI扁平化,不超过两级 | GET/articles/author?id=1 |
GET/articles?au=lily | ||
DELETE/articles/1 | URI名词区分单复数 | DELETE/article/1 |
(5)开发第一个RESTful应用
开始工作与MVC配置过程一致,这里不做具体陈述
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>RESTful实验室</title>
<script src="jquery-3.3.1.min.js"></script>
<script>
$(function () {
$("#btnGet").click(function () {
$.ajax({
url: "/restful/request",
type: "get",
dataType: "json",
success: function (json) {
$("#message").text(json.message);
}
})
})
})
</script>
<script>
$(function () {
$("#btnPost").click(function () {
$.ajax({
url: "/restful/request",
type: "post",
dataType: "json",
success: function (json) {
$("#message").text(json.message);
}
})
})
})
</script>
<script>
$(function () {
$("#btnPut").click(function () {
$.ajax({
url: "/restful/request",
type: "put",
dataType: "json",
success: function (json) {
$("#message").text(json.message);
}
})
})
})
</script>
<script>
$(function () {
$("#btnDelete").click(function () {
$.ajax({
url: "/restful/request",
type: "delete",
dataType: "json",
success: function (json) {
$("#message").text(json.message);
}
})
})
})
</script>
</head>
<body>
<input type="button" id="btnGet" value="发送Get请求">
<input type="button" id="btnPost" value="发送Post请求">
<input type="button" id="btnPut" value="发送Put请求">
<input type="button" id="btnDelete" value="发送Delete请求">
<h1 id="message"></h1>
</body>
</html>
RestfulController控制器
@Controller
@RequestMapping("/restful")
public class RestfulController {
@GetMapping("/request")
@ResponseBody
public String doGetRequest(){
return "{\"message\":\"返回查询结果\"}";
}
@PostMapping("/request")
@ResponseBody
public String doPostRequest(){
return "{\"message\":\"数据新建成功\"}";
}
@PutMapping("/request")
@ResponseBody
public String doPutRequest(){
return "{\"message\":\"数据更新成功\"}";
}
@DeleteMapping("/request")
@ResponseBody
public String doDeleteRequest(){
return "{\"message\":\"数据删除成功\"}";
}
}
ApplicationContext.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.xiaofeng.restful"/>
<!--启用Spring MVC的注解开发模式-->
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<!--response.setContentType=("text/html;charset=utf-8")-->
<value>text/html;charset=utf-8</value>
<value>application/json;charset=utf-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!--将图片/css/js等静态资源排除在外,可提高执行效率-->
<mvc:default-servlet-handler/>
</beans>
(6)@RestController注解与路径变量
@RestController//使用此注解默认在response中,不用在使用ResponseBody
@RequestMapping("/restful")
public class RestfulController {
@PostMapping("/request/{rid}")
//@ResponseBody
//路径表达式:restful/request/100
//@PathVariable("rid") 路径变量
public String doPostRequest(@PathVariable("rid") Integer requestId){
return "{\"message\":\"数据新建成功\",\"id\":"+requestId+"}";
}
}
<script>
$(function () {
$("#btnPost").click(function () {
$.ajax({
//路径表达式
url: "/restful/request/100",
type: "post",
dataType: "json",
success: function (json) {
$("#message").text(json.message+":"+json.id);
}
})
})
})
</script>
(7)简单请求与非简单请求
简单请求:指标准结构的HTTP请求,对应GET/POST请求
非简单请求:复杂要求的HTTP请求,指PUT/DELETE、扩展标准请求
两者最大的区别是非简单请求发送前需要发送预检请求
(1)非简单请求需要在web.xml中配置过滤器
<!-- 非简单请求 -->
<filter>
<filter-name>formContextFilter</filter-name>
<filter-class>org.springframework.web.filter.FormContentFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>formContextFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
(8)JSON序列化
1)jackson依赖
<!--jackson核心包-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.9</version>
</dependency>
<!--jackson数据绑定-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.9</version>
</dependency>
<!--jackson注解-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.9</version>
</dependency>
2)控制器
//单个对象
@GetMapping("/person")
public Person findByPersonId(Integer id){
Person person=new Person();
if (id==1){
person.setName("lily");
person.setAge(23);
}else if(id==2){
person.setName("smith");
person.setAge(22);
}
return person;
}
//多个对象
@GetMapping("/persons")
public List<Person> findPersons(){
List list=new ArrayList();
Person p1=new Person();
p1.setName("lily");
p1.setAge(23);
p1.setBirthday(new Date());
Person p2=new Person();
p2.setName("smith");
p2.setAge(22);
p2.setBirthday(new Date());
list.add(p1);
list.add(p2);
return list;
}
3)person实体类
public class Person {
private String name;
private Integer age;
//时间格式化输出
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss" ,timezone="GMT+8")
private Date birthday;
}
(9)浏览器的同源策略
1)浏览器的跨域访问
同源策略阻止从一个域加载的脚本去获取另一个域上的资源
只要协议、域名、端口有任何一个不同,都被当做是不同的域
浏览器Console看到Access-Control-Allow-Origin就代表跨域了
2)同源策略示例
源URL | 目标URL | 直接访问? |
---|---|---|
http://xiaofeng.com | https://xxx.com:8080/test | 不能 |
http://xiaofeng.com | https://xiaofeng.com | 不能 |
http://xiaofeng.com | http://abc.xiaofeng.com | 不能 |
http://xiaofeng.com | http://xiaofeng.com:8080 | 不能 |
http://localhost | http://127.0.0.1 | 不能 |
http://xiaofeng.com | http://xiaofeng.com/user/test | 可以 |
3)HTML中允许跨域的标签
<img>-------显示远程图片
<script>------加载远程js
<link> ----加载远程CSS
4)Spring MVC跨域访问
CORS跨域资源访问
CORS是一种机制,使用额外的HTTP头通知浏览器可以访问其他域
URL响应头包含Access-Control-* 指明请求允许跨越
Spring MVC 解决跨域访问
方法1.@CrossOrigin -Controller跨越注解
//@CrossOrigin(origins = {"http://localhost:8080"})
//@CrossOrigin(origins = "*",maxAge = 3600)
方法2.<mvc:cors> -Spring MVC全局跨域设置
<mvc:cors>
<mvc:mapping path="/restful/**" allowed-origins="http://localhost:8080,http://www.xiaofeng.com" max-age="3600"/>
</mvc:cors>
四、Spring MVC 拦截器
(1)拦截器Interceptor
拦截器(Interceptor)用于对URL请求进行前置/后置过滤
Interceptoe与Filter用途相似,但实现方式不同
Interceptor底层就是基于Spring AOP面向切面编程实现
(2)拦截器开发流程
2.1Maven依赖servlet-api
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
2.2实现HandlerInterceptor接口
HandlerInterceptor接口
preHandle---前置执行处理
postHandle---目标资源也被Spring MVC框架处理
afterCompletion---响应文本已经产生
实现
public class MyInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println(request.getRequestURL()+"准备执行");
return true;
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println(request.getRequestURL()+"目标处理成功");
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println(request.getRequestURL()+"响应内容已经产生");
}
}
2.3applicationContext.xml配置过滤地址
<!--拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.xiaofeng.restful.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
(3)Interceptor使用技巧
3.1静态资源处理
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/restful/**"/>
<mvc:mapping path="/webapp/**"/>
<mvc:exclude-mapping path="/**.ico"/>
<mvc:exclude-mapping path="/**.jpg"/>
<mvc:exclude-mapping path="/**.png"/>
<mvc:exclude-mapping path="/**.gif"/>
<mvc:exclude-mapping path="/**.css"/>
<mvc:exclude-mapping path="/**.js"/>
<mvc:exclude-mapping path="/resources/**"/>
<bean class="com.xiaofeng.restful.interceptor.MyInterceptor1"/>
</mvc:interceptor>![传统web应用的问题.jpg](https://upload-images.jianshu.io/upload_images/21071511-4b13b3170c3fb5b3.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
</mvc:interceptors>
3.2多个Interceptor执行顺序
(4)logback日志组件
4.1maven依赖
<!--logback日志组件-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
4.2logback.xml
<?xml version="1.0" encoding="UTF-8" ?>
<configuration >
<!--常规配置-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
[%thread] %d %level %logger{10} - %msg%n
</pattern>
</encoder>
</appender>
<!--输出日志文件到f:logs/history.yyyy-MM-dd.log 一天一更新-->
<appender name="accessHistoryLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>f:/logs/history.%d.log</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>
[%thread] %d %level %logger{10} - %msg%n
</pattern>
</encoder>
</appender>
<!--常规配置 在控制台打印日志-->
<root level="debug">
<appender-ref ref="console"></appender-ref>
</root>
<!--输出日志文件到f:logs/history.yyyy-MM-dd.log 一天一更新 additivity="true"在控制台打印日志-->
<logger name="com.xiaofeng.restful.interceptor.AccessHistoryInterceptor" level="info" additivity="false">
<appender-ref ref="accessHistoryLog"></appender-ref>
</logger>
</configuration>
4.3logback组件的实例化
public class AccessHistoryInterceptor implements HandlerInterceptor {
//定义logback日志组件的对象
private Logger logger= LoggerFactory.getLogger(AccessHistoryInterceptor.class);
//这是在拦截器的前置处理方法
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
StringBuffer log=new StringBuffer();
log.append(request.getRemoteAddr());
log.append(" | ");
log.append(request.getRequestURL());
log.append(" | ");
log.append(request.getHeader("user-agent"));
//
logger.info(log.toString());
return true;
}
}