eclipse安装spring插件地址

https://spring.io/tools3/sts/all

①选择对应版本插件包下载

②安装步骤
  1.help=》install new software

  2.add=》archive

  3.选择四个带有springIDE字样的选项

eclipse导入spring框架包
1.导入包到lib文件夹下,Build Path
spring framework 下载地址https://repo.spring.io/release/org/springframework/spring/
Spring核心容器
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
Spring运行的时候依赖一个日志包,如果没有就报错
commons-logging-1.2.jar 下载地址http://commons.apache.org/proper/commons-logging/download_logging.cgi
2.src创建配置文件
spring配置文件中,集合了spring的ioc容器管理的所有组件(会员清单)
创建一个Spring Bean Configuration File(Spring的bean配置文件)
3.注册一个对象,spring会自动创建这个对象
一个Bean标签可以注册一个组件(对象、类),class写要注册的组件的全类名,id,这个对象的唯一标示
junit test 测试

①创建Person.java

package com.demo;
 
public class Person {
 
	private String lastName;
	private Integer age;
	private String email;
	private String gender;
 
	@Override
	public String toString() {
		return this.lastName+this.email+this.gender+this.age;
	}
 
	public String getLastName() {
		return lastName;
	}
 
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
 
	public Integer getAge() {
		return age;
	}
 
	public void setAge(Integer age) {
		this.age = age;
	}
 
	public String getEmail() {
		return email;
	}
 
	public void setEmail(String email) {
		this.email = email;
	}
 
	public String getGender() {
		return gender;
	}
 
	public void setGender(String gender) {
		this.gender = gender;
	}
 
}

②src下添加bean

<?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">
 
	<bean id="person1" class="com.demo.Person">
	  <property name="lastName" value="张三"></property>
	  <property name="age" value="11"></property>
	  <property name="email" value="412198579@qq.com"></property>
	  <property name="gender" value="男"></property>
	</bean>
 
</beans>

③创建单元测试文件

package com.demo;
 
import  static org.junit.Assert.*;
 
import org.junit.Test;
 
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
 
public class IOCTest {
 
	@Test
	public void test() {
		// 创建 Spring 的 IOC 容器
		// ApplicationContext代表IOC容器
		ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml");
		// 从 IOC 容器中获取 bean 的实例
		Person bean = (Person) ioc.getBean("person1");
		System.out.println(bean);
	}
}

报错java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory

Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory
    at org.springframework.jdbc.support.JdbcAccessor.<init>(JdbcAccessor.java:43)
    at org.springframework.jdbc.core.JdbcTemplate.<init>(JdbcTemplate.java:164)
    at DemoJDBCTemplate.Templatedemo1.main(Templatedemo1.java:13)
Caused by: java.lang.ClassNotFoundException: org.apache.commons.logging.LogFactory
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:185)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:496)
    ... 3 more

解决方案如下:导入 导入commons-logging-1.2.jar辅助类包即可。
+++++++++++++++++++
报错java.lang.Exception: No tests found matching

java.lang.Exception: No tests found matching [{ExactMatcher:fDisplayName=test2], {ExactMatcher:fDisplayName=test2(cn.amumu.spring.test.shiro.ShiroTest12)], {LeadingIdentifierMatcher:fClassName=cn.amumu.spring.test.shiro.ShiroTest12,fLeadingIdentifier=test2]] from org.junit.internal.requests.ClassRequest@33f88ab
at org.junit.internal.requests.FilterRequest.getRunner(FilterRequest.java:37)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.createFilteredTest(JUnit4TestLoader.java:77)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.createTest(JUnit4TestLoader.java:68)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.loadTests(JUnit4TestLoader.java:43)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:444)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

解决方案如下:使用的junit版本为4.12,使用的spring包是4.0.0,将spring包更换为4.1.2
+++++++++++++++++++++++++++++
使用有参构造器创建javabean
Person.java

	public Person(String lastName, Integer age, String email, String gender) {
		this.lastName = lastName;
		this.age = age;
		this.email = email;
		this.gender = gender;
	}

ioc.xml

	<bean id="person03" class="com.bean.Person">
		<constructor-arg name="lastName" value="王二麻子"></constructor-arg>
		<constructor-arg name="age" value="11"></constructor-arg>
		<constructor-arg name="email" value="haha@qq.com"></constructor-arg>
		<constructor-arg name="gender" value="男士"></constructor-arg>
	</bean>

注意:可以省略name属性,但必须严格按照构造参数顺序赋值,但是推荐使用带name属性的方式最好。
+++++++++++++++++++
通过p空间命名为bean赋值

beans属性就多了p属性 xmlns:p(xmlns:p=”http://www.springframework.org/schema/p”)

	<bean id="person04" class="com.bean.Person"  p:lastName="小小"  p:age="11" p:email="222@qq.com" p:gender="女">
	</bean>

++++++++++++++
为bean属性赋值复杂元素
ioc.xml

   <bean class="com.bean.Person" name="person01">
     <property name="lastName" value="null">
    </property>
   </bean>
 
    <bean class="com.bean.Person" name="person02">
    <property name="lastName">
    <null></null>
    </property>

单元测试

	@Test
	public  void test5() {
		ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc2.xml");
		Person bean =  ioc.getBean("person01",Person.class);
		System.out.println(bean.getLastName() == null);  // 输出false
	}
 
 
 
	@Test
	public  void test6() {
		ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc2.xml");
		Person bean =  ioc.getBean("person02",Person.class);
		System.out.println(bean.getLastName() == null);   // 输出true
 
	}

+++++++++++++++++++++++++++++
ref引用外部的值(外部bean)
ioc2.xml

	<bean class="com.bean.Person" id="person03">
		<property name="lastName" value="xiaoli"></property>
		<property name="car" ref="car01"></property> //引用外部类
	</bean>
	<bean class="com.bean.Car" id="car01">
		<property name="carName" value="大众汽车"></property>
	</bean>

test.java

		public  void test7() {
		ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc2.xml");
		Person bean =  ioc.getBean("person03",Person.class);
		Car car = bean.getCar();
		car.setCarName("宝马汽车");
		System.out.println(car.getCarName());
	}

+++++++++++++++++++++++++++++
配置xml文件改写可以达到同样的方式(内部bean)

	<bean class="com.bean.Person" id="person03">
		<property name="lastName" value="xiaoli"></property>
		<property name="car">
			<bean id="car01" class="com.bean.Car">
				<property name="carName" value="劳斯莱斯汽车"></property>
			</bean>
		</property>
	</bean>

+++++++++++++++++++++
为list属性赋值
Person.java

	private List<Book> books;

ioc2.xml

		<bean class="com.bean.Person" id="person08">
		<property name="lastName" value="xiaoli"></property>
		<property name="books">
		    <list>
		    <bean class="com.bean.Book" id="book001" p:bookName="零基础学jva" p:author="明日科技" ></bean>
		    <bean class="com.bean.Book" id="book002" p:bookName="零基础学PHP" p:author="明日科技" ></bean>
		    <ref bean="book003"></ref>
		    </list>
		</property>
	</bean>
    <bean class="com.bean.Book" id="book003">
      <property name="bookName" value="零基础学C#" ></property>
      <property name="author" value="明日科技"></property>
    </bean>

Test.java

	public  void test8() {
		ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc2.xml");
		Person bean =  ioc.getBean("person08",Person.class);
		System.out.println(bean.getBooks());
	}

++++++++++++++++++
为map属性赋值
Person.java

	private Map<String,Object> maps;
	public Map<String, Object> getMaps() {
		return maps;
	}
	public void setMaps(Map<String, Object> maps) {
		this.maps = maps;
	}

ioc.xml

	<bean class="com.bean.Person" id="person08">
		<property name="lastName" value="xiaoli"></property>
		<property name="maps">
		    <map>
		       <entry key="key01" value="喜洋洋"></entry>
		       <entry key="key02" value="灰太狼"></entry>
		       <entry key="key03" value-ref="book003"></entry>
		    </map>
		</property>
	</bean>
 
    <bean class="com.bean.Book" id="book003">
      <property name="bookName" value="零基础学C#" ></property>
      <property name="author" value="明日科技"></property>
    </bean>

Test.java

		public  void test8() {
		ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc2.xml");
		Person bean =  ioc.getBean("person08",Person.class);
		System.out.println(bean.getMaps());
	}

++++++++++++++++++
为Properties属性赋值
Person.java

	private Properties properties;
	public Properties getProperties() {
		return properties;
	}
	public void setProperties(Properties properties) {
		this.properties = properties;
	}

ioc2.xml

		<bean class="com.bean.Person" id="person08">
		<property name="properties">
		  <props>
		    <prop key="username">小强</prop>
		    <prop key="age">18</prop>
		  </props>
		</property>
	</bean>

Test.java

	@Test
	public  void test8() {
		ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc2.xml");
		Person bean =  ioc.getBean("person08",Person.class);
		System.out.println(bean.getProperties());
	}

++++++++++++++++++++++++++++++
为util命名空间创建一个可以外部引用的集合
ioc2.xml

    <bean class="com.bean.Person" id="person010">
       		<property name="maps" ref="map01"></property>
    </bean>
    <util:map id="map01">
		       <entry key="key01" value="孙悟空"></entry>
		       <entry key="key02" value="猪八戒灰太狼"></entry>
		       <entry key="key03" value-ref="book003"></entry>
    </util:map>

Test.java

	@Test
	public  void test10() {
		ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc2.xml");
		Person bean =  ioc.getBean("person010",Person.class);
		Map maps = (Map<String,Object>)(bean.getMaps());
		System.out.println(maps);
	}

++++++++++++++++++++++++++++++
级联属性赋值(为属性的属性赋值)
ioc2.xml

    <bean class="com.bean.Person" id="person011">
       		<property name="car" ref="car01"></property>
       		<property name="car.carName" value="吉利帝豪"></property>
    </bean>
 
    <bean class="com.bean.Car" id="car01">
       		<property name="carName" value="雪佛兰赛欧"></property>
    </bean>

Test.java

	@Test
	public  void test11() {
		ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc2.xml");
		Person bean =  ioc.getBean("person011",Person.class);
		System.out.println(bean.getCar().getCarName());
	}

++++++++++++++++++++++++++++++
通过bean继承,达到重用的效果,通过bean的parent属性

	<bean id="person02" class="com.bean.Person">
		<property name="lastName" value="李四"></property>
		<property name="age" value="11"></property>
		<property name="email" value="110@qq.com"></property>
		<property name="gender" value="女"></property>
	</bean> 
	<bean id="person05" class="com.bean.Person" parent="person02">
	  <property name="lastName" value="王二麻子"></property>
	</bean>

++++++++++++++++++++++++++++++
通过abstract调用一个模板bean,如果直接使用会报错,只能被继承

	<bean id="person06" class="com.bean.Person" abstract="true">
		<property name="lastName" value="凌凌漆"></property>
		<property name="age" value="11"></property>
		<property name="email" value="110@qq.com"></property>
		<property name="gender" value="女"></property>
	</bean>

++++++++++++++++++++++++++++++
默认是自上向下执行bean,depends-on可以改变默认顺序

	 <bean id=“manager” class=“cn.lovepi.***.CacheManager”depends-on=“sysInit”/>  
     <bean id=“sysInit” class=“cn.lovepi.***.SysInit” />

++++++++++++++++++++++++++++++++
静态工厂:工厂本身不用创建对象,通过静态方法调用,对象 = 工厂类.工厂方法名();
实例工厂:工厂本身需要创建对象;对象 = 工厂实例.工厂方法名();
Plane.java

package com.bean;
public class Plane {
	private String name;
	private String size;
	private String color;
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return super.toString();
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getSize() {
		return size;
	}
	public void setSize(String size) {
		this.size = size;
	}
	public String getColor() {
		return color;
	}
	public void setColor(String color) {
		this.color = color;
	}
}

①使用静态工厂创建的bean

package com.factory;
import com.bean.Plane;
public class StaticFactory {
   public static Plane makePlane(String method) {
		Plane plane = new Plane();
		plane.setName(method + "工厂造飞机");
		plane.setColor("黑色白色");
		plane.setSize("120米");
		return plane;
   }
}
    <bean class="com.factory.StaticFactory" id="staticFactory" factory-method="makePlane">
        <constructor-arg value="静态工厂"></constructor-arg>
    </bean>

②使用实例工厂创建的bean

package com.factory;
import com.bean.Plane;
public class ObjectFactory {
	public Plane makePlane(String method) {
		Plane plane = new Plane();
		plane.setName(method + "工厂造飞机");
		plane.setColor("白色");
		plane.setSize("110米");
		return plane;
	}
}
    <bean class="com.factory.ObjectFactory" id="objectFactoryBean" >
    </bean>
    <!-- 
        1.先配置实例工厂对象
        2.配置我们要创建的对象使用哪个对象创建
          ① factory-bean 指定使用哪个工厂实例
          ② factory-method 指定使用哪个工厂方法
     -->
    <bean class="com.bean.Plane" id="objectFactory2" factory-bean="objectFactoryBean" factory-method="makePlane">
       <constructor-arg value="实例工厂"></constructor-arg>
    </bean>

③实现FactoryBean的工厂

package com.factory;
 
import org.springframework.beans.factory.FactoryBean;
 
import com.bean.Book;
/**
 * 实现FactoryBean的类,Spring会识别为工厂类
 * Spring会自动调用工厂方法创建实例
 */
 
public class FactoryBeanImplements implements FactoryBean<Book>{
 
	@Override
	public Book getObject() throws Exception {
		Book book = new Book();
		book.setAuthor("机械工业出版社");
		book.setBookName("java web从入门到放弃");
		return book;
	}
 
	@Override
	public Class< ?> getObjectType() {
		return Book.class;
	}
 
	@Override
	public boolean isSingleton() {
		return false;
	}
 
}
    <!-- FactoryBean是Spring规定的一个接口
                       只要是实现这个接口的类,Spring都认为是一个工厂
     -->
   <bean class="com.factory.FactoryBeanImplements" id="FactoryBean">
   </bean>

Bean的生命周期
Book.java

	public void initMethod() {
		System.out.println("init初始化");
	}
 
	public void destroy() {
		System.out.println("销毁");
	}

单例(scope=”singleton”) 容器启动构造器-》初始化方法=》(容器关闭)销毁方法

<bean class="com.bean.Book" id="book01" init-method="initMethod" destroy-method="destroy" scope="singleton"></bean>
	@Test
	public void test() {
		ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml");
		ioc.close();
	}

输出
init初始化

销毁

多实例(scope=”prototype”) 获取Bean构造器=》初始化方法=》(容器关闭)不自动销毁Bean,需要手动销毁

<bean class="com.bean.Book" id="book011" init-method="initMethod" destroy-method="destroy" scope="prototype"></bean>
	@Test
	public void test() {
		ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml");
		Book book = (Book)ioc.getBean("book011");
		ioc.close();   // (容器关闭)不自动销毁Bean
		//book.destroy();   需要手动销毁
	}

Bean后置处理器

package com.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
 
public class BeanPostProcessorDemo implements BeanPostProcessor{
 
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("bean初始化之前的方法"+beanName);
		return bean;
	}
 
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("bean初始化之后的方法"+beanName);
		return bean;
	}
}
<?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">
 
   <bean class="com.bean.Book" id="book011" init-method="initMethod" destroy-method="destroy" scope="prototype"></bean>
   <bean class="com.bean.BeanPostProcessorDemo"></bean>
</beans>
	@Test
	public void test() {
		ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml");
		Book book = (Book)ioc.getBean("book011");
		ioc.close();
	}

运行后输出如下:
bean初始化之前的方法book011
init初始化
bean初始化之后的方法book011
Spring管理连接池

   <bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource">
     <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/tubu_dangqian"></property>
     <property name="user" value="root"></property>
     <property name="password" value="root"></property>
     <property name="driverClass" value="  "></property>
   </bean>
   	@Test
	public void jdbc() throws SQLException {
		// 1.从容器中拿到连接池
		//DataSource dataSource = (DataSource) ioc.getBean("dataSource");
		// 2.按照类型获取组件,可以获取到这个类型下的所有实现子类等
		DataSource dataSource = ioc.getBean(DataSource.class);
		System.out.println(dataSource.getConnection());
	}

