Spring框架

目录

Spring概述

一、Spring是一个开源框架
二、Spring为简化企业级开发而生,使用Spring开发可以将Bean对象,Dao组件对象,Service组件对象等交给Spring容器来管理,这样使得很多复杂的代码在Spring中开发却变得非常的优雅和简洁,有效的降低代码的耦合度,极大的方便项目的后期维护、升级和扩展。
三、Spring是一个IOC(DI)和AOP容器框架。
四、Spring的优良特性
[1]非侵入式:基于Spring开发的应用中的对象可以不依赖于Spring的API
[2]控制反转:IOC——Inversion of Control,指的是将对象的创建权交给Spring去创建。使用Spring之前,对象的创建都是由我们自己在代码中new创建。而使用Spring之后。对象的创建都是由给了Spring框架。
[3]依赖注入:DI——Dependency Injection,是指依赖的对象不需要手动调用setXX方法去设置,而是通过配置赋值。
[4]面向切面编程:Aspect Oriented Programming——AOP
[5]容器:Spring是一个容器,因为它包含并且管理应用对象的生命周期
[6]组件化:Spring实现了使用简单的组件配置组合成一个复杂的应用。在 Spring 中可以使用XML和Java注解组合这些对象。
[7]一站式:在IOC和AOP的基础上可以整合各种企业应用的开源框架和优秀的第三方类库(实际上Spring 自身也提供了表述层的SpringMVC和持久层的Spring JDBC)。

Spring的模块介绍

Spring框架分为四大模块:
Core核心模块,负责管理组件的Bean对象
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-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar

数据库操作
spring-jdbc-4.0.0.RELEASE.jar
spring-orm-4.0.0.RELEASE.jar
spring-oxm-4.0.0.RELEASE.jar
spring-tx-4.0.0.RELEASE.jar
spring-jms-4.0.0.RELEASE.jar

Web模块
spring-web-4.0.0.RELEASE.jar
spring-webmvc-4.0.0.RELEASE.jar
spring-websocket-4.0.0.RELEASE.jar
spring-webmvc-portlet-4.0.0.RELEASE.jar

IOC依赖注入

IOC(控制反转)

IOC	全称指的是 Inverse Of Control 控制反转。 
使用Spring之前,我们对Bean对象的管制,都是自己手动的去new Xxx()。
而使用了Spring模型之后,我们把new的操作。交给Spring容器。

DI(依赖注入)

DI 指的是Dependency	Injection 。是依赖注入的意思。
原来在使用Spring之前。
Class BookService{
private BookDao bookDao;	
public void setBookDao( BookDao bookDao ){
this.bookDao = bookDao
}	
}
使用了Spring之后。我们只需要使用xml配置,或者注解配置。就可以直接注入。

第一个IOC示例程序 – 通过id获取对象

测试:通过IOC容器创建对象,并为属性赋值★


1、创建一个Java工程:
2、创建log4j.properties日记配置文件

# Global logging configuration
log4j.rootLogger=INFO, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

3、创建Spring的配置文件
4、创建一个JavaBean对象
public class Person {
	private int id;
	private String name;
	private String phone;
	private int age;
}
5、到Spring配置文件中配置你的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标签用于配置一个组件对象(javaBean,Dao。Service)
			id	是给bean对象起一个唯一的标识
			class	bean对象的具体全类名
	 -->
	<bean id="p1" class="com.ddh.pojo.Person">
		<!-- property标签用来配置属性信息
				name 是属性名
				value 是属性值
		 -->
		<property name="id" value="1"/>
		<property name="name" value="华仔"/>
		<property name="age" value="18"/>
		<property name="phone" value="18699998888"/>
	</bean>
</beans>

6、从Spring容器中获取Bean对象
	@Test
	public void test1() throws Exception {
		// 创建一个Spring容器对象
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
				"application.xml");
		// getBean从Spring容器中获取指定的id值的 bean对象返回
		Person person = (Person) applicationContext.getBean("p1");
		System.out.println(person);
	}

常见的错误:
指定的id不存在。找不到bean对象。

IOC示例程序 – 通过类型获取对象(重要)

测试:根据bean的类型从IOC容器中获取bean的实例★

在application.xml中的配置:
	<!-- bean标签用于配置一个组件对象(javaBean,Dao。Service)
			id	是给bean对象起一个唯一的标识
			class	bean对象的具体全类名
	 -->
	<bean id="p1" class="com.ddh.pojo.Person">
		<!-- property标签用来配置属性信息
				name 是属性名
				value 是属性值
		 -->
		<property name="id" value="1"/>
		<property name="name" value="华仔"/>
		<property name="age" value="18"/>
		<property name="phone" value="18699998888"/>
	</bean>
	
	<bean id="p2" class="com.ddh.pojo.Person">
		<!-- property标签用来配置属性信息
				name 是属性名
				value 是属性值
		 -->
		<property name="id" value="1"/>
		<property name="name" value="华仔"/>
		<property name="age" value="18"/>
		<property name="phone" value="18699998888"/>
	</bean>


测试推荐:
	@Test
	public void test2() {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		Person person = applicationContext.getBean(Person.class);
		System.out.println(person);
	}

常见错误说明:
当在applicationContext.xml配置文件中。有多个同Person.class类型实现的时候。

IOC示例程序 – 通过构造方法参数名注入值

测试:通过构造器为bean的属性赋值
			
配置内容:
	<bean id="p3" class="com.ddh.pojo.Person">
<!-- 	public Person(int id, String name, String phone, int age)  -->
		<constructor-arg name="id" value="3" />
		<constructor-arg name="name" value="p3"/>
		<constructor-arg name="phone" value="13999998869"/>
		<constructor-arg name="age" value="18"/>
	</bean>

测试代码:
	@Test
	public void test3() {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		Person person = (Person) applicationContext.getBean("p3");
		System.out.println(person);
	}

IOC示例程序 – index属性指定参数的位置

测试:通过index属性指定参数的位置
xml中的配置如下:

	<bean id="p4" class="com.ddh.pojo.Person">
<!-- 	public Person(int id, String name, String phone, int age)  -->
		<!-- 
			constructor-arg	表示使用构造方法注入属性值
				name	表示构造方法的参数名
				index	表示构造方法的参数顺序从0开始
				value	表示要传入的构造方法的参数值
		 -->
		<constructor-arg value="4" index="0"/>
		<constructor-arg value="p4" index="1"/>
		<constructor-arg value="13999998869" index="2"/>
		<constructor-arg value="18" index="3"/>
	</bean>

测试的代码:	
	@Test
	public void test4() {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		Person person = (Person) applicationContext.getBean("p4");
		System.out.println(person);
	}

IOC示例程序 – 根据参数类型注入

测试:根据参数类型注入

xml中的配置:
	<bean id="p5" class="com.ddh.pojo.Person">
<!-- 	public Person(int id, String name, String phone, int age) -->
<!-- 	public Person(int id, String phone, int age, String name)  -->
		<constructor-arg value="5" index="0" type="int"/>
		<constructor-arg value="phone" index="1" type="java.lang.String"/>
		<constructor-arg value="10" index="2" type="java.lang.String"/>
		<constructor-arg value="20" index="3" type="int"/>
	</bean>

测试的代码:
	@Test
	public void test5() {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		Person person = (Person) applicationContext.getBean("p5");
		System.out.println(person);
	}

IOC之 P名称空间

测试:通过p名称空间为bean赋值
xml中的配置:

	<bean id="p6" class="com.ddh.pojo.Person" p:id="60" p:name="第六个" p:phone="6电话" p:age="66"/>

测试的代码:
	@Test
	public void test6() throws Exception {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		Person person = (Person) applicationContext.getBean("p6");
		System.out.println(person);
	}

IOC之子对象的赋值测试(重要)

引用其他bean★
创建个新的工程。测试Spring的开发环境。
创建新的Bean对象

public class Car {
	private int id;
	private String name;
}

public class Person {
	private int id;
	private String name;
	private String phone;
	private int age;
	private Car car;
}
在xml中的配置如下:
	<bean id="car1" class="com.ddh.pojo.Car">
		<property name="id" value="1"/>
		<property name="name" value="宝马"/>
	</bean>
	
	<bean id="car2" class="com.ddh.pojo.Car">
		<property name="id" value="2"/>
		<property name="name" value="马自达"/>
	</bean>
	
	<bean id="p7" class="com.ddh.pojo.Person">
		<property name="id" value="7"/>
		<property name="name" value="华仔7"/>
		<property name="age" value="187"/>
		<property name="phone" value="18679998888"/>
		<!-- name表示你要注入的哪个属性,ref 属性表示你要引用哪个对象 -->	
		<property name="car" ref="car1" />	
	</bean>

测试的代码:
	@Test
	public void test7() throws Exception {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		Person person = (Person) applicationContext.getBean("p7");
		System.out.println(person);
	}

IOC之内部Bean的使用

引用内部bean
xml中配置的内容如下:
	<bean id="p8" class="com.ddh.pojo.Person">
		<property name="id" value="8"/>
		<property name="name" value="华仔8"/>
		<property name="age" value="888"/>
		<property name="phone" value="18688888888"/>
		<!-- name表示你要注入的哪个属性,ref 属性表示你要引用哪个对象 -->	
		<property name="car">
			<!-- 内部的bean标签定义的对象。无法通过Spring容器获取。只能在内部赋值使用 -->
			<bean id="innerCar" class="com.ddh.pojo.Car" p:id="22" p:name="内部汽车"></bean>
		</property>	
	</bean>

测试的代码:	
	@Test
	public void test8() throws Exception {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		Person p8 = (Person) applicationContext.getBean("p8");
		System.out.println(p8);		
		System.out.println( applicationContext.getBean("innerCar") );
	}
常见错误:内部的Bean不能被外部使用

IOC之List属性的赋值

使用list子元素为List类型的属性赋值

