SSM开发笔记-尚硅谷-佟刚-Spring4.0.0

1.Spring

1.1 IOC

控制反转,在Spring中的实现形式是依赖注入DI。
例如,最直接的好处就是,假设一个人类对象有一个父亲对象,最初的话需要人类对象主动初始化父亲对象,而当父亲对象的构造方法发生变化后,人类对象也要做出改变,但是如果有了依赖注入,那么父亲类无论怎么该构造方法,都不需要人类做出修改,仅仅是在需要的时候注入即可。

1.1.1 Spring核心包

Spring的四个核心包:core、beans、context、expression
日志包:commons-logging

applicationContext.xml文件头范例:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
	
</beans>

引入p命名空间:

xmlns:p="http://www.springframework.org/schema/p"

引入util命名空间:

xmlns:util="http://www.springframework.org/schema/util"

http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd

引入context命名空间:

xmlns:context="http://www.springframework.org/schema/context"

http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd

引入mvc命名空间

xmlns:mvc="http://www.springframework.org/schema/mvc"

http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd

1.1.2 配置bean的形式

构造器的赋值和属性的赋值都可以使用value属性和value子节点的方式注入值,但是当使用内部bean和含有特殊字符的值时必须使用value子节点。

<bean id="helloworld" class="com.beibeixyzxyz.spring.HelloWorld">
    <!-- 属性注入 -->
    <property name="name" value="Spring"/>
</bean>

<bean id="helloworld" class="com.beibeixyzxyz.spring.HelloWorld">
    <!-- 构造方法注入 -->
    <constructor-arg value="spring"></constructor-arg>
</bean>

<!-- 包含特殊字符,使用<![CDATA[值]]> -->
<constructor-arg index="0">
    <value><![CDATA[<spring>]]></value>
</constructor-arg>

<!--内部bean-->
<constructor-arg>
    <bean class="com.beibeixyzxyz.spring.HelloWorld">
        <property name="name" value="内部bean名字"/>
        <property name="desc" value="内部bean描述"/>
    </bean>
</constructor-arg>

<!--赋值为null-->
<constructor-arg><null/></constructor-arg>

<!-- 使用0、1、2...指定位置(或者使用type指定类型) -->
<bean id="helloworld" class="com.beibeixyzxyz.spring.HelloWorld">
    <constructor-arg value="spring" index="0"></constructor-arg>
    <constructor-arg value="spring desc" type="java.lang.String"></constructor-arg>
</bean>

<!--集合属性赋值list和set-->
<property name="cars">
	<list>
		<ref bean="car1"/>
		<ref bean="car2"/>
    </list>
</property>

<!--集合属性赋值map-->
<property name="name">
    <map>
        <entry key="key1" value="value1"/>
        <entry key="key2" value="value2"/>
    </map>
</property>

<!--配置类的赋值-->
<property name="properties">
    <props>
        <prop key="key1">value1</prop>
        <prop key="key2">value2</prop>
    </props>
</property>


<!--配置单例的集合bin,以供多个对象进行引用,需要导入util命名空间-->
<util:list id="cars">
    <value>car1</value>
    <value>car2</value>
</util:list>
<!--直接引用-->
<bean id="helloworld" class="com.beibeixyzxyz.spring.HelloWorld">
    <property name="cars" ref="cars"/>
</bean>


<!-- 通过p命名空间简化配置,需要引入p命名空间 -->
<bean id="helloworld" class="com.beibeixyzxyz.spring.HelloWorld" p:name="xiaoming" p:cars-ref="cars"></bean>

1.1.3 创建容器,获取对象,调用方法,关闭容器的流程

ApplicationContext接口常用的实现类有
1.ClassPathXmlApplicationContext:从类路径下加载配置文件。
2.FileSystemXmlApplicationContext:从文件系统下加载配置文件。

ApplicationContext在初始化时就new好了全部的单例Bean

// 从类路径的文件下加载spring容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
// 获取对象
HelloWorld hello = (HelloWorld)ctx.getBean("helloworld");
// 调用方法
hello.hello();
// 关闭容器
((ClassPathXmlApplicationContext)ctx).close();

1.1.4 自动装配

原来的装配都要手工明确指定,而自动装配则不需要。
常用的autowire有byName和byType两个属性值,byName要求bean的id和类中字段名字对应起来,而byType只要求类型对应起来即可。

<bean id="person" class="com.beibeixyzxyz.spring.Person"
      p:name="Tom" autowire="byType"/>

bean节点中用parent属性来指定继承配置,继承的bean可以是个实体的bean也可以是个抽象的bean,抽象的bean中既可以指定class属性也可以不指定。

<bean id="address"
      p:city="Beijing" p:street="HuiLongGuan" abstract="true"/>
<bean id="address2" class="com.beibeixyzxyz.spring.Address"
      parent="address"/>
<!-- 只是继承属性和引用,而不是原来的bean,子bean中可以修改 -->

bean节点中的depends-on用来表示在创建该bean前应该容器中应该先存在哪些bean,如果有多个bean,使用逗号或空格分隔

<bean id="person" class="com.beibeixyzxyz.spring.Person"
      p:name="Tom" depends-on="address,car"/>
<!--这个是在创建person前容器中必须存在person可引用的address和car-->

bean节点中的scope属性可以指定容器创建bean的方式,默认是singleton单例的,可以指定prototype原型,这样每次从容器中拿bean就会新创建一个了。

其他的属性值还有request和session。

<bean id="person" class="com.beibeixyzxyz.spring.Person"
     p:name="Tom" scope="prototype"/>

1.1.5 使用context下的property-placeholder引用外部属性文件

示例:配置c3p0数据源

pom中引入

<!--数据库驱动和数据库连接池-->
<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.5.2</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.44</version>
</dependency>

