手把手教你如何玩转Spring

   前面已经讲了关于Hibernate和Struts2两个框架的基本内容了,如果不懂的可以看前面的文章哦,很明白从基础逐步进行延伸。下面的话,就用这篇文章来讲解一下关于Spring框架的基本知识。准备好了吗?

1:spring的搭建步骤

  一:导包(4+2+1):四个基础包,2个日志包,一个web包

:创建java对象

三:书写配置注册对象到容器(其中位置任意,建议放在src下,名字也取为applicationContext.xml)

2:spring的概念知识

(1)IOC(Inverse Of  Control  , 反转控制)知识点

          概念:就是反转了对象的创建方式,从我们自己创建反转给程序(spring)

(2)DI(Denpendcy Injection , 依赖注入)

注入方式:set方式注入,构造方法注入,字段注入

注入类型:值类型注入(8大基本类型),引用类型注入(将依赖对象注入)

(3)BeanFactory接口

1)spring原始接口,针对原始接口的实现类功能较为单一;

2)BeanFactory接口实现类的容器特点是每次在获得对象时才创建对象

(4)ApplicationContext

每次容器启动时就会创建容器中配置的所有对象

加载文件的方式:第一:从类路径下加载,ClassPathXmlApplicationContext

第二:从硬盘的绝对路径:FileSystemXmlApplicationContext("d:/xxx/yy/zzz")

  其中的(3)和(4)对比中,在web环境中,使用(4),而在资源匮乏的时候,可以使用(3)。

3:Bean元素

(1)基本知识点

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


	<!-- 将User对象交给spring容器管理 -->
	<!-- Bean元素:使用该元素描述需要spring容器管理的对象
			class属性:被管理对象的完整类名.
			name属性:给被管理的对象起个名字.获得对象时根据该名称获得对象.  
					可以重复.可以使用特殊字符.
			id属性: 与name属性一模一样. 
					名称不可重复.不能使用特殊字符.
			结论: 尽量使用name属性.
	  -->
	<bean  name="user" class="cn.itcast.bean.User" ></bean>
	<!-- 导入其他spring配置文件 -->
	<import resource="cn/itcast/b_create/applicationContext.xml"/>
	
</beans>

(2)scope属性(四种):

类型一:singleton(默认值):单例对象,被标识为单例类型,则在spring容器中,只会存在一个实例

类型二:prototype:多例模型,被标识为多例的对象,每次再获得才会得到对象,每次创建都是新的对象,整合structs2的时候,需要配置ActionBean为多例模式

类型三:request:web环境下,对象与request生命周期一样

类型四:session:web环境下,对象与session生命周期一样

(3)生命周期

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd ">
	<bean  name="user" class="cn.itcast.bean.User"
		 init-method="init" destroy-method="destory" ></bean>
</beans>

1:当scope是单例(默认)的情况时候,就是当spring容器初始化的时候就进行Bean的初始化

2:当scope是prototype的时候,就是当调用getBean()方法的时候才进行Bean的初始化。

4:三种对象创建方式

(1)空参构造(创建一个对象默认会调用空参构造方法)

(2)静态工厂(了解)

(3)实例工厂(了解)

applicationContext配置文件: 

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

	<!-- 创建方式1:空参构造创建  -->
	<bean  name="user" class="cn.itcast.bean.User"
		 init-method="init" destroy-method="destory" ></bean>
	<!-- 创建方式2:静态工厂创建 
		  调用UserFactory的createUser方法创建名为user2的对象.放入容器
	 -->
	<bean  name="user2" 
		class="cn.itcast.b_create.UserFactory" 
		factory-method="createUser" ></bean>
	<!-- 创建方式3:实例工厂创建 
		 调用UserFactory对象的createUser2方法创建名为user3的对象.放入容器
	 -->
	<bean  name="user3" 
		factory-bean="userFactory"
		factory-method="createUser2" ></bean>
		
	<bean  name="userFactory" 
		class="cn.itcast.b_create.UserFactory"   ></bean>
</beans>

上面其中的工厂类如下所示:

package cn.itcast.b_create;

import cn.itcast.bean.User;

public class UserFactory {

	public static User createUser(){
		
		System.out.println("静态工厂创建User");
		
		return new User();
		
	}
	
	public  User createUser2(){
		
		System.out.println("实例工厂创建User");
		
		return new User();
		
	}
	
}	

5:属性注入(三种方式)

(1)set方法(掌握,最重要)

(2)构造函数(掌握)

(3)p名称空间注入(了解)

(4)spel注入(了解)

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

	<!-- set方式注入: -->
	<bean  name="user" class="cn.itcast.bean.User" >
		<!--值类型注入: 为User对象中名为name的属性注入tom作为值 -->
		<property name="name" value="tom" ></property>
		<property name="age"  value="18" ></property>
		<!-- 引用类型注入: 为car属性注入下方配置的car对象 -->
		<property name="car"  ref="car" ></property>
	</bean>
	
	<!-- 将car对象配置到容器中 -->
	<bean name="car" class="cn.itcast.bean.Car" >
		<property name="name" value="兰博基尼" ></property>
		<property name="color" value="黄色" ></property>
	</bean>
<!-- ============================================================ -->
	<!-- 构造函数注入 -->
<bean name="user2" class="cn.itcast.bean.User" >
	<!-- name属性: 构造函数的参数名 -->
	<!-- index属性: 构造函数的参数索引 -->
	<!-- type属性: 构造函数的参数类型-->
	<constructor-arg name="name" index="0" type="java.lang.Integer" value="999"  ></constructor-arg>
	<constructor-arg name="car" ref="car" index="1" ></constructor-arg>
</bean>

<!-- ============================================================= -->

<!-- p名称空间注入, 走set方法
	1.导入P名称空间  xmlns:p="http://www.springframework.org/schema/p"
	2.使用p:属性完成注入
		|-值类型: p:属性名="值"
		|-对象类型: p:属性名-ref="bean名称"
 -->
	<bean  name="user3" class="cn.itcast.bean.User" p:name="jack" p:age="20" p:car-ref="car"  >
	</bean>
	
<!-- ============================================================= -->

<!-- 
	spel注入: spring Expression Language sping表达式语言
 -->
<bean  name="user4" class="cn.itcast.bean.User" >
		<property name="name" value="#{user.name}" ></property>
		<property name="age" value="#{user3.age}" ></property>
		<property name="car" ref="car" ></property>