public class Person {
	private int id;
	private String name;
	private String phone;
	private int age;
	private Car car;
	private List<Object> list;
}
xml中的配置:

	<bean id="p9" class="com.ddh.pojo.Person">
		<property name="id" value="8"/>
		<property name="name" value="华仔8"/>
		<property name="age" value="888"/>
		<property name="phone" value="18688888888"/>	
		<property name="list">
			<list>
				<value>list1</value>
				<value>list2</value>
				<value>list3</value>
			</list>
		</property>
	</bean>


测试的代码:
	@Test
	public void test9() throws Exception {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		Person p9 = (Person) applicationContext.getBean("p9");
		System.out.println(p9);
}

IOC之Map属性的赋值

使用map子元素为Map类型的属性赋值
public class Person {
	private int id;
	private String name;
	private String phone;
	private int age;
	private Car car;
	private List<Object> list;
	private Map<String, Object>map;
}

在xml中的配置如下:
	<bean id="p10" class="com.ddh.pojo.Person">
		<property name="name" value="这是第10个实验" />
		<property name="map">
			<map>
				<entry key="key1" value="value1" />
				<entry key="key2" value="value2" />
				<entry key="key3" value="value3" />
			</map>
		</property>
	</bean>

测试的代码:	
	@Test
	public void test10() throws Exception {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		Person person = (Person) applicationContext.getBean("p10");
		System.out.println(person);
	}

IOC之Properties属性的赋值

使用prop子元素为Properties类型的属性赋值
public class Person {
	private int id;
	private String name;
	private String phone;
	private int age;
	private Car car;
	private List<Object> list;
	private Map<String, Object> map;
	private Properties prop;
}

xml中的配置如下:
	<bean id="p11" class="com.ddh.pojo.Person">
		<property name="name" value="这是第10个实验" />
		<property name="prop">
			<props>
				<prop key="jdbc.url">jdbc:mysql://localhost:3306/test</prop>
				<prop key="jdbc.password">root</prop>
			</props>
		</property>
	</bean>

测试的代码:
	@Test
	public void test11() throws Exception {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		Person person = (Person) applicationContext.getBean("p11");
		System.out.println(person);
	}

IOC之util 名称空间

util名称空间,可以定义
实验13:通过util名称空间创建集合类型的bean
在xml中的配置:
	<!-- util:list定义一个list集合 -->
	<util:list id="list1">
		<list>
			<value>list1</value>
			<value>list2</value>
			<value>list3</value>
		</list>
	</util:list>
	
	<bean id="p12" class="com.ddh.pojo.Person">
		<property name="name" value="这是12个Person" />
		<property name="list" ref="list1" />
	</bean>

测试的代码:
	@Test
	public void test12() {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		Person person = (Person) applicationContext.getBean("p12");
		System.out.println(person);
		System.out.println( applicationContext.getBean("list1") );
	}

IOC之级联属性赋值

给bean的级联属性赋值
在xml中的配置:
	 <bean id="p13" class="com.ddh.pojo.Person">
		<property name="name" value="这是12个Person" />
		<!-- 先赋值 -->
		<property name="car" ref="car1"></property>
		<!-- 再级联修改属性 -->
		<property name="car.id" value="2" />
		<property name="car.name" value="级联属性赋值" />
	 </bean>

测试的代码:
	@Test
	public void test13() {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		Person person = (Person) applicationContext.getBean("p13");
		System.out.println(person);
	}
常见错误:级联属性一定要先注入对象。再注入对象的属性

IOC之静态工厂方法创建Bean

配置通过静态工厂方法创建的bean

1、创建一个工厂类
public class PersonFactory {
	public static Person createPerson() {
		Person person = new Person();
		person.setName("这是通过静态工厂方法创建的Person对象");
		return person;
	}	
}
2、到xml中去配置:
	<!-- 
		id		还是bean的唯一标识
		class	属性定义工厂类全类名
		factory-method	属性是调用哪个方法,的方法名
	 -->
	<bean id="p14" class="com.ddh.util.PersonFactory" factory-method="createPerson" />
	
测试的代码:	
	@Test
	public void test14() throws Exception {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		Person person = (Person) applicationContext.getBean("p14");
		System.out.println(person);
	}

IOC之工厂实例方法创建Bean

配置通过实例工厂方法创建的bean
public class PersonFactory {
	public static Person createPerson() {
		Person person = new Person();
		person.setName("这是通过静态工厂方法创建的Person对象");
		return person;
	}	
	public Person createPerson2() {
		Person person = new Person();
		person.setName("这是通过工厂实例对象创建的Person");
		return person;
	}	
}

在xml中的配置:
	<bean id="personFactory" class="com.ddh.util.PersonFactory" />
	<!-- 
		id是唯一的标识
		factory-bean	引用哪个工厂对象的实例
		factory-method	同时是调用哪个方法,的方法名
	 -->
<bean id="p15" factory-bean="personFactory" factory-method="createPerson2" />


测试的代码:
	@Test
	public void test15() throws Exception {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		Person person = (Person) applicationContext.getBean("p15");
		System.out.println(person);
	}

IOC之FactoryBean接口方式创建对象

配置FactoryBean接口创建Bean对象

1、实现FactoryBean接口

public class PersonFactoryBean implements FactoryBean<Person> {
	/**
	 * 返回创建的对象
	 */
	@Override
	public Person getObject() throws Exception {
		Person person = new Person();
		person.setName("这是FactoryBean接口的方式创建的");
		return person;
	}
	/**
	 * 返回创建的对象的类型
	 */
	@Override
	public Class<?> getObjectType() {
		return Person.class;
	}
	/**
	 * 是否是单例
	 */
	@Override
	public boolean isSingleton() {
		return true;
	}
}

3、在xml配置文件中的配置:
	<bean id="p16" class="com.ddh.util.PersonFactoryBean" />	
测试的代码:
	@Test
	public void test16() throws Exception {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		Person person = (Person) applicationContext.getBean("p16");
		System.out.println(person);
	}

IOC之继承Bean配置

通过继承实现bean配置信息的重用
xml配置文件中的内容:
	<!-- bean标签用于配置一个组件对象(javaBean,Dao。Service)
			id	是给bean对象起一个唯一的标识
			class	bean对象的具体全类名
	 -->
	<bean id="p1" class="com.ddh.pojo.Person">
		<!-- property标签用来配置属性信息
				name 是属性名
				value 是属性值
		 -->
		<property name="id" value="1"/>
		<property name="name" value="华仔"/>
		<property name="age" value="18"/>
		<property name="phone" value="18699998888"/>
	</bean>

	<!-- 
		parent 设置你继承哪个bean的信息
	 -->	
	<bean id="p17" class="com.ddh.pojo.Person" parent="p1">
		<property name="list">
			<list>
				<value>这是继承的1</value>
				<value>这是继承的2</value>
			</list>
		</property>
	</bean>

测试的代码:
	@Test
	public void test17() throws Exception {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		Person person = (Person) applicationContext.getBean("p17");
		System.out.println(person);
	}

IOC之abstract抽象Bean

通过abstract属性创建一个模板bean
xml中的配置:

	<!-- bean标签用于配置一个组件对象(javaBean,Dao。Service)
			id	是给bean对象起一个唯一的标识
			class	bean对象的具体全类名
			
			表示当前bean标签所配置的bean对象,不能实例化
	 -->
	<bean id="p1" class="com.ddh.pojo.Person" abstract="true">
		<!-- property标签用来配置属性信息
				name 是属性名
				value 是属性值
		 -->
		<property name="id" value="1"/>
		<property name="name" value="华仔"/>
		<property name="age" value="18"/>
		<property name="phone" value="18699998888"/>
	</bean>

如果配置的bean标签对象,设置了属性abstract="true",那么此标签中的对象不能实例化

IOC之组件创建顺序

bean之间的依赖  depends-on 属性

<?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">

	<!-- depends-on="b" 如果要创建a对象,就要有b对象
			也就是要先创建b再创建a
	 -->
	<bean id="a" class="com.ddh.pojo.A" depends-on="b"></bean>	
	<bean id="b" class="com.ddh.pojo.B"></bean>
	<bean id="c" class="com.ddh.pojo.C"></bean>

</beans>


测试的代码:
	@Test
	public void test20() throws Exception {
		// 默认情况下。在Application.xml中配置的bean对象。默认在创建Spring容器的时候都会创建,
		// 而且创建的顺序是在配置文件中从上到下的顺序。
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application2.xml");	
	}

IOC之Bean的单例和多例(重要)

测试bean的作用域,分别创建单实例和多实例的bean★

 	<!-- 
 		scope 属性设置范围
 			singleton		默认情况	是singleton,表示Spring容器中只有一个单例
 							单例是在Spring容器创建的时候。初始化所有单例Bean对象
 							并且每次调用Bean对象的时候,原来原来Spring容器中的对象
 							
 			prototype				prototype是表示当前配置的Bean对象是多例。
 									在Spring容器被创建的时候,bean不会被创建出来。
 									并且每次调用getBean方法都会创建一个对象实例
 									
 			request				表示一次请求内,不管调用几次getBean方法,返回的都是同一个bean对象
 								Object bean = request.getAttribute(xxx);
 								if (bean == null) {
 									bean = 创建一个
 									request.setAttribute(xxx,bean);
 								}
 			session				表示一个Session对象内,不管调用几次getBean方法,返回的都同一个Bean对象
  								Object bean = session.getAttribute(xxx);
 								if (bean == null) {
 									bean = 创建一个
 									session.setAttribute(xxx,bean);
 								}
 	 -->
	<bean id="p21" class="com.ddh.pojo.Person" scope="prototype">
		<property name="id" value="1"/>
		<property name="name" value="华仔"/>
		<property name="age" value="18"/>
		<property name="phone" value="18699998888"/>
	</bean>

基于xml配置文件的自动注入

先创建Person类和Car类
public class Car {
private int id;
	private String name;

public class Person {

	private Car car;

	public Person(Car car) {
		this.car = car;
	}


xml中的配置如下:

	 <bean id="car" class="com.ddh.pojo.Car">
	 	<property name="id" value="1" />
	 	<property name="name" value="宝马1" />
	 </bean>
	 
	 <bean id="car1" class="com.ddh.pojo.Car">
	 	<property name="id" value="2" />
	 	<property name="name" value="宝马2" />
	 </bean>
	 
	 <!-- 
	 	autowire	设置自动装配的方式
	 		default	和 no一样,都表示不装配,如果对象不你手动设置,就没值。	 		
	 		byName		表示Spring容器会自动按照子对象的属性名,当成是id来查找对象。
	 						找到就注入,找不到就为null
	 			举例:private Car car;
	 			就会以car做为id去spring容器中去查找对象。找到就把值注入给属性car。
	 			
	 		byType			表示Spring容器会自动的按照子对象的类型去查找bean对象注入。
	 			举例:private Car car;
	 			Spring容器就会自动的按照Car.class类型去Spring容器中查找。
	 			如果说,找到一个,就注入
	 			如果没有找到,值就为null
	 			如果说,找到多个,就报错。
	 			
	 		constructor		表示Spring容器会按照子对象的类型去查找构造方法,中参数需要的类型去注入。
	 					先按照类型查询,如果找到一个就注入。
	 					如果找到多个,再按照构造方法中参数的变量名做为id来查找,如果找到就注入。如果没有找到,就为null
	 					如果没有找到,值也为null
	  -->
	 <bean id="p22" class="com.ddh.pojo.Person" autowire="constructor">
		<property name="id" value="1"/>
		<property name="name" value="华仔"/>
		<property name="age" value="18"/>
		<property name="phone" value="18699998888"/>	
	 </bean>

对象的生命周期

IOC之Bean的生命周期

创建带有生命周期方法的bean
给A类添加生命周期方法:
public class A {
	public A() {
		System.out.println("这是A对象被创建了");
	}
	public void initA() {
		System.out.println("这里是初始化A的方法");
	}	
	public void destroyA() {
		System.out.println("这里是销毁A的方法");
	}
}

在xml配置文件中配置如下:

	 <!-- 
	 	init-method="initA" 		设置初始化方法
	 	destroy-method="destroyA"	设置销毁的方法
	  -->
	 <bean id="a" class="com.ddh.pojo.A" init-method="initA" destroy-method="destroyA"></bean>

测试的代码:
	@Test
	public void test23() {
		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application2.xml");
		applicationContext.close();
	}

Bean的后置处理器BeanPostProcessor

测试bean的后置处理器
创建一个AI接口
public interface AI {
	public void show();	
}

修改A类实现AI接口
public class A implements AI {
	public A() {
		System.out.println("这是A对象被创建了");
	}
	public void initA() {
		System.out.println("这里是初始化A的方法");
	}
	public void destroyA() {
		System.out.println("这里是销毁A的方法");
	}
	@Override
	public void show() {
		System.out.println("--这是目标A对象的show方法--");
	}
}


1、创建一个类去实现后置处理器的接口
public class MyBeanPostProcessor implements BeanPostProcessor {
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
		System.out.println("这是初始化之前: bean->[" + bean + "] , beanName ->["
				+ beanName + "]");

		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName)
			throws BeansException {
		System.out.println("这是初始化之后: bean->[" + bean + "] , beanName ->["
				+ beanName + "]");

		if ("a".equals(beanName)) {
			// 创建一个jdk动态代理
			AI proxyAi = (AI) Proxy.newProxyInstance(bean.getClass()
					.getClassLoader(), bean.getClass().getInterfaces(),
					new InvocationHandler() {

						@Override
						public Object invoke(Object proxy, Method method,
								Object[] args) throws Throwable {
							System.out.println("这是前置增强代码");
							Object result = method.invoke(bean, args);
							System.out.println("这是后置增强代码");
							return result;
						}
					});
			return proxyAi;
		} else {
			return bean;
		}
		// return bean;
	}
}
2、到Spring的配置文件中去配置后置处理器
	 <!-- 
	 	init-method="initA" 		设置初始化方法
	 	destroy-method="destroyA"	设置销毁的方法
	  -->
	<bean id="a" class="com.ddh.pojo.A" init-method="initA" destroy-method="destroyA"></bean>
	<!-- 配置Bean的后配置处理器。 -->
	<bean class="com.ddh.util.MyBeanPostProcessor" />
测试的代码:
	@Test
	public void test24() {
		@SuppressWarnings("resource")
		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(
				"application2.xml");
		AI ai = (AI) applicationContext.getBean("a");
		ai.show();
	}

6、Spring管理数据库连接池(重要)

再次搭建Spring的开发环境:
1、创建一个Java工程 
2、导入Spring需要的jar包
commons-logging-1.1.3.jar
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
3、还有日记包
log4j-1.2.17.jar
4、log4j.properties属性配置文件
# Global logging configuration
log4j.rootLogger=INFO, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
5、还需要导入数据库驱动包以及数据库连接池
c3p0-0.9.1.2.jar
mysql-connector-java-5.1.37-bin.jar
6、创建Spring的配置文件application.xml

Spring引入单独的jdbc.properties配置文件(重点)

1、抽取四个jdbc连接属性到jdbc.properties属性配置文件中
jdbc.username=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/ddh
jdbc.driverClassName=com.mysql.jdbc.Driver

2、使用PropertyPlaceholderConfigurer 来加载jdbc.properties属性配置文件
	<!-- 它可以加载jdbc.properties属性配置文件 -->
	<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<!-- location 属性是你要加载的jdbc.properties属性配置文件的路径 -->
		<property name="location" value="classpath:jdbc.properties" />
	</bean>

3、使用加载后的jdbc.properties属性配置文件中的连接属性。
	<!-- 配置一个数据库连接池对象 -->
	<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
		<property name="url " value="${jdbc.url}" />
		<property name="username" value="${jdbc.user}" />
		<property name="password" value="${jdbc.password}" />
		<property name="driverClassName" value="${jdbc.driverClass}" />
	</bean>

使用context名称空间加载jdbc.properties配置文件(重点)

<!-- 也可以加载classpath路径下的jdbc.properties属性配置文件 -->
	<context:property-placeholder location="classpath:jdbc.properties"/>

Spring EL表达式

创建java实体Bean对象
public class Person {
	private int id;
	private String name;
	private String phone;
	private double salary;
	private Car car;
}
public class Car {
	private String name;
	private String carNo;
}

实验26[SpEL测试I]在SpEL中使用字面量
使用格式:#{数值} 		#{“字符串” || ‘字符串’}
实验27[SpEL测试II]在SpEL中引用其他bean
使用格式:#{bean的id}
实验28[SpEL测试III]在SpEL中引用其他bean的某个属性值
使用格式: #{bean.属性名}
实验29[SpEL测试IV]在SpEL中调用非静态方法
使用格式: #{bean.方法名(参数)}
实验30[SpEL测试V]在SpEL中调用静态方法
使用格式:#{T(全名类).方法名(参数)}
实验31[SpEL测试VI]在SpEL中使用运算符
使用格式:#{表达式}

	<bean id="car" class="com.ddh.pojo.Car">
		<property name="name" value="宝马" />
		<property name="carNo" value="京B123421" />
	</bean>
	
	<bean id="personEL" class="com.ddh.pojo.Person">
		<!-- 实验26[SpEL测试I]在SpEL中使用字面量 -->
		<!-- 使用格式:#{数值} 		#{“字符串” || ‘字符串’} -->
<!-- 		<property name="name" value="#{'这是EL表达式常量值'}" /> -->
		<!-- 实验27[SpEL测试II]在SpEL中引用其他bean -->
		<!-- 使用格式:#{bean的id} -->
		<property name="car" value="#{car}"/>
		<!-- 实验28[SpEL测试III]在SpEL中引用其他bean的某个属性值 -->
		<!-- 使用格式: #{bean.属性名} -->
		<property name="phone" value="#{car.name}" />
		<!-- 实验29[SpEL测试IV]在SpEL中调用非静态方法 -->
		<!-- 使用格式: #{bean.方法名(参数)} -->
<!-- 		<property name="name" value="#{car.fun1()}"/> -->
		<!-- 实验30[SpEL测试V]在SpEL中调用静态方法 -->
		<!-- 使用格式:#{T(全名类).方法名(参数)} -->
		<property name="name" value="#{T(com.ddh.pojo.Car).staticFun()}" />
		<!-- 实验31[SpEL测试VI]在SpEL中使用运算符 -->
		<!-- 使用格式:#{表达式}	 -->
		<property name="salary" value="#{10*1024}"/>
	</bean>

注解配置Dao、Service、Controller组件

当我们使用Spring的注解功能的时候。需要把aop的jar包导入
通过注解分别创建Dao、Service、Controller★
Spring配置bean的常用注解有
@Controller	 			专门标注给web层的组件
@Service					专门给Service层的组件注解
@Repository				给Dao层组件标注
@Component				给Dao、Service、控制器Web层之外的组件进行标注。
@Scope					可以修改bean的Scope属性,默认不标注此注解表示单例。
也可以通过注解修改为多例@Scope(value="prototype")

注解在类上的使用:

/**
 * @Repository注解的功能相当于在Spring配置文件中做了如下的配置:
 * <bean id="bookDao" class="com.ddh.dao.BookDao" scope="singleton"></bean>
 */
@Scope(value="prototype")
@Repository(value="bookDao")
public class BookDao {
	public BookDao() {
		System.out.println("BookDao也被初始化了");
	}
}

当我们在类上使用了注解之后。一定要在Spring配置文件中加上包扫描的配置才能生效
	<!-- context:component-scan 表示包扫描
			base-package	指定要扫描哪些包下的类(并且包含子包)
	 -->	
	<context:component-scan base-package="com.ddh"></context:component-scan>


测试代码:
	@Test
	public void test1() throws Exception {
		@SuppressWarnings("resource")
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		System.out.println( applicationContext.getBean("bookDao") );
		System.out.println( applicationContext.getBean("bookDao") );
		System.out.println( applicationContext.getBean("bookDao") );
		System.out.println( applicationContext.getBean("bookService") );
		System.out.println( applicationContext.getBean("bookServlet") );
		System.out.println( applicationContext.getBean("person") );
	}

指定扫描包时的过滤内容(重要)

使用context:include-filter指定扫描包时要包含的类
使用context:exclude-filter指定扫描包时不包含的类