db.properties

jdbc.username=root
jdbc.password=123456
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_review_test

jdbc.initPoolSize=3
jdbc.maxPoolSize=5

spring配置文件

<context:property-placeholder location="classpath:db.properties"/>
<!-- 配置c3p0数据源 -->
<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}"/>

    <property name="initialPoolSize" value="${jdbc.initPoolSize}"/>
    <property name="maxPoolSize" value="${jdbc.maxPoolSize}"/>
</bean>

测试

public class Test1 {
    ApplicationContext ctx = null;
    @Before
    public void before(){
        ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
    }

    @Test
    public void test2() throws SQLException {
        DataSource ds = ctx.getBean(DataSource.class);
        System.out.println(ds.getConnection());
    }

    @After
    public void after(){
        ((ClassPathXmlApplicationContext)ctx).close();
    }
}

1.1.6 SpEL

基本语法:#{value}
如果value是个字符串字面值,则用单引号括起来。

示例:
Car的属性:

private String brand;
private double price;
private double tireRadius;

Address的属性:

private String city;
private String street;

Person的属性:

private String name;
private String city;
private String info;
private Car car;

配置bean:

<bean id="address" class="com.beibeixyzxyz.spring.Address">
    <!-- SpEL赋普通字面量 -->
    <property name="city" value="#{'Beijing'}"/>
    <property name="street" value="WuDaokou"/>
</bean>

<bean id="car" class="com.beibeixyzxyz.spring.Car">
    <property name="brand" value="Audi"/>
    <property name="price" value="500000"/>
    <!-- SpEL引用类的静态属性以及计算 -->
    <property name="tireRadius" value="#{T(java.lang.Math).PI*20}"/>
</bean>

<bean id="person" class="com.beibeixyzxyz.spring.Person">
    <property name="name" value="Tom"/>
    <!-- 引用对象 -->
    <property name="car" value="#{car}"/>
    <!-- 引用对象内的属性 -->
    <property name="city" value="#{address.city}"/>
    <!--三目运算-->
    <property name="info" value="#{car.price > 300000 ? '金领' : '白领'}"/>
</bean>

1.1.7 bean的生命周期

1.通过构造器或工厂方法创建Bean的实例

2.为Bean的属性设置值和对其他Bean的引用

3.将Bean实例传递给Bean后置处理器的postProcessBeforeInitialization方法
4.调用Bean的初始化方法
5.将Bean实例传递给Bean后置处理器的postProcessAfterInitialization方法

6.使用Bean

7.当容器关闭,调用Bean的销毁方法

指定bean的创建和销毁方法

示例:

<bean id="address" class="com.beibeixyzxyz.spring.Address" init-method="init" destroy-method="destroy">
</bean>

指定后置处理器

public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
        //在这里面可以对未投入使用的Bean随意更改
        Address a = (Address)o;
        a.setCity("Shanghai");
        return a;
    }

    @Override
    public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
        System.out.println(s);
        return o;
    }
}

配置Bean

<bean class="com.beibeixyzxyz.spring.MyBeanPostProcessor"/>

1.1.8 工厂方法创建bean

静态工厂

实例工厂

package com.beibeixyzxyz.spring;

import java.util.HashMap;
import java.util.Map;

public class CarFactory {
    private Map<String,Car> cars = null;

    public CarFactory() {
        cars = new HashMap<>();
        cars.put("audi",new Car("audi",300000));
        cars.put("ford",new Car("ford",500000));
    }

    public Car getCar(String brand){
        return cars.get(brand);
    }
}

配置工厂和bean

<!-- 配置工厂实例 -->
<bean id="carFactory" class="com.beibeixyzxyz.spring.CarFactory"/>
<!-- 通过实例工厂方法来配置bean -->
<bean id="car" factory-bean="carFactory" factory-method="getCar">
    <constructor-arg value="ford"/>
</bean>

1.1.9 四个自动扫描注解

@Component
@Repository
@Service
@Controller

在bean中的id是哪个类就是哪个类的头字母小写后的串,和接口没有关系。里面的value可以指定bean的名字。
如:

@Service("userServiceAssigned")

默认包扫描器是扫描该包和子包下的所有类的,如果禁用默认包扫描器,那么就之扫描context:include-filter节点指定的特定类了。
指定扫描包的时候要想指定多个,中间可以用逗号隔开。

<!--resource-pattern:筛选要扫描的文件-->
<context:component-scan
   base-package="com.beibeixyzxyz.spring.scan_component"
   resource-pattern="T*.class">
</context:component-scan>


<context:component-scan base-package="com.beibeixyzxyz.spring.scan_component">
    <!--不扫描Service这个注解-->
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>


<context:component-scan base-package="com.beibeixyzxyz.spring.scan_component" use-default-filters="false">
    <!--只扫描带Service和Component这两个注解的-->
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
</context:component-scan>


<context:component-scan base-package="com.beibeixyzxyz.spring.scan_component">
    <!--不扫描这个类,如果是个接口则不扫描这个接口及其实现类-->
    <context:exclude-filter type="assignable" expression="com.beibeixyzxyz.spring.scan_component.TestCar"/>
</context:component-scan>


<context:component-scan base-package="com.beibeixyzxyz.spring.scan_component" use-default-filters="false">
    <!--仅扫描这个类,如果是个接口则仅扫描这个接口及其实现类-->
    <context:include-filter type="assignable" expression="com.beibeixyzxyz.spring.scan_component.UserService"/>
</context:component-scan>

1.2 AOP

1.2.1 JDK动态代理实例

JDK动态代理必须使用接口才可以使用
下面以一个计算器的小例子来做示例:

public interface ArithmeticCalculator {
    int add(int i,int j);
    int sub(int i,int j);
    int mul(int i,int j);
    int div(int i,int j);
}
public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
    public int add(int i,int j){
        return i+j;
    }

    public int sub(int i,int j){
        return i-j;
    }

    public int mul(int i,int j){
        return i*j;
    }

    public int div(int i,int j){
        return i/j;
    }
}
public class ArithmeticCalculatorLoggingProxy {
    //要代理的对象
    private ArithmeticCalculator target;

    public ArithmeticCalculatorLoggingProxy(ArithmeticCalculator target) {
        this.target = target;
    }

    public ArithmeticCalculator getLoggingProxy(){
        ArithmeticCalculator proxy = null;

        //代理对象由哪一个类加载器负责加载
        ClassLoader loader = target.getClass().getClassLoader();
        //代理对象的类型,即其中有哪些方法
        Class[] interfaces = new Class[]{ArithmeticCalculator.class};
        //当调用代理对象其中的方法时,执行的代码
        InvocationHandler h = new InvocationHandler() {

            //在invoke方法中,proxy表示正在被代理的对象,如果在invoke中调用proxy的方法,则会导致递归堆栈溢出
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                String methodName = method.getName();
                System.out.println(methodName+"方法传入参数:"+ Arrays.toString(args));
                Object result = method.invoke(target,args);
                System.out.println(methodName+"方法传出结果:"+result);
                return result;
            }
        };
        proxy = (ArithmeticCalculator) Proxy.newProxyInstance(loader,interfaces,h);
        return proxy;
    }
}
public class ProxyMain {
    public static void main(String[] args) {
        ArithmeticCalculator arithmeticCalculator = new ArithmeticCalculatorImpl();
        ArithmeticCalculator proxy = new ArithmeticCalculatorLoggingProxy(arithmeticCalculator).getLoggingProxy();
        System.out.println(proxy.sub(1,2));
    }
}

1.2.2 AspectJ

Spring中的AspectJ,其本质是一个底层核心为动态代理的用于面向切面编程的框架。

要在 Spring 应用中使用 AspectJ 注解, 必须在 classpath 下包含 AspectJ 类库: aopalliance.jar、aspectj.weaver.jar 和 spring-aspects.jar

在上面IOC的基础上添加如下Maven配置即可

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.6.8</version>
</dependency>

在spring配置文件中引入aop命名空间

xmlns:aop="http://www.springframework.org/schema/aop"

http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
1.2.2.1 注解方式切面开发流程实例

1.在spring配置文件中启用Aspect注解自动代理

<aop:aspectj-autoproxy/>

2.创建切面类
@Before注解标识的是切点。即在切点处做相应的动作
可以是哦那个JoinPoint对象获取切点的信息

@Aspect
@Component
public class LoggingAspect {
    @Before("execution(public int com.beibeixyzxyz.spring.aspect.ArithmeticCalculator.*(int,int))")
    public void beforeMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The method [" +methodName+ "] begins...");
    }
}

通知类型

1.前置通知,@Before
2.环绕通知,@Around
3.正常返回通知,@AfterReturn
4.异常返回通知,@AfterThrowing
5.后置通知,@After

切面优先级

在切面上使用@Order([数字])注解指定优先级,[数字]越小,优先级越高。如@Order(1)、@Order(2)
1.2.2.2 配置方式切面开发流程实例

不需要任何注解

1.原bean

public class ArithmeticCalculator{
    public int add(int i,int j){
        return i+j;
    }

    public int sub(int i,int j){
        return i-j;
    }

    public int mul(int i,int j){
        return i*j;
    }

    public int div(int i,int j){
        return i/j;
    }
}

2.日志切面

public class LoggingAspect {
    public void beforeMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The method [" +methodName+ "] begins...");
    }
}

3.验证切面

public class ValidationAspect {
    public void beforeMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The method [" +methodName+ "] begins validation...");
    }
}

4.spring配置文件中的配置

<!--原bean-->
<bean id="arithmeticCalculator" class="com.beibeixyzxyz.spring.aspect.ArithmeticCalculator"/>

<!--切面-->
<bean id="loggingAspect" class="com.beibeixyzxyz.spring.aspect.LoggingAspect"/>
<bean id="validationAspect" class="com.beibeixyzxyz.spring.aspect.ValidationAspect"/>

<!--配置AOP-->
<aop:config>
    <!--配置切点表达式-->
    <aop:pointcut id="pointcut" expression="execution(public int com.beibeixyzxyz.spring.aspect.ArithmeticCalculator.*(int,int))"/>
    <!--配置切面和通知-->
    <aop:aspect ref="loggingAspect" order="2">
        <aop:before method="beforeMethod" pointcut-ref="pointcut"/>
    </aop:aspect>
    <aop:aspect ref="validationAspect" order="1">
        <aop:before method="beforeMethod" pointcut-ref="pointcut"/>
    </aop:aspect>
</aop:config>

1.2.3 事务

1.2.3.1 使用注解方式配置事务

在spring配置文件中引入tx命名空间

xmlns:tx="http://www.springframework.org/schema/tx"

http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd

配置spring配置文件

<!--配置事务管理器-->
<!--这儿是springTemplate的事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!--启用自动事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"/>

在方法上加注解

//propagation传播行为,isolation隔离等级,noRollbackFor抛出特定异常不进行回滚操作,readOnly是否只读,timeout超时(s)
@Transactional(propagation=Propagation.REQUIRES_NEW,
               isolation=Isolation.READ_COMMITTED,
               noRollbackFor={UserAccountException.class},
               readOnly=false,
               timeout=30)
public void purchase(String username, String isbn) {
    
}