</bean>
<!-- ============================================================= -->
<!-- 复杂类型注入 -->
<bean name="cb" class="cn.itcast.c_injection.CollectionBean" >
	<!-- 如果数组中只准备注入一个值(对象),直接使用value|ref即可 
	<property name="arr" value="tom" ></property>
	-->
	<!-- array注入,多个元素注入 -->
	<property name="arr">
		<array>
			<value>tom</value>
			<value>jerry</value>
			<ref bean="user4" />
		</array>
	</property>
	
	<!-- 如果List中只准备注入一个值(对象),直接使用value|ref即可 
	<property name="list" value="jack" ></property>-->
	<property name="list"  >
		<list>
			<value>jack</value>
			<value>rose</value>
			<ref bean="user3" />
		</list>
	</property>
	<!-- map类型注入 -->
	<property name="map"  >
		<map>
			<entry key="url" value="jdbc:mysql:///crm" ></entry>
			<entry key="user" value-ref="user4"  ></entry>
			<entry key-ref="user3" value-ref="user2"  ></entry>
		</map> 
	</property>
	<!-- prperties 类型注入 -->
	<property name="prop"  >
		<props>
			<prop key="driverClass">com.jdbc.mysql.Driver</prop>
			<prop key="userName">root</prop>
			<prop key="password">1234</prop>
		</props>
	</property>
</bean>
</beans>

其中上面的User类,Car类和CollectionBean类代码如下:

User类:

package cn.itcast.bean;

public class User {
	
	
	public User() {
			System.out.println("User对象空参构造方法!!!!");
	}
	private String name;
	private Integer age;
	private Car car;
	
	
	
	public User(String name, Car car) {
		System.out.println("User(String name, Car car)!!");
		this.name = name;
		this.car = car;
	}
	
	public User(Car car,String name) {
		System.out.println("User(Car car,String name)!!");
		this.name = name;
		this.car = car;
	}
	
	public User(Integer name, Car car) {
		System.out.println("User(Integer name, Car car)!!");
		this.name = name+"";
		this.car = car;
	}
	
	public Car getCar() {
		return car;
	}
	public void setCar(Car car) {
		this.car = car;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	
	public void init(){
		System.out.println("我是初始化方法!");
	}
	public void destory(){
		System.out.println("我是销毁方法!");
	}
	@Override
	public String toString() {
		return "User [name=" + name + ", age=" + age + ", car=" + car + "]";
	}
	
}
Car类:
package cn.itcast.bean;

public class Car {
	private String  name;
	private String color;
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getColor() {
		return color;
	}
	public void setColor(String color) {
		this.color = color;
	}
	@Override
	public String toString() {
		return "Car [name=" + name + ", color=" + color + "]";
	}
	
	
}
CollectionBean类(这个主要是用来讲解高级的注入方式)
package cn.itcast.c_injection;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;

public class CollectionBean {
	private Object[] arr;//数组类型注入
	private List list;//list/set 类型注入
	private Map map;//map类型注入
	private Properties prop;//properties类型注入
	
	public Object[] getArr() {
		return arr;
	}
	public void setArr(Object[] arr) {
		this.arr = arr;
	}
	public List getList() {
		return list;
	}
	public void setList(List list) {
		this.list = list;
	}
	public Map getMap() {
		return map;
	}
	public void setMap(Map map) {
		this.map = map;
	}
	public Properties getProp() {
		return prop;
	}
	public void setProp(Properties prop) {
		this.prop = prop;
	}
	@Override
	public String toString() {
		return "CollectionBean [arr=" + Arrays.toString(arr) + ", list=" + list + ", map=" + map + ", prop=" + prop
				+ "]";
	}
	
	
}

测试类的代码,也顺便贴出来,大家可以运行进行测试下:

package cn.itcast.c_injection;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.itcast.bean.User;

public class Demo {
	@Test
	public void fun1(){
		
		//1 创建容器对象
		ApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/c_injection/applicationContext.xml");
		//2 向容器"要"user对象
		User u = (User) ac.getBean("user");
		//3 打印user对象
		System.out.println(u);
		
	}
	@Test
	public void fun2(){
		
		//1 创建容器对象
		ApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/c_injection/applicationContext.xml");
		//2 向容器"要"user对象
		User u = (User) ac.getBean("user2");
		//3 打印user对象
		System.out.println(u);
		
	}
	
	@Test
	public void fun3(){
		
		//1 创建容器对象
		ApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/c_injection/applicationContext.xml");
		//2 向容器"要"user对象
		User u = (User) ac.getBean("user3");
		//3 打印user对象
		System.out.println(u);
		
	}
	
	@Test
	public void fun4(){
		
		//1 创建容器对象
		ApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/c_injection/applicationContext.xml");
		//2 向容器"要"user对象
		User u = (User) ac.getBean("user4");
		//3 打印user对象
		System.out.println(u);
		
	}
	
	@Test
	public void fun5(){
		
		//1 创建容器对象
		ApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/c_injection/applicationContext.xml");
		//2 向容器"要"user对象
		CollectionBean cb = (CollectionBean) ac.getBean("cb");
		//3 打印user对象
		System.out.println(cb);
		
	}
}

PS:这里另外讲一点知识就是关于注解注入的问题。因为在实际开发中,一般都是通过注解来开发的,要不然XML文件就会显得很臃肿,所以就需要有注解的使用。对于注入的话,有两个注解@Resource和@Autowire,它们两者的差别在于:

区别:@Resource中有个属性name(可选),所以首先是会从spring容器中找到对应的名字的装配,如果不存在,然后才会找对应的类型进行注入;而@Autowire,它只会找对应的类型进行装配,进行注入。

6:保持容器中的对象的生命周期的方法

首先在web.xml文件中进行配置一个监听器,以便spring容易中获取对象保持单例

<!-- 可以让spring容器随项目的启动而创建,随项目的关闭而销毁 -->
  <listener>
  	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <!-- 指定加载spring配置文件的位置 -->
  <context-param>
  	<param-name>contextConfigLocation</param-name>
  	<param-value>classpath:applicationContext.xml</param-value>
  </context-param>
  
其次,在Action类中进行获取对象(下面是个例子)。
//获得spring容器=>从Application域获得即可
		
			//1 获得servletContext对象
			ServletContext sc = ServletActionContext.getServletContext();
			//2.从Sc中获得ac容器
			WebApplicationContext ac = WebApplicationContextUtils.getWebApplicationContext(sc);
			//3.从容器中获得CustomerService
			CustomerService cs = (CustomerService) ac.getBean("customerService");  //这个是需要将对象在spring配置文件中进行配置好的

7:使用注解配置spring

步骤:

一:导包4+2+spring-aop包

二:为主配置文件引入新的命名空间(约束)

三:开启使用注解代理配置文件(在spring的配置文件applicationContext.xml中配置)

<!-- 指定扫描cn.itcast.bean报下的所有类中的注解.
	 注意:扫描包时.会扫描指定报下的所有子孙包
 -->
<context:component-scan base-package="cn.itcast.bean"></context:component-scan>

四:在类中使用注解完成配置

//@Component("user")
//等价于在applicationContext.xml中配置<bean name="user" class="cn.itcast.bean.User"  />
public class User {
...........
}

知识点1:将对象注册到容器

//@Component("user")  //等价在spring配置文件中设置为:<bean name="user" class="cn.itcast.bean.User"  />
//	@Service("user") // service层
//	@Controller("user") // web层
	@Repository("user")// dao层
public class User {
.............
}

知识点2:修改对象的作用范围

//指定对象的作用范围
@Scope(scopeName="singleton")  //设置为单例   ,设置为prototype,则为多例
public class User {
..........
}

知识点3:值类型注入

public class User {
	private String name;
	@Value("18")  //设置年龄为18
	private Integer age;
}

知识点4:引用类型注入