spring连接池引用外部配置文件
dbconfig.properties

	username=root
	password=root
	jdbcUrl=jdbc:mysql://localhost:3306/tubu_dangqian
	driverClass=com.mysql.jdbc.Driver

ioc.xml

   <!-- 加载外部配置文件 固定写法classpath表示引用类路径下的一个资源 -->
   <context:property-placeholder location="classpath:dbconfig.properties"/>
   <bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource">
     <property name="jdbcUrl" value="${jdbcUrl}"></property>
     <property name="user" value="${username}"></property>
     <property name="password" value="${password}"></property>
     <property name="driverClass" value="${driverClass}"></property>
   </bean>

基于xml的自动装配

①autowire=”default” / autowire=”no”:不自动装配,不自动为car属性赋值赋值
②autowire=”byName”: 按照名字赋值
private Car car;以属性名car作为ID去容器中找到这个组件,给他赋值,如果找不到就装配null
③autowire=”byType”:以属性的类型作为查找依据去容器中找到这个组件;如果容器中有多个这种类型的组件,就会报错
car = ioc.getBean(Car.class)
④autowire=”constructor”:按照构造器进行赋值

  1. 先按照有参构造器参数的类型进行装配,成功就赋值;没有就报错(但是视频上是装配null)
  2. 如果按照类型找到了多个,参数的名字作为id继续装配;找到就装配,找不到就装配null

Person.java中的构造方法

   	public Person(Car car) {
		System.out.println("构造方法");
		this.car = car;
	}
	 <bean id="car" class="com.bean.Car">
	  <property name="carName" value="宝马汽车"></property>
	</bean>
	<bean id ="person07" class="com.bean.Person" autowire="constructor">
	</bean>

SpEL(Spring Expression Language),spring表达式语言

		<bean id="person08" class="com.bean.Person">
		  <!-- 引用其他bean的某个属性 -->
		  <property name="lastName" value="#{car.carName}"></property>
		  <!-- 字面量 -->
		  <property name="age" value="#{12*5}"></property>
		  <!-- 引用其他bean -->
		  <property name="car" value="#{car}"></property>
		  <!-- 调用静态方法 #{T(全类名).静态方法 -->
		  <property name="email" value="#{T(java.util.UUID).randomUUID().toString()}@qq.com"></property>
          <!-- 调用静态方法 对象.非静态方法 #{对象.非静态方法}  -->
	      <property name="gender" value="#{car.getCarName()}"></property>
	</bean>

通过注解分别创建Dao,Service,Servlet
导入jar包spring-aop-4.1.2.RELEASE.jar
某个类上添加上任何一个注解都能快速的将bean加入到这个ioc容器的管理中
spring注解

  1. @Controller 控制器:我们推荐给控制器层servlet包下的这些组件加这个注解 import org.springframework.stereotype.Controller;
  2. @Service 业务逻辑:我们推荐业务逻辑层的组件添加这个注解 import org.springframework.stereotype.Service;
  3. @Repository 给数据库层(持久化层,dao层)的组件添加这个注解 import org.springframework.stereotype.Repository;
  4. @Component 给不属于以上基层的组件添加这个注解

使用注解将组件快速的加入到容器中需要几步:

  1. 给要添加的组件上标四个注解的任何一个
  2. 告诉Spring自动扫描注解的加了注解的组件,以来context名称空间

application.xml

   <!-- context:component-scan 自动组件扫描
        base-package 指定扫描的基础包,把基础包及他下面所有包的所有加了注解的类,自动扫描到ioc容器中
    -->
   <context:component-scan base-package="com.wuxinzhe.service"></context:component-scan>

Servicec.java

package com.wuxinzhe.service;
import org.springframework.stereotype.Service;
 
@Service
public class ServiceC {
 
}

Test.java

	ApplicationContext ioc = new ClassPathXmlApplicationContext("application.xml");
 
	@org.junit.Test
	public void test() {
		ServiceC bean = (ServiceC)ioc.getBean("serviceC");  // 为类名首字母小写,组件的作用域,默认就是单实例
		System.out.println(bean);
	}

基于注解加入到ioc容器中组件的默认行为调整
Dao.java

package com.wuxinzhe.dao;
 
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Repository;
 
@Repository("daoChangeName")
@Scope(value = "prototype")
public class Dao {
 
}

application.xml

   <context:component-scan base-package="com.wuxinzhe.dao"></context:component-scan>

Test.java

   package com.wuxinzhe.test;
import com.wuxinzhe.dao.Dao;
 
import static org.junit.Assert.*;
 
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
 
public class Test {
	ApplicationContext ioc = new ClassPathXmlApplicationContext("application.xml");
 
	@org.junit.Test
	public void test() {
		Object bean = ioc.getBean("daoChangeName");
		Object bean2 = ioc.getBean("daoChangeName");
		System.out.println(bean);
		System.out.println(bean2);
	}
}

输出如下:
com.wuxinzhe.dao.Dao@2d710f1a
com.wuxinzhe.dao.Dao@29215f06
context:exclude-filter指定扫描包时,不包含的类

  1. type=”annotation” (常用) 指定注解进行排除,标注了指定注解的不要 expression:注解的全类名,例如 org.springframework.stereotype.Service org.springframework.stereotype.Controller…
  2. type=”assignable” (常用)指定类进行排除,标注了指定类不要 expression:标注的全类名,例如com.wuxinzhe.service.ServiceC
  3. type=”aspectj”
  4. type=”custom”
  5. type=”regex”

application.xml

   <context:component-scan base-package="com.wuxinzhe">
           <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> 
           <context:exclude-filter type="assignable" expression="com.wuxinzhe.servlet.Servlet"/>    
    </context:component-scan>

context:include-filter指定扫描要包含的类
指定扫描包时,要包含的类,只扫描进入哪些组件,默认是全部扫描进来。一定要禁用掉默认规则才有用 use-default-filters=”false” :这个属性,默认为true,会扫描到@Service与@Reposity等,所以配置成false,只扫描白名单中的bean注解。
application.xml

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

使用@Autowired注解实现根据类型实现自动装配
application.xml

      <context:component-scan base-package="com.wuxinzhe">
      </context:component-scan>

Servlet.java

@Controller
public class Servlet {
 
	@Autowired
	private ServiceC service;
 
	public void doGet() {
		System.out.println("servlet.doGet...");
		service.save();
	}
}

Service.java

@Service
public class ServiceC {
 
	@Autowired
	private Dao dao;
 
	public void save() {
		System.out.print("serviceC.save方法。。。");
		dao.saveData();
	}
}

Dao.java

@Repository("daoChangeName")
@Scope(value = "prototype")
public class Dao {
 
	public void saveData() {
		System.out.println("dao正在保存数据");
	}
}

如果资源类型的bean不止一个,默认根据@Autowired注解标记的成员变量名作为id查找bean,进行装配
@Autowired
先按照类型去容器中找到对应的组件
1.找到一个,找到就赋值
2.没找到,抛出异常
3.找到多个?装配上?
①按照变量名作为id继续匹配:private ServiceB serviceB; private ServiceB serviceC;
ServiceB.java

package com.wuxinzhe.service;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
import com.wuxinzhe.dao.Dao;
@Service
public class ServiceB {
	@Autowired
	private Dao dao;
 
	public void save() {
		System.out.print("serviceB.save方法。。。");
		dao.saveData();
	}
}

ServiceC.java

package com.wuxinzhe.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
import com.wuxinzhe.dao.Dao;
 
@Service
public class ServiceC extends ServiceB{
 
	@Autowired
	private Dao dao;
 
	public void save() {
		System.out.print("serviceC.save方法。。。");
		dao.saveData();
	}
}

Servlet.java

package com.wuxinzhe.servlet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
 
import com.wuxinzhe.service.ServiceB;
 
@Controller
public class Servlet {
 
	@Autowired
	private ServiceB serviceC;
	public void doGet() {
		System.out.println("servlet.doGet...");
		serviceC.save();
	}
}

②没有匹配上,则报错,原因,是因为按照变量名进行匹配的,可以采用以下方式避免错误@Qualifier(“serviceC”) 指定一个id,让spring装配

@Controller
public class Servlet {
	@Qualifier("serviceC")
	@Autowired
	private ServiceB serviceD;
	public void doGet() {
		System.out.println("servlet.doGet...");
		serviceD.save();
	}
}

③没有匹配上,也可以通过设置@Autowired(required=false)避免异常

@Controller
public class Servlet {
	@Qualifier("serviceCCC")
	@Autowired(required=false)
	private ServiceB serviceD;
 
	public void doGet() {
		System.out.println("servlet.doGet..." + serviceD);
		// serviceD.save();
	}
}

输出如下
servlet.doGet…null
在方法的形参位置使用@Qualifier注解

	/**方法上有@Autowired的话
	 * 这个方法会在bean创建的时候自动运行
	 * 这个方法上的每一个参数都会自动注入值
	 */
	 @Autowired 
	public void doGet2(@Qualifier("serviceC")ServiceB serviceB,Dao dao) {
		System.out.println("doGet2"+serviceB+dao);
	}

Spring单元测试

	package com.wuxinzhe.test;
 
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 
import com.wuxinzhe.dao.Dao;
import com.wuxinzhe.service.ServiceB;
import org.junit.Test;
 
/**
 * 导入 spring-test-4.1.2.RELEASE.jar
 * @ContextConfiguration(locations="")使用它来指定spring配置文件的位置
 * @RunWith指定用哪种驱动进行测试,如果不指定则默认就是Junit
 * 以前的注解是由Junit执行
 */
@ContextConfiguration(locations="classpath:application.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class IOCTest {
 
	// ApplicationContext ioc = new ClassPathXmlApplicationContext("application.xml");
	ApplicationContext ioc = null;
 
	@Autowired
	private ServiceB serviceB;
 
	@Autowired
	private Dao dao;
 
 
	@Test
	public void test() {
		System.out.println(serviceB + "" + dao);
	}
}

泛型依赖注入效果
文件最终路径

application.xml

<context:component-scan base-package="com.wuxinzhe"></context:component-scan>

User.java

package com.wuxinzhe.bean;
 
public class User {
 
}

Book.java

package com.wuxinzhe.bean;
 
public class Book {
 
}

BaseDao.java

package com.wuxinzhe.dao;
 
public abstract class BaseDao<T> {
	public abstract void save();
}

BookDao.java

package com.wuxinzhe.dao;
 
import org.springframework.stereotype.Repository;
 
import com.wuxinzhe.bean.Book;
@Repository
public class BookDao extends BaseDao<Book>{
 
	@Override
	public void save() {
		// TODO Auto-generated method stub
		System.out.println("图书用户Dao正在保存。。。");
	}
 
}

UserDao.java

package com.wuxinzhe.dao;
 
import org.springframework.stereotype.Repository;
 
import com.wuxinzhe.bean.User;
@Repository
public class UserDao extends BaseDao<User> {
 
	@Override
	public void save() {
		// TODO Auto-generated method stub
		System.out.println("用户Dao正在保存。。。");
	}
}

BaseService.java

package com.wuxinzhe.service;
 
import org.springframework.beans.factory.annotation.Autowired;
 
import com.wuxinzhe.dao.BaseDao;
 
public class BaseService<T> {
	@Autowired
	private BaseDao<T> baseDao;
 
	public void save() {
		System.out.println("自动注入的Dao:" + baseDao);
		baseDao.save();
	}
}

UserService.java

package com.wuxinzhe.service;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
import com.wuxinzhe.bean.User;
import com.wuxinzhe.dao.UserDao;
 
@Service
public class UserService extends BaseService<User>{
 
}

BookService.java

package com.wuxinzhe.service;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
import com.wuxinzhe.bean.Book;
import com.wuxinzhe.dao.BookDao;
@Service
public class BookService extends BaseService<Book>{
 
}

IocTest.java

package com.wuxinzhe.test;
 
import static org.junit.Assert.*;
 
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
 
import com.wuxinzhe.service.BookService;
import com.wuxinzhe.service.UserService;
 
public class IocTest {
 
	ApplicationContext ioc = new ClassPathXmlApplicationContext("application.xml");
 
	@Test
	public void test() {
		UserService userService = ioc.getBean(UserService.class);
		BookService bookService = ioc.getBean(BookService.class);
		bookService.save();
		userService.save();
	}
}

输出如下:
自动注入的Daocom.wuxinzhe.dao.BookDao@f09733f
图书用户Dao正在保存。。。
自动注入的Daocom.wuxinzhe.dao.UserDao@e350b40
用户Dao正在保存。。。
泛型依赖注入原理

输出带泛型的父类类型

System.out.println(bookService.getClass().getGenericSuperclass());

输出如下:

		com.wuxinzhe.service.BaseService<com.wuxinzhe.bean.Book>

AOP
AOP(Aspect Oriented Programming)面向切面编程,是基于OOP基础之上新的编程思想,在程序运行期间,将某段代码动态的切入到指定方法的指定位置进行运行的编程方式。
动态代理
Calculator.java

package com.wuxinzhe.inter;
 
public interface Calculator {
 
	public int add(int a,int b);
 
	public int minus(int a,int b);
 
	public int multi(int a,int b);
 
	public int div(int a,int b);
}

MyCalculator.java

package com.wuxinzhe.impl;
 
import com.wuxinzhe.inter.Calculator;
 
public class MyCalculator implements Calculator{
 
	@Override
	public int add(int a,int b) {
		int result = a + b;
		return result;
	}
 
 
	@Override
	public int minus(int a, int b) {
		int result = a - b;
		return result;
	}
 
	@Override
	public int multi(int a, int b) {
		int result = a * b;
		return result;
	}
 
	@Override
	public int div(int a, int b) {
		int result = a / b;
		return result;
	}
 
}

CalculatorProxy.java

package com.wuxinzhe.proxy;
 
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
 
import com.wuxinzhe.impl.MyCalculator;
import com.wuxinzhe.inter.Calculator;
import com.wuxinzhe.log.LogUtils;
 
/**
 * 帮Calculator.java生成代理对象的类 Object newProxyInstance
 */
public class CalculatorProxy {
 
	/**
	 * @param calculator 被代理对象
	 * @return 代理对象
	 */
	public static Calculator getProxy(MyCalculator calculator) {
 
		// 被代理对象的类加载器
		ClassLoader loader = calculator.getClass().getClassLoader();
		// 被代理对象实现的接口
		Class<?>[] interfaces = calculator.getClass().getInterfaces();
		// 方法执行器,帮我们目标对象执行目标方法
		InvocationHandler h = new InvocationHandler() {
			/**
			 * Object proxy 代理对象,给jdk使用,任何时候都不要动这个对象 Method method 当前将要执行目标对象的方法 Object[]
			 * args 这个方法调用时,外界传入的值
			 */
 
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				// 利用反射执行目标方法
				// 目标方法执行后的返回值
				Object result = null;
				try {
					LogUtils.start(method,args);
					result = method.invoke(calculator, args);
				} catch (Exception e) {
					LogUtils.exceptionMsg(e);
				} finally {
				}
				// 返回值必须返回出去,外界才能拿到真正执行后的返回值
				return result;
			}
		};
		// Proxy为目标对象创建代理对象
		Object proxy = Proxy.newProxyInstance(loader, interfaces, h);
		// 返回代理对象
		return (Calculator) proxy;
	}
}

LogUtils.java

package com.wuxinzhe.log;
 
import java.lang.reflect.Method;
import java.util.Arrays;
 
public class LogUtils {
 
	public static void start(Method method, Object[] args) {
		System.out.println(method.getName() + "方法开始执行");
		System.out.println("参数" + Arrays.asList(args));
	}
 
	public static void exceptionMsg(Exception e) {
		System.out.println(e.getCause());
	}
}

AopTest.java