常用传播行为
Propagation.REQUIRES_NEW:事务中嵌套事务时新开事务
Propagation.REQUIRED:默认的,继承原来的事务。

隔离等级
Isolation.DEFAULT:默认隔离等级(使用后端数据库默认的隔离级别,MySQL的InnoDB是RR——可重复读——级别)
Isolation.READ_UNCOMMITTED:读未提交
Isolation.READ_COMMITTED:读已提交
Isolation.REPEATABLE_READ:可重复读
Isolation.SERIALIZABL:序列化

1.2.3.2 使用配置方式配置事务

配置spring配置文件

<!--配置事务管理器-->
<!--这儿是springTemplate的事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!--配置事务切面-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="purchase" propagation="REQUIRES_NEW"/>
        <tx:method name="get*" read-only="true"/>
        <tx:method name="find*" read-only="true"/>
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>

<!--配置事务切入点,以及把事务切入点和事务切面关联起来-->
<aop:config>
    <aop:pointcut id="txPointCut" expression="execution(* com.beibeixyzxyz.spring.tx.BookShopService.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>

2.SpringMVC

2.1 SpringMVC请求处理流程图

在这里插入图片描述

2.2 在web项目中使用SpringMVC并配置解析JSP文件

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         id="WebApp_ID" version="2.5">


  <!--配置DispatcherServlet-->
  <servlet>
    <servlet-name>springDispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!--配置SpringMVC配置文件的位置和名称-->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <!--应用加载即创建该Servlet-->
    <load-on-startup>1</load-on-startup>
  </servlet>
  <!--指定DispatcherServlet接管哪些类型的请求-->
  <servlet-mapping>
    <servlet-name>springDispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

如果不配置init-param,则默认的配置文件位置为/WEB-INF/${servlet-name}-servlet.xml。

springmvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context.xsd">
    <!--配置自动扫描的包-->
    <context:component-scan base-package="com.beibeixyzxyz.spring.handlers"/>

    <!--配置视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

使用maven加入tomcat插件,然后运行tomcat7:run命令就可以了

<!-- 配置Tomcat插件 -->
<plugin>
    <groupId>org.apache.tomcat.maven</groupId>
    <artifactId>tomcat7-maven-plugin</artifactId>
    <version>2.2</version>
    <configuration>
        <!--端口号-->
        <port>8080</port>
        <!--设置项目路径-->
        <path>/</path>
    </configuration>
</plugin>

2.3 接收请求

2.3.1 @RequestMapping注解

可以修饰类,也可以修饰方法。

常用注解内属性:
value:请求URL
method:请求方法
params:请求参数
headers:请求头

例:

//使用value和method
@ResponseBody
@RequestMapping(value = "/testMethod",method = RequestMethod.GET)
public String testMathod(){
    System.out.println("testMethod");
    return "success";
}

2.3.1.(1)@PathVariable注解

可以引用URL中占位符的值。

例:

@ResponseBody
@RequestMapping("/delete/{id}")
public String delete(@PathVariable("id") Integer id){
    System.out.println("删除了id为"+id+"条目");
    return "delete success!";
}

2.3.2 REST风格增删改查

  • GET:获取
  • POST:新增
  • DELETE:删除
  • PUT:修改

在传统表单的表单域中加入一个表单项——method,然后再配置HiddenHttpMethodFilter,即可使用这种请求方法标识进行增删改查的区别。

配置HiddenHttpMethodFilter

<!--POST转PUT和DELETE的过滤器-->
<filter>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

写方法:

@ResponseBody
@RequestMapping(value = "/testRest/{id}",method = RequestMethod.GET)
public String testRestGet(@PathVariable("id") Integer id){
    System.out.println("获取id为"+id+"的条目");
    return "获取成功";
}

@ResponseBody
@RequestMapping(value = "/testRest/{id}",method = RequestMethod.DELETE)
public String testRestDelete(@PathVariable("id") Integer id){
    System.out.println("删除id为"+id+"的条目");
    return "删除成功";
}

@ResponseBody
@RequestMapping(value = "/testRest/{id}",method = RequestMethod.PUT)
public String testRestPut(@PathVariable("id") Integer id){
    System.out.println("修改id为"+id+"的条目");
    return "修改成功";
}

@ResponseBody
@RequestMapping(value = "/testRest/{id}",method = RequestMethod.POST)
public String testRestPost(@PathVariable("id") Integer id){
    System.out.println("新增条目");
    return "新增成功";
}

发请求

<!--相当于发送了delete请求-->
<form action="/testRest/12" method="post">
    <input type="hidden" name="_method" value="DELETE">
    <input type="submit" value="submit">
</form>

2.3.3 接收请求参数:@RequestParam注解

@ResponseBody
@RequestMapping(value = "/testRequestParam")
public String testRequestParam(@RequestParam(value = "username") String username,
                               @RequestParam(value = "age",required = false,defaultValue = "0") int age){
    System.out.println(username+","+age);
    return "success";
}

2.3.4 接收请求头参数:@RequestHeader注解

@ResponseBody
@RequestMapping(value = "/testRequestHeader")
public String testRequestHeader(@RequestHeader(value = "User-Agent") String userAgent){
    System.out.println(userAgent);
    return "success";
}

2.3.5 接收Cookie的值:@CookieValue注解

@ResponseBody
@RequestMapping(value = "/testCookieValue")
public String testCookieValue(@CookieValue(value = "JSESSIONID") String sessionId){
    System.out.println(sessionId);
    return "success";
}

2.3.6 使用POJO接收请求参数

POJO

public class User {
    private String username;
    private String password;
    private String email;
    private Integer age;
    private Address address;
    //getter和setter
}

public class Address {
    private String province;
    private String city;
    //getter和setter
}

接收方法