	//@Autowired //方法一:自动装配
	//问题:如果匹配多个类型一致的对象.将无法选择具体注入哪一个对象.
	//@Qualifier("car2")//方法二:使用@Qualifier注解告诉spring容器自动装配哪个名称的对象
	
	@Resource(name="car")//方法三:手动注入,指定注入哪个名称的对象,推荐使用
	private Car car;

对应的要在相应的javabean中进行配置的设置

@Component("car")  //关键点,配置注解
public class Car {
	@Value("玛莎拉蒂")
	private String  name;
	@Value("呕吐绿")
	private String color;
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getColor() {
		return color;
	}
	public void setColor(String color) {
		this.color = color;
	}
	@Override
	public String toString() {
		return "Car [name=" + name + ", color=" + color + "]";
	}
	
	
}

知识点5:初始化和销毁方法

@PostConstruct //在对象被创建后调用.init-method
	public void init(){
		System.out.println("我是初始化方法!");
	}
	@PreDestroy //在销毁之前调用.destory-method
	public void destory(){
		System.out.println("我是销毁方法!");
	}

8:spring与junit整合(作用:节省编写测试代码的冗余行数)

步骤:

一:导包4+2+aop+test包

二:配置注释

//帮我们创建容器
@RunWith(SpringJUnit4ClassRunner.class)
//指定创建容器时使用哪个配置文件
@ContextConfiguration("classpath:applicationContext.xml")

三:进行测试

package cn.itcast.b_test;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import cn.itcast.bean.User;
//帮我们创建容器
@RunWith(SpringJUnit4ClassRunner.class)
//指定创建容器时使用哪个配置文件
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo {
	//将名为user的对象注入到u变量中
	@Resource(name="user")
	private User u;
	
	@Test
	public void fun1(){
		
		System.out.println(u);
		
	}
	@Test
	public void fun2(){
		
		System.out.println(u);
		
	}
}

9:Spring中的aop知识点

(1)spring中的aop概念-------简单点说,就是通过spring来实现动态代理对象。

比如具有代表性的例子:1)消除乱码的时候,用的filter

2)动态代理

3)intercepter(拦截器)中

(2)spring实现aop的原理

方法一:动态代理:

缺点:被代理的对象必须要实现接口,才能产生代理对象,如果没有接口将不能使用动态代理技术

方法二:cglib代理

该方法是第三方代理技术,对任何对象生成代理,代理的原理是对代理的对象进行继承代理。如果目标对象被final修饰,那么该类无法被cglib代理(其实这个在Hebinate中的对象的规则中,就有这一点限制)。

PS:在spring中,优先是执行动态代理,如果该类不能通过动态代理来实现,就会通过cglib代理还实现。

另外顺便提一下,关于动态代理到底是怎么回事,下面通过一个代码例子进行显示一下,一般的动态代理是如何实现,了解就好,因为用spring的话就不需要这么麻烦了(注意就是,要实现动态代理的那个类,就一定要实现一个接口,否则无法进行)

接口:(这个就随便写一下,就假设要实现增删改查)

public interface UserService {
	void save();
	void delete();
	void update();
	void find();
}
实现类:(比如,每个方法都要打开事务和提交事务,那么就会有重复代码,所以就将打开事务和提交事务通过动态代理来实现)
package cn.itcast.service;
public class UserServiceImpl implements UserService {
	@Override
	public void save() {
		System.out.println("保存用户!");
	}
	@Override
	public void delete() {
		System.out.println("删除用户!");
	}
	@Override
	public void update() {
		System.out.println("更新用户!");
	}
	@Override
	public void find() {
		System.out.println("查找用户!");
	}
}
动态代理类:
package cn.itcast.c_proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import cn.itcast.service.UserService;
import cn.itcast.service.UserServiceImpl;
//观光代码=>动态代理
public class UserServiceProxyFactory implements InvocationHandler {
	
	public UserServiceProxyFactory(UserService us) {
		super();
		this.us = us;
	}

	private UserService us;
	
	public UserService getUserServiceProxy(){
		//生成动态代理(参数一:类加载对象;参数二:实现类的接口;参数三:实现InvacationHandler接口的类,而我这里就通过该类自身来实现的!)
		UserService usProxy = (UserService) Proxy.newProxyInstance(UserServiceProxyFactory.class.getClassLoader(),
									UserServiceImpl.class.getInterfaces(), 
									this);
		//返回
		return usProxy;
		
	}

	@Override
	public Object invoke(Object arg0, Method method, Object[] arg2) throws Throwable {
		System.out.println("打开事务!");
		Object invoke = method.invoke(us, arg2);
		System.out.println("提交事务!");
		return invoke;
	}

}

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

cglib的手动实现:(针对上面的代码,只是把动态代理转成该方式来实现)
package cn.itcast.c_proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import cn.itcast.service.UserService;
import cn.itcast.service.UserServiceImpl;

//观光代码=>cglib代理
public class UserServiceProxyFactory2 implements MethodInterceptor {
	

	public UserService getUserServiceProxy(){
		
		Enhancer en = new Enhancer();//帮我们生成代理对象
		
		en.setSuperclass(UserServiceImpl.class);//设置对谁进行代理
		
		en.setCallback(this);//代理要做什么
		
		UserService us = (UserService) en.create();//创建代理对象
		
		return us;
	}

	@Override
	public Object intercept(Object prxoyobj, Method method, Object[] arg, MethodProxy methodProxy) throws Throwable {
		//打开事务
		System.out.println("打开事务!");
		//调用原有方法
		Object returnValue = methodProxy.invokeSuper(prxoyobj, arg);
		//提交事务
		System.out.println("提交事务!");
		
		return returnValue;
	}


}

测试代码如下:

package cn.itcast.c_proxy;

import org.junit.Test;

import cn.itcast.service.UserService;
import cn.itcast.service.UserServiceImpl;

public class Demo {
	