package com.wuxinzhe.test;
 
import static org.junit.Assert.*;
 
import org.junit.Test;
 
import com.wuxinzhe.impl.MyCalculator;
import com.wuxinzhe.inter.Calculator;
import com.wuxinzhe.proxy.CalculatorProxy;
 
public class AopTest {
 
	@Test
	public void test() {
		MyCalculator calculator = new MyCalculator();
		Calculator proxy = CalculatorProxy.getProxy(calculator);
		int result2 = proxy.add(3, 4);
		System.out.println(result2);
	}
 
}

java动态代理缺点:
1.写起来难
2.jdk默认的动态代理,如果目标对象没有实现任何接口,是无法为他创建代理对象的,Spring实现了AOP功能,底层就是动态代理。
1.可以利用Spring一句代码都不写的去创建动态代理,实现简单,而且没有强制要求目标对象必须实现借口,将某段代码(比如日志)动态的切入(不把日志代码写死在业务逻辑方法中)到指定方法(加减乘除)的指定位置(方法的开始、结束、异常)进行运行的这种编码方式(Spring简化了面向切面编程)
AOP术语

AOP简单配置
spring-aspects-4.1.2.RELEASE.jar基础版

加强版的面向切面编程(即使目标对象没有实现任何接口也能创建动态代理)
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

AOP简单配置四步骤
1.将目标类和切面类封装了通知方法(在目标方法执行前后执行的方法)加入到ioc容器中
2.还应告诉Spring到低哪个是切面类@Aspect
3.告诉Spring,切面类里面的每一个方法,都是何时何地运行
4.开启基于注解的AOP功能:aop名称空间
Caluculator.java同上
MyCalculator.java

package com.wuxinzhe.impl;
 
import org.springframework.stereotype.Service;
 
import com.wuxinzhe.inter.Calculator;
 
@Service
public class MyCalculator implements Calculator{
 
	@Override
	public int add(int a,int b) {
		int result = a + b;
		return result;
	}
 
	@Override
	public int minus(int a, int b) {
		int result = a - b;
		return result;
	}
 
	@Override
	public int multi(int a, int b) {
		int result = a * b;
		return result;
	}
 
	@Override
	public int div(int a, int b) {
		int result = a / b;
		return result;
	}
 
}

LogUtils.java

package com.wuxinzhe.log;
 
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
 
@Aspect
@Component
public class LogUtils {
 
	/**
	 * 告诉Spring每个方法都什么时候运行
	 * 通知注解
	 * @Before:告诉在目标方法之前运行                        前置通知
	 * @After:告诉在目标方法运行结束之后运行                 后置通知
	 * @AfterReturning:在目标方法正常返回之后                返回通知
	 * @AfterThrowing:在目标方法抛出异常之后运行             异常通知
	 * @Around:环绕                                         环绕通知
	 */
 
	// 目标方法执行之前执行
	@Before("execution(public int com.wuxinzhe.impl.MyCalculator.*(int , int ))")
	public static void logStart() {
		System.out.println("开始执行");
	}
 
	// 目标方法正常执行完成之后执行
	@AfterReturning("execution(public int com.wuxinzhe.impl.MyCalculator.*(int , int ))")
	public static void logEnd() {
		System.out.println("方法有返回值");
	}
 
	// 目标方法出现异常的时候执行
	@AfterThrowing("execution(public int com.wuxinzhe.impl.MyCalculator.*(int , int ))")
	public static void exceptionMsg() {
		System.out.println("出现异常的时候");
	}
 
	// 目标方法结束的时候执行
	@After("execution(public int com.wuxinzhe.impl.MyCalculator.*(int , int ))")
	public static void logFinally() {
		System.out.println("最终执行方法");
	}
 
}

application.xml

     <context:component-scan base-package="com.wuxinzhe"></context:component-scan>
 
     <!-- 开启基于注解的AOP功能:aop名称空间 -->
     <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>

AopTest.java

package com.wuxinzhe.test;
 
import static org.junit.Assert.*;
 
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.wuxinzhe.inter.Calculator;
 
public class AopTest {
	ApplicationContext ioc = new ClassPathXmlApplicationContext("application.xml");
 
	@Test
	public void test() {
		// 1.从ioc容器中拿到目标对象,注意,如果要用类型,一定用他的接口类型,不要用它本类
		Calculator calculator = ioc.getBean(Calculator.class);
		calculator.div(1,2);
	}
}

AOP的底层就是动态代理,容器中保存的组件是他的代理对象
cglib为没有接口的组件也可以创建代理对象
切入点表达式方法(通配符)
1.匹配一个或者多个字符
execution(public int com.wuxinzhe.impl.My*.*(int , int ))

2.匹配任意一种类型的参数:
execution(public int com.wuxinzhe.impl.My*.*(int , * ))

3.匹配任意多个参数,任意类型参数
execution(public int com.wuxinzhe.impl.My*.*(..))

4…任意多层路径
execution(public int com..impl.My*.*(..))

5.任意包任意类任意方法,任意参数,不建议使用
execution(* * (..))

通知方法的执行顺序


JoinPoint获取目标方法的信息

     import org.aspectj.lang.JoinPoint;
 
	// 目标方法执行之前执行
	@Before("execution(public int com.wuxinzhe.impl.MyCalculator.*(int , int ))")
	public static void logStart(JoinPoint joinPoint) {
		Object[] args = joinPoint.getArgs();
		Signature signature = joinPoint.getSignature();
		String name = signature.getName();
		System.out.println("参数"+Arrays.asList(args));
		System.out.println("signature:"+signature);
		System.out.println("signature.getName():"+name);
		System.out.println("开始执行");
	}

输出如下:
参数[1, 2]
signature:int com.wuxinzhe.impl.MyCalculator.add(int,int)
signature.getName():add

returning告诉Spring用result这个变量来接收返回值

	// 目标方法正常执行完成之后执行
	@AfterReturning(value="execution(public int com.wuxinzhe.impl.MyCalculator.*(int , int ))",returning="result")
	public static void logEnd(JoinPoint joinpoint,Object result) {
		System.out.println("返回值"+result);
	}

throwing告诉Spring用exception用这个变量来接收异常值

	// 目标方法出现异常的时候执行
	@AfterThrowing(value="execution(public int com.wuxinzhe.impl.MyCalculator.*(int , int ))",throwing="exception")
	public static void exceptionMsg(JoinPoint joinpoint,Exception exception) {
		Signature signature = joinpoint.getSignature();
		String name = signature.getName();
		System.out.println(name+"方法出现异常");
		System.out.println(exception.getMessage());
	}

抽取可重用的切入点表达式
①定义

	/**抽取可重用的切入点表达式
	 * 1.随便声明一个没有实现的返回void的方法
	 * 2.给方法上标注一个@Pointcut注解
	 */
	@Pointcut("execution(public int com.wuxinzhe.impl.MyCalculator.*(int , int ))")
	public void getFun() {};

②使用

	@Before(value = "getFun()")
	public static void logStart(JoinPoint joinPoint) {
		System.out.println("开始执行");
	}

@Around环绕通知
切面类 LogUtils

package com.wuxinzhe.log;
 
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
 
@Aspect
@Component
public class LogUtils2 {
 
	@Pointcut("execution(public int com.wuxinzhe.impl.MyCalculator.*(int , int ))")
	public void getFun() {};
 
	/**
	 * @throws Throwable 
	 * @Around
	 * 环绕通知是Spring中强大的通知
	 * 动态代理
	 * try{
	 *   //前置通知
	 *   method.invoke();
	 *   //返回通知
	 * }catch(e){
	 *   //异常通知
	 * }finally{
	 *   //后置通知
	 * }
	 *  四合一通知就是环绕通知
	 */
 
	@Around(value = "getFun()")
	public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
 
		Object[] args = pjp.getArgs();
		// 利用反射调用目标方法 ,等同于method.invoke(obj.arg);
		Object proceed = null;
		try {
			System.out.println("方法执行之前");
			proceed = pjp.proceed(args);
			System.out.println("方法执行结束");
		} catch (Exception e) {
			System.out.println("触发异常");
		}finally {
			System.out.println("最终执行");
		}
 
		System.out.println("调用反射后的返回值"+proceed);
		// 反射调用后的返回值
		return proceed;
	}
}

AopTest.java

	@Test
	public void test() {
		// 1.从ioc容器中拿到目标对象,注意,如果要用类型,一定用他的接口类型,不要用它本类
		Calculator calculator = ioc.getBean(Calculator.class);
		calculator.add(1,2);
		//calculator.div(2,0);
	}

输出结果:
方法执行之前
方法执行结束
最终执行
调用反射后的返回值3
环绕通知执行顺序

①正常执行顺序
环绕通知执行之前
普通通知开始执行
环绕通知方法执行结束
环绕通知最终执行
环绕通知返回值3
普通通知最终执行方法
普通通知返回值3

②执行异常的顺序
环绕通知执行之前
普通通知开始执行
环绕通知触发异常
环绕通知最终执行
环绕通知返回值null
普通通知最终执行方法
普通通知返回值null
多切面运行顺序
LogUtils中加入环绕通知

AOP使用场景

基于配置的AOP

<?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:aop="http://www.springframework.org/schema/aop"
	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/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
 
   <bean class="com.wuxinzhe.log.LogUtils3" id="log"></bean>
   <bean class="com.wuxinzhe.impl.MyCalculator" id="myCalculator"></bean>
   <aop:config>
       <aop:pointcut expression="execution(* com.wuxinzhe.impl.*.*(..))" id="globalPoint"/>
	   <aop:aspect ref="log">
	        <aop:pointcut expression="execution(* com.wuxinzhe.impl.*.*(..))" id="onePoint"/>
			<aop:before method="logStart" pointcut-ref="onePoint"/>
			<aop:after-returning method="logEnd" pointcut="execution(* com.wuxinzhe.impl.*.*(..))" returning="result"/>
			<aop:after method="logFinally" pointcut="execution(* com.wuxinzhe.impl.*.*(..))"/>
			<aop:after-throwing method="exceptionMsg" pointcut="execution(* com.wuxinzhe.impl.*.*(..))" throwing="exception"/>
	        <aop:around method="myAround" pointcut-ref="globalPoint"/>
	   </aop:aspect>
   </aop:config>
</beans>

注解:快速方便;配置:功能完善;重要的用配置,不重要的用注解。
jdbcTemplate
创建测试数据时候报错
org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘dataSource’ defined in class path resource [applicationContext.xml]: Instantiation of bean failed; nested exception is java.lang.NoClassDefFoundError: com/mchange/v2/ser/Indirector
解决方案:
加入jar包mchange-commons-java-0.2.12.jar

1.导入springjdbc数据库模块
spring-jdbc-4.0.0.RELEASE
spring-orm-4.0.0.RELEASE
spring-tx-4.0.0.RELEASE

2.配置ioc文件

    <context:property-placeholder location="classpath:dbconfig.properties" />
	<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource">
		<property name="user" value="${jdbc.user}"></property>
		<property name="password" value="${jdbc.password}"></property>
		<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
		<property name="driverClass" value="${jdbc.driverClass}"></property>
	</bean>
 
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
	   <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
	</bean>

3.执行sql操作

	ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
	JdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class);

①更新

	String sql = "UPDATE test1 SET name = ? WHERE id = ?";
	int a = jdbcTemplate.update(sql, "小明",18);

②批量插入

	String sql = "insert into test1(name,age,sex,content) VALUES(?,?,?,?)";
	List<Object[]> object = new ArrayList<Object[]>();
	object.add(new Object[] { "小明", 11, "男", "帅帅哒" });
	object.add(new Object[] { "小莉莉", 22, "女", "萌萌哒" });
	int[] result = jdbcTemplate.batchUpdate(sql, object);
	for (int i : result) {
		System.out.println(i);
	}

③查询单个对象

	/**查询时,javaBean需要和数据库中字段名保持一致,否则无法完成封装
	 * 查询集合jdbcTemplate.query()
	 * 查询单个对象jdbcTemplate.queryForObject(),如果查询结果为空,则会抛出报错,则用try..catch捕获异常避免错误发生
	 */
	@Test
	public void test4() {
		String sql = "SELECT id,name,age,sex,content FROM test1 WHERE id = ?";
		User user = null;
		try {
			// RowMapper 每一行记录和JavaBean的属性如何映射
			user = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), 29000);
		} catch (DataAccessException e) {
			// TODO Auto-generated catch block
		}
	    System.out.println(user);
	    System.out.println(user.getId());
	}

④查询集合

		String sql = "SELECT id,name,age,sex,content FROM test1 WHERE id > ?";
		List<User> users = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class), 1);
		// System.out.println(users);
		for (User user : users) {
			System.out.println(user.getName());
		}

⑤查询最大值

	String sql = "SELECT MAX(id) FROM test1";
	//System.out.println(jdbcTemplate.queryForInt(sql));
	System.out.println(jdbcTemplate.queryForObject(sql, Integer.class));

⑥使用具名参数插入记录
配置ioc容器文件

	<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
	    <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
	</bean>
	String sql = "insert into test1(name,age,sex,content) VALUES(:name,:age,:sex,:content)";
	NamedParameterJdbcTemplate namedParameterJdbcTemplate = ioc.getBean(NamedParameterJdbcTemplate.class);
	Map<String,Object> paramMap = new HashMap<>();
	paramMap.put("name","佟丽娅");
	paramMap.put("age",33);
	paramMap.put("sex","女");
	paramMap.put("content","我的女神哦~");
	int res = namedParameterJdbcTemplate.update(sql, paramMap);
	System.out.println(res);

⑧以SqlParameterSource形式传入参数值

    NamedParameterJdbcTemplate namedParameterJdbcTemplate = ioc.getBean(NamedParameterJdbcTemplate.class);
	String sql = "insert into test1(name,age,sex,content) VALUES(:name,:age,:sex,:content)";
	User user = new User();
	user.setName("佟丽娅");
	user.setAge(33);
	user.setSex("女");
	user.setContent("我的女神哦~~");
	int res = namedParameterJdbcTemplate.update(sql, new BeanPropertySqlParameterSource(user));
	System.out.println(res);

⑨自动装配JdbcTemplate对象
UserDao.java文件

	package com.wuxinzhe.dao;
 
	import org.springframework.beans.factory.annotation.Autowired;
	import org.springframework.jdbc.core.JdbcTemplate;
	import org.springframework.stereotype.Repository;
 
	import com.wuxinzhe.test.User;
 
	@Repository
	public class UserDao {
 
		@Autowired
		JdbcTemplate jdbcTemplate;
 
		public  void  save(User user) {
			String sql = "insert into test1(name,age,sex,content) VALUES(?,?,?,?)";
			jdbcTemplate.update(sql, user.getName(),user.getAge(),user.getSex(),user.getContent());
		}
 
	}

ioc容器配置文件

	<context:component-scan base-package="com.wuxinzhe.dao"></context:component-scan>

测试文件

	@Test
	public void test9() {
		UserDao userDao = ioc.getBean(UserDao.class);
		User user = new User();
		user.setName("张XX");
		user.setAge(40);
		user.setSex("男");
		user.setContent("风一样的男人");
		userDao.save(user);
	}

声明式事务

	<!-- 事务控制 -->
    <!-- 1:配置事务管理器(切面)让其进行事务控制,一定导入切面的几个包 -->
    <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      <property name="dataSource" ref="dataSource"></property>
    </bean>
 
    <!-- 2:开启基于注解的事务控制模式,依赖tx名称空间-->
    <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
    <!-- 3:给事务方法加注解 -->
	package com.wuxinzhe.service;
	import org.springframework.beans.factory.annotation.Autowired;
	import org.springframework.stereotype.Service;
	import org.springframework.transaction.annotation.Transactional;
	import com.wuxinzhe.dao.BookDao;
	@Service 
	public class BookService {
 
		@Autowired
		BookDao bookDao;
 
		@Transactional
		public void checkOrder(String userName,String isbn) {
			int bookPrice  = bookDao.getBookPrice(isbn);
			bookDao.updateUserBalance(userName, bookPrice);
 
			bookDao.updateBookStock(isbn);
		}
	}