@ResponseBody
@RequestMapping("/testPojo")
public String testPojo(User user){
    System.out.println(user);
    return "success";
}

请求示例

username:beibei
password:123456
email:beibei@qq.com
age:12
address.province:shandong
address.city:weifang

2.3.7 使用Servlet原生API的参数

引入Servlet原生API

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.0.1</version>
    <scope>provided</scope>
</dependency>

可以使用的对象:

HttpServletRequest request
HttpServletResponse response
HttpSession session
Principal principal
Locale locale
InputStream inputStream
OutputStream outputStream
Reader reader
Writer writer

使用示例:

    @RequestMapping("/testServletAPI")
    public void testServletAPI(HttpServletRequest request,
                               HttpServletResponse response,
                               HttpSession session,
                               Principal principal,
                               Locale locale,
                               Reader reader,
                               Writer writer) throws IOException {
        System.out.println(request+","+response);
        System.out.println(session);
        System.out.println(principal);
        System.out.println(locale);
        System.out.println(reader+","+writer);
        writer.write("test writer");
    }

2.3.8 接收AJAX请求中包含的对象

同form表单提交一样获取即可。

2.3.9 接收格式化参数

在配置文件中加入一行

<mvc:annotation-driven/>
2.3.9.1 接收格式化的日期参数

在要格式化的字段上加入一行注解

@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birth;
2.3.9.2 接收格式化的数字参数
@NumberFormat(pattern = "#,###,###.#")
private BigDecimal price;

2.3.10 JSR303参数验证

引入jar包

<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>1.1.0.Final</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>5.4.1.Final</version>
</dependency>

配置文件中加入一行

<mvc:annotation-driven/>

实体类

public class Dog {
    @NotEmpty
    private String name;
    @Email
    private String email;
    private Integer age;
    private String desc;
    @Past
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date birth;
    @NumberFormat(pattern = "#,###,###.#")
    private BigDecimal price;
    //getter和setter
}

控制器中使用验证

@RequestMapping("/testBindDog")
public String testBindDog(@Valid Dog dog, BindingResult result, Model model){
    //接收错误信息
    if(result.getErrorCount()>0){
        System.out.println("出错了!");
        result.getFieldErrors().forEach((error)->
                                        System.out.println(error.getField()+":"+error.getDefaultMessage()));
        //若验证出错,重定向至定制的页面
        model.addAttribute("org.springframework.validation.BindingResult.exField",result);
        return "/dogErrors";
    }
    System.out.println(dog);
    return "success";
}

错误处理页面

<%@ page contentType="text/html;charset=UTF-8" isELIgnored="false" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
    <body>
        <%--显示所有的错误--%>
        <form:errors path="exField.*"></form:errors>

        <%--显示特定字段的错误--%>
        <form:errors path="exField.name"></form:errors>
    </body>
</html>

2.3.11 自定义JSR303验证的错误消息

国际化配置文件

i18n_en_US.properties

NotEmpty.dog.name=error,狗名不能为空
Email.dog.email=error,狗的邮件格式不正确
Past.dog.birth=error,狗的生日不能晚于当前时间
#数据类型不匹配
typeMismatch.dog.birth=error,狗生日格式不对
#参数必须,但是没有填入使用required
#SpringMVC调用处理方法时发生异常methodInvocation

i18n_zh_CN.properties

NotEmpty.dog.name=错误,狗名不能为空
Email.dog.email=错误,狗的邮件格式不正确
Past.dog.birth=错误,狗的生日不能晚于当前时间
#数据类型不匹配
typeMismatch.dog.birth=错误,狗生日格式不对
#参数必须,但是没有填入使用required
#SpringMVC调用处理方法时发生异常methodInvocation

配置国际化

<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="basename" value="i18n"/>
</bean>

这样子就实现了消息定制了

2.4 响应请求

2.4.1 ModelAndView:存储视图和数据信息

使用方法:
里面设置的数据在jsp中可以el的requestScope获取。

@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){
    //ModelAndView存储了视图和数据的信息。
    ModelAndView mv = new ModelAndView("success");
    mv.addObject("time",new Date());
    return mv;
}

2.4.2 Map、ModelMap、Model和ExtendedModelMap:存储数据

实际传入的是一个BindingAwareModelMap的类型,BindingAwareModelMap类继承了ExtendedModelMap,ExtendedModelMap继承了ModelMap并实现了Model接口,ModelMap继承了LinkedHashMap<String, Object>,LinkedHashMap<String, Object>继承了HashMap<String, Object>实现了Map<String, Object>接口。因此如果功能足够的话,上面任何一个类或接口都可以当作参数。

数据获取方法同ModelAndView。

@RequestMapping("/testMap")
public String testMap(Model model){
    model.addAttribute("names", Arrays.asList("Tom","Jerry","Mike"));
    return "success";
}

2.4.3 @SessionAttributes注解:使用Session域

@SessionAttributes放在类上,标识该类中涉及的哪些特定名称或类型request域的对象在设置到request域的同时也设置到Session域中。

@Controller
@SessionAttributes(value = {"user"},types = {String.class})
public class HelloWorld {
    @RequestMapping("/testSession")
    public String testSession(Model model){
        User user = new User();
        user.setUsername("beibei");

        model.addAttribute(user);
        model.addAttribute("parent","laoming");
        model.addAttribute("pre_parent","laolaoming");
        return "success";
    }
}

上面名为user的实体,还有两个字符串的实体均被同时存放到了sessionScope和requestScope。

2.4.4 @ModelAttribute注解:传入对象预处理

最常见的使用场景:修改一个对象的属性
在要修改的对象传入前,根据其id从数据库中查询到旧的数据,然后再与传入的参数比较,如果有不同的就使用新传入的值,达到修改数据的目的。