	@Test
	//动态代理
	public void fun1(){
		UserService us = new UserServiceImpl();
		
		UserServiceProxyFactory factory = new UserServiceProxyFactory(us);
		
		UserService usProxy = factory.getUserServiceProxy();
		
		usProxy.save();
		
		//代理对象与被代理对象实现了相同的接口
		//代理对象 与 被代理对象没有继承关系
		System.out.println(usProxy instanceof UserServiceImpl );//false
	}
	
	@Test
	public void fun2(){         //cglib方式
		
		UserServiceProxyFactory2 factory = new UserServiceProxyFactory2();
		
		UserService usProxy = factory.getUserServiceProxy();
		
		usProxy.save();
		
		//判断代理对象是否属于被代理对象类型
		//代理对象继承了被代理对象=>true        这也就证明了上面提到的消息
		System.out.println(usProxy instanceof UserServiceImpl );//true
	}
}
(3)aop中的一些名词(结合刚才的实例进行分析,并用不同的颜色进行了标注)


(4)spring中的aop的操作

第一步:导包(4+2):其中的4就是最基础的那几个,前面也有提到


第二步:准备目标对象(简单点说,就是想通过aop操作来进行的事情)

比如上面例子中的该实现类:

public class UserServiceImpl implements UserService {
	@Override
	public void save() {
		System.out.println("保存用户!");
		//int i = 1/0;
	}
	@Override
	public void delete() {
		System.out.println("删除用户!");
	}
	@Override
	public void update() {
		System.out.println("更新用户!");
	}
	@Override
	public void find() {
		System.out.println("查找用户!");
	}
}

第三步:准备通知(编写通知类)

package cn.itcast.e_annotationaop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

//通知类
@Aspect
//表示该类是一个通知类
public class MyAdvice {
	@Pointcut("execution(* cn.itcast.service.*ServiceImpl.*(..))")
	public void pc(){}
	//前置通知
	//指定该方法是前置通知,并制定切入点
	@Before("MyAdvice.pc()")
	public void before(){
		System.out.println("这是前置通知!!");
	}
	//后置通知
	@AfterReturning("execution(* cn.itcast.service.*ServiceImpl.*(..))")
	public void afterReturning(){
		System.out.println("这是后置通知(如果出现异常不会调用)!!");
	}
	//环绕通知
	@Around("execution(* cn.itcast.service.*ServiceImpl.*(..))")
	public Object around(ProceedingJoinPoint pjp) throws Throwable {
		System.out.println("这是环绕通知之前的部分!!");
		Object proceed = pjp.proceed();//调用目标方法
		System.out.println("这是环绕通知之后的部分!!");
		return proceed;
	}
	//异常通知
	@AfterThrowing("execution(* cn.itcast.service.*ServiceImpl.*(..))")
	public void afterException(){
		System.out.println("出事啦!出现异常了!!");
	}
	//后置通知
	@After("execution(* cn.itcast.service.*ServiceImpl.*(..))")
	public void after(){
		System.out.println("这是后置通知(出现异常也会调用)!!");
	}
}

第四步:配置进行织入,将通知织入目标对象中

1)导入aop约束(这个百度就可以了。很简单,而且这个是spring框架等多种框架都需要知道的)

方法一:通过配置文件来实现

2)编写配置文件(自身习惯这样来编写,免得逻辑代码快过于混乱)

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

<!-- 准备工作: 导入aop(约束)命名空间 -->
<!-- 1.配置目标对象 -->
	<bean name="userService" class="cn.itcast.service.UserServiceImpl" ></bean>
<!-- 2.配置通知对象 -->
	<bean name="myAdvice" class="cn.itcast.d_springaop.MyAdvice" ></bean>
<!-- 3.配置将通知织入目标对象 -->
	<aop:config>
		<!-- 配置切入点 
			public void cn.itcast.service.UserServiceImpl.save() 
			void cn.itcast.service.UserServiceImpl.save()
			* cn.itcast.service.UserServiceImpl.save()
			* cn.itcast.service.UserServiceImpl.*()
			
			* cn.itcast.service.*ServiceImpl.*(..)
			* cn.itcast.service..*ServiceImpl.*(..)
		-->
		<aop:pointcut expression="execution(* cn.itcast.service.*ServiceImpl.*(..))" id="pc"/>
		<aop:aspect ref="myAdvice" >
			<!-- 指定名为before方法作为前置通知 -->
			<aop:before method="before" pointcut-ref="pc" />
			<!-- 后置 -->
			<aop:after-returning method="afterReturning" pointcut-ref="pc" />
			<!-- 环绕通知 -->
			<aop:around method="around" pointcut-ref="pc" />
			<!-- 异常拦截通知 -->
			<aop:after-throwing method="afterException" pointcut-ref="pc"/>
			<!-- 后置 -->
			<aop:after method="after" pointcut-ref="pc"/>
		</aop:aspect>
	</aop:config>
</beans>
3)测试
package cn.itcast.d_springaop;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import cn.itcast.bean.User;
import cn.itcast.service.UserService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:cn/itcast/d_springaop/applicationContext.xml")
public class Demo {
	@Resource(name="userService")
	private UserService us;
	
	@Test
	public void fun1(){
		us.save();
	}
	
}

方法二:通过注解实现(了解)

步骤:(1)编写配置文件(其实这个也可以通过注解来实现,具体的看前面知识点有提到)

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

<!-- 准备工作: 导入aop(约束)命名空间 -->
<!-- 1.配置目标对象 -->
	<bean name="userService" class="cn.itcast.service.UserServiceImpl" ></bean>
<!-- 2.配置通知对象 -->
	<bean name="myAdvice" class="cn.itcast.e_annotationaop.MyAdvice" ></bean>
<!-- 3.开启使用注解完成织入 -->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>

(2)编写通知类的注解

package cn.itcast.e_annotationaop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

//通知类
@Aspect
//表示该类是一个通知类
public class MyAdvice {
	@Pointcut("execution(* cn.itcast.service.*ServiceImpl.*(..))")
	public void pc(){}
	//前置通知
	//指定该方法是前置通知,并制定切入点
	@Before("MyAdvice.pc()")
	public void before(){
		System.out.println("这是前置通知!!");
	}
	//后置通知
	@AfterReturning("execution(* cn.itcast.service.*ServiceImpl.*(..))")
	public void afterReturning(){
		System.out.println("这是后置通知(如果出现异常不会调用)!!");
	}
	//环绕通知
	@Around("execution(* cn.itcast.service.*ServiceImpl.*(..))")
	public Object around(ProceedingJoinPoint pjp) throws Throwable {
		System.out.println("这是环绕通知之前的部分!!");
		Object proceed = pjp.proceed();//调用目标方法
		System.out.println("这是环绕通知之后的部分!!");
		return proceed;
	}
	//异常通知
	@AfterThrowing("execution(* cn.itcast.service.*ServiceImpl.*(..))")
	public void afterException(){
		System.out.println("出事啦!出现异常了!!");
	}
	//后置通知
	@After("execution(* cn.itcast.service.*ServiceImpl.*(..))")
	public void after(){
		System.out.println("这是后置通知(出现异常也会调用)!!");
	}
}

