如果对你有帮助,点个赞或者评论一下呗 😃
Spring学习笔记
文章目录
Bean
Bean的基本属性
- id(唯一标识一个bean)
- class(使用完全限定名称指定类路径)
Scope
取值范围 | 说明 |
---|---|
singleton | 默认值,单例的 |
prototype | 多例的 request |
request | WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中 |
session | WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中 |
global | WEB 项目中,应用在 Portlet 环境,如果没有 Portlet 环境那么globalSession 相当于session |
1)当scope的取值为singleton时
Bean的实例化个数:1个
Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例
Bean的生命周期:
对象创建:当应用加载,创建容器时,对象就被创建了
对象运行:只要容器在,对象一直活着
对象销毁:当应用卸载,销毁容器时,对象就被销毁了
2)当scope的取值为prototype时
Bean的实例化个数:多个
Bean的实例化时机:当调用getBean()方法时实例化Bean
对象创建:当使用对象时,创建新的对象实例
对象运行:只要对象在使用中,就一直活着
对象销毁:当对象长时间不用时,被 Java 的垃圾回收器GC回收了W
如下配置实例:
<bean id="userDao" class="com.itcast.dao.impl.UserDaoImpl" scope="prototype"></bean> // 多例
Bean标签的生命周期
init-method:指定类中的初始化方法名称
destroy-method:指定类中销毁方法名称
Bean标签的实例化三种形式
1) 使用无参构造方法实例化,它会根据默认无参构造方法来创建类对象,如果bean中没有默认无参构造函数,将会创建失败。如下
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
2) 工厂静态方法实例化工厂的静态方法返回Bean实例
//工厂方法
public class StaticFactoryBean {
public static UserDao createUserDao(){
return new UserDaoImpl();
}
}
// spring的配置
<bean id="userDao" class="com.itheima.factory.StaticFactoryBean" factory-method="createUserDao" />
3) 工厂实例方法实例化,工厂的非静态方法返回Bean实例
// 工厂类
public class DynamicFactoryBean {
public UserDao createUserDao(){
return new UserDaoImpl();
}
}
// 配置文件配置
<bean id="factoryBean" class="com.itheima.factory.DynamicFactoryBean"/>
<bean id="userDao" factory-bean="factoryBean" factory-method="createUserDao"/>
Bean的依赖注入
依赖注入(Dependency Injection):它是 Spring 框架核心 IOC 的具体实现。在编写程序时,通过控制反转,把对象的创建交给了 Spring,但是代码中不可能出现没有依赖的情况。IOC 解耦只是降低他们的依赖关系,但不会消除。例如:业务层仍会调用持久层的方法。那这种业务层和持久层的依赖关系,在使用 Spring 之后,就让 Spring 来维护了。简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。依赖注入的方式有:Set方法注入、构造方法注入。
Set方法
主要的类:
DAO:
public class UserDaoImpl implements UserDao {
public void save() {
System.out.println("user dao 启动了");
}
}
Service:
public class UserServiceImpl implements UserService {
private UserDaoImpl userDao;
private String name;
private int age;
private Date date;
public void setUserDao(UserDaoImpl userDao) {
this.userDao = userDao;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setDate(Date date) {
this.date = date;
}
public UserServiceImpl(UserDaoImpl userDao, String name, int age, Date date) {
this.userDao = userDao;
this.name = name;
this.age = age;
this.date = date;
}
public UserServiceImpl(){}
public void save() {
}
}
从上面的service中可以看出service依赖 dao的UserDao,以及一些其他普通数据类型。下面就通过配置进行依赖注入。
<bean id="userDao" class="com.itcast.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.itcast.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"></property> // ref 代表使用了引用数据类型,若引用了其他的数据类型,就使用其他数据类型的id
<property name="name" value="wallace"></property> // value是普通的数据类型
<property name="age" value="11"></property> // name属性对应的是该类的 setXXX方法中的XXX的开头小写。
<property name="date" ref="now"></property>
</bean>
<bean id="now" class="java.util.Date"></bean>
构造器方法:
类依然使用上面的UserDao和UserService
配置方式如下:
<bean id="userDao" class="com.itcast.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.itcast.service.impl.UserServiceImpl">
<constructor-arg name="userDao" ref="userDao"></constructor-arg>
<constructor-arg name="name" value="wallace"></constructor-arg>
<constructor-arg name="age" value="11"></constructor-arg>
<constructor-arg name="date" ref="now"></constructor-arg>
</bean>
<bean id="now" class="java.util.Date"></bean>
总结:set注入使用==标签,构造函数注入使用==标签。
Bean的依赖注入的数据类型
上面的操作,都是注入的引用Bean,除了对象的引用可以注入,普通数据类型,集合等都可以在容器中进行注入。注入数据的三种数据类型:
-
普通数据类型
-
引用数据类型
-
集合数据类型
引入其他配置文件
实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分配置拆解到其他配置文件中,而在Spring主配置文件通过import标签进行加载。
<import resource="applicationContext-xxx.xml"/>
Spring 的相关API
ApplicationContext的继承体系以及实现类
applicationContext:接口类型,代表应用上下文,可以通过其实例获得 Spring 容器中的 Bean 对象
实现类:
1)ClassPathXmlApplicationContext
它是从类的根路径下加载配置文件 推荐使用这种
2)FileSystemXmlApplicationContext
它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
3)AnnotationConfifigApplicationContext
当使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。
getBean()方法
public Object getBean(String name) throws BeansException {
assertBeanFactoryActive();
return getBeanFactory().getBean(name);
}
public <T> T getBean(Class<T> requiredType) throws BeansException {
assertBeanFactoryActive();
return getBeanFactory().getBean(requiredType);
}
其中,当参数的数据类型是字符串时,表示根据Bean的id从容器中获得Bean实例,返回是Object,需要强转。当参数的数据类型是Class类型时,表示根据类型从容器中匹配Bean实例,当容器中相同类型的Bean有多个时,则此方法会报错.
方法调用:
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService1 = (UserService) applicationContext.getBean("userService");
UserService userService2 = applicationContext.getBean(UserService.class);
配置文件的加载
jdbc.properties配置文件
加载这种类型的文件需要再application.xml文件加入context的命名空间和约束。如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" // bean的命名空间
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" // context命名空间
xmlns:context="http://www.springframework.org/schema/context" // bean的约束条件
xsi:schemaLocation="http://www.springframework.org/schema/beans
htt p://www.springframework.org/schema/beans/spring-beans.xsd//context约束条件 http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
上面加载之后就可以通过加载配置文件信息,如下:
<context:property-placeholder location="classpath:jdbc.properties"/> // 类路径下的jdbc.properties文件
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"/> // 通过${变量名}加载文件属性
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
Spring注解开发
Spring原始注解
Spring是轻代码而重配置的框架,配置比较繁重,影响开发效率,所以注解开发是一种趋势,注解代替xml配置文件可以简化配置,提高开发效率。Spring原始注解主要是替代的配置 ,从而尽量解决配置繁重的问题。
注解 说明
@Component 使用在类上用于实例化Bean
@Controller 使用在web层类上用于实例化Bean
@Service 使用在service层类上用于实例化Bean
@Repository 使用在dao层类上用于实例化Bean
@Autowired 使用在字段上用于根据类型依赖注入
@Qualififier 结合@Autowired一起使用用于根据名称进行依赖注入
@Resource 相当于@Autowired+@Qualififier,按照名称进行注入
@Value 注入普通属性
@Scope 标注Bean的作用范围
@PostConstruct 使用在方法上标注该方法是Bean的初始化方法
@PreDestroy 使用在方法上标注该方法是Bean的销毁方法
要使用注解,需要在application.xml上进行配置,配置组件扫描作用是指定哪个包及其子包下的Bean需要进行扫描以便识别使用注解配置的类、字段和方法,如下:
<!--注解的组件扫描,扫描该报下的所有类-->
<context:component-scan base-package="com.itheima"></context:component-scan>
Spring新注解
通过上面的注解配置减少了Bean在xml的配置,通过注解进行了简化,降低了配置。但无法全部取代,如下:
加载配置文件: context:property-placeholder
组件扫描: context:component-scan
随着技术的深入,大部分引入了新注解,通过新注解全面取代XML的配置。有如下新注解:
注解 说明
@Configuration 用于指定当前类是一个 Spring 配置类,当创建容器时会从该类上加载注解
@ComponentScan 用于指定 Spring 在初始化容器时要扫描的包。 作用和在 Spring 的 xml 配置文件中的 <context:component-scan base-package="com.itheima"/>一样
@Bean 使用在方法上,标注将该方法的返回值存储到 Spring 容器中
@PropertySource 用于加载.properties 文件中的配置
@Import 用于导入其他配置类
SpringAOP
什么是AOP?
AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。在程序运行期间,在不修改源码的情况下对方法进行功能增强。AOP 是 OOP 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
如何实现?
实际上,AOP 的底层是通过 Spring 提供的的动态代理技术实现的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。
AOP的动态代理技术
在Spring的AOP中用两种动态代理的方式来实现AOP。
JDK动态代理
使用JDK动态代理,是一种基于接口的动态代理。通过接口来创建一个动态代理对象。如下是实现方式:
1.目标类接口:
package com.itcast.proxy.jdk;
public interface TargetInterface {
public void method()
}
2.目标类:
package com.itcast.proxy.jdk;
public class Target implements TargetInterface{
public void method() {
System.out.println("Target running .......");
}
}
3.动态代理对象创建
package com.itcast.proxy.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyTest {
public static void main(String[] args) {
final Target target = new Target();
// 创建代理对象
TargetInterface proxyInstance = (TargetInterface) Proxy.newProxyInstance(
target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置增强
System.out.println("前置增强。。。。");
Object invoke = method.invoke(target, args);
System.out.println("后置增强。。。。");
return invoke;
}
});
// 测试代理对象,无需更改target类的代码对其方法进行增强
proxyInstance.method();
}
}
cglib动态代理
对象为非接口时,使用cglib来创建动态代理对象。
1.目标类
package com.itcast.proxy.cjlib;
public class Target {
public void method(){
System.out.println("target running .....");
}
}
2.cglib创建动态代理对象
package com.itcast.proxy.cjlib;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CjlibTest {
public static void main(final String[] args) {
final Target target = new Target(); // 创建目标对象
Enhancer enhancer = new Enhancer(); // 创建增强器
enhancer.setSuperclass(Target.class); // 设置父类
enhancer.setCallback(new MethodInterceptor() { // 设置回调
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("前置增强");
Object invoke = method.invoke(target, objects);
System.out.println("后置增强");
return invoke;
}
});
Target proxy = (Target) enhancer.create(); // 创建代理对象
proxy.method();
}
}
AOP的相关概念
Spring 的 AOP 实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。对于Spring的AOP,有以下概念:
Target(目标对象): 代理的目标对象
Proxy (代理): 一个类被 AOP 织入增强后,就产生一个结果代理类
Joinpoint(连接点): 所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
Pointcut(切入点): 所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义
Advice(通知/ 增强): 所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知
Aspect(切面): 是切入点和通知(引介)的结合
Weaving(织入): 是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入
AOP开发注意事项
1. 在spring 中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。
2. Spring 框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。
基于XML的AOP开发
1.导入 AOP 相关坐标
2.创建目标接口和目标类(内部有切点)
3.创建切面类(内部有增强方法)
4.将目标类和切面类的对象创建权交给 spring
5.在 applicationContext.xml 中配置织入关系
6.测试代码
代码示例
- 导入坐标:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!--aspectj织入,即功能增强依赖-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
- 接口和目标类:
package com.itcast.spring_aop;
public interface TargetInterface {
public void method();
}
package com.itcast.spring_aop;
public class Target implements TargetInterface {
public void method(){
System.out.println("target running init .....");
// 下面的代码用于测试异常时的情况
int i = 1/0;
System.out.println("target running ..... ");
}
}
- 创建切面类:即增强方法类:
package com.itcast.spring_aop;
public class MyAspect {
public void before(){
System.out.println("前置增强.....");
}
public void afterRunning(){
System.out.println("后置增强.....");
}
public void after(){
System.out.println("无论如何都增强.....");
}
public void throwException(){
System.out.println("报错增强.....");
}
}
- 配置织入关系:
<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" // 这里是引入aop的命名空间
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop // 注意这里是引入aop的约束 http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置目标类-->
<bean id="target" class="com.itcast.spring_aop.Target"></bean>
<!--配置切面类-->
<bean id="myAspect" class="com.itcast.spring_aop.MyAspect"></bean>
<!--配置织入关系-->
<aop:config><!--引用myAspect的Bean为切面对象-->
<aop:aspect ref="myAspect">// ref 表示的是切面类的ID
<!--配置Target的method方法执行时要进行myAspect的before方法前置增强:即通知的方法名称, pointCut是切点表达式,表示那些地方要增强-->
<aop:before method="before" pointcut="execution( * com.itcast.spring_aop.*.*(..))"></aop:before>
<aop:after-returning method="afterRunning" pointcut="execution( * com.itcast.spring_aop.*.*(..))"></aop:after-returning>
<aop:after-throwing method="throwException" pointcut="execution( * com.itcast.spring_aop.*.*(..))"></aop:after-throwing>
<aop:after method="after" pointcut="execution( * com.itcast.spring_aop.*.*(..))"></aop:after>
</aop:aspect>
</aop:config>
</beans>
- 测试:
import com.itcast.spring_aop.Target;
import com.itcast.spring_aop.TargetInterface;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationConfiguration.xml")
public class AopTest {
// @Autowired
// private Target target;
// UnsatisfiedDependencyException 注意这里的目标对象需要用接口来接收,否则回报错。
// 因为底层的动态代理proxy对象是用jdk来实现的
// .BeanNotOfRequiredTypeException: Bean named 'target' is expected to be of type
// 'com.itcast.spring_aop.Target' but was actually of type 'com.sun.proxy.$Proxy14'
@Autowired
private TargetInterface target;
@Test
public void test(){
target.method();
}
}
切点表达式的写法
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
访问修饰符可以省略
返回值类型、包名、类名、方法名可以使用星号* 代表任意
包名与类名之间一个点 . 代表当前包下的类,两个点 .. 表示当前包及其子包下的类
参数列表可以使用两个点 .. 表示任意个数,任意类型的参数列表
如下实例:
execution(public void com.itcast.aop.Target.method())
execution(void com.itcast.aop.Target.*(..))
execution(* com.itcast.aop.*.*(..))
execution(* com.itcast.aop..*.*(..))
execution(* *..*.*(..))
XML的配置内容
通知类型语法:
<aop:通知类型 method=“切面类中方法名” pointcut=“切点表达式"></aop:通知类型>
基于注解的AOP开发
配置的方法过于繁琐,可以考虑注解的方式。
开发流程
创建目标接口和目标类(内部有切点)
创建切面类(内部有增强方法)
将目标类和切面类的对象创建权交给 spring
在切面类中使用注解配置织入关系
在配置文件中开启组件扫描和 AOP 的自动代理
测试
开发示例
接口类和目标类:
package com.itcast.spring_aop_anno;
public interface TargetInterface {
public void method();
}
package com.itcast.spring_aop_anno;
import org.springframework.stereotype.Component;
// 将对象创建交给spring容器
@Component("target")
public class Target implements TargetInterface {
public void method() {
System.out.println("Target running .......");
}
}
切面类:
package com.itcast.spring_aop_anno;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component("myAspect")
@Aspect // 表示该类是切面类
public class MyAspect {
@Before("MyAspect.myPoint()") // 使用抽取方式进行切点表达式获取
public void before(){
System.out.println("前置增强.....");
}
// 直接在注解上以参数的形式
@AfterReturning("execution(* com.itcast.spring_aop_anno.*.*(..))")
public void afterRunning(){
System.out.println("后置增强.....");
}
@After("execution(* com.itcast.spring_aop_anno.*.*(..))")
public void after(){
System.out.println("无论如何都增强.....");
}
@AfterThrowing("execution(* com.itcast.spring_aop_anno.*.*(..))")
public void throwException(){
System.out.println("报错增强.....");
}
@Pointcut("execution(* com.itcast.spring_aop_anno.*.*(..))")
public void myPoint(){}
}
配置组件扫描和自动代理启动:
<!--组件扫描-->
<context:component-scan base-package="com.itcast.spring_aop_anno"></context:component-scan>
<!--aop自动代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
测试代码:
import com.itcast.spring_aop_anno.TargetInterface;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:application.xml")
public class AopAnnoTest {
@Autowired
private TargetInterface target;
@Test
public void test(){
target.method();
}
}
注解配置详情
语法:
通知的配置语法:@通知注解(“切点表达式")
通知类型:
SpringMVC
SpringMVC概述
SpringMVC 是一种基于 Java 的实现 MVC 设计模型的请求驱动类型的轻量级 Web 框架,属于SpringFrameWork 的后续产品,已经融合在 Spring Web Flow 中。
SpringMVC 已经成为目前最主流的MVC框架之一,并且随着Spring3.0 的发布,全面超越 Struts2,成为最优秀的 MVC 框架。它通过一套注解,让一个简单的 Java 类成为处理请求的控制器,而无须实现任何接口。同时它还支持 RESTful 编程风格的请求。
SpringMVC优点
无需事先任何接口,实现操作方便
支持RESTFUL编程风格
SpirngMVC实现
1.坐标导入
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!--集成的web坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!--spring-webmvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
2.在web.xml文件中配置SpirngMVC的核心控制器
<!--配置spring-mvc核心控制器-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
3.启动组件扫描在spring-mvc.xml文件中配置,该配置文件放在resources下
<?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:context="http://www.springframework.org/schema/context"
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 http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--组件扫描-->
<context:component-scan base-package="com.itcast"></context:component-scan>
</beans>
4.测试:
编写controller和业务方法
@Controller
public class QuickController {
// 页码跳转返回
// 返回字符串形式
@RequestMapping("/quick")
public String quickMethod(){
System.out.println("quickMethod running .....");
return "index";
}
}
可以通过浏览器进行该路径访问:例:localhost:8080/mvc/quick
mvc为虚拟路径
quick为RequestMapping的参数
SpringMVC流程图
SpringMVC组件解析
SpringMVC的执行流程
1.用户发送请求至前端控制器DispatcherServletw。
2.DispatcherServlet收到请求调用HandlerMapping处理器映射器。
3.处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
4.DispatcherServlet调用HandlerAdapter处理器适配器。
5.HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
6.Controller执行完成返回ModelAndView。
7.HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。
8.DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
9.ViewReslover解析后返回具体View。
10.DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。DispatcherServlet响应用户。
doDispath方法源码
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
try {
ModelAndView mv = null;
Object dispatchException = null;
try {
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
//获取包含处理器Handler和拦截器AdapterIntercepters的处理器执行链HandlerExecutionChain。
mappedHandler = this.getHandler(processedRequest);
if (mappedHandler == null) {
this.noHandlerFound(processedRequest, response);
return;
}
//根据HandlerExecutionChain中的处理器Handler获取处理器适配器。
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//做最后的处理,执行处理器(controller)返回视图模型modelAndView。
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
this.applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
}
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
} catch (Exception var22) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
} catch (Throwable var23) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
}
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else if (multipartRequestParsed) {
this.cleanupMultipart(processedRequest);
}
}
}
组件解析
1. 前端控制器:DispatcherServlet
用户请求到达前端控制器,它就相当于 MVC 模式中的 C,DispatcherServlet 是整个流程控制的中心,由
它调用其它组件处理用户的请求,DispatcherServlet 的存在降低了组件之间的耦合性。
2. 处理器映射器:HandlerMapping
HandlerMapping 负责根据用户请求找到 Handler 即处理器,SpringMVC 提供了不同的映射器实现不同的
映射方式,例如:配置文件方式,实现接口方式,注解方式等。
3. 处理器适配器:HandlerAdapter
通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理
器进行执行。
4. 处理器:Handler
它就是我们开发中要编写的具体业务控制器。由 DispatcherServlet 把用户请求转发到 Handler。由
Handler 对具体的用户请求进行处理。
5. 视图解析器:View Resolver
View Resolver 负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名,即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。
6. 视图:View
SpringMVC 框架提供了很多的 View 视图类型的支持,包括:jstlView、freemarkerView、pdfView等。最常用的视图就是 jsp。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面
注解解析
@RequestMapping
作用:用于建立请求 URL 和处理请求方法之间的对应关系
位置:
类上,请求URL 的第一级访问目录。此处不写的话,就相当于应用的根目录
方法上,请求 URL 的第二级访问目录,与类上的使用@ReqquestMapping标注的一级目录一起组成访问虚拟路径
属性:
value:用于指定请求的URL。它和path属性的作用是一样的
method:用于指定请求的方式
params:用于指定限制请求参数的条件。它支持简单的表达式。要求请求参数的key和value必须和配置的一模一样
例如:
params = {"accountName"},表示请求参数必须有accountName
params = {"moeny!100"},表示请求参数中money不能是100
1. mvc命名空间引入
命名空间:xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"
约束地址: http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
2. 组件扫描
SpringMVC基于Spring容器,所以在进行SpringMVC操作时,需要将Controller存储到Spring容器中,如果使用@Controller注解标注的话,就需要使用<context:component-scan base-package=“com.itheima.controller"/>进行组件扫描。
XML配置解析
1. 视图解析器
SpringMVC有默认组件配置,默认组件都是DispatcherServlet.properties配置文件中配置的,该配置文件地址org/springframework/web/servlet/DispatcherServlet.properties,该文件中配置了默认的视图解析器,如下
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
翻看该解析器源码,可以看到该解析器的默认设置,如下:
REDIRECT_URL_PREFIX = "redirect:" --重定向前缀
FORWARD_URL_PREFIX = "forward:" --转发前缀(默认值)
prefix = ""; --视图名称前缀
suffix = ""; --视图名称后缀
视图解析器
我们可以通过属性注入的方式修改视图的的前后缀
<!--配置内部资源视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"></property> <property name="suffix" value=".jsp"></property></bean>
SpringMVC的开发流程
配置前端控制器
<!--前端控制器/请求分发器-->
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!--让框架去找我们自定义的文件名与路径-->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application-mvc.xml</param-value>
</init-param>
<!--设置优先级-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
创建自定义的Controller类,实现Controller接口.复写handleRequest方法:
public class HelloWorldController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
//创建自定义Controller类,实现contrller接口复写handleRequest方法
request.setAttribute("msg","hello!");
request.getRequestDispatcher("/hello.jsp").forward(request,response);
return null;
}
}
配置Controller的映射路径,并且交给spring来管理bean:
<!--name请求的映射路径 class:该路径处理类-->
<bean name="/index" class="com.jd.controller.HelloWorldController"></bean>
使用注解的方式
SpringMVC的注解:
@Controller
控制器Controller 负责处理由DispatcherServlet 分发的请求,它把用户请求的数据经过业务处理层处理之后封装成一个Model ,然后再把该Model 返回给对应的View 进行展示。使用@RequestMapping 和@RequestParam 等一些注解用以定义URL 请求和Controller 方法之间的映射,这样的Controller 就能被外界访问到。使用@controller注解的类,方法相应的是页面,类似index.jsp。
@ResponseBody
是配合@controller返回JSON、XML或者其它文本的。
@RestController
是@Controller和@ResponseBody的结合体,返回的是内容而不是页面。
@RequestMapping
@RequestMapping主要是用来配置url映射,此注解即可以作用在控制器的某个方法上,也可以作用在此控制器类上。
当控制器在类级别上添加@RequestMapping注解时,这个注解会应用到控制器的所有处理器方法上。处理器方法上的@RequestMapping注解会对类级别上的@RequestMapping的声明进行补充。
例子一:@RequestMapping仅作用在处理器方法上
@RestController
public class HelloController {
@RequestMapping(value="/hello",method= RequestMethod.GET)
public String sayHello(){
return "hello";
}
}
以上代码sayHello所响应的url=localhost:8080/hello。
例子二:@RequestMapping仅作用在类级别上
@Controller
@RequestMapping("/hello")
public class HelloController {
@RequestMapping(method= RequestMethod.GET)
public String sayHello(){
return "hello";
}
}
以上代码sayHello所响应的url=localhost:8080/hello,效果与例子一一样,没有改变任何功能。
例子三:@RequestMapping作用在类级别和处理器方法上
@RestController
@RequestMapping("/hello")
public class HelloController {
@RequestMapping(value="/sayHello",method= RequestMethod.GET)
public String sayHello(){
return "hello";
}
@RequestMapping(value="/sayHi",method= RequestMethod.GET)
public String sayHi(){
return "hi";
}
}
这样,以上代码中的sayHello所响应的url=localhost:8080/hello/sayHello。
sayHi所响应的url=localhost:8080/hello/sayHi。
从这两个方法所响应的url可以回过头来看这两句话:当控制器在类级别上添加@RequestMapping注解时,这个注解会应用到控制器的所有处理器方法上。处理器方法上的@RequestMapping注解会对类级别上的@RequestMapping的声明进行补充。@RequestMapping中的method参数有很多中选择,一般使用get/post。
@PathVaribale
只支持一个类型为String的属性,表示绑定的请求参数的名称,省略则默认绑定同名的参数。用于将请求URL中的模板变量映射到功能处理方法的参数上,即取出uri模板中的变量作为参数。可以识别URL上的占位符,如:
@RequestMapping(value="/user/{userId}/roles/{roleId}",method = RequestMethod.GET)
public String getLogin(@PathVariable("userId") String userId,
@PathVariable("roleId") String roleId){
System.out.println("User Id : " + userId);
System.out.println("Role Id : " + roleId);
return "hello";
}
@RequestParam
- 请求处理的方法的参数的类型为Java基本类型和String.也可以接受对象类型
- 常用来处理content-type为默认的application/x-www-form-urlcoded编码
@requestParam主要用于在SpringMVC后台控制层获取参数,它有三个常用参数:defaultValue = “0”, required = false, value = “age”;defaultValue 表示设置默认值,required 通过boolean设置是否是必须要传入的参数,value 值表示接受的传入的参数类型。
@RequestMapping(value = "/testRequestParam")
public String testRequestParam(@RequestParam(value="username")String un,@RequestParam(value = "age",required = false,defaultValue = "0") int age){
System.out.println("testRequestParam,username:"+un+",age,"+age);
return "success";
}
URL就可以是:
http:localhost:8080/testRequestParam?username=tom&age=30
@RequestBody
- 常用来处理content-type==不是默认的application/x-www-form-urlcoded编码的内容,比如说:application/json或者是application/xml通过@requestBody可以将请求体中的JSON字符串绑定到相应的bean上,当然,也可 以将其分别绑定到对应的字符串上。
- 在Get请求中,没有HttpEntity,所以@RequestBody并不适用
- 在Post请求中,因为必须在请求头中申明Content-Type,则可以使用l
public void login(@requestBody String userName,@requestBody String pwd){
System.out.println(userName+" :"+pwd);
}
假如我有一个User类,拥有如下字段:
public class User {
String userName;
String pwd;
}
那么上述参数可以改为以下形式
public void login(@requestBody User user){
}
这种形式会将JSON字符串中的值赋予user中对应的属性上
注意:JSON字符串中的key必须对应user中的属性名,否则是请求不过去的
使用方法:
1.在类打上@Controller注解
2.在方法上添加@RequestMapping注解
3.在springmvc的配置文件上开启注解扫描
<!--开启注解扫描-->
<context:component-scan base-package="com.test.controller"></context:component-scan>
静态资源处理
-
springmvc不能使用/,因为Struts2里面是有自定义标签 s:form,jsp文件必须要交有struts2来解析,所以才会用到/。
SpringMVC是没有自定义标签的,也不能处理jsp文件,所以jsp文件必须交回tomcat来处理,所以才会用/,不过滤jsp文件。 -
springmvc匹配到静态资源的时候出现的重定向错误问题:
当我们在浏览器访问http://localhost/dept.html,此时的dept.html路径就会被当做url的映射路径来匹配,就会去找@RequestMapping有没有匹配到的路径,如果有的话就把dept.html当做url路径来访问(/dept.???) ?是不会加进去匹配的。也就是说访问dept.html时,只要有dept的路径就可以匹配到。解决方案有:
方案 1
交由tomcat来处理,tomcat中的default本来就是处理静态资源的
方案 2
配置springmvc处理静态资源(先匹配url是否有相同的路径,如果有就交由方法处理,如果没有才当做静态资源处理)(推荐使用)
<!--springMVC的静态资源处理配置-->
<mvc:default-servlet-handler></mvc:default-servlet-handler>
前台往后台传参的方式
1.使用request的方式
@Controller
public class ParamController {
@RequestMapping("/login")
public void login(HttpServletRequest request){
// 1.原始方式 使用request来获取参数
System.out.println(request.getParameter("username"));
System.out.println(request.getParameter("password"));
}
2.使用同名匹配的方式
@RequestMapping("/login")
public void login(String username,String password){
System.out.println(("username"));
System.out.println(("password"));
}
3.使用对象的方式
@RequestMapping("/login")
public void login(User user){
System.out.println((user));
}
4.地址栏传参
@RequestMapping("/delete/{id}")
public void delete(@PathVariable("id") Long id){
System.out.print(id);
}
解决中文乱码问题(post)
<!--编码过滤器 只对post请求有效-->
<filter>
<filter-name>characterEncoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>endoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>