	<context:include-filter />	设置包含的内容
注意:通常需要与use-default-filters属性配合使用才能够达到“仅包含某些组件”这样的效果。即:通过将use-default-filters属性设置为false<context:exclude-filter />	设置排除的内容

类别	示例	说明
annotation	com.ddh.XxxAnnotation	过滤所有标注了XxxAnnotation的类。这个规则根据目标组件是否标注了指定类型的注解进行过滤
assignable	com.ddh.BaseXxx	过滤所有BaseXxx类的子类。这个规则根据目标组件是否是指定类型的子类的方式进行过滤。
aspectj	com.ddh.*Service+	所有类名是以Service结束的,或这样的类的子类。这个规则根据AspectJ表达式进行过滤。
regex	com\.ddh\.anno\.*	所有com.ddh.anno包下的类。这个规则根据正则表达式匹配到的类名进行过滤。
custom	com.ddh.XxxTypeFilter	使用XxxTypeFilter类通过编码的方式自定义过滤规则。该类必须实现org.springframework.core.type.filter.TypeFilter接口

applicationContext.xml	中配置的内容如下
 	<!-- use-default-filters="false" 设置取消默认包含规则 -->
	<context:component-scan base-package="com.ddh" use-default-filters="false">
		<!-- context:include-filter 设置包含的内容 -->
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
		<!-- context:exclude-filter 设置排除的内容 -->
		<context:exclude-filter type="assignable" expression="com.ddh.service.BookService"/>
	</context:component-scan>
以上配置会包含所有@Service注解的类。排除com.ddh.service.BookService类

使用注解@Autowired自动装配

使用@Autowired注解实现根据类型实现自动装配★
@Autowired 注解 会自动的根据标注的对象类型在Spring容器中查找相对应的类。找到就自动装配。
使用@Autowired注解,不需要get/set方法

@Repository
public class BookDao {
	public BookDao() {
		System.out.println("BookDao也被初始化了");
	}
}

@Service("bookService")
public class BookService {
	public BookService() {
		System.out.println("bookService被初始化了");
	}
	/**
	 * 使用@Autowired注解实现根据类型实现自动装配★<br/>
	 * 1、Spring容器会自动的根据对象的类型到Spring容器中去查找,然后注入值。
	 * 		1、如果找到一个,就直接注入值<br/>
	 */
	@Autowired
	private BookDao bookDao;
	@Override
	public String toString() {
		return "BookService [bookDao=" + bookDao + "]";
	}
}

多个同类型的bean如何自动装配

如果资源类型的bean不止一个,默认根据@Autowired注解标记的成员变量名作为id查找bean,进行装配
@Repository
public class BookDao {
	public BookDao() {
		System.out.println("BookDao也被初始化了");
	}
}
@Repository
public class BookDaoExt extends BookDao{
	public BookDaoExt() {
		System.out.println("BookDaoExt也被初始化了");
	}
}
@Service("bookService")
public class BookService {
	public BookService() {
		System.out.println("bookService被初始化了");
	}

	/**
	 * 实验35:使用@Autowired注解实现根据类型实现自动装配★<br/>
	 * 1、Spring容器会自动的根据对象的类型到Spring容器中去查找,然后注入值。
	 * 		1、如果找到一个,就直接注入值<br/>
	 * 		2、如果找到多个,Spring容器会自动按照@Autowired标注的变量做为id来查找
	 * 			2.1、查找就注入
	 * 			2.2、找不到就报错。
	 */
	@Autowired
	private BookDao bookDao;

	@Override
	public String toString() {
		return "BookService [bookDao=" + bookDao + "]";
	}
}

使用@Qualifier装配指定id的bean对象

如果根据成员变量名作为id还是找不到bean,可以使用@Qualifier注解明确指定目标bean的id★


@Repository(value="aaa")
public class BookDao {
	public BookDao() {
		System.out.println("BookDao也被初始化了");
	}
}

@Repository(value="bbb")
public class BookDaoExt extends BookDao{
	public BookDaoExt() {
		System.out.println("BookDaoExt也被初始化了");
	}
}



	/**
	 * 实验35:使用@Autowired注解实现根据类型实现自动装配★<br/>
	 * 1、Spring容器会自动的根据对象的类型到Spring容器中去查找,然后注入值。
	 * 		1、如果找到一个,就直接注入值<br/>
	 * 		2、如果找到多个,Spring容器会自动按照@Autowired标注的变量做为id来查找
	 * 			2.1、查找就注入
	 * 			2.2、找不到就报错。
	 * 		
	 */
	@Autowired
	/**
	 * @Qualifier 这个注解,可以给你标注的组件。按照给定的id去spring容器中查找,然后注入。
	 * 这个注解会改变默认规则,默认是按照变量名,做为id来查找。但是,当使用了。这个注解之后。就会按照给定的id来查找。
	 * 		如果找到指定id的bean对象,就注入。如果找不到就报错
	 */
	@Qualifier("bbb")
	private BookDao bookDao;

@Autowired注解的required属性作用

@Autowired注解的required属性作用
实验39@Autowired注解的required属性指定某个属性允许不被设置


	/**
	 * 实验35:使用@Autowired注解实现根据类型实现自动装配★<br/>
	 * 1、Spring容器会自动的根据对象的类型到Spring容器中去查找,然后注入值。
	 * 		1、如果找到一个,就直接注入值<br/>
	 * 		2、如果找到多个,Spring容器会自动按照@Autowired标注的变量做为id来查找
	 * 			2.1、查找就注入
	 * 			2.2、找不到就报错。
	 * 		3、@Autowired注解中有一个属性,叫required是必须,必要的意思,默认值是true,
	 * 		表示被标注的bean对象。必须要有值注入。如果找不到注入。就报错。
	 * 		如果找不到,不希望它报错。就把此值改为false。则此对象的值可以为null。
	 * 		
	 */
	@Autowired(required=false)
	/**
	 * @Qualifier 这个注解,可以给你标注的组件。按照给定的id去spring容器中查找,然后注入。
	 * 这个注解会改变默认规则,默认是按照变量名,做为id来查找。但是,当使用了。这个注解之后。就会按照给定的id来查找。
	 * 		如果找到指定id的bean对象,就注入。如果找不到就报错
	 */
	@Qualifier("ccc")
private BookDao bookDao;


@Autowired和@Qualifier在方法上的使用

在方法的形参位置使用@Qualifier注解



	private BookDao bookDaoExt;

	/**
	 * @Autowired 注解标注的方法和组件都是初始化的时候,就已经调用和注入了。<br/>
	 */
	@Autowired
	public void abc(@Qualifier(value = "aaa") BookDao bookDao) {
		System.out.println("这是被标注了@Autowired注解的方法…………");
		System.out.println(bookDao);
		this.bookDaoExt = bookDao;
	}

泛型注入

测试泛型依赖注入★
所有的Dao代码:
public abstract class BaseDao<T> {
	public abstract void save(T entity);
}
@Repository
public class BookDao extends BaseDao<Book> {
	@Override
	public void save(Book entity) {
		System.out.println("这是BookDao,保存一个Book --->>>" + entity);
	}
}

@Repository
public class UserDao extends BaseDao<User> {
	@Override
	public void save(User entity) {
		System.out.println("这是UserDao,保存一个User --->>>" + entity);
	}
}
所有的Service代码:
public abstract class BaseService<T> {
	@Autowired
	protected BaseDao<T> dao; 
	public void save(T entity) {
		dao.save(entity);
	}
}
@Service
public class BookService extends BaseService<Book> {
}
@Service
public class UserService extends BaseService<User> {
}

Spring的配置文件内容:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

	<!-- 扫描全部的包 -->
	<context:component-scan base-package="com.ddh"></context:component-scan>
</beans>

测试的代码:
public class SpringTest {
	@Test
	public void test1() {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		BookService bookService = (BookService) applicationContext.getBean("bookService");
		bookService.save(new Book());
		UserService userService = (UserService) applicationContext.getBean("userService");
		userService.save(new User());
	}	
}

Spring的专有测试

先导入spring-test-4.0.0.RELEASE.jar包 

@ContextConfiguration(locations = "classpath:application.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringJunit {
	@Autowired
	private BookService bookService;
	@Autowired
	private UserService userService;	
	@Autowired
	private UserDao userDao;
	
	@Test
	public void test1() {
		bookService.save(new Book());
		userService.save(new User());
		System.out.println(userDao);
	}

}

AOP切面编程

什么是AOP

AOP是面向切面编程。全称:Aspect Oriented Programming	
面向切面编程指的是:程序是运行期间,动态地将某段代码插入到原来方法代码的某些位置中。这就叫面向切面编程。

一个简单计算数功能加日记

准备计算器相关类
计算接口
public interface Calculate {
	public int add(int num1, int num2);
	public int mul(int num1, int num2);
	public int div(int num1, int num2);
	public int sub(int num1, int num2);
}

计算机类
public class Calculator implements Calculate {
	public int add(int num1, int num2) {
		System.out.println("日记 :【add】 方法调用前 。参数1是:" + num1 + " , 参数2是:" + num2);
		return num1 + num2;
	}
	public int mul(int num1, int num2) {
		System.out.println("日记 :【mul】 方法调用前 。参数1是:" + num1 + " , 参数2是:" + num2);
		return num1 * num2;

	}
	public int div(int num1, int num2) {
		System.out.println("日记 :【div】 方法调用前 。参数1是:" + num1 + " , 参数2是:" + num2);
		return num1 / num2;
	}
	public int sub(int num1, int num2) {
		System.out.println("日记 :【sub】 方法调用前 。参数1是:" + num1 + " , 参数2是:" + num2);
		return num1 - num2;
	}
}

测试的代码
public class CalculatorTest {
	public static void main(String[] args) {
		Calculate calculate = new Calculator();
		int result = calculate.add(12, 12);
		System.out.println("相加的结果:" + result);
		
		result = calculate.mul(12, 12);
		System.out.println("相乘的结果:" + result);
	}
}

上面这种方法加日记处理操作。日记的代码就会耦合到业务代码中。而且后期如果需要修改日记就需要去指的修改所有方法中的日记操作。这个维护操作非常不方便。
可以说是一个很失败的例子。

原始方法统一日记处理

把日记的内容封装到一个类去中集中处理。

编写一个日记处理工具类
public class LogUtil {
	public static void log(String method, int num1, int num2) {
		System.out.println("日记 :【" + method + "】 方法调用前 。参数1是:" + num1 + " , 参数2是:" + num2);
	}
}

修改原来Calculator中的日记代码
	@Override
	public int add(int num1, int num2) {
		LogUtil.log("add", num1, num2);
		return num1 + num2;
	}
	@Override
	public int mul(int num1, int num2) {
		LogUtil.log("mul", num1, num2);
		return num1 * num2;
	}

但是这种方式的不足之处是,每有一个需要加日记的类,都需要到类的代码中去添加日记功能代码。
无法做到所有对象都统一处理。

使用代理实现日记

使用jdk动态代理统一日记

创建一个计算器代理工具类
public class CalculateProxyFactory {