AOP实际开发用到的小例子:

(1)统计Action,Service,Dao每个类每个方法的使用次数

需求:在开发过程中,我们有时候需要统计一下执行某个类中的某个方法,它在一个调用过程中,执行了什么其他方法,并且执行每个方法的时间或者次数(用到的都是AOP),从而来进行性能的优化,所以这时候就使用AOP来进行分析就会得到比较好的处理。

(Action,service,dao的方法,我就不写了,自己按照开发模式来写就可以了),下面是AOP代理类的具体代码:

package com.hnu.scw.aop.xml.methodinvocation.count.aspect;
import java.util.HashMap;
import java.util.Map;
import org.aspectj.lang.JoinPoint;
public class MethodInvocationCount {
	private Map<String, Integer> count = new HashMap<String, Integer>();
	public void count(JoinPoint joinPoint){
		//获取的是目标类
		String targetClassName = joinPoint.getTarget().getClass().getSimpleName();
		//目标方法
		String targetMethodName = joinPoint.getSignature().getName();
		String key = targetClassName+":"+targetMethodName;
		if(count.containsKey(key)){
			Integer value = count.get(key);
			count.put(key, ++value);
		}else{
			count.put(key, 1);
		}
		System.out.println(targetClassName+"的"+targetMethodName+"方法被调用了"+count.get(key)+"次");
	}
}
再写一个测试类吧,这样大体就知道是干了什么

package com.hnu.scw.aop.xml.methodinvocation.count.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.itheima11.spring.aop.xml.methodinvocation.count.action.PersonAction;
import com.itheima11.spring.aop.xml.methodinvocation.count.service.PersonService;
public class MethodInvocationCountTest {
	@Test
	public void testCount(){
		ApplicationContext context = 
				new ClassPathXmlApplicationContext("applicationContext.xml");
		PersonAction personAction = (PersonAction)context.getBean("personAction"); //这里用的是xml配置bean,当然如果用注解也是一样,并没什么影响
		personAction.savePerson();
		personAction.updatePerson();
		PersonService personService = (PersonService)context.getBean("personService");
		personService.savePerson();
	}
}

(2)权限验证------------------------这个功能在系统中是很重要的吧。实现方法也有很多种,这里就用AOP来实现

步骤的话,主要分为如下几个步骤:步骤:
   1、准备service层和dao层的类和接口
   2、准备权限类Privilege
   3、准备注解@PrivilegeInfo
   4、准备注解解析器@PrivilegeInfo(name='')
        注解解析器应该把@PrivilegeInfo中的name属性的值解析出来
   5、切面
                    环绕通知
   6、spring的配置文件
   7、客户端

代码:(service和dao层就不多说了吧,这个随便写个crud都行)

Action类:就是调一下service中的方法,从而来说明权限验证的功能 

package com.hnu.scw.spring.aop.xml.privilege.action;
import com.hnu.scw.spring.aop.xml.privilege.service.PersonService;
public class PersonAction {
	private PersonService personService;

	public void setPersonService(PersonService personService) {
		this.personService = personService;
	}
	
	
	public void savePerson(){
		Object obj = this.personService.savePerson();//这个获取到调用了方法之后的返回值,在这里如果是error就表示权限验证失败了。
		System.out.println(obj);
	}
}

权限类:

package com.hnu.scw.spring.aop.xml.privilege.bean;
public class Privilege {
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
}
自定义权限注解:

package com.hnu.scw.spring.aop.xml.privilege.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PrivilegeInfo {
	String name() default "";
}
注解解析:

package com.hnu.scw.spring.aop.xml.privilege.annotation;
import java.lang.reflect.Method;
public class AnnotationParse {
	public static String parse(Class targetClass,String methodName) throws Exception{
		//目标方法
		Method method = targetClass.getMethod(methodName);
		String methodAccess = ""; //方法的权限的名称
		//判断目标方法上面是否存在@PrivilegeInfo注解
		//@Privilege(name="savePerson")
		if(method.isAnnotationPresent(PrivilegeInfo.class)){
			PrivilegeInfo privilegeInfo = method.getAnnotation(PrivilegeInfo.class);
			methodAccess = privilegeInfo.name();
		}
		return methodAccess;
	}
}
权限验证切面类:

package com.hnu.scw.spring.aop.xml.privilege.aspect;
import java.util.ArrayList;
import java.util.List;
import org.aspectj.lang.ProceedingJoinPoint;
import com.itheima11.spring.aop.xml.privilege.annotation.AnnotationParse;
import com.itheima11.spring.aop.xml.privilege.bean.Privilege;
public class AccessTargetMethod {
	private List<Privilege> userPrivilege = new ArrayList<Privilege>();//模拟一下能够验证通过的权限有哪些
	
	public List<Privilege> getUserPrivilege() {
		return userPrivilege;
	}

	public Object accessMethod(ProceedingJoinPoint joinPoint) throws Throwable{
		/*
		 * 得到目标类的class形式  
		 * 得到目标方法
		 */
		Class targetClass = joinPoint.getTarget().getClass();
		String targetMethodName = joinPoint.getSignature().getName();
		String methodAccess = AnnotationParse.parse(targetClass, targetMethodName);
		boolean flag = false;
		for (Privilege privilege: userPrivilege) {
			if(methodAccess.equals(privilege.getName())){//放行
				flag = true;
				break;
			}
		}
		if(flag){//放行
			return joinPoint.proceed();
		}else{
			return "error";
		}
	}
}
Spring配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:aop="http://www.springframework.org/schema/aop"
       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-4.2.xsd
           http://www.springframework.org/schema/aop 
           http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
   <bean id="personDao" class="com.hnu.scw.spring.aop.xml.privilege.dao.impl.PersonDaoImpl"></bean>
   <bean id="personService" class="com.hnu.scw.spring.aop.xml.privilege.service.impl.PersonServiceImpl">
   	  <property name="personDao">
   	  	<ref bean="personDao"/>
   	  </property>
   </bean>
   
   <bean id="personAction" class="com.hnu.scw.spring.aop.xml.privilege.action.PersonAction">
   		<property name="personService">
   			<ref bean="personService"/>
   		</property>
   </bean>
   
   <bean id="accessMethod" class="com.hnu.scw.spring.aop.xml.privilege.aspect.AccessTargetMethod"></bean>

	<aop:config>
		<aop:pointcut 
			expression="execution(* com.hnu.scw.spring.aop.xml.privilege.service.impl.*.*(..))" 
				id="perform"/>
		<aop:aspect ref="accessMethod">
			<aop:around method="accessMethod" pointcut-ref="perform"/>
		</aop:aspect>
	</aop:config>
