一、Spring
- IoC(Inversion of Control)中文名称:控制反转(对象的创建交给Spring管理)。
- DI(dependency injection )依赖注入。
- 容器(Container):放置所有被管理的对象。
- beans:容器中所有被管理的对象称为beans。
一.Spring IOC
1. 介绍
控制反转,对象的创建交给Spring管理。
2. Bean实例化的两种方式(面试题)
2.1.BeanFactory(spring创建对象)
通过构造方法进行实例化。默认使用无参构造。
创建方式:
- <bean> 标签配置。在Spring.xml配置中进行bean对象的创建。
<bean id="peo" class="com.bjsxt.pojo.People"></bean>
- @Component及子注解管理对象,配合 ComponentScan的扫描。
@Service
public class UserServiceImpl implements UserService{
}
<!-- 扫描service中的注解(Component及子注解) -->
<context:component-scan base-package="com.lyx.service"/>
- @Bean修饰的方法,方法的返回值交由spring管理,方法名为key。
- @Import 指定的类由spring管理。
2.2.FactoryBean(使用自定义的工厂类创建对象)
分为静态工厂和实例工厂两种方式。静态工厂和实例工厂类最主要的区别是,创建bean的方法是static修饰的。
1.自定对象工厂类。
public class PeopleFactory {
People peo = new People();
public People getInstance(){
return peo;
}
}
2.工厂交由spring管理,从工厂方法中获取对象交由spring管理。
<!-- 创建工厂实例 -->
<!-- 工厂bean-->
<bean id="factory" class="com.bjsxt.factory.PeopleFactory"></bean>
<!--
- factory-bean:工厂bean的id属性值。
- factory-method:工厂中创建bean的方法。
-->
<bean id="peo2" factory-bean="factory" factory-method="getInstance"></bean>
<!-- 静态工厂的配置 -->
<bean id="peo3" class="com.bjsxt.factory.PeopleStaticFactory" factory-method="newInstance"></bean>
2.3.bean标签的scope属性(面试题)
Spring中
<bean>
的scope控制的是Bean的有效范围。一共有6个可取值。
<!--
singleton:默认值。bean是单例的,每次获取Bean都是同一个对象。
prototype:bean时原型的,每次获取bean都重新实例化。
request:每次请求重新实例化对象,同一个请求中多次获取时单例的。
session:每个会话内bean是单例的。
application:整个应用程序对象内bean是单例的。
websocket:同一个websocket对象内对象是单例的。
里面的singleton和prototype在Spring最基本的环境中就可以使用,不需要web环境。
但是里面的request、session、application、websocket都只有在web环境才能使用。
-->
<bean id="people" class="com.bjsxt.pojo.People" scope="singleton"></bean>
二.Spring DI(依赖注入)
1.手动注入(spring.xml中进行配置)
1.构造注入(依赖于构造方法完成属性赋值)
<bean id="peo4" class="com.bjsxt.pojo.People">
<constructor-arg type="int" value="1"></constructor-arg>
<constructor-arg type="java.lang.String" value="张三"></constructor-arg>
</bean>
2.设置注入(依赖于set方法完成属性赋值)
<bean id="peo5" class="com.bjsxt.pojo.People">
<property name="id" value="2"></property>
<property name="name" value="李四"></property>
</bean>
2.自动注入(使用注解)
自动注入指的都是bean之间的自动注入。能够自动注入必须保证Spring 容器中包含能被自动注入的bean。
1.两种方式进行配置:
- 在根标签
<beans>
中配置default-autowire属性 。- 在
<bean>
标签中配置autowire属性,和default-autowire取值相同。2.属性取值
- byName:根据bean的名字自动注入,依赖于set方法。
- byType:通过类的类型自动注入。
- constructor:通过构造方法进行注入。调用有参构造方法完成对象创建及属性自动注入。
3.@Autowired注解(自动注入)
用在类上的注解,先根据类型找,类型出现多个,再根据名字找。
1.特殊用法
- 用在属性上,使用反射进行注入。
- 用在set方法上,使用set方法注入。
- 用在有参构造方法上,使用有参构造进行注入。
2.特点
注解所在的bean和注入的bean都为spring容器管理的bean。
@Resouce(非spring注解) :先根据名字找,没有找到,再根据类型找。
三.单例设计模式(面试中笔试题)
只创建一个对象的设计模式。
1. 饿汉式
/*
单例:希望类只有一个
核心思想:
1. 构造方法私有
2. 对外提供一个能够获取对象的方法。
饿汉式:
优点:实现简单
缺点:无论是否使用当前类对象,加载类时一定会实例化。
*/
public class Singleton {
// 之所以叫做饿汉式:因为类加载时就创建了对象
private static Singleton singleton = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return singleton;
}
}
2. 懒汉式
/**
* 核心思想:
* 1. 构造方法私有。
* 2. 对外提供一个能够获取对象的方法。
*
* 懒汉式优点和缺点:
* 优点:
* 按需创建对象。不会在加载类时直接实例化对象。
* 缺点:
* 写法相对复杂。
* 多线程环境下,第一次实例化对象效率低。
*/
public class Singleton2 {
//懒汉式:不会立即实例化
private static Singleton2 singleton2;
private Singleton2() {}
public static Singleton2 getInstance() {
if (singleton2 == null) {// 不是第一次访问的线程,直接通过if判断条件不成立。直接return
synchronized (Singleton2.class) {
if(singleton2==null) {// 防止多个线程已经执行到synchronized
singleton2 = new Singleton2();
}
}
}
return singleton2;
}
}
四.循环依赖问题(重要面试题)
1.介绍
循环注入即多个类相互依赖,产生了一个闭环。
2. 解决循环依赖 (三级缓存)
- 一级缓存(singletonObjects):存放 实例化、属性注入和初始化完成的对象。
- 二级缓存(earlySingletonObjects):存放早期暴露出来的Bean对象,实例化和属性注入完成的对象。
- 三级缓存(singletonFactories):存放实例化完成的对象。存放bean创建工厂,以便于后面扩展有机会创建代理对象。
单例模式创建对象的步骤 :
实例化:调用对象的构造方法实例化对象。
属性注入:填充属性,这一步主要是对bean的依赖属性进行填充。
初始化:属性注入后,执行自定义初始化操作。
五.BeanFactory和ApplicationContext(经典面试题)
1. BeanFactory接口(懒汉式)
- BeanFactory是Spring中的顶级接口,接口中定了Spring容器最基本功能。是Spring IoC的最核心接口。
- ApplicationContext的getBean方法其实就是BeanFactory的方法。
- BeanFactory是在调用getBean方法的时候才实例化Bean。(懒汉式)
BeanFactory最常用实现类是XmlBeanFactory。但是从Spring 3.1 版本开始,使用DefaultListableBeanFactory和XMLBeanDefinitionReader替代。
2. ApplicationContext接口(饿汉式)
BeanFactory的子接口。比BeanFactory的功能更加强大,除了BeanFactory的功能,还包含了:
- AOP 功能
- 国际化(MessageSource)
- 访问资源,如URL和文件(ResourceLoader)
- 消息发送机制(ApplicationEventPublisher)
- Spring集成Web时的WebApplicationContext
ApplicationContext是在加载配置文件后立即实例化Bean。(饿汉式)
在使用时ApplicationContext时多使用ClassPathXmlApplicationContext。
六.动态代理设计模式
动态创建代理对象。
1.JDk动态代理
JDK动态代理是基于接口。先创建接口,然后创建被代理对象类。
//JDK动态代理
/*
功能:
JDK动态代理为类创建代理对象,实现目标对象功能的增强
JDK动态代理为接口创建实现类
*/
/*
Proxy.newProxyInstance中的参数
参数一:类加载
参数二:接口的类对象(JDK动态代理基于接口实现)
参数三:运行时底层实现select()方法【该过程看不到】,实现的方法中调用了 invoke()
*/
@Test
public void test01() {
//创建被代理对象
UserServicesImpl userServicesImpl = new UserServicesImpl();
UserService userService = (UserService) Proxy.newProxyInstance(
Test2.class.getClassLoader(),//
UserServicesImpl.class.getInterfaces(),//必须为接口的类对象
new InvocationHandler() {//
@Override
/*
invoke方法中的参数:
参数一:代理对象
参数二:目标方法(被代理方法)
参数三:目标方法参数
* */
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
Object invoke = method.invoke(userServicesImpl, objects);
return invoke;
}
}
);
//代理对象调用方法
userService.select();
}
2.Cglib动态代理
Cglig动态是第三方提供的技术,需要导入jar包,并且可以是基于类继承,也可以是基于接口实现。
使用:先创建接口或类,然后创建被代理的对象类(继承或实现)。
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
@Test//2.为类创建代理对象,实现目标方法的功能增强(基于接口实现)
public void test04(){
//创建目标对象(被代理对象)
UserServicesImpl userServicesImpl = new UserServicesImpl();
Enhancer enhancer = new Enhancer();
enhancer.setInterfaces(UserServicesImpl.class.getInterfaces());
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("前置 增强");
//调用 目标方法
Object invoke = method.invoke(userServicesImpl, objects);
System.out.println("后置 增强");
return invoke;
}
});
//获取代理对象并调用方法
UserService userService = (UserService) enhancer.create();
userService.select();
}
@Test// 3.为类创建代理对象,实现目标方法的功能增强(基于继承实现)
public void test05(){
Enhancer enhancer = new Enhancer();//增强器
enhancer.setSuperclass(Aaa.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("前置 增强");
Object invoke = methodProxy.invokeSuper(o, objects);
System.out.println("后置 增强");
return invoke;
}
});
//获取代理对象调用方法
UserServicesImpl2 userServices = (UserServicesImpl2) enhancer.create();
userServices.querry(1,2);
}
}
3.总结
JDK动态代理机制是委托机制,只能对实现了接口的类生成代理,底层通过反射机制实现。(重点)
CGLIB动态代理机制是接口或继承机制,针对类生成代理,被代理类和代理类是继承关系,底层通过字节码处理框架asm,修改字节码生成子类
七.AOP
1. SpringAOP介绍(常见面试题)
面向切面编程是对面向对象编程的补充。 AOP 中模块化的单位是切面。
AOP中的专业术语:
Aspect: 切面。为方法添加增强功能的过程。
join point: 切入点。就是被代理的方法,也叫目标方法。
Advice: 通知。就是增强内容。前置、后置、环绕、异常等通知。
Pointcut: 切点。就是表达式,通过切点表达式可以找到目标方法(join point)。
Weaving: 织入。织入就是把 通知 添加到 切入点 的过程。
AOP Proxy:代理。Spring支持JDK动态代理和cglib动态代理两种方式,可以通过proxy- target- class=true把默认的JDK动态代理修改为Cglib动态代理。
2. 实现AOP的两种方式
1.实现AOP需要引入依赖:
<!--支持切点表达式等-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.9.1</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
2.实现AOP的两种方式:
-
Schema-based:所有的通知都需要实现特定类型的接口实现通知。
在Schema-based方式中通知的分类(面试题):
- 前置通知,通知需要实现MethodBeforeAdvice接口。
- 后置通知,通知需要实现AfterReturningAdvice接口。
- 环绕通知,通知需要实现MethodInterceptor接口。
- 异常通知,通知需要实现ThrowsAdvice接口。
<!-- 配置异常通知对象 -->
<bean id="mythrow" class="com.bjsxt.advice.MyThrow"/>
<aop:config>
<aop:pointcut id="mypoint" expression="execution(* com.bjsxt.service.impl.PeopleServiceImpl.test())"/>
<!-- 织入异常通知 -->
<aop:advisor advice-ref="mythrow" pointcut-ref="mypoint"/>
</aop:config>
</beans>
-
AspectJ:可以使用普通Java类结合特定的配置实现通知。
在AspectJ方式中通知的分类(面试题):
前置通知:before。
后置通知:after。
after是否出现异常都执行的后置通知。
after-returning切入点不出现异常时才执行的后置通知。
环绕通知:around。
异常通知:after-throwing。
<!-- 配置通知对象 -->
<bean id="myadvice2" class="com.bjsxt.advice.MyAdvice2"></bean>
<aop:config>
<!-- 基于Aspectj方式配置 -->
<aop:aspect ref="myadvice2">
<!--
切点配置
args():编写参数名,参数名称和目标方法中参数名称一致。
-->
<aop:pointcut id="mypoint" expression="execution(* com.bjsxt.service.impl.PeopleServiceImpl.test(int,boolean)) and args(id1,bool1)"/>
<aop:before method="mybefore" pointcut-ref="mypoint" arg-names="id1,bool1"/>
<aop:after-returning method="myafter" pointcut-ref="mypoint" arg-names="id1,bool1"/>
<aop:after method="myafter2" pointcut-ref="mypoint" arg-names="id1,bool1"/>
<!-- pjp由Spring自动注入 -->
<aop:around method="myaround" pointcut-ref="mypoint" arg-names="pjp,id1,bool1"/>
<aop:after-throwing method="mythrow" pointcut-ref="mypoint" arg-names="ex,id1,bool1" throwing="ex"/>
</aop:aspect>
</aop:config>
Schema-based和Aspectj的区别:
- Schema-based:基于接口实现的。 AspectJ方式:是基于配置实现的。
- Schame-based是运行时增强,AspectJ是编译时增强。
- 切面比较多时,最好选择AspectJ方式,因为AspectJ方式要快很多。
3.注解方式实现AOP
<!--配置注解扫描路径-->
<context:component-scan base-package="com.bjsxt"/>
<!--配置AOP注解生效-->
<aop:aspectj-autoproxy expose-proxy="true"/>autoproxy expose-proxy="true"/>
注意:
配置Spring容器注解扫描的路径。
配置AOP注解生效。
@Component | 相当于配置文件的bean标签,将某个类的对象扫描到Spring容器中。 |
@Aspect | 声明该类为通知类。结合@Component在通知类上使用。 |
@pointcut | 声明切点,方法上使用。 |
@Before | 声明方法为前置通知方法。 |
@After | 声明方法为后置通知方法。 |
@Around | 声明方法为环绕通知方法。 |
@AfterThrowing | 声明方法为异常通知方法。 |
AOP工作流程:
1.通过切点表达式获取切入点(目标对象和目标方法)
2.为目标对象创建代理对象
3.在代理对象中织入通知,执行的目标方法
八.Sping声明式事务
声明式事务是基于AOP实现的。把开启事务的代码放在前置通知中,把事务回滚和事务提交的代码放在了后置通知中。
1.配置声明式事务
需要在配置文件中引入xmlns:tx命名空间。
<!--1. 配置声明式事务(事务开启,事务提交,事务回滚都进行了封装,配合AOP完成事务的控制 -->
<!--
前置通知:开启事务
后置通知:事务提交
异常通知:事务回滚
-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 事务管理必须连接数据库,需要注入数据源对象 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 2. 配置事务通知 配置哪些方法织入事务的通知 -->
<!-- 相当于通知类,只有方法出现了异常触发异常通知,实现事务回滚,所以绝对不能在service里面try...catch-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- 配置的方法才会织入事务的通知 -->
<tx:attributes>
<!-- -->
<tx:method name="alertAccountt"/>
<!-- <tx:method name="select*"/>-->
<!-- 所有的方法都需要进行事务管理。在配置方法名称是支持*作为通配符 -->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!--
3. 设定哪个方法需要被声明式事务管理,使用AOP完成,
根据切点表达式获取切入点(目标对象中的目标方法),为目标对象创建代理对象,
将通知和目标方法完成织入。
-->
<aop:config>
<aop:pointcut id="mypoint" expression="execution(* com.bjsxt.service.impl.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="mypoint"></aop:advisor>
</aop:config>
2.注解配置声明式事务(主要使用)
Spring 注解配置事务时,只需要在需要有事务管理的方法上添加@Transactional注解。
必须保证配置注解的方法所在的类已经放入到Spring容器中。
- 配置注解扫描
<context:component-scan base-package="com.bjsxt.service.impl"></context:component-scan>
- 开启事务注解的支持
<tx:annotation-driven></tx:annotation-driven>
- 必须配置事务管理器类
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
- @Transactional的使用
@Transactional用在类上,整个类中方法都生效。
@Transactional用在方法上,该方法生效。用在方法上优先级更高。
3.声明式事务四个基础属性
<tx:method>
标签有下面属性的配置,@Transaction注解也支持部分属性(如果有则含义相同,如果没有则表示在注解中不需要配置)。
1. name属性
配置哪些方法需要有事务控制,支持*通配符。
2. readonly属性
是否为只读事务,有true和false两种。
3. rollback-for属性
异常类型全限定路径,表示出现什么类型的异常进行数据回滚。 默认运行时异常及子类异常回滚,检查时异常不回滚。
4. no-rollback-for属性
异常类型全限定路径,当出现什么异常的时候不进行数据回滚。
5. timeout属性
执行超过了设置的超时时间,回滚操作。(超时抛出异常)
4.事务传播行为(面试题)
4.1.介绍
事务传播行为:当出现service方法调用另一个service方法时(这些方法都被声明式事务管理)这些方法如何进行事务管理。
可以通过进行配置tx:method或@Transactional中的propagation属性来进行传播行为的设置 。
@Transactional(propagation = Propagation.MANDATORY)
- 默认情况下都认为每个方法都是没有事务的(事务自动提交)。
- 整个调用最终都是在调用者里面统一提交回滚。
- 在声明式事务中,如果是同一个类的多个方法相互调用,属于同一个事务。
因为声明式事务是基于AOP实现的,AOP是基于动态代理实现的,为同一个对象创建一个代理对象,所以实现出来的效果只有对第一个调用的方法添加上了声明式事务管理,其他方法都是普通的方法调用。
4.2 propagation属性的可选值
Propagation.REQUIRED(默认) | 当前有事务,使用当前事务。当前没有事务,开启新的事务。 |
Propagation.NEVER | 必须在非事务状态下执行。没有事务,正常运行。有事务,抛出异常。 |
Propagation.NESTED | 必须在事务状态下执行。没有事务,创建事务。有事务,创建嵌套事务。 |
Propagation.REQUIRES_NEW | 没有事务,创建事务。有事务,挂起当前事务,创建新的事务。最后统一操作。 |
SUPPORTS | 没有事务,非事务执行。有事务,使用当前事务。 |
Propagation.NOT_SUPPORTED | 非事务下执行。有事务,挂起事务,以非事务执行,执行后,恢复挂起的事务。 |
propagation = Propagation.MANDATORY | 事务下执行。没有事务,抛出异常。 |
九、Bean的生命周期(非常重要)
Spring中Bean的生命周期就是指Bean从初始化到销毁的过程。Bean最简单的实现就是直接使用<bean>
标签定义这个Bean。
Bean生命周期流程:
- 编写bean的定义信息(xml,注解)。
- 通过BeanDefinitionReader 读取bean的定义信息。
- 解析出bean的定义信息。
- 可以通过BeanFactoryPostProcessor接口实现类,操作bean的定义信息。
- 实例化bean对象。
- 属性注入。
- 可以使用相关的Aware接口,获取bean的相关信息,容器信息...。
- 可以使用BeanPostProcessor接口中before方法操作对象。
- 可以使用init-method调用自定义的初始化方法。
- 可以使用BeanPostProcessor接口中after方法操作对象。
- 存储到单例池(一级缓存中)。
二、Sping MVC
一. Spring MVC介绍
- Spring MVC本质为Spring 框架的一个扩展 ,属于Spring Framework的二级子项目。
- Spring MVC是基于Front设计模式。
- Spring MVC中有前端入口DispatcherServlet,里面编写了请求分发功能。
- EmpController在Spring MVC称为控制器类(Handler),里面的方法称为:控制单元(HandlerMethod)。
MVC三层中都有自己的功能。例如:
- M:在模型层包含:数据校验。
- V:在视图层包含:国际化、标签库。
- C:在控制层包含的功能就更多了:转发重定向、参数、拦截器、作用域等。
Spring中的父子容器问题:
二.Spring MVC环境搭建
导入依赖:
<!-- 依赖了Spring框架核心功能的5个依赖以及Spring整合Web的依赖spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.16</version>
</dependency>
配置springMVC.xml:
<context:component-scan base-package="com.bjsxt.controller"></context:component-scan>
<!-- 让Spring MVC的注解生效 不要引错xsd-->
<mvc:annotation-driven></mvc:annotation-driven>
编写web.xml内容:
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!-- 参数名称必须叫做:contextConfigLocation。单词和大小写错误都导致配置文件无法正确加载 -->
<param-name>contextConfigLocation</param-name>
<!-- springmvc.xml 名称自定义,只要和后面创建的文件名称对应就可以了。 -->
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!-- Tomcat启动立即加载Servlet,而不是等到访问Servlet才去实例化DispatcherServlet -->
<!-- 配置上的效果:Tomcat启动立即加载Spring MVC框架的配置文件-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!-- /表示除了.jsp结尾的uri,其他的uri都会触发DispatcherServlet。此处前往不要写成 /* -->
<url-pattern>/</url-pattern>
</servlet-mapping>
创建控制器类:
@Controller// 放入到Spring MVC容器中
public class FirstController {
/*
* 官方标准写法:
* 返回值是ModelAndView,对象中存储跳转资源路径及作用域值
*/
// 当前方法的映射路径
@RequestMapping("/first")
public ModelAndView test1(){
ModelAndView modelAndView = new ModelAndView("first.jsp");
return modelAndView;
}
/*
* 简化写法(平时使用的方式)
* 返回值是String,表示跳转的资源路径
*/
@RequestMapping("/first2")
public String test2(){
return "first.jsp";
}
}
三.@RequestMapping注解
1.使用
- @RequestMapping注解可以写在控制器类上,也可以写在控制单元方法上。
- 如果写在类上,表示当前类所有控制单元的映射路径,都以指定路径开头。
- 如果写在方法上,表示当前方法的映射路径。
2.注解的属性
1.path:映射的路径。
映射一个访问路径:path = {"aa"} 省略{} path = "aa"
映射多个访问路径:path = {"aa,bb"}。
2.value:和path作用相同,只有一个value属性时,value可以省略。
3.name:添加描述信息。
4.method:允许的请求方式。
RequestMethod.POST
RequestMethod.GET
...
简化:
@GetMapping 为 @RequestMapping(method = RequestMethod.GET)
@PostMapping 为 @RequestMapping(method = RequestMethod.POST)
...
5.params: 指定请求中必须携带的请求参数。
6.headers: 指定请求中必须携带的请求头。7.consumes:表示处理请求内容(Content-Type)的类型。
8.produces:配合@ResponseBody注解使用,指定响应内容的类型。单独使用没有意 义。
四.转发和重定向(重要)
- 在Spring MVC框架中,默认情况下都使用转发进行寻找资源。
- 在资源路径前面添加 forward: , 表示转发。
- 在资源路径前面添加 redirect: ,表示重定向。
五.静态资源放行
<!--配置静态资源放行-->
<!--mapping:当URI是什么样格式时,不再执行控制器,而是寻找静态资源。 ** 是通配符,表示任意层路径 -->
<!--location:去哪个目录中寻找静态资源。mapping中**的值是什么,就去location目录中找对应资源-->
<!--例如URL是http://localhost:8080/bjsxt/js/jquery.js 其中mapping的**就是jquery.js,就会去location的/js/目录中寻找jquery.js -->
<mvc:resources mapping="/js/**" location="/js/"></mvc:resources>
<mvc:resources mapping="/css/**" location="/css/"></mvc:resources>
<mvc:resources mapping="/images/**" location="/images/"></mvc:resources>
<!-- 静态资源没有在WEB-IBF中可以使用该方式 -->
<mvc:default-servlet-handler/>
六.控制单元的参数(接收请求参数)
@Controller
@RequestMapping("emp")
public class EmpController {
/* 解耦合方式:获取请求数据,springmvc封装好的 */
/*
* DispatcherServlet 会接收请求数据,可以将请求数据进行相关处理,然后传递给控制单元。
* */
//1、接收普通参数
//建议:尽量使用封装类型,请求参数中没有携带该参数,自动赋值为null
//@RequestParam():请求参数中的名字 和 接收参数名不一致时,使用该注解指定为请求参数名的名字
@RequestMapping("a1")
public String a1(@RequestParam("name") String uname, Integer age){
System.out.println(uname);
System.out.println(age);
return "/index.jsp";
}
//2、使用JavaBean对象接收请求参数
//注意:即使请求参数中没有Emp的属性,创建emp对象,传递到控制单元中
// 使用JavaBean接收请求参数需要依赖于set方法
@RequestMapping("a2")
public String a2(Emp emp){
System.out.println(emp);
return "/index.jsp";
}
@RequestMapping("a3")
public String a3(Emp emp, String name, String age){
System.out.println(emp);
System.out.println(name);
System.out.println(age);
return "/index.jsp";
}
//3、接收多个同名参数
//数组:请求参数名和控制单元参数名相同即可直接接收
//集合(List集合为例):必须通过@RequestParam执行请求参数名
// 如果为JavaBean中的属性为List集合接收同名多个属性,不需要进行额外处理。
@RequestMapping("a4")
public String a4(String[] strs){
System.out.println(Arrays.toString(strs));
return "/index.jsp";
}
@RequestMapping("a5")
public String a5(@RequestParam("strs") List<String> strs){
System.out.println(strs);
return "/index.jsp";
}
@RequestMapping("a6")
public String a6(Emp emp){
System.out.println(emp);
return "/index.jsp";
}
@RequestMapping("a7")
//注意:默认支持 yyyy/MM/dd 的日期格式
//手动指定接收的日期格式(@DateTimeFormat),一定手动指定了接收的日期格式,默认的日期格式不再生效
public String a7(@DateTimeFormat(pattern = "yyyy-MM-dd") Date bir){
System.out.println(bir);
return "/index.jsp";
}
@RequestMapping("a8")
public String a8(Emp emp){
System.out.println(emp);
return "/index.jsp";
}
/* 接收请求头中的数据 */
@RequestMapping("a9")
public String a9(@RequestHeader String accept){
System.out.println(accept);
return "/index.jsp";
}
@RequestMapping("a10")
public String a10(@RequestHeader(value = "Accept-Language") String str){
System.out.println(str);
return "/index.jsp";
}
/* 向作用域对象中存值(request作用域) */
//方式一:
@RequestMapping("a11")
public ModelAndView a11(){
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("/index.jsp");
modelAndView.addObject("uname", "zs");
return modelAndView;
}
//方式二:
@RequestMapping("a12")
public String a12(Map<String, Object> map){
map.put("uname", "lsssss");
return "/index.jsp";
}
//方式三:
@RequestMapping("a13")
public String a13(Model model){
model.addAttribute("uname", "wwwwww");
return "/index.jsp";
}
}
七.@ResponseBody和@RequestBody注解
1.@ResponseBody
@ResponseBody:控制单元添加了该注解,不会执行视图解析器,将控制单元的返回值放入到响应流直接响应回到客户端。
使用:
- 默认:
1.控制单元只能返回String类型的数据。返回其他数据类型出现406状态码。
2.配合@RequestMapping(produces = "text/plain;charset=utf-8")设置响应内容类 型及编码格式。
- 引入了Jackson的依赖:
1.控制单元可以返回,JavaBean,数组[JavaBean], List<JavaBean>, Map, List<Map> 等类型数据。
2.springmvc默认使用jack将控制单元的返回值变为json格式的字符串,设置响应 内容类型为application/json;charset=utf-8。
@RestController注解:效果和控制器中所有的方法都包含@ResponseBody注解一样。
2.@RequestBody注解
将格式为 json ,xml 的客户端请求参数转换为 javabean。需要引入相依赖(Jackson)。
$.ajax({
url:"testContentType",
contentType:"application/json",// 修改请求内容类型为JSON
data:'{"id":1,"name":"张三"}',// json格式传多个数据必须有单引号,没有单引号无效
type:"post",// 不能是GET类型请求
success:function (data) {
console.log(data);
},
dataType:"json"
});
@RequestMapping("/testContentType")
@ResponseBody
public People testContentType(@RequestBody People peo) {
System.out.println(peo);
return peo;
}
八.Spring MVC文件上传和下载
1. 文件上传
导入依赖:
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
在页面中编写文件上传代码:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
姓名:<input type="text" name="name"/><br/>
头像:<input type="file" name="photo"/><br/>
地址:<input type="text" name="address"/><br/>
<input type="submit" value="提交"/><br/>
</form>
</body>
</html>
配置上传文件解析器:
<!-- 文件上传时,必须配置文件解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
<!-- 限制上传文件大小 -->
bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="1024"></property>
</bean>
编写单元方法处理上传请求:
/**
* 文件上传控制单元方法实现
* @param name 也可以使用JavaBean接收name的值
* @param address 也可以使用JavaBean接收address的值
* @param photo 名字必须和表单中文件域的name属性值相同
* @return
* @throws IOException transferTo抛出的异常,可以使用try...catch处理异常。示例中为了让代码看起来简洁直接抛出了。
*/
@RequestMapping("/upload")
public String upload(String name, String address, MultipartFile photo,HttpServletRequest request) throws IOException {
if(!photo.isEmpty()) {
long timeMillis = System.currentTimeMillis();
Random random = new Random();
String fileName = timeMillis + "" + random.nextInt(1000);
String oldName = photo.getOriginalFilename();
String suffix = oldName.substring(oldName.lastIndexOf("."));
// 获取到当前项目images目录,发布到Tomcat后的绝对路径。
String realPath = request.getServletContext().getRealPath("/images");
System.out.println(realPath);
// 保存到当前项目的images目录中。
photo.transferTo(new File(realPath,fileName + suffix));
}
return "/upload.jsp";
}
2. 文件下载
@RequestMapping("/download")
public void download(HttpServletRequest req, HttpServletResponse response, String filename) {
try {
// 因为是GET请求,所以要解决请求参数中文乱码问题
String fileNameUtf8 = new String(filename.getBytes("iso-8859-1"), "utf-8");
// 图片名称满足固定格式
String newFilenameUtf8 = "来自尚学堂的"+fileNameUtf8;
String newFilenameISO = new String(newFilenameUtf8.getBytes("utf-8"),"iso-8859-1");
// 此处是ISO-8859-1编码的内容
response.setHeader("Content-Disposition", "attachment;filename=" + newFilenameISO);
// 此处必须是UTF-8解决参数乱码问题的名称
File file = new File(req.getServletContext().getRealPath("/images"), fileNameUtf8);
FileInputStream fis = new FileInputStream(file);
ServletOutputStream os = response.getOutputStream();
IOUtils.copy(fis, os);
} catch (IOException e) {
e.printStackTrace();
}
}
九.拦截器(重点)
只有URL匹配到了控制单元,拦截器才能生效。
1. 使用拦截器
- 创建拦截器:
public class MyInterceptor implements HandlerInterceptor {
@Override
//执行时机:单元方法执行之前。返回false表示拦截此次请求,返回true表示放行。
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor.preHandle");
return fasle;
}
//执行时机:单元方法执行之后,视图解析器解析渲染视图之前。
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor.postHandle");
}
//执行时机:视图解析器解析渲染视图完成之后。
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptor.afterCompletion");
}
}
- 配置拦截器:
<!--
配置拦截器
作用:
让声明的拦截器类生效完成单元方法请求的拦截
使用:
在springmvc.xml文件中使用<mvc:interceptors>标签声明拦截的配置信息
在<mvc:interceptors>标签下使用子标签完成拦截器的配置
全局拦截
在<mvc:interceptors>直接声明bean标签配置拦截器的bean,拦截所有的单元方法请求。
局部拦截
在<mvc:interceptors>标签下使用子标签<mvc:interceptor>来声明局部拦截
在<mvc:interceptor>标签下使用子标签配置拦截返回以及拦截器的bean
<mvc:mapping path="/demo"/> 要拦截的范围,可以声明多个
<bean id="mm" class="com.bjsxt.inter.MyInter"></bean> 拦截器
-->
<!--配置拦截器-->
<mvc:interceptors>
<!--配置拦截器的bean对象,拦截所有的单元方法-->
<bean class="com.bjsxt.interceptor.MyInterceptor2"></bean>
<!--配置具体的拦截器的bean极其拦截范围,可以配置多个-->
<mvc:interceptor>
<mvc:mapping path="/myController/demo"/><!--配置拦截的单元方法的访问路径,第一个/表示项目根目录,可以多个-->
<mvc:mapping path="/myController/kk/*"/><!--支持*通配符表示任意个数的任意字符,**表示路径及子路径-->
<bean class="com.bjsxt.interceptor.MyInterceptor"></bean><!--配置拦截器的bean对象,只在当前mvc:interceptor内有效-->
</mvc:interceptor>
</mvc:interceptors>
拦截器执行顺序总结:
拦截器栈执行顺序:
2.拦截器和过滤器的区别(面试题)
- 过滤器(Filter):由JavaEE提供的过滤器. 请求到达资源(servlet,页面,css,js,img,...)之前都要经过过滤器,资源响应回到客户端之前经过过 滤器。
- 拦截器(Interceptor):由SpringMVC提供的拦截器. 请求到到控制单元之前经过拦截器(preHandle),控制单元执行完成后经过拦截器(postHandle),向页面响应完成,经过拦截器(afterCompletion)。
区别:
来源不同
拦截器是SpringMVC中的技术,过滤器是Java EE中的技术。
生效位置不同
拦截器是进入DispatcherServlet后才能执行,过滤器是进入到Servlet容器前就可以触发。
目标不同
拦截器拦截的目标是HandlerMethod(控制单元,控制器方法),过滤器可以过滤所有的URL。
运行机制不同
拦截器是在HandlerMethod执行前后和视图处理完成后执行,分为三部分。过滤器只能在目标资源前后执行。
接口中方法类型不同
拦截器中的方法都是default方法,可以重写也可以不重写。过滤器中的方法都是abstract方法,如果当前类不是抽象类,必须重写。
上下文不同
拦截器被Spring MVC管理,可以获取到Spring容器中内容。Filter被Tomcat管理,所以无法获取Spring容器内容。
十.视图解析器(重点)
1.ModelAndView:模型数据(存储业务数据)和视图
Model:模型数据(存储模型层中查询到的数据)
View: 视图(页面属于视图中的一种)
2.控制单元执行完成后,将控制单元返回的结果固定封装为ModelAndView对象。Model中存储了业务数据,View通常存储视图名。最终目的:将Model中的业务数据通过 视图 渲染 到客户端。
底层源码:
ModelAndView mv;
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
执行控制单元,控制单元返回结果封装为ModelAndView。3. 视图解析器:ViewResolver(接口),根据ModelAndView对象中解析出的视图名找到 对应的视图对象,并返回。
以.jsp视图为例:
1.控制单元执行后,执行handle(processedRequest, response, mappedHandler.getHandler()),返回ModelAndView对象 (viewname为index.jsp)
2.获取到ModelAndView后,执行render(mv, request, response);
3.在render方法中调用了view = resolveViewName(viewName, mv.getModelInternal(), locale, request),获取到视图名。
4.在resolveViewName(String viewName,Locale locale)方法中, 视图解析器 根据视图 名找到对应的视图对象,并返回。
(1)默认的使用的视图解析器:InternalResourceViewResourceView。
(2) .jsp的视图对象为:InternalResourceView。
5.返回视图对象后,调用view.render(mv.getModelInternal(), request, response)。实际完 成Model中的数据通过视图响应回到客户端。
6. .jsp的InternalResourceView的视图对象渲染时:
(1)将Model中的数据存储到请求域对象中。
(2)将请求转发到.jsp -> .java(获取请求去对象中的数据)-> .class -> 将结果输出 到客户端。
十一.Spring MVC中组件
1. 组件介绍
DispatcherServlet被初始化的时候其底层内部也会完成第2到第10个组件的初始化,调用其initStrategies方法来完成。
protected void initStrategies(ApplicationContext context) {
this.initMultipartResolver(context);
this.initLocaleResolver(context);
this.initThemeResolver(context);
this.initHandlerMappings(context);
this.initHandlerAdapters(context);
this.initHandlerExceptionResolvers(context);
this.initRequestToViewNameTranslator(context);
this.initViewResolvers(context);
this.initFlashMapManager(context);
}
2.常用组件说明
- DispatcherServlet:前端控制器。Spring MVC的入口,也是整个流程控制中心。其他组件 由DispatcherServlet统一调度,降低了组件和组件之间的耦合度。
- MultipartResovler:多部分处理器。文件上传时需要使用。
- LocaleResolver:解决客户端的区域和时区问题。
- ThemeResolver:主题解析器。提供自定义布局。
- HandlerMapping: 映射处理器。主要负责处理URL,并找到对应的HandlerMethod(只是找到控制单元)。简单说就是找@RequestMapping注解中映射路径是否有和URL匹配的。
- HandlerAdapter:适配器。负责调用具体的HandlerMethod(调用并运行)。
- HandlerExceptionResovler:异常处理器。异常处理,根据不同异常返回视图。
- RequestToViewNameTranslator:从请求中获取到视图名称。
- ViewResovler:视图解析器,负责解析字符串视图名和物理视图文件的。
- FlashMapManager:主要用于存储属性的,本质是一个Map。多用在重定向时。FlashMap在重定向之前存储,重定向之后删除。
- ModelAndView:模型和视图。Spring MVC中提供的模型和视图接口。
- HandlerInterceptor:拦截器。拦截控制器资源的。
十二.SpringMVC运行原理(常见面试题)
SpringMVC执行流程:
Tomcat启动:
- 监听到了ServletContext对象创建,加载Spring配置文件,创建Spring容器。
- Tomcat启动后创建DispatcherServlet,DispatcherServlet初始化时:
1.加载Springmvc配置文件,创建Springmvc容器。
2.初始化SpringMVC相关组件:MultipartResovler、HandlerMapping、 HandlerAdapterHandler、ExceptionResovler、ModelAndView、ViewResovler、 HandlerInterceptor。
客户端向服务端发起请求,Spring MVC总体入口中央调度器DispatcherServlet进行请求分发。
中央调度器DispatcherServlet把URL交给映射处理器HandlerMapping进行解析URL。
映射处理器HandlerMapping将请求映射为HandlerExecutionChain处理器执行链
可以为多个处理器拦截器HandlerInterceptor
处理器Handler对象(处理Controller)。
将处理器执行链HandlerExecutionChain返回到中央调度器DispatcherServlet。
DispatcherServlet根据返回的处理器执行链HandlerExecutionChain获得的处理器Handler,根据处理器Handler选择处理器适配器HandlerAdapter。
执行拦截器的preHandle()方法。
调用具体的Handler处理器(处理Controller),在填充Handler的入参过程中会执行数据转换、数据格式化、数据验证,调用具体的Controller完成处理功能,并创建ModelAndView对象。
执行拦截器的postHandle()方法。
将ModelAndView对象返回到处理器适配器HandlerAdapter。
处理器适配器HandlerAdapter将ModelAndView对象返回到中央调度器DispatcherServlet。
中央调度器DispatcherServlet调用视图解析器ViewResolver解析视图。
将解析的视图View对象返回到中央调度器DispatcherServlet。
渲染视图,将视图返回到中央调度器DispatcherServlet。
执行拦截器afterCompletion()方法。
中央调度器DispatcherServlet响应回到浏览器。