	public static Calculate getProxy(Calculate target) {
		// 定义一个计算器拦截处理类
		class CalculateInvocationHandler implements InvocationHandler {
			Calculate target;

			public CalculateInvocationHandler(Calculate calculate) {
				this.target = calculate;
			}

			/**
			 * proxy	代理对象
			 * method	调用的方法
			 * args		方法的参数
			 */
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				if (method.getDeclaringClass().equals(Object.class)) {
					return method.invoke(target, args);
				}
				LogUtil.log(method.getName(), (int) args[0], (int) args[1]);
				Object result = null;
				try {
					result = method.invoke(target, args);
					System.out.println("后置日记");
				} catch (Exception e) {
					System.out.println("异常日记");
				} finally {
					System.out.println("finally日记");
				}
				return result;
			}
		}
		return (Calculate) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass()
				.getInterfaces(), new CalculateInvocationHandler(target));
	}
}

修改原来计算器中的日记代码
public class Calculator implements Calculate {
	@Override
	public int add(int num1, int num2) {
		return num1 + num2;
	}

	@Override
	public int mul(int num1, int num2) {
		return num1 * num2;
	}

	@Override
	public int div(int num1, int num2) {
		return num1 / num2;
	}

	@Override
	public int sub(int num1, int num2) {
		return num1 - num2;
	}
}

测试代码调整
	public static void main(String[] args) {
		Calculate calculate = CalculateProxyFactory.getProxy( new Calculator() );
		int result = calculate.add(12, 12);
		System.out.println("相加的结果:" + result);
		
		result = calculate.mul(12, 12);
		System.out.println("相乘的结果:" + result);
	}

优点:这种方式已经解决我们前面所有日记需要的问题。非常的灵活。而且可以方便的在后期进行维护和升级。
缺点:当然使用jdk动态代理,需要有接口。如果没有接口。就无法使用jdk动态代理。

使用cglib代理

public class CGLibProxyFactory implements MethodInterceptor {

	public static Object getCGLibProxy(Object target, Callback callback) {
		// 创建一个CGLig生成器
		Enhancer enhancer = new Enhancer();
		// 设置父类。因为cglib是通过类,进行代码,不是通过接口
		enhancer.setSuperclass(target.getClass());
		// 设置拦截的代理方法
		enhancer.setCallback(callback);
		// create 方法创建一个代理对象并返回
		return enhancer.create();
	}

	@Override
	public Object intercept(Object proxy, Method method, Object[] params, MethodProxy methodProxy)
			throws Throwable {
		LogUtil.log(method.getName(), (int) params[0], (int) params[1]);
		// 调用实际的对象的方法
		// 一定要使用methodProxy对象
		// 第一个参数是proxy代码对象的父类方法
		Object result = methodProxy.invokeSuper(proxy, params);
		System.out.println("这是后置代码");

		return result;
	}

	public static void main(String[] args) {
		Calculator calculator = (Calculator) CGLibProxyFactory.getCGLibProxy(new Calculator(),
				new CGLibProxyFactory());
		calculator.add(12, 13);
	}
}

优点:在没有接口的情况下,同样可以实现代理的效果。
缺点:同样需要自己编码实现代理全部过程。
但是为了更好的整合Spring框架使用。所以我们需要学习一下Spring 的AOP 功能。也就是学习Spring提供的AOP功能。

AOP编程的专业术语

通知(Advice)
通知就是增强的代码。比如前置增强的代码。后置增强的代码。异常增强代码。返回结果通知代码。这些就叫通知
切面(Aspect)
切面就是包含有通知代码的类叫切面。

横切关注点
横切关注点,就是我们可以添加增强代码的位置。比如前置位置,后置位置,异常位置。和返回值位置。这些都叫横切关注点。
目标(Target)
目标对象就是被关注的对象。或者被代理的对象。

代理(Proxy)
为了拦截目标对象方法,而被创建出来的那个对象,就叫做代理对象。	

连接点(Joinpoint)
连接点指的是横切关注点和程序代码的连接,叫连接点。

切入点(pointcut)
切入点指的是用户真正处理的连接点,叫切入点。

在Spring中切入点通过org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。

使用Spring实现AOP简单切面编程

需要导入工程的jar包
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的测试包
spring-test-4.0.0.RELEASE.jar

Spring日记相关包
commons-logging-1.1.3.jar
log4j-1.2.17.jar

Spring的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


需要有的类
public interface Calculate {

	public int add(int num1, int num2);

	public int mul(int num1, int num2);

	public int div(int num1, int num2);

	public int sub(int num1, int num2);
}

@Component
public class Calculator implements Calculate {
	@Override
	public int add(int num1, int num2) {
		return num1 + num2;
	}

	@Override
	public int mul(int num1, int num2) {
		return num1 * num2;
	}

	@Override
	public int div(int num1, int num2) {
		return num1 / num2;
	}

	@Override
	public int sub(int num1, int num2) {
		return num1 - num2;
	}
}

@Aspect
@Component
public class LogUtil {
	@Before(value = "execution(public int com.ddh.aop.Calculator.add(int, int))")
	public static void logBefore() {
		System.out.println("前置 日记 :【xxx】 方法调用前 。参数1是:xxxx");
	}

	@After(value = "execution(public int com.ddh.aop.Calculator.add(int, int))")
	public static void logAfter() {
		System.out.println("后置 日记 :【xxxx】 方法调用前 。参数1是:xxxx");
	}

	@AfterReturning(value = "execution(public int com.ddh.aop.Calculator.add(int, int))")
	public static void logAfterReturn() {
		System.out.println("返回之后: 日记 :【xxxxx】 方法调用前 。参数1是:xxxxxx");
	}

	@AfterThrowing(value = "execution(public int com.ddh.aop.Calculator.add(int, int))")
	public static void logThrowException() {
		System.out.println("抛异常:日记 :【xxxxx】 方法调用前 。参数1是:xxxxxx");
	}

}

applicationContext.xml配置文件中的内容
	<context:component-scan base-package="com.ddh" />
	<aop:aspectj-autoproxy />

测试代码
@ContextConfiguration(locations = "classpath:applicationContext.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringAopTest {
	@Autowired
	private Calculate calculate;
	@Test
	public void test1() {
		System.out.println( "添加:" + calculate.add(1, 2));
	}
}

Spring的切入点表达式

@PointCut切入点表达式语法格式是:	execution(访问权限 返回值类型 方法全限定名(参数类型列表))
限定符:
*1)	匹配某全类名下,任意或多个方法。
表示匹配com.ddh.aop.Calculator下以a打头的任意方法。并且返回值和两个参数都是int类型。
execution(public int com.ddh.aop.Calculator.a*(int, int))

表示匹配com.ddh.aop.Calculator下的任意方法。并且返回值和两个参数都是int类型。
execution(public int com.ddh.aop.Calculator.*(int, int))

2)	在Spring中只有public权限能拦截到,访问权限可以省略(访问权限不能写*)。
// 权限省略,表示任意类型的访问权限 ,但Spring现在只支持public权限 
execution(int com.ddh.aop.Calculator.*(int, int))

3)	匹配任意类型的返回值,可以使用 * 表示
// 表示任意类型的返回值
execution(* com.ddh.aop.Calculator.*(int, int))

4)	匹配任意子包。
// 表示匹配com的子包
execution(* com.*.aop.Calculator.*(int, int))

5)	任意类型参数
// 表示第二个参数是任意类型
execution(* com.ddh.aop.Calculator.*(int,*))

..:可以匹配多层路径,或任意多个任意类型参数
// 表示com和aop之间可以有任意层级的包
execution(* com..aop.Calculator.*(int,int))
// 表示第一个参数是int。之后可以有任意个任意类型的参数
execution(* com.ddh.aop.Calculator.*(int,..))
模糊匹配:
// 表示任意返回值,任意方法全限定符,任意参数
execution(* *(..))
// 表示任意返回值,任意包名+任意方法名,任意参数
execution(* *.*(..))

精确匹配:
// int 返回值,com.ddh.aop.Calculator类的add方法,两个int参数
execution(public int com.ddh.aop.Calculator.add(int, int))

切入点表达式连接:&&|| 
// 表示需要同时满足两个表达式
	@Before("execution(public int com.ddh.aop.Calculator.add(int, int))"
			+ " && "
+ "execution(public * com.ddh.aop.Calculator.add(..))")