6 事务的隔离级别
6.1 数据库事务并发问题
假设现在有两个事务:Transaction01和Transaction02并发执行。
①脏读
[1]Transaction01将某条记录的AGE值从20修改为30。
[2]Transaction02读取了Transaction01更新后的值:30。
[3]Transaction01回滚,AGE值恢复到了20。
[4]Transaction02读取到的30就是一个无效的值。
②不可重复读
[1]Transaction01读取了AGE值为20。
[2]Transaction02将AGE值修改为30。
[3]Transaction01再次读取AGE值为30,和第一次读取不一致。
③幻读
[1]Transaction01读取了STUDENT表中的一部分数据。
[2]Transaction02向STUDENT表中插入了新的行。
[3]Transaction01读取了STUDENT表时,多出了一些行。
6.2 隔离级别
数据库系统必须具有隔离并发运行各个事务的能力,使它们不会相互影响,避免各种并发问题。一个事务与其他事务隔离的程度称为隔离级别。SQL标准中规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性越弱。
①读未提交:READ UNCOMMITTED
允许Transaction01读取Transaction02未提交的修改。
②读已提交:READ COMMITTED
要求Transaction01只能读取Transaction02已提交的修改。
③可重复读:REPEATABLE READ
确保Transaction01可以多次从一个字段中读取到相同的值,即Transaction01执行期间禁止其它事务对这个字段进行更新。
④串行化:SERIALIZABLE
确保Transaction01可以多次从一个表中读取到相同的行,在Transaction01执行期间,禁止其它事务对这个表进行添加、更新、删除操作。可以避免任何并发问题,但性能十分低下。
⑤各个隔离级别解决并发问题的能力见下表
脏读 不可重复读 幻读
READ UNCOMMITTED 有 有 有
READ COMMITTED 无 有 有
REPEATABLE READ 无 无 有
SERIALIZABLE 无 无 无
⑥各种数据库产品对事务隔离级别的支持程度
Oracle MySQL
READ UNCOMMITTED × √
READ COMMITTED √ √
REPEATABLE READ × √(默认值)
SERIALIZABLE √ √

mysql事务操作常用命令
修改MySQL隔离级别
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}

如:SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

查询MySQL的隔离级别
SELECT @@global.tx_isolation; //查询全局隔离级别
SELECT @@session.tx_isolation;//查询当前会话隔离级别
SELECT @@tx_isolation;//同上

事务操作
开启事务 start transaction;
提交事务 commit;
回滚事务 rollback;

读未提交下的脏读
读已提交下的不可重复读

	 /**事务的几个属性
	 * 
	 * 异常分类:
	 *   运行时异常:发生异常时,默认回滚
	 *   编译异常:通过try...catch或者throw处理异常 发生异常时,默认不回滚
	 * 
	 * noRollbackFor = class[] 类   指定那些异常不回滚,可以指定默认运行时异常,某些异常可以不用回滚(如ArithmeticException)
	 * noRollbackForClassName = String[] 全类名
	 * 
	 * rollbackFor = class[] 类  指定编译时异常不回滚,但是让某些编译时异常可以回滚(如FileNotFoundException)
	 * rollbackForClassName = String[]
	 * 
	 * timeout:int超时时间
	 * readOnly:boolean事务是否为只读事务,可以进行事务优化,不用管事务那一堆了,但是在有增删改的不可以
	 */
 
	@Transactional(timeout = 3,readOnly = false,noRollbackFor = {ArithmeticException.class},rollbackFor = {FileNotFoundException.class})
	public void checkOrder(String userName,String isbn)  {
 
		int bookPrice  = bookDao.getBookPrice(isbn);
		bookDao.updateUserBalance(userName, bookPrice);
		/**
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		**/
		bookDao.updateBookStock(isbn);
 
	    //int res = 10/0;
		//new FileInputStream("D://HAHAHHAA");
 
	}

事务传播行为
测试①
myltiService.java

	@Transactional
	public void myltiTx() {
 
		bookService.checkOrder("Tom", "ISBN-001");
		bookService.updateBookPrice("ISBN-002", 900);
 
	}

bookService.java

	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void checkOrder(String userName,String isbn)  {
 
		int bookPrice  = bookDao.getBookPrice(isbn);
		bookDao.updateUserBalance(userName, bookPrice);
 
		bookDao.updateBookStock(isbn);
 
	}
 
 
	@Transactional(propagation  = Propagation.REQUIRED)
	public void updateBookPrice(String isbn,Integer price) {
 
		bookDao.updateBookPrice(isbn, price);
		int res = 10/0;
	}

Test.java

	MultiService multiService = ioc.getBean(MultiService.class);
	multiService.myltiTx();

结果checkOrder提交成功,updateBookPrice出现异常回滚。

测试②

	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void checkOrder(String userName,String isbn)  {
 
		int bookPrice  = bookDao.getBookPrice(isbn);
		bookDao.updateUserBalance(userName, bookPrice);
 
		bookDao.updateBookStock(isbn);
 
		int res = 10/0;
 
	}
 
	@Transactional(propagation  = Propagation.REQUIRED)
	public void updateBookPrice(String isbn,Integer price) {
 
		bookDao.updateBookPrice(isbn, price);
 
	}

结果checkOrder发生异常,也会影响之后的事务。

如果是REQUIRED,所有的属性都继承于上级事务的属性

本类事务方法之间的调用,就是一个事务。

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"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:aop="http://www.springframework.org/schema/aop"
	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-4.1.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
 
    <context:property-placeholder location="dbconfig.properties"/>
 
    <bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource">
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="jdbcUrl"  value="${jdbc.jdbcUrl}"></property>
        <property name="driverClass"  value="${jdbc.driverClass}"></property>
    </bean>
 
    <bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
       <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
    </bean>
 
    <context:component-scan base-package="com.wuxinzhe"></context:component-scan>
 
    <!-- 1:配置事务管理器(切面)让其进行事务控制,一定导入切面的几个包 -->
    <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      <property name="dataSource" ref="dataSource"></property>
    </bean>
 
    <!-- 事务控制 -->
    <aop:config>
       <aop:pointcut expression="execution(* com.wuxinzhe.service.*.*(..))" id="txPoint"/>
       <!-- 事务建议:事务增强 advice-ref指向事务管理器的配置 -->
       <aop:advisor advice-ref="myAdvice" pointcut-ref="txPoint"/>
    </aop:config>
 
    <tx:advice id="myAdvice" transaction-manager="dataSourceTransactionManager">
        <tx:attributes>
          <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>
 
</beans>

SpringMVC
导包
导入核心容器包
commons-logging-1.2.jar
spring-aop-4.1.2.RELEASE.jar
spring-beans-4.1.2.RELEASE.jar
spring-context-4.1.2.RELEASE.jar
spring-core-4.1.2.RELEASE.jar
spring-expression-4.1.2.RELEASE.jar
web模块
spring-web-4.1.2.RELEASE.jar
spring-webmvc-4.1.2.RELEASE.jar

通过dispatcherservlet补全在web.xml创建如下内容

	<servlet>
		<servlet-name>springDispatcherServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<!--springmvc.xml配置文件-->
			<param-value>classpath:springmvc.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
 
	<!-- Map all requests to the DispatcherServlet for handling -->
	<servlet-mapping>
		<servlet-name>springDispatcherServlet</servlet-name>
		<!-- 
		//*都可以拦截请求
		/会拦截所有请求,但是不会拦截*.jsp请求,能保证jsp访问正常
		/*的范围更大,还会拦截到*.jsp请求,一旦拦截jsp页面就不能正常访问了
		 -->
		<url-pattern>/</url-pattern>
	</servlet-mapping>

配置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:aop="http://www.springframework.org/schema/aop"
		xmlns:context="http://www.springframework.org/schema/context"
		xmlns:p="http://www.springframework.org/schema/p"
		xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
			http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
			http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
		<context:component-scan base-package="com.wuxinzhe"></context:component-scan>
 
		<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		  <property name="prefix" value="/WEB-INF/pages/"></property>
		  <property name="suffix" value=".jsp"></property>
		</bean>
	</beans>

FirstController.java文件

	package com.wuxinzhe.controller;
 
	import org.springframework.stereotype.Controller;
	import org.springframework.web.bind.annotation.RequestMapping;
 
	@Controller
	public class FirstController {
 
		@RequestMapping("/hello")
		public String helloWorld() {
 
		   System.out.println("请求处理成功");
		   //return "/WEB-INF/pages/success.jsp"; 同下边配置
 
			/*
			 * <bean
			 * class="org.springframework.web.servlet.view.InternalResourceViewResolver">
			 * <property name="prefix" value="/WEB-INF/pages/"></property> <property
			 * name="suffix" value=".jsp"></property> </bean>
			 */
		   return "success";
		}
	}

一个路径必须对应一个方法
类使用@RequestMapping注解,为所有方法创建一个基准路径

method规定请求方式
@RequestMapping(value=”test1″,method = RequestMethod.GET)

params规定请求参数
①规定必须包含的参数名字
@RequestMapping(value=”test2″,params = {“username”})
对应url ?username=value
②规定不能包含的参数名字
@RequestMapping(value=”test2″,params = {“!username”})
③规定必须包含的参数名字,且规定了参数的值
@RequestMapping(value=”test2″,params = {“username=3″}) 参数值必须等于3
@RequestMapping(value=”test2”,params = {“username!=3″}) 参数值必须不等于3
④规定多个参数
@RequestMapping(value=”test2”,params = {“username!=3″,”age”})
对应url ?username=2&age=3

consumes
只接受内容是哪种请求,规定请求中的Content-Type

produces
告诉浏览器返回的内容类型是什么,给响应头加上Content-Type: text/html;charset=UTF-8

@RequestMapping ant风格模糊匹配
?匹配文件名中的一个字符
*匹配文件名中的任意字符
**匹配多层路径

@PathVariable获取路径上的占位符
@RequestMapping(value = “test2/{userName}”)
public String test3(@PathVariable(“userName”) String userName) {
System.out.println(userName);
return “test1”;
}

REST风格请求

如何发送PUT/DELETE请求
①创建一个post请求的表单
②表单中携带一个_method参数,值为PUT或者DELETE

	<form method="post" action="book/1">
		<input name="_method" value="delete" /> 
		<input type="submit" value="删除图书" />
	</form>
 
	<form method="post" action="book/1">
		<input name="_method" value="put" /> 
		<input type="submit" value="更新图书" />
	</form>

filter过滤器 把普通请求转化为规定形式的请求

  <!-- The front controller of this Spring Web application, responsible for handling all application requests -->
	<servlet>
		<servlet-name>springmvc</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>springmvc-servlet.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
 
	<!-- Map all requests to the DispatcherServlet for handling -->
	<servlet-mapping>
	    <!--对应WebContent/springmvc-servlet.xml文件-->
		<servlet-name>springmvc</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
 
	<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>

// 查询

	@RequestMapping(value="book/{id}",method = RequestMethod.GET)
	public String getBook(@PathVariable("id") Integer id) {
		System.out.println("您获取了id为"+id+"的图书");
		return "success";
	}
 
 
	@RequestMapping(value="book",method = RequestMethod.POST)
	public String addBook() {
		System.out.println("添加图书成功");
		return "success";
	}
 
	@RequestMapping(value="book/{id}",method = RequestMethod.PUT)
	public String updateBook(@PathVariable("id") Integer id) {
		System.out.println("更新图书" + id + "成功");
		return "success";
	}
 
	@RequestMapping(value="book/{id}",method = RequestMethod.DELETE)
	public String deleteBook(@PathVariable("id") Integer id) {
		System.out.println("删除图书" + id + "成功");
		return "success";
	}

成功jsp内容
success.jsp 注意:加上isErrorPage=”true” 8.0以上才能响应PUT/DELETE请求

	<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" isErrorPage="true"%>
	<!DOCTYPE html>
	<html>
	<head>
	<meta charset="UTF-8">
	<title>Insert title here</title>
	</head>
		<body>
		恭喜您,请求成功~~
		</body>
	</html>

@RequestParam获取参数
url:?id=3
①默认方式获取参数值,?id= 或者 未传参 获取的值都为空字符串

	@RequestMapping(value="test1")
	public String test1(Integer id) {
		System.out.println("获取的参数值为" + id);
		return "success";
	}

②@RequestParam获取参数值,参数默认是必须带的 ?id= 获取的值为空字符串 未传参则报错

	@RequestMapping(value="test1")
	public String test1(@RequestParam("id") String id) {
		System.out.println("获取的参数值为" + id);
		return "success";
	}

②@RequestParam其他属性
设置参数非必传 不传则为null

	@RequestMapping(value="test1")
	public String test1(@RequestParam(value="id",required = false)String id) {
		System.out.println("获取的参数值为" + id);
		return "success";
	}

设置参数默认值看,不传参的时候则为null

	@RequestMapping(value="test1")
	public String test1(@RequestParam(value="id",defaultValue = "defaultValue") String id) {
		System.out.println("获取的参数值为" + id);
		return "success";
	}

获取请求头信息

	@RequestMapping(value="test1")
	public String test1(@RequestHeader("Accept") String accept) {
		System.out.println(accept);
		return "success";
	}

@CookieValue 获取cookie信息
required默认值为true,所以首次访问网站时候,cookie信息如下,Cookie: __jsluid=f0bd1f001ae397d3438d9f7d9c82a8b3; Hm_lvt_d7682ab43891c68a00de46e9ce5b76aa=1559743349,不包含JSESSIONID,所以会报错, 将required 改为 false即可。
由于浏览器会在首次访问网站的时候,响应头里包含设置cookie的信息Set-Cookie: JSESSIONID=66A16E314AC3EA9E4DFCF8F6799C605F; Path=/02Rest; HttpOnly

	@RequestMapping(value = "cookie")
	public String test2(@CookieValue(value = "JSESSIONID", required = false) String jessId) {
 
		System.out.println("cookie获取成功,参数值为" + jessId);
 
		return "success";
	}

传入POJO,Springmvc自动封装

	com.wuxinzhe.dao
	package com.wuxinzhe.dao;
 
public class User {
 
	private String userName;
	private Integer age;
	private Address address;
 
    // 无参构造器
 
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	public Address getAddress() {
		return address;
	}
	public void setAddress(Address address) {
		this.address = address;
	}
	@Override
	public String toString() {
		return "User [userName=" + userName + ", age=" + age + ", address=" + address + "]";
	}
 
}

Address.java

package com.wuxinzhe.dao;
 
public class Address {
 
	private String province;
	private String city;
	private String area;
	public String getProvince() {
		return province;
	}
	public void setProvince(String province) {
		this.province = province;
	}
	public String getCity() {
		return city;
	}
	public void setCity(String city) {
		this.city = city;
	}
	public String getArea() {
		return area;
	}
	public void setArea(String area) {
		this.area = area;
	}
 
	  @Override 
	public String toString() {
		return "Address [province=" + province + ", city=" + city + ", area=" + area + "]";
	}
}

提交表单

	<form method="post" action="user">
 
	  用户名:<input type="text" name="userName"/>
 
	  年龄:<input type="text" name="age"/>
 
	  省:<input type="text" name="address.province"/>
 
	 市:<input type="text" name="address.city"/>
 
	 县:<input type="text" name="address.area"/>
 
	  <input type="submit"/>
 
	</form>
	<pre lang="java"  escaped="true">
	处理表单
	<pre lang="java"  escaped="true">
	@RequestMapping(value="user",method=RequestMethod.POST)
	public String userAdd(User user) {
		System.out.println("用户详细信息" + user);
		return "success";
	}

虽然数据可以接收,但是发生了中文乱码

	用户详细信息User [userName=??????, age=11, address=Address [province=?±±???, city=??????, area=????±±]]

提交的数据有乱码
请求乱码
GET:改server.xml

	<Connector URIencoding="UTF-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443" />

POST:

request.setCharacterEncoding(“UTF-8”)
web.xml写filter过滤器来设置

设置了filter之后并没有改变乱码,编译器Preferences=》general=》Workspace=》Text file encoding设置为utf-8
解决方案,将CharacterEncodingFilter放在HiddenHttpMethodFilter之前就可以解决乱码问题

	<filter>
	  <filter-name>CharacterEncodingFilter</filter-name>
	  <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
	   <init-param>
	      <param-name>encoding</param-name>
	      <param-value>utf-8</param-value>
	   </init-param>
	   	<init-param>
	   		<param-name>forceEncoding</param-name>
	        <param-value>true</param-value>
	   </init-param>
	</filter>
 
	<filter-mapping>
		<filter-name>CharacterEncodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

数据输出:
Map java.util.Map

	@RequestMapping("handle01")
	public String handle01(Map<String,Object> map) {
 
		map.put("userName", "佟丽娅");
 
		return "output";
	}
	pageContext:${pageScope.userName}
	session:${ sessionScope.userName } 
	request:${ requestScope.userName}  可以输出
	application:${applicationScope.userName}

Model org.springframework.ui.Model

	@RequestMapping("handle02")
	public String handle02(Model model) {
 
		model.addAttribute("userName", "佟丽娅");
 
		return "output";
	}
	pageContext:${pageScope.userName}
	session:${ sessionScope.userName } 
	request:${ requestScope.userName}  可以输出
	application:${applicationScope.userName}

ModelMap org.springframework.ui.ModelMap

	@RequestMapping("handle03")
	public String handle03(ModelMap modelMap) {
 
		modelMap.addAttribute("userName", "佟丽娅");
 
		return "output";
	}
	pageContext:${pageScope.userName}
	session:${ sessionScope.userName } 
	request:${ requestScope.userName}  可以输出
	application:${applicationScope.userName}

ModelAndView

	@RequestMapping("handle04")
	public ModelAndView handle04() {
		ModelAndView mv = new ModelAndView("output");
		mv.addObject("userName", "佟丽娅");
		return mv;
	}
	pageContext:${pageScope.userName}
	session:${ sessionScope.userName } 
	request:${ requestScope.userName}  可以输出
	application:${applicationScope.userName}

Springmvc提供了一种可以临时给Session中保存数据的方式
使用一个注解 @SessionAttributes(只能标注在类上)
@SessionAttributes(value= {“msg”,”userName”})给BindingAwareModelMap中保存数据,或者在ModelAndView中的数据,同时给session中保存一份;
value指定保存数据时,要给session中存放数据的key
@SessionAttributes(types= {String.class})
给BindingAwareModelMap,或者ModelAndView中保存固定数据类型数据,同时给session中保存一份;

	@SessionAttributes
	@SessionAttributes(value= {"msg","userName"},types= {String.class})
	@Controller
	public class OutController {
 
		@RequestMapping("handle01")
		public String handle01(Map<String, Object> map) {
 
			map.put("userName", "佟丽娅");
 
			return "output";
		}
 
	}

不推荐使用@SessionAttributes,会引发异常,推荐使用原生API。

forward转发

	@RequestMapping("handle05")
	public String handle05(){
		//return "forward:/forward.jsp";  // 跳转到根目录下WebContent的forward.js页面
		return "forward:/handle04";  
	}

redirect重定向

	@RequestMapping("handle06")
	public String handle06(){
		return "redirect:handle05";  
	}

view-controller

	<!-- 发送一个请求直接来到WEB-INF下的页面
      path指定哪个请求 view-name指定映射哪个视图,需要开启mvc注解模式 -->
    <mvc:view-controller path="/haha" view-name="haha"/>
    <mvc:annotation-driven></mvc:annotation-driven>

使用表单标签完成数据

	@RequestMapping(value = "userAdd")
	public String userAdd(Model model) {
		model.addAttribute("depts", department.getDepartments());
		//model.addAttribute("employee", new Employee(1001, "张三", "111@qq.com", 1, new Department(104, "D-DD")));
		model.addAttribute("employee", new Employee());
		return "userAdd";
	}

userAdd.jsp

	<%@ page language="java" contentType="text/html; charset=UTF-8"
		pageEncoding="UTF-8"%>
	 <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>   
	 <%@taglib  prefix="form" uri="http://www.springframework.org/tags/form"%>
 
	<!DOCTYPE html>
	<html>
	<head>
	<meta charset="UTF-8">
	<title>Insert title here</title>
	</head>
	<body>
	<h1>员工添加页面</h1>
 
	<form:form action="" modelAttribute="employee">
		 姓名:<form:input path="lastName"/><br/>
		 邮箱:<form:input path="email"/><br/>
		性别:<form:radiobutton path="gender" value="0"/><form:radiobutton path="gender" value="1"/>
	   部门:<form:select path="department.id" items="${depts}" itemLabel="departmentName" itemValue="id">
		  </form:select>  
		<input type="submit" value="提交"/>
	</form:form>
 
	<!-- 
	  <form action="user" method="post">
	   姓名:<input type="text" name="lastName"><br>
	 邮箱:<input type="text" name="email"/> <br>
	  性别:男<input type="radio" name="gender" value="0"/><input type="radio" name="gender"><br>
	 部门:
	 <select name="department.id">
	   <c:forEach items="${depts }" var="dept" >
	   <option value="${dept.id}">${dept.departmentName}</option>
	   </c:forEach>
	 </select> 
 
	 <input type="submit" value="提交"/>
 
	</form> 
	 -->
 
	</body>
	</html>

静态资源无法访问问题

	<%pageContext.setAttribute("ctp", request.getContextPath()); %>
    <script src="${ctp}/scripts/jquery-1.9.1.min.js"></script>
	springmvc.xml中加入以下配置即可:
	<mvc:default-servlet-handler/>

自定义转换器
1.实现Converter接口,写一个自定义的类型转换器
2.Converter是ConverterService的组件:
①你的Converter放入ConverSionService中;
②将WebDataBinder中的ConversionService设置成我们这个加了自定义类型转换器的ConversionService
所以在springmvc.xml中配置

    <mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
    <bean class="org.springframework.context.support.ConversionServiceFactoryBean">
      <property name="converters">
        <set>
          <bean class="com.wuxinzhe.component.StringToEmployeeConverter">
          </bean>
        </set>
      </property>
    </bean>

日期格式化
①实体类属性上加上注解,规定页面提交的日期格式
@DateTimeFormat(pattern = “yyyy-MM-dd”)
private Date birth;

②配置文件中将ConversionServiceFactoryBean替换为FormattingConversionServiceFactoryBean,只是功能上多了格式化的功能

	<bean class="org.springframework.context.support.ConversionServiceFactoryBean" id="conversionService">
      <property name="converters">
        <set>
          <bean class="com.wuxinzhe.component.StringToEmployeeConverter">
          </bean> 
        </set>
      </property>
    </bean>

改为

	<bean class="org.springframework.format.support.FormattingConversionServiceFactoryBean" id="conversionService">
      <property name="converters">
        <set>
          <bean class="com.wuxinzhe.component.StringToEmployeeConverter">
          </bean> 
        </set>
      </property>
    </bean>