示例:

@ModelAttribute
public void getUser(@RequestParam(value = "id",required = false) Integer id,
                    Model model){
    if(id != null){
        //模拟从数据库中获取原来的对象
        User user = new User(1, "Tom", "123456", "tom@qq.com", 12);
        System.out.println("从数据库获取了原来id为"+id+"的对象数据");
        model.addAttribute("user",user);
    }
}

@ResponseBody
@RequestMapping("/testModelAttribute")
public String testModelAttribute(User user){
    System.out.println("修改:"+user);
    return "success";
}

默认是model里面放的名字要和入参名匹配且必须都为类名首字母小写后的全名,但是也可以在入参处再次使用@ModelAttribute将其手动匹配起来。

@ModelAttribute
public void getUser(@RequestParam(value = "id",required = false) Integer id,
                    Model model){
    if(id != null){
        //模拟从数据库中获取原来的对象
        User user = new User(1, "Tom", "123456", "tom@qq.com", 12);
        System.out.println("从数据库获取了原来id为"+id+"的对象数据");
        model.addAttribute("abc",user);
    }
}

@ResponseBody
@RequestMapping("/testModelAttribute")
public String testModelAttribute(@ModelAttribute(value = "abc") User user){
    System.out.println("修改:"+user);
    return "success";
}

2.4.5 引入JSTL标签

引入JSTL的jar包,内置视图解析器会自动识别,然后将原来的视图自动转化为JSTL视图。

<dependency>
    <groupId>jstl</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>
<dependency>
    <groupId>taglibs</groupId>
    <artifactId>standard</artifactId>
    <version>1.1.2</version>
</dependency>

2.4.6 JSTL应用-国际化示例。

切换不同的浏览器语言,能加载不同地区的语言。

配置文件,放于根类路径下:

i18n_zh_CN.properties:

i18n.username=用户名

i18n_en_US.properties:

i18n.username=UserName

在springmvc的配置文件中配置国际化资源管理器

<!--配置国际化资源文件-->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="basename" value="i18n"/>
</bean>

使用JSTL引用国家化资源

<%@ page contentType="text/html;charset=UTF-8" isELIgnored="false" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<html>
  <body>
    <h3><fmt:message key="i18n.username"/></h3>
  </body>
</html>

2.4.7 在Controller中获取国际化的消息

@Autowired
private MessageSource messageSource;

@ResponseBody
@RequestMapping("/testi18n")
public String testi18n(Locale locale){
    String val = messageSource.getMessage("i18n.name",null,locale);
    return val;
}

2.4.8 通过超链接进行国际化消息管理

Controller中还是和上面那样写就行。

2.4.8.1 处理过程

参数->拦截器->解析器->handler
在这里插入图片描述

2.4.8.2 实现步骤

加入一个拦截器和一个解析器即可

<!--配置拦截器-->
<mvc:interceptors>
    <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"></bean>
</mvc:interceptors>
<!--配置解析器-->
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"/>
2.4.8.3 请求示例
http://localhost:8080/testi18n?locale=en_US
http://localhost:8080/testi18n?locale=zh_CN

2.4.9 直接响应请求的资源,无需经过handler

唉,以前太傻了,不知道有这个。

<mvc:annotation-driven/>
<mvc:view-controller path="/success" view-name="success"/>

2.4.10 返回Json字符串

引入Jackson

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.7.3</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.7.3</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.7.3</version>
</dependency>

方案一,在服务器端转化为字符串再发送给客户端

@ResponseBody
@RequestMapping("/testJson")
public String testJson(){
    User user = new User(1, "Tom", "123456", "tom@qq.com", 12);

    String mapJakcson=null;
    try {
        ObjectMapper mapper = new ObjectMapper();
        mapJakcson = mapper.writeValueAsString(user);
    } catch (JsonProcessingException e) {
        e.printStackTrace();
    }
    return mapJakcson;
}

方案二,在服务器端配置<mvc:annotation-driven/>后直接返回对象即可

@ResponseBody
@RequestMapping("/testJson")
public Dog testJson(){
    return new Dog();
}

2.4.11 自定义视图

定义视图类

@Component
public class HelloView implements View {
    @Override
    public String getContentType() {
        return "text/html";
    }

    @Override
    public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        response.getWriter().print("hello view, time: "+new Date());
    }
}

springmvc的配置文件中

<!--配置按视图名字解析视图解析器,order越小,优先级越高,将这个视图解析器优先级设高,则优先使用视图名字解析视图-->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
    <property name="order" value="100"/>
</bean>

使用视图

@RequestMapping("/testView")
public String testView(){
    System.out.println("testView");
    return "helloView";
}

//或者
    
@RequestMapping("/testView")
public ModelAndView testView(){
    ModelAndView mv = new ModelAndView("helloView");
    System.out.println("testView");
    return mv;
}

2.4.12 转发和重定向

转发

@RequestMapping("/forwardObject")
public void forwardObject(){
    System.out.println("转发过来的");
}

@RequestMapping("/testForward")
public String testForward(){
    return "forward:/forwardObject";
}

重定向

@ResponseBody
@RequestMapping("/redirectObject")
public String redirectObject(){
    return "redirect object";
}

@RequestMapping("/testRedirect")
public String testRedirect(){
    return "redirect:/redirectObject";
}

2.4.13 自定义类型转换器

例子,将接收的String类型的参数转换为为Dog对象
String参数的格式为:字段1-字段2-字段3。

Dog

public class Dog {
    private String name;
    private Integer age;
    private String gender;
    //getter的setter
}

转换器
Converter接口里面的两个泛型参数分别为源类型和目标类型