</beans>
测试类:

package com.hnu.scw.spring.aop.xml.privilege.test;
import java.util.List;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.itheima11.spring.aop.xml.privilege.action.PersonAction;
import com.itheima11.spring.aop.xml.privilege.aspect.AccessTargetMethod;
import com.itheima11.spring.aop.xml.privilege.bean.Privilege;
import com.itheima11.spring.aop.xml.privilege.service.PersonService;

public class PrivilegeTest {
	@Test
	public void testPrivilege(){
		ApplicationContext context = 
				new ClassPathXmlApplicationContext("applicationContext.xml");
		//初始化用户的权限
		AccessTargetMethod accessTargetMethod = (AccessTargetMethod)context.getBean("accessMethod");
		List<Privilege> privileges = accessTargetMethod.getUserPrivilege();
		Privilege privilege = new Privilege();
		privilege.setName("savePerson");
		privileges.add(privilege);
		PersonAction personAction = (PersonAction)context.getBean("personAction");
		personAction.savePerson();
	}
}

十:spring整合jdbc

概念:spring中提供了一个可以操作数据库的对象(JDBCTemplate   JDBC模版对象),对象封装了jdbc技术

在这先提一下,如何用原始的方法进行操作,这个在基础中的连接池都有说过(就是非spring的开发代理模式)

public void fun1() throws Exception{
		
		//0 准备连接池
		ComboPooledDataSource dataSource = new ComboPooledDataSource();
		dataSource.setDriverClass("com.mysql.jdbc.Driver");
		dataSource.setJdbcUrl("jdbc:mysql:///testdata");
		dataSource.setUser("root");
		dataSource.setPassword("123456");
		//1 创建JDBC模板对象
		JdbcTemplate jt = new JdbcTemplate();
		jt.setDataSource(dataSource);
		//2 书写sql,并执行
		String sql = "insert into t_user values(null,'rose') ";
		jt.update(sql);
		
	}

     好了,上面的内容是不是在基础学习中都有类似的印象呢?确实,其实JdbcTemplate类对象和基础中的QueryRunner是类似的。下面就把上面代码实现的功能通过spring容器来进行演示。

通过spring整合jdbc操作的步骤(用实际例子来进行分析):

(1)导包:4+2+spring-aop包+spring-test包+junit4+JDBC驱动包+c3p0连接池包+spring-jdbc包+spring-tx事务包

(2)编写dao接口

package cn.itcast.a_jdbctemplate;

import java.util.List;

import cn.itcast.bean.User;

public interface UserDao {

	//增
	void save(User u);
	//删
	void delete(Integer id);
	//改
	void update(User u);
	//查
	User getById(Integer id);
	//查
	int getTotalCount();
	//查
	List<User> getAll();
}

(3)编写实现接口类

package cn.itcast.a_jdbctemplate;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;

import cn.itcast.bean.User;
//使用JDBC模板实现增删改查
public class UserDaoImpl  implements UserDao {
	private JdbcTemplate js ;
	@Override
	public void save(User u) {
		String sql = "insert into t_user values(null,?) ";
		js.update(sql, u.getName());
	}
	@Override
	public void delete(Integer id) {
		String sql = "delete from t_user where id = ? ";
		js.update(sql,id);
	}
	@Override
	public void update(User u) {
		String sql = "update  t_user set name = ? where id=? ";
		js.update(sql, u.getName(),u.getId());
	}
	@Override
	public User getById(Integer id) {
		String sql = "select * from t_user where id = ? ";
		return js.queryForObject(sql,new RowMapper<User>(){
			@Override
			public User mapRow(ResultSet rs, int arg1) throws SQLException {
				User u = new User();
				u.setId(rs.getInt("id"));
				u.setName(rs.getString("name"));
				return u;
			}}, id);
		
	}
	@Override
	public int getTotalCount() {
		String sql = "select count(*) from t_user  ";
		Integer count = js.queryForObject(sql, Integer.class);
		return count;
	}

	@Override
	public List<User> getAll() {
		String sql = "select * from t_user  ";
		List<User> list = js.query(sql, new RowMapper<User>(){
			@Override
			public User mapRow(ResultSet rs, int arg1) throws SQLException {
				User u = new User();
				u.setId(rs.getInt("id"));
				u.setName(rs.getString("name"));
				return u;
			}});
		return list;
	}
	public void setJs(JdbcTemplate js){this.js = js;}

}

(4)编写配置文件

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

<!-- 1.将连接池放入spring容器  (该连接池的配置不是通过properties来的,最原始的方法,通过properties的方法,在后面有写出来)-->
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
	<property name="jdbcUrl" value="jdbc.mysql:///testdata" ></property>      //数据库名
	<property name="driverClass" value="com.mysql.jdbc.Driver" ></property>  //驱动类型
	<property name="user" value="root" ></property>   //用户数据库名
	<property name="password" value="123456" ></property> //密码
</bean>

<!-- 2.将JDBCTemplate放入spring容器 -->
<bean name="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" >
	<property name="dataSource" ref="dataSource" ></property>
</bean>

<!-- 3.将UserDao放入spring容器 -->
<bean name="userDao" class="cn.itcast.a_jdbctemplate.UserDaoImpl" >
	<property name="js" ref="jdbcTemplate" ></property> 
</bean>
	
</beans>

(5)进行测试方法类(当然这个在实际开发中,就不需要了,只是方便测试方法是否正确)

package cn.itcast.a_jdbctemplate;

import java.beans.PropertyVetoException;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import cn.itcast.bean.User;

//演示JDBC模板
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo {
		@Resource(name="userDao")
	private UserDao ud;
	
	@Test
	public void fun1() throws Exception{
		
		//0 准备连接池
		ComboPooledDataSource dataSource = new ComboPooledDataSource();
		dataSource.setDriverClass("com.mysql.jdbc.Driver");
		dataSource.setJdbcUrl("jdbc:mysql:///hibernate_32");
		dataSource.setUser("root");
		dataSource.setPassword("1234");
		//1 创建JDBC模板对象
		JdbcTemplate jt = new JdbcTemplate();
		jt.setDataSource(dataSource);
		//2 书写sql,并执行
		String sql = "insert into t_user values(null,'rose') ";
		jt.update(sql);
		
	}
	
	@Test
	public void fun2() throws Exception{
		User u = new User();
		u.setName("tom");
		ud.save(u);
	}
	@Test
	public void fun3() throws Exception{
		User u = new User();
		u.setId(2);
		u.setName("jack");
		ud.update(u);
		
	}
	
	@Test
	public void fun4() throws Exception{
		ud.delete(2);
	}
	
	@Test
	public void fun5() throws Exception{
		System.out.println(ud.getTotalCount());
	}
	
	@Test
	public void fun6() throws Exception{
		System.out.println(ud.getById(1));
	}
	
	@Test
	public void fun7() throws Exception{
		System.out.println(ud.getAll());
	}
	
}

