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。