@Component
public class StringToDogConverter implements Converter<String,Dog>{
    @Override
    public Dog convert(String source) {
        String[] values = source.split("-");
        Dog dog = new Dog();
        dog.setName(values[0]);
        dog.setAge(Integer.parseInt(values[1]));
        dog.setGender(values[2]);
        return dog;
    }
}

在spring容器中配置该转换器

<!--使用类型转换器-->
<mvc:annotation-driven conversion-service="conversionService"/>
<!--配置类型转换器-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <ref bean="stringToDogConverter"/>
        </set>
    </property>
</bean>

测试该转换器

@ResponseBody
@RequestMapping("/testConverter")
public String testConverter(@RequestParam("dog") Dog dog){
    System.out.println(dog);
    return "success";
}

2.4.14 设置数据绑定器(例如指定不绑定某个字段)

这个样子就不会绑定Dog中的name字段了。

@RequestMapping("/testBindDog")
public String testBindDog(Dog dog){
    System.out.println(dog);
    return "success";
}

@InitBinder
public void initBinder(WebDataBinder dataBinder){
    dataBinder.setDisallowedFields("name");
}

2.4.15 配置拦截器

2.4.15.1 拦截器代码
public class FirstInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle:我在调用处理器之前执行。可以做权限控制、日志、事务");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle:我在调用处理器之后,渲染视图之前执行,此时视图已经存在,但是还没有数据。可以对请求域中属性或视图做出更改");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion:我在都调用完成之后进行。可以释放资源");
    }
}
2.4.15.2 配置拦截器bean:拦截全路径
<!--配置拦截器-->
<mvc:interceptors>
    <bean class="com.beibeixyzxyz.spring.interceptors.FirstInterceptor"/>
</mvc:interceptors>
2.4.15.3 配置拦截器bean:拦截指定路径(或排除指定路径)
<!--配置拦截器-->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/testi18n"/>
        <bean class="com.beibeixyzxyz.spring.interceptors.FirstInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>
2.4.15.4 多个拦截器的执行顺序

在这里插入图片描述

按拦截器在配置文件中的顺序执行
例如有两个拦截器interceptor1和interceptor2,且interceptor1配置在interceptor2前面。
则方法的执行顺序为(注意后面两个是反序)

interceptor1.preHandle
interceptor2.preHandle
interceptor2.postHandle
interceptor1.postHandle
interceptor2.afterCompletion
interceptor1.afterCompletion

2.4.16 使用@ResponseStatus注解标识方法应该返回的状态码和信息

@ResponseStatus(value = HttpStatus.BAD_REQUEST,reason = "未找到用户名")
@ResponseBody
@RequestMapping("/testHttpMessageConverter")
public String testHttpMessageConverter(@RequestBody String body){
    System.out.println(body);
    return "success"+(new Date());
}

2.5 配置文件内容详解

2.5.1 <mvc:annotation-driven/>配置

这个配置在容器中注册了RequestMappingHandlerMapping(处理器映射器)、RequestMappingHandlerAdapter(处理器适配器)、ExceptionHandlerExceptionResolver(异常解析器)三个bean。
还启用了以下内容:
1.支持使用@NumberFormat注解格式化数字字段。
2.支持使用@DateTimeFormat注解格式化日期。
3.支持使用@Valid注解进行JSR303数据校验。
4.支持一系列通用转换器。
5.支持使用RequestBody和ResponseBody注解(就算不加这个在4.x以上版本中照样可以使用,只不过使用的是3.x的旧版而已)。

2.5.2 <mvc:default-servlet-handler/>配置

可以使得当springmvc的handler处理不了的时候让服务器的行为变得像静态服务器一样,返回webapp或者WebContent下的资源文件。

2.6 上传/下载文件

2.6.1 上传文件示例1:使用@RequestBody注解

服务器端

    @ResponseBody
    @RequestMapping("/testHttpMessageConverter")
    public String testHttpMessageConverter(@RequestBody String body){
        System.out.println(body);
        return "success"+(new Date());
    }

客户端

<form action="/testHttpMessageConverter" method="post" enctype="multipart/form-data">
    File:<input type="file" name="file"/>
    Desc:<input type="text" name="desc">
    <input type="submit" value="上传">
</form>

2.6.2 上传文件示例2:使用commons-fileupload

导入jar包

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.1</version>
</dependency>

配置bean

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="defaultEncoding" value="UTF-8"/>
    <property name="maxUploadSize" value="1024000000"/>
</bean>

服务端

@RequestMapping("/testFileUpload")
public String testFileUpload(@RequestParam("desc") String desc,
                             @RequestParam("file") MultipartFile file) throws IOException {
    System.out.println("desc:"+desc);
    //获取文件信息
    System.out.println("OriginalFilename"+file.getOriginalFilename());
    //保存文件到某位置
    file.transferTo(new File("C:\\Users\\Administrator\\Desktop\\test\\"+file.getOriginalFilename()));
    return "success";
}

客户端

<form action="/testHttpMessageConverter" method="post" enctype="multipart/form-data">
    File:<input type="file" name="file"/>
    Desc:<input type="text" name="desc">
    <input type="submit" value="上传">
</form>

2.6.3 下载文件示例1:使用ResponseEntity<T>

服务器端

@RequestMapping("/testResponseEntity")
public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException {
    byte[] body = null;
    ServletContext servletContext = session.getServletContext();
    InputStream in = servletContext.getResourceAsStream("/WEB-INF/files/test.txt");
    body = new byte[in.available()];
    in.read(body);

    HttpHeaders headers = new HttpHeaders();
    headers.add("Content-Disposition","attachment;filename=abc.txt");

    HttpStatus statusCode = HttpStatus.OK;

    ResponseEntity<byte[]> responseEntity = new ResponseEntity<byte[]>(body,headers,statusCode);
    return  responseEntity;
}