上面这个例子,相对于实际开发中对数据库的操作基本上已经很齐全了,增删改查都有,如果有什么特别的需求的话,自己按照类似的形式进行编写就可以了,关键是要自己看得懂哪个API是适合什么操作的。。。

好了,下面把在配置文件中读取properties文件的方法也写出来(实际开发中更习惯这一种,因为如果要修改数据库,那么只需要该properties中的内容就可以了,比单独修改配置文件内容方便很多)

properties文件代码(命名为:db.properties):

jdbc.jdbcUrl=jdbc:mysql:///hibernate_32
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.user=root
jdbc.password=1234

PS:其中上面的jdbc只是用来一个前缀而已,防止与其他的关键字的内容发送冲突,这个可以根据自己的习惯来自由定义即可。

spring配置文件代码(等价上面的配置文件):

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

<!-- 指定spring读取db.properties配置 -->
<context:property-placeholder location="classpath:db.properties"  />

<!-- 1.将连接池放入spring容器 -->
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
	<property name="jdbcUrl" value="${jdbc.jdbcUrl}" ></property>
	<property name="driverClass" value="${jdbc.driverClass}" ></property>
	<property name="user" value="${jdbc.user}" ></property>
	<property name="password" value="${jdbc.password}" ></property>
</bean>


<!-- 2.将JDBCTemplate放入spring容器 -->
<bean name="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" >
	<property name="dataSource" ref="dataSource" ></property>
</bean>

<!-- 3.将UserDao放入spring容器 -->
<bean name="userDao" class="cn.itcast.a_jdbctemplate.UserDaoImpl" >
	<property name="js" ref="jdbcTemplate" ></property> 	
</bean>
	

</beans>

十一:spring中的事务知识点

一:基本知识梳理

(1)事务特性:ACID------原子性,一致性,隔离性,持续性

(2)事务并发问题----------1:脏读(一个事务读取到另一个事务未提交的更新数据)不可重复读(在同一事务中,多次读取同一数据返回的结果有所不同)幻读(一个事务读取到另一个事务已提交的insert数据)

(3)事务隔离级别-------1:读未提交     2:读已提交         4:可重复读         8:串行化

(4)spring封装了事务管理代码:打开事务,提交事务,回滚事务

(5)spring提供了一个操作事务的接口:PlatformTransactionMnager接口。针对不同平台会提供不同的实现类。注意:在spring中玩事务管理,最为关键的核心对象就是TransactionManager对象

(6)spring管理事务的属性介绍:事务的隔离级别,是否只读(限制操作是否能修改数据库,true只读,false可操作),事务的传播行为(决定业务方法之间的调用,事务应该如何处理),其中的传播属性有如下所示:


二:搭建事务的环境(直接用代码分析)

Dao层内容:

public interface AccountDao {
	
	//加钱
	void increaseMoney(Integer id,Double money);
	//减钱
	void decreaseMoney(Integer id,Double money);
}

Dao层实现层:

import org.springframework.jdbc.core.support.JdbcDaoSupport;

public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao  {

	@Override
	public void increaseMoney(Integer id, Double money) {
		
		getJdbcTemplate().update("update t_account set money = money+? where id = ? ", money,id);
		
	}

	@Override
	public void decreaseMoney(Integer id, Double money) {

		getJdbcTemplate().update("update t_account set money = money-? where id = ? ", money,id);
	}

}

Service层接口:

package cn.itcast.service;

public interface AccountService {
	
	//转账方法
	void transfer(Integer from,Integer to,Double money);
	
}

service实现:

package cn.itcast.service;

import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

import cn.itcast.dao.AccountDao;

@Transactional(isolation=Isolation.REPEATABLE_READ,propagation=Propagation.REQUIRED,readOnly=true)
public class AccountServiceImpl implements AccountService {

	private AccountDao ad ;

	@Override
	public void transfer(final Integer from,final Integer to,final Double money) {			
				//减钱
				ad.decreaseMoney(from, money);
				//int i = 1/0;
				//加钱
				ad.increaseMoney(to, money);		
	}
	public void setAd(AccountDao ad) {
		this.ad = ad;
	}
}

spring配置文件:

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

<!-- 指定spring读取db.properties配置 -->
<context:property-placeholder location="classpath:db.properties"  />

<!-- 1.将连接池 -->
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
	<property name="jdbcUrl" value="${jdbc.jdbcUrl}" ></property>
	<property name="driverClass" value="${jdbc.driverClass}" ></property>
	<property name="user" value="${jdbc.user}" ></property>
	<property name="password" value="${jdbc.password}" ></property>
</bean>

<!-- 2.Dao-->
<bean name="accountDao" class="cn.itcast.dao.AccountDaoImpl" >
	<property name="dataSource" ref="dataSource" ></property>
</bean>
<!-- 3.Service-->
<bean name="accountService" class="cn.itcast.service.AccountServiceImpl" >
	<property name="ad" ref="accountDao" ></property>
</bean>  

</beans>

好了,上面就是比较正常的一个开发结构的代码了,但是不包含事务处理的内容,这时候运行的话,是能够正常运行的。但是如果在transfer方法中,出现异常,比如int  i = 1/0;那么转账的总数就会出现问题了。

PS:默认的Spring容器中,只对unchecked类型的异常(非编译时异常,如NullPointException,ArryaryOutOfException)进行事务的回滚,而对于checked类型异常(编译时异常,如IOException)是不进行回滚的,但是可以通过进行设置回滚的类型,来进行修改的哦。。所以也就是下面所要讲解的知识点了。

(1)spring管理事务的方式(三种)

方法一:编码式(了解)

spring配置文件:(基于上面的配置文件,添加下面的内容)

<!-- 事务核心管理器,封装了所有事务操作. 依赖于连接池 -->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
	<property name="dataSource" ref="dataSource" ></property>
</bean>
<!-- 事务模板对象 -->
<bean name="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate" >
	<property name="transactionManager" ref="transactionManager" ></property>
</bean>
service实现层代码:
package cn.itcast.service;

import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

import cn.itcast.dao.AccountDao;

@Transactional(isolation=Isolation.REPEATABLE_READ,propagation=Propagation.REQUIRED,readOnly=true)
public class AccountServiceImpl implements AccountService {

	private AccountDao ad ;
	private TransactionTemplate tt;
	@Override
	public void transfer(final Integer from,final Integer to,final Double money) {
		
		tt.execute(new TransactionCallbackWithoutResult() {
			@Override
			protected void doInTransactionWithoutResult(TransactionStatus arg0) {
				//减钱
				ad.decreaseMoney(from, money);
				int i = 1/0;
				//加钱
				ad.increaseMoney(to, money);
			}
		});				
	}
	public void setAd(AccountDao ad) {
		this.ad = ad;
	}