数字格式化(步骤同上)
@NumberFormat(pattern = “###,###.##”)

后端数据校验
①可以写程序将我们每一个数据取出进行校验,如果校验失败,则跳到添加页面重新填写
②SpringMVC可以使用JSR303来做数据校验
快速使用JSR303:规范–Hibernate Validator(第三方校验框架)
①导入jar包

hibernate-validator-5.0.0.CR2.jar
hibernate-validator-annotation-processor-5.0.0.CR2.jar
classmate-0.8.0.jar
jboss-logging-3.1.1.GA.jar
validation-api-1.1.0.CR1.jar

注意:如果是tomcat7.0以下,将带el的三个jar包放入tomcat的lib文件夹下
②在javabean中加上校验注解,如下

	@Length(min=6,max=8)
	private String lastName;
 
	@Email
	private String email;

③在Springmvc封装对象的时候,告诉springmvc这个javabean需要校验

	@RequestMapping(value="emps",method=RequestMethod.POST)
	public String addEmp(@Valid Employee employee)

④如何知道校验结果
给需要校验的javaBean后面紧跟一个BindingResult,这个BindingResult就是封装前一个bean的校验结果

	@RequestMapping(value="emps",method=RequestMethod.POST)
	public String addEmp(@Valid Employee employee,BindingResult result) {
		if(result.hasErrors()) {
			System.out.println("有校验错误");
			return "userAdd";
		}else {
			employees.save(employee);
			System.out.print("添加操作");
			return "redirect:/emps";
		}
	}

5.视图中回显错误

<form:errors path="lastName"></form:errors>
<form:errors path="email"></form:errors>

java.lang.ClassNotFoundException: javax.xml.bind.JAXBException
JAXB API是java EE 的API,因此在java SE 9.0 以上不再包含这个 Jar 包。
java 9 中引入了模块的概念,默认情况下,Java SE中将不再包含java EE 的Jar包
而在 java 6/7 / 8 时关于这个API 都是捆绑在一起的
解决方案:下载
javax.activation-1.2.0.jar
jaxb-api-2.3.0.jar
jaxb-core-2.3.0.jar
jaxb-impl-2.3.0.jar
添加到libs下即可
https://repo1.maven.org/maven2/com/sun/xml/bind/jaxb-core/2.3.0/jaxb-core-2.3.0.jar
https://repo1.maven.org/maven2/com/sun/xml/bind/jaxb-impl/2.3.0/jaxb-impl-2.3.0.jar
https://repo1.maven.org/maven2/javax/xml/bind/jaxb-api/2.3.0/jaxb-api-2.3.0.jar
https://repo1.maven.org/maven2/com/sun/activation/javax.activation/1.2.0/javax.activation-1.2.0.jar

普通表单可以获取校验错误信息

	@RequestMapping(value="emps",method=RequestMethod.POST)
	public String addEmp(@Valid Employee employee,BindingResult result) {
 
		if(result.hasErrors()) {
			List<FieldError> fieldErrors = result.getFieldErrors();
			for(FieldError fieldError:fieldErrors) {
				System.out.println("错误字段"+fieldError.getField());
				System.out.print(fieldError.getDefaultMessage());
			}
			System.out.println("有校验错误");
			return "userAdd";
		}else {
			employees.save(employee);
			System.out.print("添加操作");
			return "redirect:/emps";
		}
	}

如何定制自己的国际化错误信息

[Email.employee.email 校验规则.隐含模型中这个对象的key.对象的属性
,Email.email, 校验规则.属性名
Email.java.lang.String, 校验规则.属性类型
Email] 校验规则
1.Email.employee.email如果是隐含模型中的employee对象的email属性字段发生了@Email校验错误,
2.Email.email所有的email属性只要发生了@Email校验错误
3.Email.java.lang.String只要是String类型发生了@Email错误
4.Email只要发生了@Email校验错误

1.创建国际化错误文件
errors_en_US.properties

Email.employee.email=email format incorrect!!!
Length.lastName=lastName'length between 6 and 8!!!
typeMismatch.birth=birth'format incorrect!!!

errors_zh_CN.properties

Email.employee.email=\u90AE\u7BB1\u683C\u5F0F\u6709\u8BEF\uFF01\uFF01\uFF01
Length.lastName=\u59D3\u540D\u957F\u5EA6\u6709\u8BEF\uFF01\uFF01\uFF01
typeMismatch.birth=\u751F\u65E5\u683C\u5F0F\u6709\u8BEF\uFF01\uFF01\uFF01

2.配置javabean

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

动态灵活传入参数
Length.java.lang.String=lastName’length between 6 and 8 {0} {1} {2}!!!

数据校验message指定错误消息

SpringMVC支持ajax
1.导入json包

jackson-annotations-2.1.5.jar
jackson-core-2.1.5.jar
jackson-databind-2.1.5.jar

2.在响应方法中加上@ResponseBody注解

	@ResponseBody
	@RequestMapping(value="allemps")
	public Collection<Employee> getAllEmp() {
 
		Collection<Employee> employees =  employeeDao.getAll();
 
		return employees;
 
	}

ResponseBody就是将响应数据放在响应体中,如果是对象,jackson将对象转为json格式

SpirngMVC发送json数据给服务器

ajax发送json数据

<script>
 
	var emp = {
		lastName : "哈哈哈",
		email : "qqq@qq.com"
	}
 
	emp = JSON.stringify(emp)
 
	$.ajax({
		url : "${ctp}/requestbody",
		data : emp,
		type : "POST",
		contentType : "application/json",
		success : function() {
 
			alert("发送成功")
		}
	})
</script>

@RequestBody接收json数据页面

	@RequestMapping(value="requestbody")
	public  String testRequestBody(@RequestBody Employee employee) {
 
		System.out.println(employee);
 
		return "hello";
	}

HttpEntity获取请求头

	@RequestMapping(value="httpEntity")
	public String httpEntity(HttpEntity<String> httpEntity) {
		System.out.println(httpEntity);  //
		return "hello";
	}

System.out.println(httpEntity) 输出如下:

<{"lastName":"哈哈哈","email":"qqq@qq.com"},{host=[localhost:8080], connection=[keep-alive], content-length=[45], pragma=[no-cache], cache-control=[no-cache], accept=[*/*], x-requested-with=[XMLHttpRequest], user-agent=[Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36], origin=[http://localhost:8080], sec-fetch-site=[same-origin], sec-fetch-mode=[cors], referer=[http://localhost:8080/curd/test_ajax.jsp], accept-encoding=[gzip, deflate, br], accept-language=[en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7], cookie=[JSESSIONID=2372BBFAF89B035C7624882255336F5D; __jsluid=f0bd1f001ae397d3438d9f7d9c82a8b3; Hm_lvt_d7682ab43891c68a00de46e9ce5b76aa=1559743349], Content-Type=[application/json;charset=UTF-8]}>

ResponseEntity返回响应数据,定制响应头

	@RequestMapping(value="responseHttpEntity")
	public ResponseEntity<String> responseHttpEntity(){
		MultiValueMap<String,String> headers = new HttpHeaders();
		String body = "<h1>this is a response</h1>";
		headers.add("set-cookie", "11111111111");
		return new ResponseEntity(body,headers,HttpStatus.OK);
	}

Springmvc文件下载

	@RequestMapping(value="download")
	public ResponseEntity<byte[]> download(HttpServletRequest request) throws Exception{
		// 获取下载文件的真实路径
		ServletContext context = request.getServletContext();
		String realPath = context.getRealPath("/scripts/jquery-1.9.1.min.js");
		// 读取文件流
		FileInputStream is = new FileInputStream(realPath);
		byte[] tmp = new byte[is.available()];
		is.read(tmp);
		is.close();
 
		HttpHeaders headers = new HttpHeaders();
		headers.set("Content-Disposition", "attachment;filename=jquery-1.9.1.min.js");
		return new ResponseEntity<byte[]>(tmp,headers,HttpStatus.OK); 
	}

SpringMVC文件上传
①导入jar包
commons-fileupload-1.2.1.jar
commons-io-2.0.jar
②配置文件

	    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
         <property name="maxUploadSize" value="#{1024*1024*20}"></property>
         <property name="defaultEncoding" value="utf-8"></property>
    </bean>

③MultipartFile封装文件信息

	@RequestMapping(value="upload")
	public String upload(@RequestParam(value="username",required=false) String userName,@RequestParam(value="headerimg")MultipartFile file,Model model) {
 
		System.out.println(userName);
		System.out.println(file);
		System.out.println("文件大小"+file.getSize());
		System.out.println("文件名字"+file.getName());
		System.out.println("文件原始名字"+file.getOriginalFilename());
		System.out.println("文件类型"+file.getContentType());
 
		try {
			file.transferTo(new File("C:\\Users\\Dell\\Desktop\\demo\\"+file.getOriginalFilename()));
			model.addAttribute("msg","文件上传成功");
		} catch (Exception e) {
			model.addAttribute("msg", "文件上传失败");
		} 
 
		return "hello";
	}

SpringMVC拦截器HandlerInterceptor
拦截器允许运行目标方法进行一些拦截工作,或者目标方法运行之后进行一些其他处理
在目标方法运行之前调用,返回boolean return true, chain.doFilter()放行;return false,chain.doFilter()不放行
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception;
在目标方法运行之后调用
void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception;
在请求整个完成之后,来到目标页面之后,chain.doFilter()放行,资源响应之后
void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception;

拦截器HandlerInterceptor是一个接口,我们要使用拦截器,必须实现HandlerInterceptor这个接口
preHandle=》目标方法=》postHandle=》页面=》afterCompletion
注意:
①preHandle不放行,则后边的都不执行
②只要preHandle放行,无论怎样,afterCompletion都会执行

多拦截器执行流程
①哪一块不放行,则此拦截器以后的任何流程都没有,但是它之前已经放行了的拦截器的afterCompletion总会执行
②多个拦截器执行流程
preHandle 顺序执行
postHandle 逆序执行
afterCompletion 逆序执行

localeResolver切换国际化
①实现一个本地化信息的javabean

package com.wuxinzhe.component;
 
import java.util.Locale;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.springframework.web.servlet.LocaleResolver;
 
public class MyLocaleResolver implements LocaleResolver {
 
	@Override
	public Locale resolveLocale(HttpServletRequest request) {
 
		Locale locale = null;
		String localeStr = request.getParameter("locale");
 
		if (localeStr != null && !"".equals(localeStr)) {
			// 通过参数,返回参数对应的区域信息
			String[] localInfo = localeStr.split("_");
			return new Locale(localInfo[0],localInfo[1]);
 
		}else {
			// 通过请求获取默认的区域信息
			return request.getLocale();
		}
	}
 
	@Override
	public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
	}
}

②注册javabean(id必须用localeResolver)

    <bean id="localeResolver" class="com.wuxinzhe.component.MyLocaleResolver">
    </bean>

③视图中就可以和之前一样使用了。

SessionLocaleResolver切换国际化
①配置SessionLocaleResolver

    <!-- 区域信息从session中获取 -->
    <bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
    </bean>

②在session中设置区域信息

	@RequestMapping(value="/login")
	public String login(@RequestParam(value="locale",defaultValue="zh_CN") String localeStr,Locale locale,HttpSession session) {
 
		String[] localeInfo = localeStr.split("_");
 
		session.setAttribute(SessionLocaleResolver.class.getName()+".LOCALE", new Locale(localeInfo[0],localeInfo[1]));
 
		return "login";
	}

③视图中就可以和之前一样使用了。

SessionLocaleResolver配合LocaleChangeInterceptor切换国际化
①配置javabean

    <!-- 区域信息从session中获取 -->
    <bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
    </bean>
    <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
    </bean>

②就可以从域名+?locale=en_US,通过参数自动设置国际化

默认就是这几个HandlerExceptionResolver
ExceptionHandlerExceptionResolver:@ExceptionHandler
ResponseStatusExceptionResolver:@ResponseStatus
DefaultHandlerExceptionResolver:判断是否是SpringMVC自带的异常
如果异常解析器都不能处理,就直接抛出去

@ExceptionHandler使用
如果一个类中发生了异常,则会使用@ExceptionHandler注解的方法,进行处理。处理异常方法只能接收一个参数,并且返回ModelAndView
@ExceptionHandler

	@ExceptionHandler(Exception.class)
	public ModelAndView exceptionHandler(Exception exception) {
 
		ModelAndView view = new ModelAndView("success");
 
		view.addObject("exception", exception);
 
		return view;
	}

精确指定某个异常
@ExceptionHandler(value= {ArithmeticException.class})

@ControllerAdvice定义一个处理全局异常的类

@ControllerAdvice
public class ExceptionTestController {
 
	@RequestMapping(value="caculate")
	public String caculate() {
 
		Integer i = 10/0;
 
		return "hello";
	}
}

本类异常和全局类异常,本类异常优先,无关异常类型。

@ResponseStatus使用
@ResponseStatus定义一个运行时异常类

package com.wuxinzhe.controller;
 
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
 
@ResponseStatus(reason = "用户未找到", value = HttpStatus.NOT_ACCEPTABLE)
public class UserNotFoundException extends RuntimeException {
 
	//private static final long serialVersionUID = 1L;
 
}

在运行时,抛出异常

	@RequestMapping(value="caculate")
	public String caculate() {
		if(true) {
			throw new UserNotFoundException();
		}
		return "success";
	}

Spring和SpringMVC整合
ContextLoaderListener
web.xml配置ContextLoaderListener

	<!-- needed for ContextLoaderListener -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring.xml</param-value>
	</context-param>
 
	<!-- Bootstraps the root web application context before servlet initialization -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
 
  <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:springmvc.xml</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

springmvc配置扫描类

  	<!-- use-default-filters过滤所有,需要禁用 -->
	<context:component-scan base-package="com.wuxinzhe" 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.xml配置扫描类

  <context:component-scan base-package="com.wuxinzhe">
    <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>

spring作为父容器,springmvc作为子容器,子容器无法装配父容器的javabean,如下

    @Autowired BookController bookController;
	public BookService() {
		System.out.println("bookService");
	}

MyBatis
①导入包

mybatis-3.4.1.jar
log4j-1.2.17.jar
mysql-connector-java-5.1.48-bin.jar

②创建javabean文件

package com.wuxinzhe.bean;
 
public class Employee {
 
	private Integer id;
 
	private String employeeName;
 
	private Integer gender;
 
	public Employee() {
		super();
	}
 
	public Employee(Integer id, String employeeName, Integer gender) {
		super();
		this.id = id;
		this.employeeName = employeeName;
		this.gender = gender;
	}
 
	public Integer getId() {
		return id;
	}
 
	public void setId(Integer id) {
		this.id = id;
	}
 
	public String getEmployeeName() {
		return employeeName;
	}
 
	public void setEmployeeName(String employeeName) {
		this.employeeName = employeeName;
	}
 
	public Integer getGender() {
		return gender;
	}
 
	public void setGender(Integer gender) {
		this.gender = gender;
	}
 
	@Override
	public String toString() {
		return "Employee [id=" + id + ", employeeName=" + employeeName + ", gender=" + gender + "]";
	}
}

③dao接口文件

package com.wuxinzhe.dao;
 
import com.wuxinzhe.bean.Employee;
 
public interface EmployeeDao {
	// 获取某个雇员信息
	public Employee getEmployeeById(Integer Id);
	// 更新某个雇员信息
	public Integer updateEmp(Employee employee);
	// 添加某个雇员信息
	public Integer insertEmp(Employee employee);
	// 删除某个雇员信息
	public boolean deleteEmp(Integer Id);
}

④mybatis-config.xml全局配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
 
   <!-- properties引入外部配置文件
        resource从类路径下引入
        url从磁盘或者网络路径引入
   -->
   <properties resource="dbconfig.properties"></properties>
    <settings>
       <!-- javabean属性名驼峰str1Str2 自动对应    数据表字段 str1_str2 -->
       <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
 
    <typeAliases>
       <!-- ①为一个javabean起别名,方便引用   引用名默认就是类名,也可以使用alias属性可以指定一个新的别名 -->
       <!-- <typeAlias type="com.wuxinzhe.bean.Employee" /> -->
       <!-- ②批量起名 默认名字就是包名,也可以通过Alias注解在类上重新命名 -->
       <package name="com.wuxinzhe.bean"/>
       <!-- 不建议使用别名,因为别名不利于读 -->
    </typeAliases>
 
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
      </dataSource>
    </environment>
  </environments>
 
  <!-- 注册写好的sql映射文件 -->
  <mappers>
    <!-- <mapper resource="employee.xml"/> -->
 
    <!-- 批量注册映射文件 -->
    <!-- 批量注册文件名必须对应dao名称 -->
    <package name="com.wuxinzhe.dao" />
  </mappers>
</configuration>

⑤在包名为com.wuxinzhe.dao的包下,创建EmployeeDao.xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wuxinzhe.dao.EmployeeDao">
  <select id="getEmployeeById" resultType="Employee">
    select * from t_employee where id = #{id}
  </select>
 
   <insert id="insertEmp">
         INSERT INTO t_employee(employeeName,gender) VALUES(#{employeeName},#{gender})
   </insert>
 
   <update id="updateEmp">
       UPDATE t_employee SET employeeName=#{employeeName},gender=#{gender} WHERE id=#{id}
   </update>
 
   <delete id="deleteEmp">
      DELETE * FROM t_employee WHERE id=#{id}
   </delete>
 
</mapper>

⑥测试

package com.wuxinzhe.test;
 
import static org.junit.jupiter.api.Assertions.*;
 
import java.io.IOException;
import java.io.InputStream;
 
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
 
import com.wuxinzhe.bean.Employee;
import com.wuxinzhe.dao.EmployeeDao;
 
class MyBatisTest {
 
	SqlSessionFactory sqlSessionFactory;
 
	@BeforeEach
	public void initSqlSessionFatctory() throws IOException {
		String resource = "mybatis-config.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
	}
 
	@Test
	void test() throws IOException {
		//根据全局配置文件创建一个SqlSessionFactory
		// SqlSessionFactory是SqlSession工厂,负责创建从中获得 SqlSession 的实例
		// SqlSession:sql会话(代表和数据库的一次会话)
		SqlSession sqlSession = sqlSessionFactory.openSession();
		try {
			//获取dao接口实现
			EmployeeDao employeeDao = sqlSession.getMapper(EmployeeDao.class);
			Employee employee = employeeDao.getEmployeeById(1);
			System.out.println(employee);
		} finally {
			sqlSession.close();
		}
	}
 
	@Test
	void InsertEmp() {
		SqlSession sqlSession = sqlSessionFactory.openSession();
		try {
			EmployeeDao employeeDao = sqlSession.getMapper(EmployeeDao.class);
			employeeDao.insertEmp(new Employee(null,"哈哈哈",1));
		}finally {
			sqlSession.close();
		}
	}
 
}

导入dtd写xml配置有提示
http://mybatis.org/dtd/mybatis-3-config.dtd -//mybatis.org//DTD Config 3.0//EN
http://mybatis.org/dtd/mybatis-3-mapper.dtd -//mybatis.org//DTD Mapper 3.0//EN
windows->preferences->XML->xml Catalog->Add
添加时候key为-//mybatis.org//DTD Config 3.0//EN,key type来选择URI,Location选择file systems配置文件路径
注意:mybatis-3-config.dtd mybatis-3-mapper.dtd从Mybatis jar包中获取。

获取自增主键的值
useGeneratedKeys获取自增的主键,通过keyProperty指定封装对象的属性

   <insert id="insertEmp" useGeneratedKeys="true" keyProperty="id">
         INSERT INTO t_employee(employeeName,gender) VALUES(#{employeeName},#{gender})
   </insert>

获取非自增主键的值
order=”BEFORE”:在sql运行之前运行
keyProperty=”id”:运行后赋值给
resultType=”integer”:返回值类型为整型

   <insert id="insertEmp2">
      <selectKey order="BEFORE" keyProperty="id" resultType="integer">
         SELECT max(id)+1 FROM t_employee
      </selectKey> 
      INSERT INTO t_employee(id,employeeName,gender) VALUES(#{id},#{employeeName},#{gender})
   </insert>

参数的各种取值
单个参数
#{名字随意}
多个参数
只要传入了多个参数,mybatis会自动的将这些参数封装在一个map中,封装时使用的key就是参数的索引和参数的第几个表示,Map map = new HashMap<>();map.put(“0”,传入的值1);map.put(“param1”,传入的值1),可以通过以下方式实现自定义key值
接口方法
public Employee getEmployeeByIdAndEmpname(@Param(“id”) Integer id,@Param(“empName”) String empName);
sql映射文件

  <select id="getEmployeeByIdAndEmpname" resultType="Employee">
    select * from t_employee where id = #{id} AND employeeName=#{empName}
  </select>

map
POJO

sql语句中${}和#{}取值的区别
#{}参数预编译的方式,防止sql注入,更安全,
${}直接拼接sql语句,不安全,在sql语句中有些位置是不支持预编译的,如表名,就得用${}方式取值

查询返回List resultType返回list的集合里面的元素类型

  public List<Employee> allEmps();
  </[re?>
  	<pre lang="conf" escaped="true">
  <select id="allEmps" resultType="Employee">
     SELECT * FROM t_employee
  </select>

返回单条记录封装成map
接口方法

  public Map<String,Object> getEmpByIdReturnMap(Integer id);

sql映射文件

  <select id="getEmpByIdReturnMap" resultType="map">
      SELECT * FROM t_employee WHERE id=#{id}
  </select>

返回多条记录封装成map,@MapKey指定哪个字段作为Map的key,resultType指定Map中的值类型

  @MapKey("id")
  public Map<Integer,Employee> allEmpsReturnMap();
  <select id="allEmpsReturnMap" resultType="Employee">
      SELECT * FROM t_employee
  </select>

默认MyBatis自动封装结果集
1.按照列名和属性名一一对应规则(不区分大小写)
2.如果不一一对应
①开启驼峰命名(字段中必须有下划线_)
②sql映射语句字段起别名方式 select id,name employeeName,sex gender FROM t_employee,其中employeeName和gender为封装javabean的属性
③resultMap

resultMap自定义封装规则
resultMap自定义结果集和javabean的映射规则

  <select id="getEmployeeById" resultMap="getEmployeeById">
     select * from t_employee where id = #{id} 
  </select>
  <!--type:指定为哪一个javabean封装规则,全类名
      id:唯一标识,供sql语句引用-->
  <resultMap type="Employee" id="getEmployeeById">
     <!-- 主键列 id指定主键的对应规则 column指定哪一列是主键列  property指定javabean的哪个属性 -->
     <id column="id" property="id"/>
     <!-- 普通列 -->
     <result column="name" property="employeeName"/>
     <result column="sex" property="gender"/>
  </resultMap>

级联属性
keyDao.xml

  <?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  <mapper namespace="com.wuxinzhe.dao.KeyDao">
     <select id="getKeyById" resultMap="getKeyById">
        SELECT k.id,k.key_name,k.lock_id,l.id l_id,l.lock_name FROM t_key k 
          LEFT JOIN t_lock l ON k.lock_id=l.id 
          WHERE k.id=#{id}
     </select>
 
     <resultMap type="com.wuxinzhe.bean.Key" id="getKeyById">
       <id property="id" column="id"/>
       <result property="keyName" column="key_name" />
       <result property="lock.id" column="lock_id"/>
       <result property="lock.lockName" column="lock_name"/>
     </resultMap>
  </mapper>

推荐采用association达到上例同样的效果

  	<resultMap type="com.wuxinzhe.bean.Key" id="getKeyById">
		<id property="id" column="id" />
		<result property="keyName" column="key_name" />
		<!--association表示联合了一个对象-->
		<association property="lock"
			javaType="com.wuxinzhe.bean.Lock">
			<id property="id" column="l_id" />
			<result property="lockName" column="lock_name" />
		</association>
	</resultMap>

collection定义集合类型属性的封装规则
com.wuxinzhe.bean.Lock

		private Integer id;
		private String lockName;
		private List<Key> keys;

LockDao.xml

		  <select id="getLockById" resultMap="getLockById">
			  SELECT l.id,l.lock_name,k.id kid,k.key_name FROM t_lock l 
				LEFT JOIN t_key k ON k.lock_id=l.id
				WHERE l.id=#{id}
		  </select>
 
		  <resultMap id="getLockById" type="com.wuxinzhe.bean.Lock" >
			<id property="id" column="id"/>
			<result property="lockName" column="lock_name"/>
			<collection property="keys" ofType="com.wuxinzhe.bean.Key">
			   <id property="id" column="kid"/>
			   <result property="keyName" column="key_name"/>
		<!--        		<association property="lock"
					javaType="com.wuxinzhe.bean.Lock">
					<id property="id" column="id" />
				</association> -->
			</collection>
		  </resultMap>

使用select属性指定分区查询,简化sql语句
LockDao.java

	public Lock getLockByIdSimple(Integer id);

KeyDao.java

	public Key getKeyByIdSimple(Integer id);

KeyDao.xml

	<select id="getLockByIdSimple" resultType="com.wuxinzhe.bean.Lock">
      SELECT * FROM t_lock WHERE id=#{id}
    </select>

LockDao.xml

	<select id="getKeyByIdSimple" resultMap="myKey">
         SELECT * FROM t_key WHERE id=#{id}
     </select>
 
     <resultMap id="myKey" type="com.wuxinzhe.bean.Key">
         <id property="id" column="id"/>
         <result property="keyName" column="key_name"/>
         <association property="lock" select="com.wuxinzhe.dao.LockDao.getLockByIdSimple" column="lock_id">
         </association>
     </resultMap>

测试查询

	KeyDao keyDao = sqlSession.getMapper(KeyDao.class);
	Key key = keyDao.getKeyByIdSimple(1);
	System.out.println(key);

输出如下:

Key [id=1, keyName=1号钥匙, lock=Lock [id=1, lockName=1号锁]]

延迟加载和按需加载
全局配置

	<settings>
       <!-- javabean属性名驼峰str1Str2 自动对应    数据表字段 str1_str2 -->
       <setting name="mapUnderscoreToCamelCase" value="true"/>
       <!-- 开启延迟开关 -->
       <setting name="lazyLoadingEnabled" value="true"/>
       <!-- 开启属性按需加载 -->
       <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

测试

			KeyDao keyDao = sqlSession.getMapper(KeyDao.class);
			Key key = keyDao.getKeyByIdSimple(1);
			String keyName = key.getKeyName();
			System.out.println(keyName);
			Thread.sleep(3000);
			System.out.println(key.getLock());
				   <pre lang="java" escaped="true">
 
    输出如下:	
		   <pre lang="conf" escaped="true">
		DEBUG 02-07 18:48:59,305 ==>  Preparing: SELECT * FROM t_key WHERE id=?   (BaseJdbcLogger.java:145) 
		DEBUG 02-07 18:48:59,389 ==> Parameters: 1(Integer)  (BaseJdbcLogger.java:145) 
		DEBUG 02-07 18:48:59,495 <==      Total: 1  (BaseJdbcLogger.java:145) 
		1号钥匙
		DEBUG 02-07 18:49:02,499 ==>  Preparing: SELECT * FROM t_lock WHERE id=?   (BaseJdbcLogger.java:145) 
		DEBUG 02-07 18:49:02,500 ==> Parameters: 1(Integer)  (BaseJdbcLogger.java:145) 
		DEBUG 02-07 18:49:02,503 <==      Total: 1  (BaseJdbcLogger.java:145) 
		Lock [id=1, lockName=1号锁]

动态sql语句 where

StudentDao.java

		   public List<Student> getStudentByCondition(Student student);
		   <select id="getStudentByCondition" resultType="Student">
		   SELECT * FROM t_students
		   <where>
			   <if test="address !=null && address!=""">
				 address LIKE #{address}
			   </if>
			   <if test="gender!=null">
				 AND gender = #{gender}
			   </if>
			   <if test="age!=null">
				 AND age > #{age}
			   </if>
		   </where>
		   </select>
		   	@Test
			void getStudentByCondition() {
				Student student = new Student();
 
				//student.setAddress("%小区%");
				//student.setGender(1);
				student.setAge(11);
				SqlSession sqlSession = sqlSessionFactory.openSession();
				try {
					StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
					System.out.println(studentDao.getStudentByCondition(student));
				}finally {
					sqlSession.close();
				}
			}

sql语句打印:

			DEBUG 02-08 14:16:52,021 ==>  Preparing: SELECT * FROM t_students WHERE age > ?   (BaseJdbcLogger.java:145) 
			DEBUG 02-08 14:16:52,085 ==> Parameters: 11(Integer)  (BaseJdbcLogger.java:145) 
			DEBUG 02-08 14:16:52,120 <==      Total: 5  (BaseJdbcLogger.java:145) 
			[Student [id=1, name=小丽, address=哈哈小区, gender=2, age=13], Student [id=2, name=小明, address=牛牛小区, gender=1, age=14], Student [id=3, name=小强, address=奔腾小区, gender=1, age=20], Student [id=4, name=小北, address=吉利小区, gender=1, age=21], Student [id=5, name=小菜, address=奔驰小区, gender=2, age=18]]
        可以看出来拼接后的sql语句会把第一个语句的and去掉

动态sql语句trim标签

   <select id="getStudentByCondition" resultType="Student">
   SELECT * FROM t_students
 
   <!-- 
     prefix:前缀,如果有条件存在,则为下面整体sql添加一个前缀
     prefixOverrides:取出整体字符串开头多余的某个字符串
     suffix:为整天添加一个后缀
     suffixOverrides:取出整体字符串尾部多余的某个字符串
    -->
   <trim prefix="where" prefixOverrides="and">
       <if test="address !=null && address!=""">
         address LIKE #{address}
       </if>
       <if test="gender!=null">
         AND gender = #{gender}
       </if>
       <if test="age!=null">
         AND age > #{age}
       </if>
     </trim>  
   </select>

动态sql语句collection标签

	public List<Student> getStudentIn(@Param("ids")List<Integer> ids);
      <select id="getStudentIn" resultType="Student">
       SELECT * FROM t_students WHERE id IN
       <!-- 
       collection默认值为list,如果想自定义参数,可以通过@Param传参
	   open sql语句以什么开始
	   close sql语句以什么结束
	   如果遍历的当前值一个List
	     index 当前索引项的索引
		 item  指定当前索引项的元素值
		如果遍历的当前值时一个Map
	     index 当前索引项的key
		 item  指定当前索引项的元素值
		separator每个索引项分隔的符号 
        -->
       <foreach collection="ids" open="(" close=")" item="item" separator="," index="index">
          #{item}
       </foreach>
   </select>

动态sql语句choose标签(满足任何一个条件,结束语句)

   	<select id="getStudentByChoose" resultType="Student">
		SELECT * FROM t_students
		<where>
			<choose>
				<when test="id !=null">
				  id=#{id}
				</when>
				<when test="gender!=null">
				  gender=#{gender}
				</when>
				<when test="age!=null">
				  age=#{age}
				</when>
				<otherwise>
				1=1
				</otherwise>
			</choose>
		</where>
	</select>

使用if结合set动态更新

	 <update id="updateStudent">
	   UPDATE t_students 
	   <set>
	      <if test="name!=null and name!=""">
	         name=#{name},
	      </if>
	      <if test="address!=null and address!=""">
	         address=#{address},
	      </if>
	      <if test="gender!=null">
	         gender=#{gender},
	      </if>
	      <if test="age!=null">
	         age=#{age},
	      </if>
	   </set>
	   WHERE id=#{id}
	</update>

测试文件

		Student student = new Student();
		student.setId(1);
		student.setAge(1);
		student.setAddress("欢城镇");
		student.setGender(2);
		student.setName("哈哈哈");
		SqlSession sqlSession = sqlSessionFactory.openSession();
		try {
			StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
			studentDao.updateStudent(student);
		}finally {
			sqlSession.close();
		}

更新执行的sql语句

	UPDATE t_students SET name=?, address=?, gender=?, age=? WHERE id=?

sql和include标签抽取可重用sql语句

一级缓存:线程级别的缓存,本地缓存,SqlSession级别的缓存
二级缓存:全局范围的缓存,除了当前线程,SqlSession能用外,其他都可以用。

一级缓存失效的几种情况以及观察原理
①一级缓存是SqlSession级别的缓存,不同的sqlSession,使用不同的一级缓存,只有在同一个sqlSession期间查询到的数据回报存在SqlSession的缓存中,下次使用这个sqlSession会从缓存中拿
②同一个方法,不同的参数,由于可能之前没查询过,所以还会发新的sql
③在一个SqlSession期间执行任何上一次增删改操作,增删改操作会把缓存清空
④手动清空缓存sqlSession.clearCache();

二级缓存
全局作用域缓存,二级缓存默认不开启,需要手动配置,MyBatis提供二级缓存的接口以及实现,缓存实现要求POJO实现Serializable接口
二级缓存在SqlSession关闭或者提交之后才会生效,每一个Dao有它自己的二级缓存
使用步骤
1.全局配置文件中开启二级缓存

<setting name="cacheEnabled" value="true"/>

2.需要使用二级缓存的映射文件处使用cache配置缓存

<cache/>

3.注意:POJO需要实现Serializable接口

缓存的查询顺序
①一级缓存和二级缓存不会有同一个数据
二级缓存中:一级缓存关闭了就有了
一级缓存中:二级缓存中没有此数据,就会看一级缓存,一级缓存没有数据,就去查询数据库,数据库的查询结果,就放在一级缓存中了。
② 任何时候都是先看二级缓存,再看一级缓存,如果大家都没有就去查询数据库

SSM整合

SSM导包

Spring

【IOC核心】
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
commons-logging-1.1.3.jar

【aop】
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

【jdbc】
spring-jdbc-4.0.0.RELEASE.jar
spring-orm-4.0.0.RELEASE.jar
spring-tx-4.0.0.RELEASE.jar

【测试】
spring-test-4.0.0.RELEASE.jar

SpringMVC

【核心】
spring-web-4.0.0.RELEASE.jar
spring-webmvc-4.0.0.RELEASE.jar

【上传下载】
commons-fileupload-1.2.1.jar
commons-io-2.0.jar

【jstl-jsp标准标签库】
standard.jar
jstl.jar

【数据校验】
hibernate-validator-5.0.0.CR2.jar
hibernate-validator-annotation-processor-5.0.0.CR2.jar
classmate-0.8.0.jar
jboss-logging-3.1.1.GA.jar
validation-api-1.1.0.CR1.jar

【ajax】
jackson-annotations-2.1.5.jar
jackson-core-2.1.5.jar
jackson-databind-2.1.5.jar

MyBatis

【核心包】
mybatis-3.4.1.jar

【ehcache】
ehcache-core-2.6.8.jar
mybatis-ehcache-1.0.3.jar
log4j-1.2.17.jar
slf4j-api-1.7.21.jar
slf4j-log4j12-1.7.21.jar

【数据源/数据库驱动】
mysql-connector-java-5.1.48-bin.jar
c3p0-0.9.5.2.jar
mchange-commons-java-0.2.12.jar

jdk8以上还需要报错java.lang.NoClassDefFoundError:javax/xml/bind/JAXBException
javax.activation-1.2.0.jar
jaxb-api-2.3.0.jar
jaxb-core-2.3.0.jar
jaxb-impl-2.3.0.jar

SSM配置
ehcache.xml
log4j.xml

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_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>07SSM</display-name>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
 
  <!-- 配置Spring容器启动 -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<!-- 指定Spring配置文件位置 -->
		<param-value>classpath:spring/applicationContext.xml</param-value>
	</context-param>
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
 
	<!-- 配置SpringMVC前端控制器 -->
	<servlet>
		<servlet-name>springDispatcherServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:spring/springmvc.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
 
	<servlet-mapping>
		<servlet-name>springDispatcherServlet</servlet-name>
		<!-- 拦截所有请求 -->
		<url-pattern>/</url-pattern>
	</servlet-mapping>
 
	<!-- 字符编码 -->
	<filter>
		<filter-name>CharacterEncodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
		<init-param>
			<param-name>forceEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>CharacterEncodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
 
 
	<!-- 支持REST风格的过滤器 -->
	<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>
</web-app>

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"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx"
	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-4.0.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
 
   <context:component-scan base-package="com.wuxinzhe">
     <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
   </context:component-scan>
 
   <!-- 导入外部配置文件 -->
   <context:property-placeholder location="classpath:dbconfig.properties"/>
 
   <!-- 配置数据源 -->
   <bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource"  >
     <property name="user" value="${jdbc.user}"></property>
     <property name="password"  value="${jdbc.password}"></property>
     <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
     <property name="driverClass" value="${jdbc.driverClass}"></property>
     <property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
     <property name="minPoolSize" value="${jdbc.minPoolSize}"></property>
   </bean>
 
   <!-- 配置JdbcTemplate操作数据库 -->
 
   <!-- 配置Mybatis操作数据库
        SqlSessionFactoryBean 可以根据配置文件
    -->
   <bean class="org.mybatis.spring.SqlSessionFactoryBean">
     <property name="configLocation" value="classpath:mybatis-config.xml"></property>
     <property name="dataSource" ref="dataSource"></property>
     <!-- 指定xml映射文件的位置 -->
     <property name="mapperLocations" value="classpath:mybatis/dao/*.xml"></property>
   </bean>
 
   <!-- 把每一个dao接口的实现加入到ioc容器中  -->
   <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
      <!-- 指定dao接口所在的包 -->
     <property name="basePackage" value="com.wuxinzhe.dao"></property>
   </bean>
 
   <!-- 配置事务控制,配置事务管理器,让他控制数据源里边的链接关闭和提交 -->
   <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
     <property name="dataSource" ref="dataSource"></property>
   </bean>
 
   <!-- 基于xml配置,配置事务,哪些方法切入事务还要写切入点表达式 -->
 
   <aop:config>
    <!-- 配置切入点表达式 -->
    <aop:pointcut expression="execution(* com.wuxinzhe.service.*.*(..))" id="txPoint"/>
    <aop:advisor  advice-ref="myTx" pointcut-ref="txPoint"/>
   </aop:config>
 
   <!-- 配置事务增强、事务属性、事务建议
 
   transaction-manager指定要配置的事务管理id
    -->
   <tx:advice id="myTx" transaction-manager="transactionManager">
     <!-- 配置事务的属性 -->
     <tx:attributes>
       <tx:method name="*" rollback-for="java.lang.Exception"/>
       <tx:method name="get*" read-only="true"/>
       <tx:method name="insertEmp" isolation="READ_UNCOMMITTED"/>
     </tx:attributes>
   </tx:advice>
 
</beans>

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
 
   <!-- properties引入外部配置文件
        resource从类路径下引入
        url从磁盘或者网络路径引入
   -->
   <properties resource="dbconfig.properties"></properties>
    <settings>
       <!-- javabean属性名驼峰str1Str2 自动对应    数据表字段 str1_str2 -->
       <setting name="mapUnderscoreToCamelCase" value="true"/>
       <!-- 开启延迟开关 -->
       <setting name="lazyLoadingEnabled" value="true"/>
       <!-- 开启属性按需加载 -->
       <setting name="aggressiveLazyLoading" value="false"/>
    </settings>
 
    <typeAliases>
       <!-- ①为一个javabean起别名,方便引用   引用名默认就是类名,也可以使用alias属性可以指定一个新的别名 -->
       <!-- <typeAlias type="com.wuxinzhe.bean.Employee" /> -->
       <!-- ②批量起名 默认名字就是包名,也可以通过Alias注解在类上重新命名 -->
       <package name="com.wuxinzhe.bean"/>
       <!-- 不建议使用别名,因为别名不利于读 -->
    </typeAliases>
 
<!--   <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
      </dataSource>
    </environment>
  </environments> -->
 
  <!-- PageHelper分页插件使用 -->
  	<plugins>
		<!-- com.github.pagehelper为PageHelper类所在包名 -->
		<plugin interceptor="com.github.pagehelper.PageInterceptor">
			<!-- 使用下面的方式配置参数,后面会有所有的参数介绍 -->
		</plugin>
	</plugins>
 
  <!-- 注册写好的sql映射文件 -->
  <mappers>
    <!-- <mapper resource="employee.xml"/> -->
 
    <!-- 批量注册映射文件 -->
    <!-- 批量注册文件名必须对应dao名称 -->   
    <package name="mybatis.dao" />
  </mappers>
</configuration>

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:mvc="http://www.springframework.org/schema/mvc"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
		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-4.0.xsd">
 
   <!-- springMVC只扫描控制器,需要禁用掉默认规则 -->
   <context:component-scan base-package="com.wuxinzhe" use-default-filters="false">
      <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
   </context:component-scan>
 
   <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
       <property name="prefix" value="/WEB-INF/pages/"></property>
       <property name="suffix" value=".jsp"></property>
   </bean>
 
   <!-- 配置文件上传解析器 -->
   <bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver">
     <property name="defaultEncoding" value="utf-8"></property>
     <property name="maxUploadSize" value="#{1024*1024*20}"></property>
   </bean>
 
   <!-- 扫静态资源 -->
   <mvc:default-servlet-handler/>
 
   <!-- 扫动态 -->
   <mvc:annotation-driven></mvc:annotation-driven>
 
</beans>

MBG

①导包
mybatis-generator-core-1.3.2.jar

②写配置(本目录下mbg.xml)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
  PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
  "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
 
<generatorConfiguration>
 
  <!-- targetRuntime 
          MyBatis3 生成复杂版的sql
          MyBatis3Simple 生成简单版CURD sql  -->
  <context id="DB2Tables" targetRuntime="MyBatis3">
    <jdbcConnection driverClass="com.mysql.jdbc.Driver"
        connectionURL="jdbc:mysql://localhost:3306/employee"
        userId="root"
        password="root">
    </jdbcConnection>
 
    <javaTypeResolver >
      <property name="forceBigDecimals" value="false" />
    </javaTypeResolver>
 
    <!-- javaModelGenerator 生成POJO
         targetPackage 生成的POJO放在哪个包里
         targetProject 放在哪个工程的哪个目录下
     -->
    <javaModelGenerator targetPackage="com.wuxinzhe.bean" targetProject=".\src">
      <property name="enableSubPackages" value="true" />
      <property name="trimStrings" value="true" />
    </javaModelGenerator>
 
    <!-- sqlMapGenerator sql映射文件生成器  -->
    <sqlMapGenerator targetPackage="com.wuxinzhe.dao"  targetProject=".\conf">
      <property name="enableSubPackages" value="true" />
    </sqlMapGenerator>
 
    <!-- javaClientGenerator javadao接口生成的地方  -->
    <javaClientGenerator type="XMLMAPPER" targetPackage="com.wuxinzhe.dao"  targetProject=".\src">
      <property name="enableSubPackages" value="true" />
    </javaClientGenerator>
 
    <!-- table逆向生成哪个数据表
    tableName 表名
    domainObjectName 这个表对应的对象
 
     -->
<!--     <table schema="DB2ADMIN" tableName="ALLTYPES" domainObjectName="Customer" >
      <property name="useActualColumnNames" value="true"/>
      <generatedKey column="ID" sqlStatement="DB2" identity="true" />
      <columnOverride column="DATE_FIELD" property="startDate" />
      <ignoreColumn column="FRED" />
      <columnOverride column="LONG_VARCHAR_FIELD" jdbcType="VARCHAR" />
    </table> -->
 
    <table schema="DB2ADMIN" tableName="t_employee" domainObjectName="Employee" >
    </table>
 
    <table schema="DB2ADMIN" tableName="t_lock" domainObjectName="Lock" >
    </table>
 
    <table schema="DB2ADMIN" tableName="t_key" domainObjectName="Key" >
    </table>
 
  </context>
</generatorConfiguration>

③利用mbg生成mybatis相关文件

package com.wuxinzhe.test;
 
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
 
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.exception.InvalidConfigurationException;
import org.mybatis.generator.exception.XMLParserException;
import org.mybatis.generator.internal.DefaultShellCallback;
 
public class MBGTest {
 
	public static void main(String[] args) throws IOException, XMLParserException, Exception {
		   List<String> warnings = new ArrayList<String>();
		   boolean overwrite = true;
		   File configFile = new File("mbg.xml");
		   ConfigurationParser cp = new ConfigurationParser(warnings);
		   Configuration config = cp.parseConfiguration(configFile);
		   DefaultShellCallback callback = new DefaultShellCallback(overwrite);
		   MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
		   myBatisGenerator.generate(null);
	}
 
}

批量插入1000条数据
映射文件

  <insert id="insertBatch" parameterType="list">
    INSERT INTO t_employee(employeeName,gender) VALUES
    <foreach collection="employees" item="employee" separator=",">
      (#{employee.employeename},#{employee.gender})
    </foreach>
  </insert>

批量插入数据dao

    void insertBatch(@Param("employees") List<Employee> employees);

测试文件

		@Test
	void testInsertBatch() {
 
		SqlSession sqlSession = sqlSessionFactory.openSession();
		try {
 
			EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
			List<Employee> employees = new ArrayList<Employee>();
			for (int i = 0; i <= 1000; i++) {
				Employee employee = new Employee();
				employee.setEmployeename(UUID.randomUUID().toString().substring(0, 5));
				employee.setGender(1);
				employees.add(employee);
			}
			mapper.insertBatch(employees);
			sqlSession.commit();
		} finally {
			sqlSession.close();
		}
	}

pageHelper分页使用
pageHelper分页插件jar下载地址
https://repo1.maven.org/maven2/com/github/pagehelper/pagehelper/
jsqlparser.jar下载
https://www.mvnjar.com/com.github.jsqlparser/jsqlparser/0.9.5/detail.html
①导包
jsqlparser-0.9.5.jar
pagehelper-5.0.3.jar
②mybatis-config.xml全局配置

  <!-- PageHelper分页插件使用 -->
  	<plugins>
		<!-- com.github.pagehelper为PageHelper类所在包名 -->
		<plugin interceptor="com.github.pagehelper.PageInterceptor">
			<!-- 使用下面的方式配置参数,后面会有所有的参数介绍 -->
		</plugin>
	</plugins>

③mapper映射

 <select id="getEmpsByPage" resultType="Employee">
     SELECT * FROM t_employee 
  </select>

④dao

  	public List<Employee> getEmpsByPage();

⑤Controller调用

	@RequestMapping("getEmpsByPage")
	public String getEmpsByPage(Model model, @RequestParam(value = "page", defaultValue = "1") Integer page) {
 
		PageHelper.startPage(page, 5);
		 //紧跟着的第一个select方法会被分页
		List<Employee> lists = employeeService.getEmpsByPage();
 
		PageInfo pageInfo = new PageInfo(lists);
 
		//分页列表Page{count=true, pageNum=5, pageSize=5, startRow=20, endRow=25, total=1017, pages=204, reasonable=false, pageSizeZero=false}
		System.out.println("分页列表" + lists);
 
		//分页信息PageInfo{pageNum=5, pageSize=5, size=5, startRow=21, endRow=25, total=1017, pages=204, list=Page{count=true, pageNum=5, pageSize=5, startRow=20, endRow=25, total=1017, pages=204, reasonable=false, pageSizeZero=false}, prePage=4, nextPage=6, isFirstPage=false, isLastPage=false, hasPreviousPage=true, hasNextPage=true, navigatePages=8, navigateFirstPage1, navigateLastPage8, navigatepageNums=[1, 2, 3, 4, 5, 6, 7, 8]}
		System.out.println("分页信息"+pageInfo);
		model.addAttribute("pageInfo", pageInfo);
 
		return "empList";
	}

⑥empList.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<table cellpadding="5" cellspacing="0" border="1">
  <tr>
    <th>id</th>
    <th>employeeName</th>
    <th>gender</th>
  </tr>
		<c:forEach items="${ pageInfo.list}" var="item"  >
			<tr>
				<td>${item.id} </td>
				<td>${item.employeeName}</td>
				<td>${item.gender }</td>
			</tr>
		</c:forEach>
 
    <tr>
      <td colspan="3">
        <a href="./getEmpsByPage?page=1">首页</a>
        <a href="./getEmpsByPage?page=${pageInfo.prePage}">上一页</a>
        <c:forEach items="${pageInfo.navigatepageNums}" var="pageNum">
            <c:if test="${pageInfo.pageNum==pageNum}">
               <b>${pageNum}</b>
            </c:if>
            <c:if test="${pageInfo.pageNum!=pageNum}">
               <a href="./getEmpsByPage?page=${pageNum}">${pageNum}</a>
            </c:if>
        </c:forEach>
        <a href="./getEmpsByPage?page=${pageInfo.nextPage}">下一页</a>
        <a href="./getEmpsByPage?page=${pageInfo.pages}">末页</a>
      </td>
    </tr>
	</table>
</body>
</html>

监听器
监听对象:
监听事件:
触发行为:

pageContext–>request–>session–>application

八个监听器
按照监听对象分为三类:
1.监听ServletContextListener事件的
①ServletContextListener
作用:监听ServletContext生命周期(从创建到销毁的过程(服务器启动创建–服务器停止销毁))
①ServletContextAttributeListener
作用:监听ServletContext域中属性变化
2.监听HttpSession事件的
①HttpSessionListener
作用:监听session的生命周期(session是第一次使用的时候创建;session超时销毁,session手动销毁)
②HttpSessionAttributeListener
作用:监听HttpSession域中属性变化
③HttpSessionActivationListener
作用:监听某个对象随着HttpSession活化钝化的
④HttpSessionBindingListener
作用:监听某个对象保存(绑定)到session中和从session中移除(解绑)
3.监听ServletRequest事件的
①ServletRequestListener
作用:监听request对象的生命周期(请求进来创建新的request对象保存请求的详细信息,请求完成销毁request对象)
②ServletRequestAttributeListener
作用:监听request域中的属性变化

按照功能:
1.监听生命周期
ServletContextListener
//ServletContext销毁(服务器停止)调用
void contextDestroyed(ServletContextEvent sce)
//ServletContext初始化(服务器启动)调用
void contextInitialized(ServletContextEvent sce)
HttpSessionListener
//session创建调用(第一次使用session)
void sessionCreated(HttpSessionEvent se)
//session超时失效,手动设置失效时调用
void sessionDestroyed(HttpSessionEvent se)
ServletRequestListener
//request销毁调用(当次请求结束)
void requestDestroyed(ServletRequestEvent sre)
//request初始化(发起一次新请求)
void requestInitialized(ServletRequestEvent sre)
2.监听域中属性变化
ServletContextAttributeListener
HttpSessionAttributeListener
ServletRequestAttributeListener
// 添加属性
void attributeAdded(ServletContextAttributeEvent event)
void attributeRemoved(ServletContextAttributeEvent event)
void attributeReplaced(ServletContextAttributeEvent event)
3.session独有的监听器
HttpSessionActivationListener
监听某个对象随着HttpSession活化
void sessionDidActivate(HttpSessionEvent se)
监听某个对象随着HttpSession钝化
void sessionWillPassivate(HttpSessionEvent se)
HttpSessionBindingListener
监听某个对象在session中绑定进来了(保存进来)
void valueBound(HttpSessionBindingEvent event)
监听某个对象在session中解绑(移除)
void valueUnbound(HttpSessionBindingEvent event)

监听器如何工作
①写接口实现监听器
②在web.xml注册监听器

ServletContextListener监听ServletContext示例
①编写实现ServletContextListener的java类

package com.wuxinzhe.listener;
 
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
 
public class MyListener implements ServletContextListener {
 
	@Override
	public void contextInitialized(ServletContextEvent sce) {
		// TODO Auto-generated method stub
		System.out.println("contextInitialized"+sce.getServletContext());
 
	}
 
	@Override
	public void contextDestroyed(ServletContextEvent sce) {
		// TODO Auto-generated method stub
		System.out.println("contextDestroyed"+sce.getServletContext());
	}
 
}

②在web.xml中注册监听器

  <listener>
    <listener-class>com.wuxinzhe.listener.MyListener</listener-class>
  </listener>

session的生命周期
1.创建
第一次使用的时候创建
第一次访问jsp内容,因为session对象在jsp创建

package com.wuxinzhe.servlet;
 
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
 
@WebServlet("/hahasession")
public class SessionServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
 
 
    public SessionServlet() {
        super();
    }
 
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doPost(request, response);
	}
 
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String string = request.getParameter("param");
		HttpSession httpSession = request.getSession();
		// 获取session
		if(string.equals("getsession")) {
			response.getWriter().write(httpSession.getId());
		}else if(string.equals("invalidatesession")) {
			// 销毁session
			httpSession.invalidate();
			response.getWriter().write("session invalidate");
		}
	}
}

Servlet中获取和销毁session

package com.wuxinzhe.servlet;
 
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
 
@WebServlet("/hahasession")
public class SessionServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
 
 
    public SessionServlet() {
        super();
    }
 
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doPost(request, response);
	}
 
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String string = request.getParameter("param");
		HttpSession httpSession = request.getSession();
		// 获取session
		if(string.equals("getsession")) {
			response.getWriter().write(httpSession.getId());
		}else if(string.equals("invalidatesession")) {
			// 销毁session
			httpSession.invalidate();
			response.getWriter().write("session invalidate");
		}
	}
}

HttpSessionListener,HttpSessionAttributeListener测试示例
MySessionLifeCycleListener监听器

package com.wuxinzhe.listener;
 
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
 
public class MySessionLifeCycleListener implements HttpSessionListener,HttpSessionAttributeListener {
 
	@Override
	public void sessionCreated(HttpSessionEvent arg0) {
		System.out.println("sessionCreated"+arg0.getSession().getId());
	}
 
	@Override
	public void sessionDestroyed(HttpSessionEvent arg0) {
		System.out.println("sessionDestroyed"+arg0.getSession().getId());
	}
 
	@Override
	public void attributeAdded(HttpSessionBindingEvent arg0) {
		System.out.println("session属性添加");
		System.out.println("session属性名" + arg0.getName());
		System.out.println("session属性值" + arg0.getValue());
 
	}
 
	@Override
	public void attributeRemoved(HttpSessionBindingEvent arg0) {
 
		System.out.println("session属性移除");
		System.out.println("session属性名" + arg0.getName());
		System.out.println("session属性值" + arg0.getValue());
	}
 
	@Override
	public void attributeReplaced(HttpSessionBindingEvent arg0) {
		System.out.println("session属性值替换");
		System.out.println("session属性名" + arg0.getName());
		System.out.println("session属性过去的值" + arg0.getValue());
		System.out.println("session重设后的值"+arg0.getSession().getAttribute(arg0.getName()));
	}
}

session.jsp测试检查session的变化

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<%
   session.setAttribute("username", "admin");
   Thread.sleep(3000);
   session.setAttribute("username", "admin2");
   Thread.sleep(3000);
   session.removeAttribute("username");
 
%>
</body>
</html>

HttpSessionActivationListener示例
钝化sessionWillPassivate:内存的数据写入到硬盘上的过程
活化sessionDidActivat:与钝化相反,就是将硬盘的数据恢复到内存中

package com.wuxinzhe.bean;
 
import java.io.Serializable;
 
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import javax.servlet.http.HttpSessionEvent;
 
/** 
 * 实现序列化接口
 *不需要在web.xml中注册的监听器
 *HttpSessionActivationListener,HttpSessionBindingListener
 **/
public class User implements Serializable, HttpSessionActivationListener,HttpSessionBindingListener {
 
	@Override
	public void sessionDidActivate(HttpSessionEvent arg0) {
		System.out.println(this + "活化");
	}
 
	@Override
	public void sessionWillPassivate(HttpSessionEvent arg0) {
		System.out.println(this + "钝化");
	}
 
	//检测这个对象绑定到session中
	@Override
	public void valueBound(HttpSessionBindingEvent arg0) {
 
	}
 
	//检测这个对象从session解绑(移除)
	@Override
	public void valueUnbound(HttpSessionBindingEvent arg0) {
 
	}
}

测试session中的对象钝化活化绑定解绑

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="com.wuxinzhe.bean.User"%>    
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<%
  User user = new User();
  session.setAttribute("user", user);
%>
</body>
</html>

监听器一些使用场景
ServletContextListener:监听服务器启动停止
服务器启动:以后推荐些绝对路径,可以初始化一个项目根路径
服务器停止:做一些善后清理工作
HttpSessionBindingListener
用户在线统计

发表评论

邮箱地址不会被公开。 必填项已用*标注