2.7 异常处理

2.7.1 使用@ExceptionHandler处理异常

异常匹配优先级:首先在控制器本类中查找处理异常的方法,如果找不到就去外面找带有@ControllerAdvice标记的类中寻找异常处理的方法。匹配时先匹配最符合的异常,找不到最符合的再去找次符合的(多态)。

2.7.1.1 类内处理
@Controller
public class HelloWorld {
    @ExceptionHandler({ArithmeticException.class})
    public ModelAndView ExceptionHandler1(ArithmeticException ex){
        ModelAndView mv = new ModelAndView("error");
        mv.addObject("errorMsg",ex);
        return mv;
    }

    @ResponseBody
    @RequestMapping("/testExceptionHandler")
    public String testExceptionHandler(@RequestParam("i") int i){
        return "result:"+(10/i);
    }
}
2.7.1.2 类外处理
@Controller
public class HelloWorld {
    @ResponseBody
    @RequestMapping("/testExceptionHandler")
    public String testExceptionHandler(@RequestParam("i") int i){
        return "result:"+(10/i);
    }
}
@ControllerAdvice
public class SimpleExceptionHandler {

    @ExceptionHandler({ArithmeticException.class})
    public ModelAndView ExceptionHandler1(ArithmeticException ex){
        ModelAndView mv = new ModelAndView("error");
        mv.addObject("errorMsg",ex);
        return mv;
    }
}

2.7.2 使用@ResponseStatus注解指定错误码和异常信息

@RequestMapping("/testResponseExceptionHandler")
public String testResponseExceptionHandler(@RequestParam("i") int i){
    if(i == 10){
        throw new UserNameNotFoundException();
    }
    return "success";
}
@ResponseStatus(value = HttpStatus.FORBIDDEN,reason = "未找到用户名")
public class UserNameNotFoundException extends RuntimeException{
}

2.7.3 DefaultHandlerExceptionResolver:默认异常处理器

这个处理器的作用是将异常转化为tomcat可以处理的异常格式,然后返回给客户端。例如一个方法规定必须用GET请求,但是发来一个POST请求,此时如果不做异常配置,这个异常处理器就会起作用了。

2.7.4 使用SimpleMappingExceptionResolver类进行映射配置异常

抛出异常的方法

@RequestMapping("/testSimpleMappingExceptionResolver")
public String testSimpleMappingExceptionResolver(@RequestParam("i") int i){
    if(i == 1){
        throw new ArrayIndexOutOfBoundsException();
    }
    return "success";
}

配置异常映射器

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="exceptionMappings">
        <props>
            <prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>
        </props>
    </property>
</bean>

error.jsp接收异常,默认参数为exception

<%@ page contentType="text/html;charset=UTF-8" isELIgnored="false" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<html>
  <body>
    <h2>${exception}</h2>
  </body>
</html>

2.8 配置实例

2.8.1 一种可以同时处理静态资源和非静态资源的配置方式

<!--配置自动扫描的包-->
<context:component-scan base-package="com.beibeixyzxyz.restful_crud"/>


<mvc:default-servlet-handler/>
<mvc:annotation-driven/>
<mvc:resources mapping="/files/**" location="/WEB-INF/views/files/"/>
<mvc:resources mapping="/scripts/**" location="/WEB-INF/views/scripts/"/>

<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/"/>
    <property name="suffix" value=".jsp"/>
</bean>

3.整合Spring和SpringMVC

在tomcat的配置文件中Spring是使用监听器加载的,SpringMVC是以Servlet的形式加载的。

3.1 整合的web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         id="WebApp_ID" version="2.5">

  <!--配置全局参数:Spring容器配置文件的路径,后面Spring启动监听器会使用这个参数加载配置文件-->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>

  <!--Spring启动监听器,其实质是一个监听整个Web应用的监听器类-->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <!--配置DispatcherServlet-->
  <servlet>
    <servlet-name>springDispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!--配置SpringMVC配置文件的位置和名称-->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <!--应用加载即创建该Servlet-->
    <load-on-startup>1</load-on-startup>
  </servlet>
  <!--指定DispatcherServlet接管哪些类型的请求-->
  <servlet-mapping>
    <servlet-name>springDispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

3.2 使两个Spring容器协同工作

一般在Spring中会配置数据源,Service,Dao,事务等功能,SpringMVC中会配置处理器。
但是如果Spring和SpringMVC所扫描的包路径存在重合的话,就有可能导致一个对象被初始化两次。

如何避免这个问题还是要看模块的划分方法

3.2.1 一、如果项目是以应用功能划分的包,则可以让两个配置文件分别扫描不同的包即可

SpringMVC

<context:component-scan base-package="com.beibeixyzxyz.spring.controller"/>

Spring

<context:component-scan base-package="com.beibeixyzxyz.spring.dao,com.beibeixyzxyz.spring.service"/>

3.2.2 二、如果项目是以模块功能划分的包,则可以使用exclude-filter和include-filter进行特定注解的区分扫描

SpringMVC

<context:component-scan base-package="com.beibeixyzxyz.spring" use-default-filters="false">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>

Spring

<context:component-scan base-package="com.beibeixyzxyz.spring" use-default-filters="false">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    <context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>

3.2.3 三、Spring IoC容器和SpringMVC IoC容器之间的关系

SpringMVC IoC容器是Spring IoC容器的一个子容器,SpringMVC IoC容器中的bean可以访问Spring IoC中的bean,反过来则不行。就像是java内部类访问外部类的属性一样。
其中Spring IoC容器是父容器,SpringMVC IoC容器是子容器,子容器能随意访问父容器的bean,而父容器不能访问子容器的bean。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_Kirito

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值