// 表示两个条件只需要满足一个,就会被匹配到
	@Before("execution(public int com.ddh.aop.Calculator.add(int, int))"
			+ " || "
			+ "execution(public * com.ddh.aop.Calculator.a*(int))")
##  Spring通知的执行顺序

```java
Spring通知的执行顺序是:
正常情况:
前置通知====>>>>后置通知=====>>>>返回值之后
异常情况:
前置通知====>>>>后置通知=====>>>>抛异常通知

测试需要的类
@Component
public class Calculator {// implements Calculate {

	public int div(int num1, int num2) {
		return num1 / num2;
	}

	public int add(int num1, int num2) {
		return num1 + num2;
	}

切面的类对象
@Aspect
@Component
public class LogUtil {

// 每个通知,都是同时监听加和除两个方法
	@Before("execution(public int com.ddh.aop.Calculator.add(int, int))" + " || "
			+ "execution(public * com.ddh.aop.Calculator.d*(..))")
	public static void logBefore() {
		System.out.println("前置 日记 :【xxx】 方法调用前 。参数1是:xxxx");
	}

	@After("execution(public int com.ddh.aop.Calculator.add(int, int))" + " || "
			+ "execution(public * com.ddh.aop.Calculator.d*(..))")
	public static void logAfter() {
		System.out.println("后置 日记 :【xxxx】 方法调用前 。参数1是:xxxx");
	}

	@AfterReturning("execution(public int com.ddh.aop.Calculator.add(int, int))" + " || "
			+ "execution(public * com.ddh.aop.Calculator.d*(..))")
	public static void logAfterReturn() {
		System.out.println("返回之后: 日记 :【xxxxx】 方法调用前 。参数1是:xxxxxx");
	}

	@AfterThrowing("execution(public int com.ddh.aop.Calculator.add(int, int))" + " || "
			+ "execution(public * com.ddh.aop.Calculator.d*(..))")
	public static void logThrowException() {
		System.out.println("抛异常:日记 :【xxxxx】 方法调用前 。参数1是:xxxxxx");
	}
}

测试的代码
@ContextConfiguration(locations = "classpath:applicationContext.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringAopTest {
	@Autowired
	private Calculator calculator;

	@Test
	public void test1() {
		//加法
		calculator.add(1, 2);
		System.out.println("===============================");
		//减法
		calculator.div(1, 0);
	}
}

获取连接点信息

JoinPoint 是连接点的信息。
只需要在通知方法的参数中,加入一个JoinPoint参数。就可以获取到拦截方法的信息。

注意:是org.aspectj.lang.JoinPoint这个类。

@Aspect
@Component
public class LogUtil {
	@Before("execution(public int com.ddh.aop.Calculator.add(int, int))" + " || "
			+ "execution(public * com.ddh.aop.Calculator.d*(..))")
	public static void logBefore(JoinPoint joinPoint) {
		System.out.println("前置 日记 :【" + joinPoint.getSignature().getName() + "】 方法调用前 。参数是:"
				+ Arrays.asList(joinPoint.getArgs()));
	}

	@After("execution(public int com.ddh.aop.Calculator.add(int, int))" + " || "
			+ "execution(public * com.ddh.aop.Calculator.d*(..))")
	public static void logAfter(JoinPoint joinPoint) {
		System.out.println("后置 日记 :【" + joinPoint.getSignature().getName() + "】 方法调用 。参数是:"
				+ Arrays.asList(joinPoint.getArgs()));
	}

	@AfterReturning("execution(public int com.ddh.aop.Calculator.add(int, int))" + " || "
			+ "execution(public * com.ddh.aop.Calculator.d*(..))")
	public static void logAfterReturn(JoinPoint joinPoint) {
		System.out.println("返回之后: 日记 :【" + joinPoint.getSignature().getName() + "】 方法调用 。参数是:"
				+ Arrays.asList(joinPoint.getArgs()));
	}

	@AfterThrowing("execution(public int com.ddh.aop.Calculator.add(int, int))" + " || "
			+ "execution(public * com.ddh.aop.Calculator.d*(..))")
	public static void logThrowException(JoinPoint joinPoint) {
		System.out.println("抛异常:日记 :【" + joinPoint.getSignature().getName() + "】 方法调用 。参数是:"
				+ Arrays.asList(joinPoint.getArgs()));
	}
}

前面测试的代码没有变。再次执行的结果。都能拿到拦截的方法名和参数

获取拦截方法的返回值和抛的异常信息

获取方法返回的值分为两个步骤:
1、在返回值通知的方法中,追加一个参数 Object result
2、然后在@AfterReturning注解中添加参数returning="参数名"

获取方法抛出的异常分为两个步骤:
1、在异常通知的方法中,追加一个参数Exception exception
2、然后在@AfterThrowing 注解中添加参数 throwing="参数名"


修改LogUtil切面类的代码

	@AfterReturning(value = "execution(public int com.ddh.aop.Calculator.add(int, int))"
			+ " || " + "execution(public * com.ddh.aop.Calculator.d*(..))", returning = "result")
	public static void logAfterReturn(JoinPoint joinPoint, Object result) {
		System.out.println("返回之后: 日记 :【" + joinPoint.getSignature().getName() + "】 方法调用 。参数是:"
				+ Arrays.asList(joinPoint.getArgs()) + ",返回值:" + result);
	}

	@AfterThrowing(value = "execution(public int com.ddh.aop.Calculator.add(int, int))"
			+ " || " + "execution(public * com.ddh.aop.Calculator.d*(..))", throwing = "exception")
	public static void logThrowException(JoinPoint joinPoint, Exception exception) {
		System.out.println("抛异常:日记 :【" + joinPoint.getSignature().getName() + "】 方法调用 。参数是:"
				+ Arrays.asList(joinPoint.getArgs()) + ",异常对象:" + exception);
	}

Spring的环绕通知

1、环绕通知使用@Around注解。
2、环绕通知如果和其他通知同时执行。环绕通知会优先于其他通知之前执行。
3、环绕通知一定要有返回值(环绕如果没有返回值。后面的其他通知就无法接收到目标方法执行的结果)。
4、在环绕通知中。如果拦截异常。一定要往外抛。否则其他的异常通知是无法捕获到异常的。

在LogUtil切面类中添加环绕通知
	@Around(value = "execution(* *(..))")
	public static Object logAround(ProceedingJoinPoint proceedingJoinPoint) {
		//获取请求参数
		Object[] args = proceedingJoinPoint.getArgs();
		Object resultObject = null;
		try {
			System.out.println("环绕前置");
			//调用目标方法
			resultObject = proceedingJoinPoint.proceed(args);
			System.out.println("环绕返回");
		} catch (Throwable e) {
			System.out.println("环绕异常:" + e);
			throw new RuntimeException(e);
		} finally {
			System.out.println("环绕后置");
		}
		//返回返回值
		return resultObject;
	}

修改测试的代码
@ContextConfiguration(locations = "classpath:applicationContext.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringAopTest {
	@Autowired
	private Calculator calculator;

	@Test
	public void test1() {
		//加法
		calculator.add(1, 2);
	}
}

切入点表达式的重用

切入点表达式的重点,需要分三个步骤:
1、在切面类中定义一个空的方法
public static void pointcut1() {}

2、在方法中使用@Pointcut定义一个切入表达式
		@Pointcut(value="execution(public int com.ddh.aop.Calculator.add(int, int))" + " || "
			+ "execution(public * com.ddh.aop.Calculator.*(..))")
	public static void pointcut1() {}

3、其他的通知注解中使用方法名()的形式引用方法上定义的切入点表达式。
比如:@Before("pointcut1()")

多个通知的执行顺序

当我们有多个切面,多个通知的时候:
1、通知的执行顺序默认是由切面类的字母先后顺序决定。
2、在切面类上使用@Order注解决定通知执行的顺序(值越小,越先执行)

再添加另一个切面类
@Component
@Aspect
@Order(1)
public class Validation {

	@Before(value = "com.ddh.aop.LogUtil.pointcut1()")
	public static void before(JoinPoint joinPoint) {
		System.out.println("这是Validation的前置通知,拦截的方法是:" + joinPoint.getSignature().getName());
	}

	@After(value = "com.ddh.aop.LogUtil.pointcut1()")
	public static void after(JoinPoint joinPoint) {
		System.out.println("这是Validation的后置通知,拦截的方法是:" + joinPoint.getSignature().getName());
	}

	@AfterReturning(value = "com.ddh.aop.LogUtil.pointcut1()", returning = "result")
	public static void afterReturning(JoinPoint joinPoint, Object result) {
		System.out.println("这是Validation的后置通知,拦截的方法是:" + joinPoint.getSignature().getName()
				+ ", 返回值:" + result);
	}

}

修改原来LogUtil中的切面内容(去掉环绕通知,留下前置,后置,返回后通知)
@Aspect
@Component
@Order(2)
public class LogUtil {
	@Pointcut(value="execution(public int com.ddh.aop.Calculator.add(int, int))" + " || "
			+ "execution(public * com.ddh.aop.Calculator.*(..))")
	public static void pointcut1() {}


测试的代码
@ContextConfiguration(locations = "classpath:applicationContext.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringAopTest {
	@Autowired
	private Calculator calculator;
	@Test
	public void test1() {
		//加法
		calculator.add(1, 0);
	}
}

如何基于xml配置aop程序

需要导入的包
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
commons-logging-1.1.3.jar
log4j-1.2.17.jar
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar
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-test-4.0.0.RELEASE.jar

工程中编写的类
public class Calculator {
	public int div(int num1, int num2) {
		return num1 / num2;
	}

	public int add(int num1, int num2) {
		return num1 + num2;
	}
}

LogUtil切面类
public class LogUtil {
	public static void logBefore(JoinPoint joinPoint) {
		System.out.println("前置 日记 :【" + joinPoint.getSignature().getName() + "】 方法调用前 。参数是:"
				+ Arrays.asList(joinPoint.getArgs()));
	}

	public static void logAfter(JoinPoint joinPoint) {
		System.out.println("后置 日记 :【" + joinPoint.getSignature().getName() + "】 方法调用 。参数是:"
				+ Arrays.asList(joinPoint.getArgs()));
	}

	public static void logAfterReturn(JoinPoint joinPoint, Object result) {
		System.out.println("返回之后: 日记 :【" + joinPoint.getSignature().getName() + "】 方法调用 。参数是:"
				+ Arrays.asList(joinPoint.getArgs()) + ",返回值:" + result);
	}

	public static void logThrowException(JoinPoint joinPoint, Exception exception) {
		System.out.println("抛异常:日记 :【" + joinPoint.getSignature().getName() + "】 方法调用 。参数是:"
				+ Arrays.asList(joinPoint.getArgs()) + ",异常对象:" + exception);
	}
}

Validation切面类
public class Validation {
	public static void before(JoinPoint joinPoint) {
		System.out.println("这是Validation的前置通知,拦截的方法是:" + joinPoint.getSignature().getName());
	}

	public static void after(JoinPoint joinPoint) {
		System.out.println("这是Validation的后置通知,拦截的方法是:" + joinPoint.getSignature().getName());
	}

	public static void afterReturning(JoinPoint joinPoint, Object result) {
		System.out.println("这是Validation的后置通知,拦截的方法是:" + joinPoint.getSignature().getName()
				+ ", 返回值:" + result);
	}
}

ApplicationContext.xml配置文件中的内容
	<!-- 注册bean对象
		添加@Component
	 -->
	<bean id="calculator" class="com.ddh.aop.Calculator" />
	<bean id="logUtil" class="com.ddh.aop.LogUtil" />
	<bean id="validation" class="com.ddh.aop.Validation" />
	<!-- 配置AOP -->
	<aop:config>
		<!-- 定义可以共用的切入点 -->
		<aop:pointcut expression="execution(* com.ddh.aop.Calculator.*(..))" id="pointcut1"/>
		<!-- 定义切面类 -->
		<aop:aspect order="1" ref="logUtil">
			<!-- 这是前置通知 
					method属性配置通知的方法
					pointcut配置切入点表达式
			 -->
			<aop:before method="logBefore" pointcut="execution(* com.ddh.aop.Calculator.*(..))"/>
			<aop:after method="logAfter" pointcut="execution(* com.ddh.aop.Calculator.*(..))"/>
			<aop:after-returning method="logAfterReturn" 
				returning="result" pointcut="execution(* com.ddh.aop.Calculator.*(..))"/>
			<aop:after-throwing method="logThrowException" throwing="exception"
				pointcut="execution(* com.ddh.aop.Calculator.*(..))"/>
		</aop:aspect>
		<!-- 定义切面类 -->
		<aop:aspect order="2" ref="validation">
			<aop:before method="before" pointcut-ref="pointcut1"/>
			<aop:after method="after" pointcut-ref="pointcut1"/>
			<aop:after-returning method="afterReturning" 
				returning="result" pointcut-ref="pointcut1"/>
		</aop:aspect>
	</aop:config>

测试的代码
@ContextConfiguration(locations = "classpath:applicationContext.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringAopTest {
	@Autowired
	Calculator calculator;
	@Test
	public void test1() {
		calculator.add(1, 2);
	}
}

Spring之数据访问

Spring数据访问工程环境搭建

创建一个Java工程,导入需要的Jar包
Spring的核心jar包
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切面的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
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar

Spring数据访问的jar包
spring-jdbc-4.0.0.RELEASE.jar
spring-orm-4.0.0.RELEASE.jar
spring-tx-4.0.0.RELEASE.jar

数据库访问需要的jar包
druid-1.1.10.jar
mysql-connector-java-5.1.37-bin.jar

日记需要的jar包
commons-logging-1.1.3.jar
log4j-1.2.17.jar

Spring的测试包
spring-test-4.0.0.RELEASE.jar

在src目录下jdbc.properties属性配置文件
jdbc.username=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/jdbctemplate
jdbc. driverClassName=com.mysql.jdbc.Driver

在src目录下的log4j.properties配置文件
# Global logging configuration
log4j.rootLogger=INFO, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

在applicationContext.xml中配置数据源
	<!-- 加载jdbc.properties配置文件 -->
	<context:property-placeholder location="classpath:jdbc.properties"/>
	<!-- 配置数据库连接池 -->
	<bean id="druidDataSource" class=" com.alibaba.druid.pool.DruidDataSource">
		<property name="username" value="${jdbc.username}"/>
		<property name="password" value="${jdbc.password}"/>
		<property name="url" value="${jdbc.url}"/>
		<property name="driverClassName" value="${jdbc.driverClassName}"/>
	</bean>
	<!-- jdbcTemplate -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource"  ref="druidDataSource"/>
	</bean>

测试数据源(测试数据库连接池)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class JdbcTest {

	@Autowired
	DataSource dataSource;

	@Test
	public void test1() throws Exception {
		System.out.println( dataSource.getConnection() );
	}

}

Spring之JdbcTemplate使用

在Spring中提供了对jdbc的封装类叫JdbcTemplate。它可以很方便的帮我们执行sql语句,操作数据库。

先准备单表的数据库数据
drop database if exists jdbctemplate;

create database jdbctemplate;

use jdbctemplate;

create table `employee` (
  `id` int(11) primary key auto_increment,
  `name` varchar(100) default null,
  `salary` decimal(11,2) default null
);

insert  into `employee`(`id`,`name`,`salary`) 
values (1,'张珊',5000.23),(2,'李思',4234.77),(3,'王舞',9034.51),
(4,'赵柳',8054.33),(5,'孔琪',6039.11),(6,'曹拔',7714.11);

select * from employee;



JdbcTemplate的使用需要在applicationContext.xml中进行配置
	<!-- jdbcTemplate -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource"  ref="druidDataSource"/>
	</bean>

实验2:将id=5的记录的salary字段更新为1300.00
	@Test
	public void test2() throws Exception {
		// 实验2:将emp_id=5的记录的salary字段更新为1300.00
		String sql = "update employee set salary = ? where id = ?";
		jdbcTemplate.update(sql, 1300, 5);
	}

实验3:批量插入
	@Test
	public void test3() throws Exception {
		String sql = "insert into employee(`name`,`salary`) values(?,?)";
		ArrayList<Object[]> params = new ArrayList<>();
		params.add(new Object[] {"aaa",100});
		params.add(new Object[] {"bbb",100});
		params.add(new Object[] {"ccc",100});
		jdbcTemplate.batchUpdate(sql, params);
	}


实验4:查询id=5的数据库记录,封装为一个Java对象返回
创建一个Employee对象
public class Employee {

	private Integer id;
	private String name;
	private BigDecimal salary;

	@Test
	public void test4() throws Exception {
		// 实验4:查询id=5的数据库记录,封装为一个Java对象返回
		String sql = "select id ,name ,salary from employee where id = ?";
		/**
		 * 在queryRunner中使用的是ResultSetHandler
		 * 	在Spring的jdbcTemplate中,使用RowMapper。
		 * 		BeanPropertyRowMapper 可以把一个结果集转成一个bean对象
		 */
		Employee employee = jdbcTemplate.queryForObject(sql,
 												new BeanPropertyRowMapper<Employee>(Employee.class), 5);
		System.out.println(employee);
	}

实验5:查询salary>4000的数据库记录,封装为List集合返回
	@Test
	public void test5() throws Exception {
		// 实验5:查询salary>4000的数据库记录,封装为List集合返回
		String sql = "select id,name,salary from employee where salary > ?";
		List<Employee> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper(Employee.class), 4000);
		System.out.println(list);
	}


实验6:查询最大salary
	@Test
	public void test6() throws Exception {
		// 实验6:查询最大salary
		String sql = "select max(salary) from employee";
		BigDecimal salary = jdbcTemplate.queryForObject(sql, BigDecimal.class);
		System.out.println(salary);
	}


实验7:使用带有具名参数的SQL语句插入一条员工记录,并以Map形式传入参数值
	<!-- namedParameterJdbcTemplate -->
	<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate" >
		<constructor-arg name="dataSource" ref="dataSource" />
	</bean>

@ContextConfiguration(locations = "classpath:applicationContext.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class JdbcTest {

	@Autowired
	DataSource dataSource;

	@Autowired
	JdbcTemplate jdbcTemplate;

	@Autowired
	NamedParameterJdbcTemplate namedParameterJdbcTemplate;

	@Test
	public void test7() throws Exception {
		// 实验7:使用带有具名参数的SQL语句插入一条员工记录,并以Map形式传入参数值
		String sql = "insert into employee(`name`,`salary`) values(:name,:salary)";
		Map<String, Object> param = new HashMap<String,Object>();
		param.put("name", "小明");
		param.put("salary", new BigDecimal(55));
		namedParameterJdbcTemplate.update(sql, param);
    }

实验8:重复实验7,以SqlParameterSource形式传入参数值
	@Test
	public void test8() throws Exception {
		// 实验8:重复实验7,以SqlParameterSource形式传入参数值
		String sql = "insert into employee(`name`,`salary`) values(:name,:salary)";
		//通过一个bean对象的属性会自动赋值
		SqlParameterSource sqlParameterSource = new BeanPropertySqlParameterSource(new Employee(0,
				"小新", new BigDecimal(11111)));
		namedParameterJdbcTemplate.update(sql, sqlParameterSource);
	}

实验9:创建Dao,自动装配JdbcTemplate对象
		// 实验9:创建Dao,自动装配JdbcTemplate对象
添加类
@Repository
public class EmployeeDao {

	@Autowired
	JdbcTemplate jdbcTemplate;

	public int saveEmployee(Employee employee) {
		String sql = "insert into employee(`name`,`salary`) values(?,?)";
		return jdbcTemplate.update(sql, employee.getName(), employee.getSalary());
	}
}

在applicationContext.xml中配置
	<!-- 添加包扫描 -->
	<context:component-scan base-package="com.d" />

测试代码
@ContextConfiguration(locations = "classpath:applicationContext.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class JdbcTest {

	@Autowired
	DataSource dataSource;

	@Autowired
	JdbcTemplate jdbcTemplate;

	@Autowired
	NamedParameterJdbcTemplate namedParameterJdbcTemplate;

	@Autowired
	EmployeeDao employeeDao;

	@Test
	public void test9() throws Exception {
		// 实验9:创建Dao,自动装配JdbcTemplate对象
		employeeDao.saveEmployee(new Employee(null, "ssssss", new BigDecimal(999)));
	}

实验10:通过继承JdbcDaoSupport创建JdbcTemplate的Dao
@Repository
public class EmployeeDao extends JdbcDaoSupport {

	@Autowired
	protected void setDataSource2(DataSource dataSource) {
		System.out.println("自动注入 + " + dataSource);
		super.setDataSource(dataSource);
	}

	public int saveEmployee(Employee employee) {
		String sql = "insert into employee(`name`,`salary`) values(?,?)";
		return getJdbcTemplate().update(sql, employee.getName(), employee.getSalary());
	}
}

声明式事务

事务分为声明式和编程式两种:
声明式事务:声明式事务是指通过注解的形式对事务的各种特性进行控制和管理。
编码式(编程式)事务:指的是通过编码的方式实现事务的声明。

声明式事务环境搭建

准备测试数据库
##创建tx数据库
drop database if exists `tx`;
CREATE database `tx`;
##切换tx数据库
USE `tx`;

##删除用户表
DROP TABLE IF EXISTS `user`;
##创建用户表
CREATE TABLE `user` (
  `id` int primary key auto_increment,	
  `username` varchar(50) NOT NULL,
  `money` int(11) DEFAULT NULL
);
##插入数据
insert  into `user`(`username`,`money`) values ('张三',1000),('李四',1000);

##删除图书表
drop table if exists `book`;
##创建图书表
create table `book`(
    `id` int primary key auto_increment,
    `name` varchar(500) not null,
    `stock` int
);
##插入数据
insert into book(`name`,`stock`) values('java编程思想',100),('C++编程思想',100);

##查看数据
select * from book;
select * from user;


11.2.2、创建一个Java工程,导入Jar包
 

JavaBean对象

public class Book {

	private Integer id;
	private String name;
private int stock;

public class User {

	private Integer id;
	private String username;
	private int money;

Dao们

@Repository
public class BookDao {

	@Autowired
	private JdbcTemplate jdbcTemplate;

	public int updateBook() {
		String sql = "update book set name = '**我被修改了!**' where id = 1";
		return jdbcTemplate.update(sql);
	}

}

@Repository
public class UserDao {

	@Autowired
	private JdbcTemplate jdbcTemplate;

	public int updateUser() {
		String sql = "update user set username = '**我被修改了**' where id = 1";
		return jdbcTemplate.update(sql);
	}

}

Service代码

@Service
public class TransactionService {

	@Autowired
	private UserDao userDao;

	@Autowired
	private BookDao bookDao;

	public void updateTwoTable() {
		userDao.updateUser();
		bookDao.updateBook();
	}

}

测试Service的默认事务

测试service服务层的默认事务

@ContextConfiguration(locations = "classpath:application.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringTest {

	@Autowired
	private DataSource dataSource;

	@Autowired
	private JdbcTemplate jdbcTemplate;
	
	@Autowired
	private TransactionService transactionService;

	@Test
	public void testDataSource() throws Exception {
		System.out.println(dataSource.getConnection());
	}
	
	@Test
	public void testJdbcTempalte() throws Exception {
		System.out.println( jdbcTemplate );
	}
	
	@Test
	public void testTransaction() throws Exception {
		transactionService.updateTwoTable();
	}

}

使用Spring的注解声明事务管制

测试Spring的声明式事务



先导入AOP包,因为Spring的底层事务使用到了aop功能
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar



配置Spring的事务需要的切面类DataSourceTransactionManager

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


在Spring的配置文件中加入tx名称空间
 

在Spring的配置文件中

	<!-- 配置启用spring的事务注解@Transaction -->
	<tx:annotation-driven transaction-manager="transactionManager"/>



还需要在Service的事务方法中添加@Transaction注解
	@Transactional
	public void updateTwoTable() {
		userDao.updateUser();
		int i = 12 / 0;
		bookDao.updateBook();
	}

noRollbackFor和noRollbackForClassName测试不回滚的异常

noRollbackFor和noRollbackForClassName测试不回滚的异常


	/**
	 * noRollbackFor=ArithmeticException.class 表示当接收到数学异常之后。不回滚<br/>
	 * noRollbackFor=ArithmeticException.class<br/> 
	 * noRollbackForClassName="java.lang.ArithmeticException" 表示当接收到指定字符串表示的全类名的异常的时候,不回滚事务
	 */
	@Transactional()
	public void updateTwoTable() {
		userDao.updateUser();
		int i = 12 / 0;
		bookDao.updateBook();
	}

自定义设置回滚异常

:rollbackFor和rollbackForClassName回滚的异常
Spring默认回滚的是RuntimeException,运行时异常或运行时异常的子异常



	/**
	 * spring默认回去的是运行时异常RuntimeException和RuntimeException的子异常<br/>
	 * rollbackFor=FileNotFoundException.class	表示FileNotFoundException也会回滚
	 * rollbackForClassName="java.io.FileNotFoundException" 表示当出现配置字符串所向定的全类名的异常的时候。也会回滚事务
	 * @throws FileNotFoundException
		表示添加了一个新的异常 也进行回滚
	 * 
	 */
	@Transactional()
	public void updateTwoTable() throws FileNotFoundException {
		userDao.updateUser();
		int i = 0;
		if (i == 0) {//java.io.FileNotFoundException
			throw new FileNotFoundException("sadf");
		}
		bookDao.updateBook();
	}

事务的只读属性

测试readOnly只读属性


	/**
	 * readOnly 如果值为true。表示只支持查询操作。不支持写操作
* <br/>如果设置为false,支持全部
	 */
	@Transactional(readOnly=true)
	public void updateTwoTable() throws FileNotFoundException {
		userDao.updateUser();
		bookDao.updateBook();
	}

事务超时属性timeout(秒为单位)

/**
	 * timeout=3表示操作不能超过3秒
	 */
	@Transactional(timeout=3)
	public void updateTwoTable() throws FileNotFoundException {
		userDao.updateUser();
		try {
			Thread.sleep(4000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		bookDao.updateBook();
	}

注解演示事物传播特性

大小事务传播特性都是REQUIRED
	@Transactional(propagation = Propagation.REQUIRED)
	public void multiUpdate() {
	@Transactional(propagation = Propagation.REQUIRED)
	public void updateBook() {
@Transactional(propagation=Propagation.REQUIRED)
public void updateUser() {


实验2:大小事务传播特性都是REQUIRES_NEW
	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void multiUpdate()
	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void updateBook()
	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void updateUser()

实验3:大事务是REQUIRED,小事务都是REQUIRES_NEW
	@Transactional(propagation = Propagation.REQUIRED)
	public void multiUpdate()
	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void updateBook()
	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void updateUser() 

实验4:大事务是REQUIRED,小1REQUIRED,小2REQUIRES_NEW
	@Transactional(propagation = Propagation.REQUIRED)
	public void multiUpdate()
	@Transactional(propagation = Propagation.REQUIRED)
	public void updateBook()
	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void updateUser()

 
实验5:大事务是REQUIRED,小1REQUIRES_NEW,小2REQUIRED
	@Transactional(propagation = Propagation.REQUIRED)
	public void multiUpdate()
	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void updateBook()
	@Transactional(propagation = Propagation.REQUIRED)
	public void updateUser()

xml配置式事务声明

去掉。所有@Transaction的注解。

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

	<!-- 事物特性 -->
	<tx:advice id="tx_advice" transaction-manager="transactionManager">
		<!-- 配置事务特性 -->
		<tx:attributes>
			<tx:method name="multiUpdate" propagation="REQUIRED"/>
			<tx:method name="updateBook" propagation="REQUIRES_NEW"  />
			<tx:method name="updateUser" propagation="REQUIRED"/>
			<tx:method name="*" read-only="true"/>
		</tx:attributes>
	</tx:advice>

	<!-- 配置aop代理 -->
	<aop:config>
		<aop:advisor advice-ref="tx_advice" pointcut="execution(* com.ddh.service.*Service.*(..))" />
	</aop:config>

Spring整合Web

在web工程中添加Spring的jar包。
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
aop包
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
JDBC-ORM包
spring-jdbc-4.0.0.RELEASE.jar
spring-orm-4.0.0.RELEASE.jar
spring-tx-4.0.0.RELEASE.jar
Spring的web整合包
spring-web-4.0.0.RELEASE.jar
测试包
spring-test-4.0.0.RELEASE.jar

整合Spring和Web容器分两个步骤:
1、导入spring-web-4.0.0.RELEASE.jar
2、在web.xml配置文件中配置org.springframework.web.context.ContextLoaderListener监听器监听ServletContext的初始化
3、在web.xml配置文件中配置contextConfigLocation上下文参数。配置Spring配置文件的位置,以用于初始化Spring容器

在web.xml中配置
  <context-param>
  	<param-name>contextConfigLocation</param-name>
  	<param-value>classpath:applicationContext.xml</param-value>
  </context-param>
  <listener>
  	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>


获取WebApplicationContext上下文对象的方法如下:
方法一(推荐):
WebApplicationContextUtils.getWebApplicationContext(getServletContext())

方法二(不推荐):
getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Java开发-斗灵

客官打个赏呗

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

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

打赏作者

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

抵扣说明:

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

余额充值