	public void setTt(TransactionTemplate tt) {
		this.tt = tt;
	}
}
所以,对于事务的处理,spring通过编码式就实现了,对于发生异常情况的处理的回滚操作。 很明显,这种方法碰到多个方法的时候,就需要写多个代码段(execute方法),这样必然显得很麻烦。

方法二:xml配置(aop)(必须掌握)

步骤(1)导包4(基础包)+4(事务包)


(2)配置新的约束(tx)


(3)spring中xml配置通知

(4)spring中xml配置织入

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

<!-- 指定spring读取db.properties配置 -->
<context:property-placeholder location="classpath:db.properties"  />

<!-- 事务核心管理器,封装了所有事务操作. 依赖于连接池 -->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
	<property name="dataSource" ref="dataSource" ></property>
</bean>
<!-- 事务模板对象 -->
<bean name="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate" >
	<property name="transactionManager" ref="transactionManager" ></property>
</bean>

<!-- 配置事务通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager" >
	<tx:attributes>
		<!-- 以方法为单位,指定方法应用什么事务属性
			isolation:隔离级别
			propagation:传播行为
			read-only:是否只读
		 -->
		<tx:method name="save*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
		<tx:method name="persist*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
		<tx:method name="update*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
		<tx:method name="modify*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
		<tx:method name="delete*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
		<tx:method name="remove*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
		<tx:method name="get*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="true" />
		<tx:method name="find*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="true" />
		<tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
	</tx:attributes>
</tx:advice>


<!-- 配置织入 -->
<aop:config  >
	<!-- 配置切点表达式 -->
	<aop:pointcut expression="execution(* cn.itcast.service.*ServiceImpl.*(..))" id="txPc"/>
	<!-- 配置切面 : 通知+切点
		 	advice-ref:通知的名称
		 	pointcut-ref:切点的名称
	 -->
	<aop:advisor advice-ref="txAdvice" pointcut-ref="txPc" />
</aop:config>


<!-- 1.将连接池 -->
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
	<property name="jdbcUrl" value="${jdbc.jdbcUrl}" ></property>
	<property name="driverClass" value="${jdbc.driverClass}" ></property>
	<property name="user" value="${jdbc.user}" ></property>
	<property name="password" value="${jdbc.password}" ></property>
</bean>

<!-- 2.Dao-->
<bean name="accountDao" class="cn.itcast.dao.AccountDaoImpl" >
	<property name="dataSource" ref="dataSource" ></property>
</bean>
<!-- 3.Service-->
<bean name="accountService" class="cn.itcast.service.AccountServiceImpl" >
	<property name="ad" ref="accountDao" ></property>
	<property name="tt" ref="transactionTemplate" ></property>
</bean>  

</beans>

方法三:注解配置(aop)(必须掌握)

步骤:(1)和(2)与spring配置文件方式一样

(3)spring配置文件中开启使用注解管理aop事务

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

<!-- 指定spring读取db.properties配置 -->
<context:property-placeholder location="classpath:db.properties"  />

<!-- 事务核心管理器,封装了所有事务操作. 依赖于连接池 -->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
	<property name="dataSource" ref="dataSource" ></property>
</bean>
<!-- 事务模板对象 -->
<bean name="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate" >
	<property name="transactionManager" ref="transactionManager" ></property>
</bean>

<!-- 开启使用注解管理aop事务 -->
<tx:annotation-driven/>

<!-- 1.将连接池 -->
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
	<property name="jdbcUrl" value="${jdbc.jdbcUrl}" ></property>
	<property name="driverClass" value="${jdbc.driverClass}" ></property>
	<property name="user" value="${jdbc.user}" ></property>
	<property name="password" value="${jdbc.password}" ></property>
</bean>

<!-- 2.Dao-->
<bean name="accountDao" class="cn.itcast.dao.AccountDaoImpl" >
	<property name="dataSource" ref="dataSource" ></property>
</bean>
<!-- 3.Service-->
<bean name="accountService" class="cn.itcast.service.AccountServiceImpl" >
	<property name="ad" ref="accountDao" ></property>
	<property name="tt" ref="transactionTemplate" ></property>
</bean>  

</beans>
(4)使用注解(在实现类方法中)
package cn.itcast.service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

import cn.itcast.dao.AccountDao;

@Transactional(isolation=Isolation.REPEATABLE_READ,propagation=Propagation.REQUIRED,readOnly=true)
public class AccountServiceImpl implements AccountService {

	private AccountDao ad ;
	private TransactionTemplate tt;
	
	@Override
	@Transactional(isolation=Isolation.REPEATABLE_READ,propagation=Propagation.REQUIRED,readOnly=false)
	public void transfer(final Integer from,final Integer to,final Double money) {
				//减钱
				ad.decreaseMoney(from, money);
				int i = 1/0;
				//加钱
				ad.increaseMoney(to, money);
	}
	public void setAd(AccountDao ad) {
		this.ad = ad;
	}

	public void setTt(TransactionTemplate tt) {
		this.tt = tt;
	}		
}

PS:对于注解方法的使用,可以使用在类的开头,这样就表示类中的方法都使用该注解。。如果想对于某个方法改变注解的形式,那么就可以单独的重新写一个注解。这两种在代码中都有体现。

---------------

十二:Spring的基本开发模式

   其实综合前面讲的这些内容,应该够基本的了解Spring的内容了,可能刚开始接触的时候会比较迷茫,感觉不是很懂,会觉得怎么那么多的东需要进行配置,然后还有这么多关联性的内容,但是当使用过后和认真分析过后,就会觉得Spring这个框架确实写得非常非常的好,看源码的时候就会发现里面很多Java的高阶知识,这在我其他的文章也会有提示到的哦。欢迎进行阅读。下面的话,就说一下Spring的一个基本的配置过程好了,这样的话,就有一个配置的基本流程,而不至于不知道从何做起。当然,这只是我个人的开发流程而已,只要适合自己就好了,主要还是要理解。。理解。。。再理解。。。。

(1)配置Bean元素

       1:可以采取XML的单独配置

2:可以采取扫描和注解结合的形式进行配置

(2)配置资源扫描------------主要就是对properties文件的扫描,这个的话主要是用于进行数据库的方便

(3)配置需要动态管理的资源-------同样可以用XML或者注解的形式

(4)配置数据源-----可以有JDBC,C3P0。。。等等

(5)配置事务管理

好了呢。。。这些就是基本的spring的知识点啦,,,期待着我持续的更新哟。。还有其他的文章,欢迎大家一起学习交流呢!!!

  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值