框架-Spring

Spring第一天

1. 传统的jdbc操作

package com.zwd.jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * jdbc操作
 * @author lenovo
 *
 */
public class JdbcDemo1 {

	/**
	 * 我们在开发中遵循的原则
	 *     编译时不依赖,运行时才依赖。
	 * 解决依赖关系:
	 *     使用反射
	 * 使用反射创建类对象引发的新问题:
	 *     使用的数据库写死了,如果更换数据库需要改源码,
	 *     重新编译,应写配置文件(使用配置文件,通过读取配置文件来反射创建类对象
	 * @param args
	 * @throws SQLException
	 * @throws ClassNotFoundException 
	 */
	public static void main(String[] args) throws SQLException, ClassNotFoundException {
		//1.加载驱动
		
		//有依赖关系,必须在有mysql驱动的前提下才能通过编译,否则编译不成功
		//DriverManager.registerDriver(new com.mysql.jdbc.Driver());
		
		Class.forName("com.mysql.jdbc.Driver");//使用的数据库写死了,如果更换数据库需要改源码,重新编译,应写配置文件
		
		
		//2.获取连接
		Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/wx?useSSL=false ","root","root");
		//3.获取操作数据库的预处理对象
		PreparedStatement ps = conn.prepareStatement("");
		//4.执行sql语句并获取返回结构
		ResultSet rs = ps.executeQuery();
		//5.封装结果集
		while(rs.next()) {
			System.out.println(rs.getString(""));
		}
		//6.释放资源
		rs.close();
		ps.close();
		conn.close();
	}
}

2. 降低程序间得依赖关系

(1) 通过反射来创建类对象。—降低耦合

(2)通过读取配置文件来获取类的全类名。—防止更改程序代码

(3)将创建好的对象放到集合中,解决单例情况下会创建多个不同的对象。

(4)通过静态代码块来初始化。

package com.zwd.factory;

import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;

import com.zwd.dao.CustomerDao;
import com.zwd.service.CustomerService;

/**
 * 一个工厂类
 * 
 * @author lenovo
 *
 */
public class BeanFactory {

//-------------------------------直接用ResourceBundle-------------------------	
/*	// 1.定义一个properties对象
	private static Properties pro = null;

	// 2.使用静态代码块给对象赋值
	static {
		try {
			InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
			//InputStream in = new FileInputStream("src/bean.properties");//绝对不能用,web工程一旦发布就没有src了	
			pro.load(in);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}*/
//-------------------------------------------------	

	//1.只能用于读取properties文件,
	//2.只能读,不能写
	//3.只能读取类路径下的文件,不在类路径下读取不了
	//注意:方法参数的写法是按照包名.类名的方式写的,所以不要写扩展名 com.zwd.bean
	private static ResourceBundle bundle = ResourceBundle.getBundle("bean");

//-------------------------解决在单列对象中会创建多个对象的问题-------------------------
	//定义一个容器,用于存放我们要使用的对象
	private static Map<String,Object> beans = new HashMap<>();
	//使用静态代码,初始化map
	static {
		
		try {
			//1.读取配置文件中所有的配置:key的部分
			Enumeration<String> keys = bundle.getKeys();
			//2.遍历keys
			while(keys.hasMoreElements()) {
				//3.取出一个key
				String key = keys.nextElement();
				//4.根据key获取beanPath
				String beanPath = bundle.getString(key);
				//5.根据beanPath反射创建类对象
				Object value = Class.forName(beanPath).newInstance();			
				//6.把key和value存入map中			
				beans.put(key,value);
			}
		} catch (Exception e) {
			throw new ExceptionInInitializerError("创建容器失败,程序停止执行!");
		}	
	}

	public static Object getBean(String beanName) {
		try {
			// 1.读取配置文件,根据beanName获取
			String beanPath = bundle.getString(beanName);
			return beans.get(beanName);
		
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
//---------------------------------------------------------------------------------	
	
	
//------------------------在单例对象时会对于多个请求会创建多个对象----------------------	

	/**
	 * 根据beanName的名称来创建类对象
	 * 
	 * @return
	 */
	/*
		public static Object getBean1(String beanName) {
		try {
			// 1.读取配置文件,根据beanName获取
			String beanPath = bundle.getString(beanName);
			return Class.forName(beanPath).newInstance();
		
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}*/
//------------------------------------------------------------------------------------
	
	
/*	public static CustomerService getCustomerService() {
		try {
			return (CustomerService) Class.forName("com.zwd.service.impl.CustomerServiceImpl").newInstance();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

		public static CustomerDao getCustomerDao() {
		try {
			return (CustomerDao) Class.forName("com.zwd.dao.impl.CustomerDaoImpl").newInstance();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}*/
}

3. 单例和多例中的线程安全问题

  • 在单例模式中:如果类中存在类成员变量,则不是线程安全的。因为单例中只会创建一个对象,每个线程都用的是一个对象,前一个线程更改了类成员变量,后一个线程则访问到的是前一个线程修改后的值。可以将类成员变量改为方法成员变量解决线程安全问题。

  • 在多例模式中:是线程安全的。每个线程都会创建自己的对象,每个线程之间的对象互不影响。

在这里插入图片描述

4. sping IOC开发环境

  • 导jar包

在这里插入图片描述

  • 在类路径下创建bean.xml,导约束,配置要入容器的类。

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd">
        
<!--配置资源:把对象的创建交给spring来管理-->
<bean id="customerSerivce" class="com.zwd.service.impl.CustomerServiceImpl"></bean>
<bean id="customerDao" class="com.zwd.dao.impl.CustomerDaoImpl"></bean>

</beans>

获取对象的方式:

package com.zwd.client;

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

import com.zwd.service.CustomerService;

public class Customer {
	/**
	 * ClassPathXmlApplicationContext:它只能加载类路径下(src下)的配置文件
	 * FileSystemXmlApplicationContext:它是可以加载磁盘任意位置的配置文件
	 */
	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
		CustomerService customerSerivce = (CustomerService) ac.getBean("customerSerivce");
		customerSerivce.test();
	}
}

5. bean创建的两种规则

在这里插入图片描述

  • BeanFactory:(延迟加载)提供的是一种延迟加载思想来创建Bean对象。Bean对象什么时候用什么时候创建。
  • ApplicationContext:(立即加载) 提供的是一种立即加载思想来创建Bean对象。只要解析完配置文件,就立马创建bean对象。
package com.zwd.ui;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import com.zwd.dao.impl.CustomerDaoImpl;
import com.zwd.service.impl.CustomerServiceImpl;
/**
 * Spring的入门案例
 * @author lenovo
 *
 */
public class Client {

	/**
	 * ClassPathXmlApplicationContext:它只能加载类路径下(src下)的配置文件
	 * FileSystemXmlApplicationContext:它是可以加载磁盘任意位置的配置文件
	 * Bean创建的两种规则:
	 *      BeanFactory:(延迟加载)提供的是一种延迟加载思想来创建bean对象。Bean对象什么时候用什么                          时候创建。
     *      ApplicationContext:(立即加载) 提供的是一种立即加载思想来创建Bean对象。只要解析完配									置文件,就立马创建bean对象。
     *Bean创建的三种方式:    
     *    第一种方式:调用默认无参构造函数创建(此种方式用的最多)
     *           默认情况下,如果类中没有默认无参构造函数,则创建失败,会报异常。
     *    第二种方式:使用静态工厂中的方法创建对象。
                                  需要使用bean标签的factory-method属性,指定静态工厂中创建对象的方法。
     *    第三种方式:使用实例工厂中的方法创建。
	 */
	
	/*立即加载*/
	@SuppressWarnings("all")
	public static void main(String[] args) {
		//就在src根目录下,否则com/...
		ApplicationContext ac =new ClassPathXmlApplicationContext("bean.xml");
		//ClassPathXmlApplicationContext ac =new ClassPathXmlApplicationContext("bean.xml");
		
		//通过id来获取对象
		CustomerServiceImpl service = (CustomerServiceImpl)ac.getBean("customerService1");
		//CustomerDaoImpl dao = (CustomerDaoImpl)ac.getBean("customerDao");
		service.saveCustomer();
		//ac.close();
		//System.out.println(service);
		//System.out.println(dao);
	}

	
/*	
	//延迟加载
	@SuppressWarnings("all")
	public static void main(String[] args) {
		Resource resource = new ClassPathResource("bean.xml");
		BeanFactory factory = new XmlBeanFactory(resource);
		//通过id来获取对象
		CustomerServiceImpl service = (CustomerServiceImpl)factory.getBean("customerService");
		//CustomerDaoImpl dao = (CustomerDaoImpl)ac.getBean("customerDao");
		service.saveCustomer();
		//System.out.println(service);
		//System.out.println(dao);
	}*/
}

6. bean的三种实例化方式

bean的三种创建方式:

  • 第一种方式:调用默认无参构造函数创建(此种方式用的最多)
    ​ 默认情况下,如果类中没有无参构造函数,则创建失败,会报异常。
  • 第二种方式:使用静态工厂中的方法创建对象。
    ​ 需要使用bean标签的factory-method属性,指定静态工厂中创建对象的方法。
  • 第三种方式:使用实例工厂中的方法创建。
<?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">
        
<!--配置资源:把对象的创建交给spring来管理-->
<bean id="customerSerivce" class="com.zwd.service.impl.CustomerServiceImpl"></bean>
<bean id="customerDao" class="com.zwd.dao.impl.CustomerDaoImpl"></bean>

<!-- 使用静态工厂创建bean -->
<bean id="staticCustomerSerive" class="com.zwd.factory.StaticFactory" factory-method="getCustomerService"></bean>

<!-- 使用实例工厂创建bean -->
<bean id="instanceFactory" class = "com.zwd.factory.InstanceFactory"></bean>
<bean id="instanceCustomerService" factory-bean="instanceFactory" factory-method="getCustomerService"></bean>

</beans>

静态工厂

package com.zwd.factory;

import com.zwd.service.CustomerService;
import com.zwd.service.impl.CustomerServiceImpl;

public class StaticFactory {
	public static CustomerService getCustomerService(){
		return new CustomerServiceImpl();
	}
}

实例工厂

package com.zwd.factory;

import com.zwd.service.CustomerService;
import com.zwd.service.impl.CustomerServiceImpl;

public class InstanceFactory {
	public  CustomerService getCustomerService(){
		return new CustomerServiceImpl();
	}
}

7.bean的作用范围

  • 单例对象的作用范围应该是整个程序,多例对象的作用范围应该是线程的一次使用过程。
  • bean的作用范围:它可以通过配置的方式来调整作用范围。
    • 配置的属性:bean标签的scope属性。
    • 属性的取值:
      • singleton:单例的(默认值)
      • prototype:多例的(当我们让spring接管struts2的action创建时,action必须配置此值)
      • request:作用范围是一次请求,和当前请求的转发。
      • session :作用范围是一次会话。
      • globalsession:作用范围是一次全局会话。
       
<!--配置资源:把对象的创建交给spring来管理-->
<bean id="customerSerivce" class="com.zwd.service.impl.CustomerServiceImpl" scope="prototype"></bean>

8. bean的生命周期

  • 涉及bean标签的两个属性:

    • init-method:创建对象会执行的方法。
    • destroy-method:销毁对象会执行的方法。
  • 单例对象的生命周期:

    • 出生:容器创建,对象就出生了。
    • 活着:只要容器在,对象就一直存在。
    • 死亡:容器销毁,对象消亡。
  • 多例对象的生命周期:

    • 出生:每个线程使用时,创建对象。
    • 活着:只要对象在使用中,就一直活着。
    • 死亡:当对象长时间不使用,并且也没有别的对象引用时,由java垃圾回收器回收。
  • 单例的例子

    首先在类中创建两个方法:init()、destroy()

package com.zwd.service.impl;

import com.zwd.dao.CustomerDao;
import com.zwd.service.CustomerService;

public class CustomerServiceImpl implements CustomerService {
	private CustomerDao customerDao = null;
	public CustomerServiceImpl(){
		System.out.println("service创建");
	}
	public void test(){
		System.out.println("service");
	}
	
	public void init(){
		System.out.println("对象创建了");
	}
	public void destroy(){
		System.out.println("对象销毁了");
	}
}

​ bean的配置文件

<bean id="customerSerivce" class="com.zwd.service.impl.CustomerServiceImpl" scope="singleton" init-method="init" destroy-method="destroy"></bean>

​ 测试

package com.zwd.client;

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

import com.zwd.service.CustomerService;

public class Customer {
	/**
	 * ClassPathXmlApplicationContext:它只能加载类路径下(src下)的配置文件
	 * FileSystemXmlApplicationContext:它是可以加载磁盘任意位置的配置文件
	 */
	public static void main(String[] args) {
//		ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
		ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
		CustomerService customerSerivce = (CustomerService) ac.getBean("customerSerivce");
		
		//销毁容器的方法close在ClassPathXmlApplicationContext中,
		//所以容器需要销毁时需要声明ClassPathXmlApplicationContext的对象
		ac.close();
	}
}

9.Spring的依赖注入

  • 注入的方式有三种:

    • 构造函数注入

      • 涉及的标签:constructor-arg

      • 标签的属性:

              type :指定参数的类型。
              Index :指定参数的索引位置,从0开始。
              name : 指定参数的名称。(一般用它)
        ===========上面三个属性指定的是给哪个参数赋值,下面两个属性指定的是参数的值============
              value :指定基本数据类型或String类型的数据
              ref : 指定其他bean类型的数据
        
      • 标签的位置: 写在bean标签的内部。

      首先给类创建一个构造函数

    package com.zwd.service.impl;
    
    import java.util.Date;
    
    import com.zwd.service.CustomerService;
    
    public class CustomerServiceImpl implements CustomerService {
    
    	private String driver;
    	private Integer port;
    	private Date today;
    	
    	public CustomerServiceImpl(String driver, Integer port, Date today) {
    		this.driver = driver;
    		this.port = port;
    		this.today = today;
    	}
    
    	@Override
    	public void test() {
    	}
    }
    

    ​ 在bean.xml中配置

    <!-- 构造函数注入 -->
    <bean id="customerSerivce" class="com.zwd.service.impl.CustomerServiceImpl">
        <constructor-arg name="driver" value="com.mysql.jdbc.Driver"></constructor-arg>
        <constructor-arg name="port" value="3306" type="java.lang.Integer">          </constructor-arg>
        <constructor-arg name="today" ref="now"></constructor-arg>
    </bean>
    <bean id="now" class="java.util.Date"></bean>
    
  • set方法注入

    • 涉及的标签:property

    • 标签的属性:

       name : 指定参数的名称。
       value :指定基本数据类型或String类型的数据
       ref : 指定其他bean类型的数据
      
    • 标签出现的位置:写在bean标签的内部。

    首先为类中的属性创建set方法

    package com.zwd.service.impl;
    
    import java.util.Date;
    
    import com.zwd.service.CustomerService;
    
    public class CustomerServiceImpl implements CustomerService {
    
    	private String driver;
    	private Integer port;
    	private Date today;
    	
    	public void setDriver(String driver) {
    		this.driver = driver;
    	}
    
    	public void setPort(Integer port) {
    		this.port = port;
    	}
    
    	public void setToday(Date today) {
    		this.today = today;
    	}
    
    	@Override
    	public void test() {
    		System.out.println("CustomerServiceImpl [driver=" + driver + ", port=" + port + ", today=" + today + "]");
    	}
    
    	@Override
    	public String toString() {
    		return "CustomerServiceImpl [driver=" + driver + ", port=" + port + ", today=" + today + "]";
    	}	
    }
    

    配置bean.xml

    <!--  set方法注入  -->
    <bean id = "customerSerivce" class="com.zwd.service.impl.CustomerServiceImpl">
        <property name="driver" value="com.mysql.jdbc.driver"></property>
        <property name="port" value="3306"></property>
        <property name="today" ref="now"></property>
    </bean>
    <bean id="now" class="java.util.Date"></bean>
    
  • 使用注解注入

  • 注入的数据类型有3类:

    • 第一类:基本数据类型和String类型
    • 第二类:其它bean类型(必须是在spring的配置文件中出现过的bean)
    • 第三类:复杂类型(集合类型)

10. 复杂类型的注入方式

  • 通过set方法注入

    package com.zwd.service.impl;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.Map;
    import java.util.Properties;
    import java.util.Set;
    
    import com.zwd.service.CustomerService;
    
    public class CustomerServiceImpl implements CustomerService {
    
    	private String[] mystr;
    	private List<String> myList;
    	private Set<String> mySet;
    	private Map<String,String> myMap;
    	private Properties pros;
    	
    	
    	public void setMystr(String[] mystr) {
    		this.mystr = mystr;
    	}
    
    	public void setMyList(List<String> myList) {
    		this.myList = myList;
    	}
    
    	public void setMySet(Set<String> mySet) {
    		this.mySet = mySet;
    	}
    
    	public void setMyMap(Map<String, String> myMap) {
    		this.myMap = myMap;
    	}
    
    	public void setPros(Properties pros) {
    		this.pros = pros;
    	}
    
    	@Override
    	public void test() {
    		System.out.println(Arrays.toString(mystr));
    		System.out.println(myList);
    		System.out.println(mySet);
    		System.out.println(myMap);
    		System.out.println(pros);
    	}
    }
    
  • bean.xml配置

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="
            http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans.xsd">
            
    <!--复杂类型注入-->
    <bean id = "customerSerivce" class="com.zwd.service.impl.CustomerServiceImpl">
         <!-- 数组 -->
         <property name="mystr">
              <array>
                 <value>AAA</value>
                 <value>BBB</value>
                 <value>CCC</value>
              </array>
         </property>
         <!-- list -->
         <property name="myList">
              <list>
                 <value>AAA</value>
                 <value>BBB</value>
                 <value>CCC</value>
              </list>
         </property>
         <!-- set -->
         <property name="mySet">
              <set>
                 <value>AAA</value>
                 <value>BBB</value>
                 <value>CCC</value>
              </set>     
         </property>
         <!-- map -->
         <property name="myMap">
              <map>
                 <entry key="AAA" value="AAA"></entry>
                 <entry key="BBB" value="BBB"></entry>
                 <entry key="CCC" value="CCC"></entry>
              </map>
         </property>
         <!-- properties -->
         <property name="pros">
              <props>
                 <prop key="AAA">AAA</prop>
                 <prop key="BBB">BBB</prop>
                 <prop key="CCC">CCC</prop>
              </props>
         </property>
    </bean>
    </beans>
    

Spring第二天

1. customer环境准备

  • 数据库:

    /*
    Navicat MySQL Data Transfer
    
    Source Server         : ZWD
    Source Server Version : 50711
    Source Host           : localhost:3306
    Source Database       : spring
    
    Target Server Type    : MYSQL
    Target Server Version : 50711
    File Encoding         : 65001
    
    Date: 2019-07-08 10:44:53
    */
    
    SET FOREIGN_KEY_CHECKS=0;
    
    -- ----------------------------
    -- Table structure for `cst_customer`
    -- ----------------------------
    DROP TABLE IF EXISTS `cst_customer`;
    CREATE TABLE `cst_customer` (
      `cust_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)',
      `cust_name` varchar(32) NOT NULL COMMENT '客户名称(公司名称)',
      `cust_source` varchar(32) DEFAULT NULL COMMENT '客户信息来源',
      `cust_industry` varchar(32) DEFAULT NULL COMMENT '客户所属行业',
      `cust_level` varchar(32) DEFAULT NULL COMMENT '客户级别',
      `cust_address` varchar(128) DEFAULT NULL COMMENT '客户联系地址',
      `cust_phone` varchar(64) DEFAULT NULL COMMENT '客户联系电话',
      PRIMARY KEY (`cust_id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=94 DEFAULT CHARSET=utf8;
    
    -- ----------------------------
    -- Records of cst_customer
    -- ----------------------------
    
  • 导包

在这里插入图片描述

  • 创建Customer类、service、dao

2.Spring管理MVC -xml

  • 导jar包

在这里插入图片描述

  • 创建db.properties文件,也可以不创建,直接再bean.xml中写

    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/spring
    jdbc.username=root
    jdbc.password=root
    
  • 创建bean.xml文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans 
                               http://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/context 
                               http://www.springframework.org/schema/context/spring-context.xsd">
    
    <!-- 配置service -->
    <bean id="customerService" class="com.zwd.service.impl.CustomerServiceImpl">
        <property name="customerDao" ref="customerDao"></property>
    </bean>
    
    <!-- 配置dao -->
    <bean id="customerDao" class="com.zwd.daoimpl.CustomerDaoImpl">
        <property name="runner" ref="runner"></property>
    </bean>
    
    <!-- 配置queryRunner -->
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner">
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>
    
    <!-- 加载数据源配置文件 -->
    <context:property-placeholder location="classpath:db.properties"/>
    <!-- 配置c3p0数据源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"/>
    	<property name="user" value="${jdbc.username}"/>
    	<property name="password" value="${jdbc.password}"/>
    </bean>
    
    </beans>
    
  • 测试

    @Test
    	public void testFindAllCustomer() {
    		ApplicationContext ac= new ClassPathXmlApplicationContext("bean.xml");
    		ICustomerService service = (ICustomerService) ac.getBean("customerService");
    		List<Customer> list = service.findAllCustomer();
    		for (Customer customer : list) {
    			System.out.println(customer);
    		}
    	}
    

3. Spring的常用注解

3.1 用于创建bean对象的注解
  • @Component
    • 作用:相当于配置了一个bean标签
    • 出现的位置:类上面。
    • 属性:value。含义是指bean的id。当不写时,它有默认值,默认值是:当前类的短名首字母改小写。
  • 有此注解衍生的的三个注解:和Component的作用以及属性都是一模一样的。
    • @Controller:一般用于控制层
    • @Service:一般用于业务层
    • @Repository:一般用于持久层
3.2 用于注入bean类型数据的注解
  • @Autowired

    作用:自动按照类型注入。只要有唯一的类型匹配就能注入成功。

    ​ 如果注入的bean在容器中的类型不唯一时,它会把变量名称作为bean的id,在容器中查找,找到后也能注入成功。

    ​ 当我们使用注解注入时,set方法就不是必须的了。

在这里插入图片描述

  • @Qualifier("")

    作用:在自动按照类型注入的基础之上,再按照bean的id注入。它给类成员注入数据时,不能独立使用。但是再给方法的形参注入数据时,可以独立使用。

    属性:value:用于指定bean的id

  • @Resource(name="")

    作用:直接按照bean的id注入。

    属性:name:用于指定bean的id。(name标识不可省略)

  	@Autowired
  	@Qualifier("dao")
  	//@Resource(name="dao") 
  	private CustomerDao customerDao;
3.3 用于注入基本数据类型和String类型的数据
  • @Value("")

    作用:它可以借助spring的el表达式读取properties文件中的配置。

    属性:value:用于指定要注入的数据。

3.4 用于改变作用范围的
  • @Scope("")

    • 作用:用于改变bean的作用范围
    • 属性:

    ​ value:用于指定范围的取值。

    ​ 取值:和xml中scope属性的取值是一样的。singleton、prototype、request、session、globalsession。

4.将作业中的xml配置改为注解和xml混合

5.只使用注解

5.1 Spring的新注解
  • @Configuration:

    • 作用:把当前类看成spring的配置类(相当于bean.xml)
  • @ComponentScan("")

    • 作用:用于指定要扫描的包
    • 属性:关联源码看
    • 相当于:<context:component-scan base-package=“com.zwd”> </context:component-scan>
  • @Bean(name="")

    • 它是把方法的返回值存入spring的容器中。该注解有一个属性,name:用于指定bean的id.当不指定时它有默认值,默认值是方法的名称。
  • @Import({}):

    • 导入其他配置类
  • @PropertySource({""})

    • 加载配置文件,可以在使用value注解时注入数据时使用el表达式${}

    • 相当于:<context:property-placeholder location=“classpath:db.properties”/>

    • 在spring4.3之前,使用el表达式需要配置 一个解析器

在这里插入图片描述在这里插入图片描述

  • @Qualifier("")
    • 作用:为方法的形参注入数据。当一个带参数的方法使用@Bean将返回值注入到容器时,spring首先会根据形参的类型查找bean对象,如果指定了@Qualifier(""),就会根据指定的id查找。
5.2 使用纯注解
  • 首先创建一个spring的配置类,相当于bean.xml

在这里插入图片描述

  • 解决bean.xml中扫描的包

在这里插入图片描述
在这里插入图片描述

  • 解决Runner和dataSource

在这里插入图片描述

在这里插入图片描述

  • 测试:将加载配置文件的方式该为使用注解加载

    ApplicationContext ac= new AnnotationConfigApplicationContext(SpringConfiguration.class);
    
改进 @PropertitySource的使用
  • 改进:将配置文件中的jdbc操作提出来。在SpringConfiguration配置文件中导入JdbcConfiguration配置类(@Import)。再使用@PropertitySource 加载数据库的配置文件,在JdbcConfiguration配置类中使用el表达式注入String类型的数据。

在这里插入图片描述

package config;

import java.beans.PropertyVetoException;

import javax.sql.DataSource;

import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;

import com.mchange.v2.c3p0.ComboPooledDataSource;
/**
 * JDBC配置类
 * @author Administrator
 *
 */
public class JdbcConfiguration {
	@Value("${jdbc.driver}")
	private String driver;
	@Value("${jdbc.url}")
	private String url;
	@Value("${jdbc.username}")
	private String user;
	@Value("${jdbc.password}")
	private String password;
	
	@Bean(name="runner")
	public QueryRunner runner(@Qualifier("ds") DataSource ds){
		return new QueryRunner(ds);
	}
	
	@Bean(name="ds")
	public DataSource dataSource(){
		ComboPooledDataSource ds = new ComboPooledDataSource();
		try {
			ds.setDriverClass(driver);
			ds.setJdbcUrl(url);
			ds.setUser(user);
			ds.setPassword(password);
			return ds;
		} catch (PropertyVetoException e) {
			throw new RuntimeException();
		}
	}
}

6.Spring整合Junit单元测试

步骤:

​ (1)导Spring提供的整合jar包。spring-test-4.2.4.RELEASE.jar

​ (2)使用JUnit 提供的一个注解,把原有的main函数替换掉,换成spring提供的。

@RunWith

​ 要换的类:SpringJunit4ClassRunner

​ (3)使用spring提供的注解告知spring,配置文件或者注解类所在的位置。

@ContextConfiguration

package com.zwd.service.test;

import static org.junit.Assert.fail;

import java.util.List;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.zwd.domain.Customer;
import com.zwd.service.ICustomerService;

import config.SpringConfiguration;

/**
 * Spring整合Junit
 * @author Administrator
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)//替换junit默认的main函数
@ContextConfiguration(classes={SpringConfiguration.class})//纯注解的spring配置
//@ContextConfiguration(locations={"classpath:bean.xml"})//有配置文件的加载
public class CustomerServiceImplTest {

	@Autowired
	ICustomerService service ;
	@Test
	public void testFindAllCustomer() {
//		ApplicationContext ac= new AnnotationConfigApplicationContext(SpringConfiguration.class);
//		ICustomerService service = (ICustomerService) ac.getBean("customerService");
		List<Customer> list = service.findAllCustomer();
		for (Customer customer : list) {
			System.out.println(customer);
		}
	}
}

Spring第三天

动态代理:
​ 作用:不改变源码的基础上,对已有方法增强。(他是AOP思想的实现技术)
​ 分类:基于接口的动态代理、基于子类的动态代理。

1.基于接口的动态代理(JDK动态代理)

要求:被代理类最少实现一个接口。
提供者:JDK官方。
涉及的类:Proxy。
创建代理对象的方法:newProxyInstance(ClassLoader,Class[],InvocationHandler)
​ 参数的含义:
​ ClassLoader:类加载器。和被代理对象使用相同的类加载器。一般都是固定写法。
​ Class[]:字节码数组。被代理类实现的接口。(要求代理对象和被代理对象具有相同的行为)。
​ InnvocationHandler:它是一个接口,就是用于我们提供增强代码的。我们一般都是写一个接口的实现类。
​ 它的含义就是:如何代理。此处的代码只能是谁用谁提供。

  • 例子:

    接口:

package com.zwd.基于接口的动态代理;

/**
 * 经纪公司对签约艺人的标准
 * @author lenovo
 *
 */
public interface IActor {
	/**
	 * 基本的表演
	 * @param money
	 */
	public void basicAct(float money);
	/**
	 * 危险的表演
	 * @param money
	 */
	public void dangerAct(float money);
}

实现类:

package com.zwd.基于接口的动态代理;
/**
 * 一个演员
 * @author lenovo
 *
 */
public class Actor implements IActor{
	/**
	 * 基本的表演
	 * @param money
	 */
	public void basicAct(float money) {
		System.out.println("拿到钱,开始基本的表演:"+money);
	}
	/**
	 * 危险的表演
	 * @param money
	 */
	public void dangerAct(float money) {
		System.out.println("拿到钱开始危险的表演:"+money);
	 
	}
}

动态代理:使用内部类的方式。

package com.zwd.基于接口的动态代理;

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

/**
 * 模拟一个剧组
 * @author lenovo
 */
public class Client {
	public static void main(String[] args) {
		final Actor actor = new Actor();
		//actor.basicAct(100f);		
		//actor:被代理对象;proxyActor代理对象
		IActor proxyActor = (IActor)Proxy.newProxyInstance(actor.getClass().getClassLoader(), 
				               actor.getClass().getInterfaces(),
				               new InvocationHandler() {		                   
			                    /**
			                     * 执行该代理对象的任何方法都会经过该方法,该方法有拦截的功能
			                     * 方法的参数:
			                     *     Object proxy:代理对象的引用。不一定每次都有。
			                     *     Method method:当前执行的方法。
			                     *     Object[] args:当前执行方法所需的参数。
			                     * 返回值:
			                     *     当前执行方法的返回值。
			                     */			
								@Override
								public Object invoke(Object proxy, Method mothod, Object[] args) throws Throwable {
									Object result = null;
									//1.取出执行方法中的参数,给的多少钱
									float money = (float)args[0];
									//2.判断钱的多少,以及要求演员所要做的表演
									if("basicAct".equals(mothod.getName())) {
										//基本演出
										if(money > 3000) {
											//执行方法
											result = mothod.invoke(actor, money/2);
										}
									}
									if("dangerAct".equals(mothod.getName())){
										//危险演出
										if(money > 60000) {
											//执行方法
											result = mothod.invoke(actor, money/2);
										}
									}
									return result;
								}});
		proxyActor.basicAct(40000);
		proxyActor.dangerAct(90000);	
	}
}

或者:继承InvocationHandler接口

package com.zwd.基于接口的动态代理;

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

public class Client1 {
	public static void main(String[] args) {
		//被代理对象
		Actor actor = new Actor();
		//代理对象
		IActor proxyIActor = (IActor) Proxy.newProxyInstance(actor.getClass().getClassLoader(), actor.getClass().getInterfaces(),new MyInvocationHandler(actor));

		proxyIActor.basicAct(2000);
	}
}

class MyInvocationHandler implements InvocationHandler{
	private Object obj;//被代理对象(要代理的原始对象)
	
	public MyInvocationHandler(Object obj) {
		super();
		this.obj = obj;
	}

    /**
     * 执行该代理对象的任何方法都会经过该方法,该方法有拦截的功能
     * 方法的参数:
     *     Object proxy:代理对象的引用。不一定每次都有。
     *     Method method:当前执行的方法。
     *     Object[] args:当前执行方法所需的参数。
     * 返回值:
     *     当前执行方法的返回值。
     */	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object result = null;
		float money = (float) args[0];
		if("basicAct".equals(method.getName())){
			if(money > 2000){
				 result = method.invoke(obj, money/2);
			}
			else{
				System.out.println("基本演出费不够!!");
			}
		}
		if("dangerAct".equals(method.getName())){
			if(money > 5000){
				result = method.invoke(obj, money-1000);
			}
			else{
				System.out.println("危险演出费不够!!!");
			}
		}
		return result;
	}
}

2.基于子类的动态代理(CGLIB动态代理)

基于子类的动态代理:
​ 要求:被代理类不能是最终类。不能被final修饰
​ 提供者:第三方CGLib
​ 涉及的类:Enhancer
​ 创建代理对象的方法:create(Class,Callback);
​ 参数含义:
​ Class:被代理对象的字节码
​ Callback:如何代理,它和InvocationHandler的作用是一样的。它也是一个接口,
​ 我们一般使用该接口MethodInterceptor
​ 在使用时我们也是创建该接口的匿名内部类。

package com.zwd.基于子类的动态代理;
/**
 * 一个演员
 * @author lenovo
 *
 */
public class Actor{
	/**
	 * 基本的表演
	 * @param money
	 */
	public void basicAct(float money) {
		System.out.println("cglib拿到钱,开始基本的表演:"+money);
	}
	/**
	 * 危险的表演
	 * @param money
	 */
	public void dangerAct(float money) {
		System.out.println("cglib拿到钱开始危险的表演:"+money);
	 
	}
}
package com.zwd.基于子类的动态代理;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;



/**
 * 模拟一个剧组
 * @author lenovo
 * 
 *动态代理:
          作用:不改变源码的基础上,对已有方法增强。(他是AOP思想的实现技术)
          分类:
       1.基于接口的动态代理:
                           要求:被代理类最少实现一个接口。
                          提供者:JDK官方。
                         涉及的类:Proxy。
                        创建代理对象的方法:newProxyInstance(ClassLoader,Class[],InvocationHandler)
                       参数的含义:
             ClassLoader:类加载器。和被代理对象使用相同的类加载器。一般都是固定写法。
             Class[]:字节码数组。被代理类实现的接口。(要求代理对象和被代理对象具有相同的行为)。
             InnvocationHandler:它是一个接口,就是用于我们提供增强代码的。我们一般都是写一个接口的实现类。
                                  它的含义就是:如何代理。此处的代码只能是谁用谁提供。
                                  策略模式:
                                       使用要求:数据已经有了
                                                 目的明确
                                                 达成目标的过程就是策略
       2.基于子类的动态代理:
                         要求:被代理类不能是最终类。不能被final修饰
                         提供者:第三方CGLib
                         涉及的类:Enhancer
                         创建代理对象的方法:create(Class,Callback);
                         参数含义:
                 Class:被代理对象的字节码
                 Callback:如何代理,它和InvocationHandler的作用是一样的。它也是一个接口,
                                                    我们一般使用该接口MethodInterceptor
                                                    在使用时我们也是创建该接口的匿名内部类。
 * 
 *
 */
public class Client {
	public static void main(String[] args) {
		final Actor actor = new Actor();
		//actor.basicAct(100f);

		Actor cgLibActor = (Actor) Enhancer.create(actor.getClass(),new MethodInterceptor() {

			/**
			 * 前三个参数和基于接口()invoke方法的参数一致
			 * MethodProxy methodProxy:当前执行方法的代理对象
			 */
			@Override
			public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
				Object result = null;
				//1.获取参数值,money
				float money = (float) args[0];
				//2.判断执行的是哪个方法以及给的钱的多少
				if("basicAct".equals(method.getName())){
					//基本演出
					if(money > 30000) {
						result = method.invoke(actor,money/2);
					}
				}
				if("dangerAct".equals(method.getName())){
					//危险演出
					if(money > 60000) {
						result = method.invoke(actor, money/2);
					}
				}
				return result;
			}});
		
		cgLibActor.basicAct(40000);
		cgLibActor.dangerAct(80000);
	}
}

3. 自定义连接池

增强代码的时候可以使用装 饰者模式 和 动态代理 。

dpcp:动态代理,获取的连接是代理对象。如下。

c3p0:静态代理,装饰者模式。

  • 导包:mysql-connector-java-5.1.39-bin.jar

  • 代码:

    dbconfig.properties

    #数据库连接的配置
    
    #数据库驱动
    DRIVER=com.mysql.jdbc.Driver
    
    #连接字符串
    URL=jdbc:mysql://localhost:3306/spring
    
    #数据库用户名
    USER=root
    
    #数据库密码
    PASSWORD=root
    

    JDBCUtil.java

    package com.zwd.utils;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.Statement;
    import java.util.ResourceBundle;
    
    /**
     * 数据库操作相关的工具类
     * @author zhy
     *
     */
    public class JDBCUtil {
    	
    	//使用ResourceBundle读取资源文件
    	private static ResourceBundle bundle = ResourceBundle.getBundle("dbconfig");
    	
    	private static String driver;
    	private static String url;
    	private static String user;
    	private static String password;
    	
    	//使用静态代码块进行赋值
    	static{
    		driver = bundle.getString("DRIVER");
    		url = bundle.getString("URL");
    		user = bundle.getString("USER");
    		password = bundle.getString("PASSWORD");
    	}
    	
    	/**
    	 * 获取连接
    	 * @return
    	 */
    	public static Connection getConnection(){
    		Connection conn = null;
    		try {
    			Class.forName(driver);
    			conn = DriverManager.getConnection(url, user, password);
    			return conn;
    		} catch (Exception e) {
    			e.printStackTrace();
    			throw new RuntimeException(e);
    		}
    	}
    	
    	/**
    	 * 释放资源
    	 * @param rs
    	 * @param st
    	 * @param conn
    	 */
    	public static void release(ResultSet rs,Statement st,Connection conn){
    		if(rs != null){
    			try{
    				rs.close();
    			}catch(Exception e){
    				e.printStackTrace();
    			}
    		}
    		if(st != null){
    			try{
    				st.close();
    			}catch(Exception e){
    				e.printStackTrace();
    			}
    		}
    		if(conn != null){
    			try{
    				conn.close();
    			}catch(Exception e){
    				e.printStackTrace();
    			}
    		}
    	}
    }
    
    

    MyDataSource.java

    package com.zwd.dataSource;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.sql.Connection;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    
    import com.zwd.utils.JDBCUtil;
    
    /**
     * 自定义连接池
     * @author Administrator
     *
     */
    public class MyDataSource {
    	//将ArrayList转为线程安全的
    	private static List<Connection> pool = Collections.synchronizedList(new ArrayList<Connection>());
    	static{
    		for(int i = 0 ;i < 10;i++){
    			Connection connection = JDBCUtil.getConnection();
    			pool.add(connection);
    		}
    	}
    	//获取连接池中的连接
    	public static Connection getConnection(){
    		 final Connection connection = pool.remove(0);
    		 Connection proxyConn = (Connection) Proxy.newProxyInstance(connection.getClass().getClassLoader(), 
    				 connection.getClass().getInterfaces(), new InvocationHandler() {
    					
    					@Override
    					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    						Object result = null;
    						if("close".equals(method.getName())){
    							//不能关闭,重新把连接添加进去
    							pool.add(connection);
    						}
    						else{
    							 result = method.invoke(connection, args);
    						}
    						return result;
    					}
    				});
    		 return proxyConn;
    	}
    	
    	public static int getPoolSize(){
    		return pool.size();
    	}
    }
    

    测试

    package com.zwd.test;
    
    import java.sql.Connection;
    import java.sql.SQLException;
    
    import org.junit.Test;
    
    import com.zwd.dataSource.MyDataSource;
    
    public class PoolTest {
    	@Test
    	public void test() throws Exception{
    		System.out.println(MyDataSource.getPoolSize());
    		for(int i = 0 ;i < 10;i++){
    			Connection connection = MyDataSource.getConnection();
    			connection.close();
    			System.out.println(connection);
    		}
    		System.out.println(MyDataSource.getPoolSize());
    		for(int i = 0 ;i < 10;i++){
    			Connection connection = MyDataSource.getConnection();
    			connection.close();
    			System.out.println(connection);
    		}
    	}
    }
    

4.AOP的相关概念

​ AOP:全称是 Aspect Oriented Programming 即:面向切面的编程。

​ 简单的说他就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对我们的已有方法进行增强。

作用:在程序运行期间,不修改源码对已有方法进行增强。
优势:减少重复代码、提高开发效率、维护方便。
AOP的实现技术:动态代理。
Spring中的AOP:
   我们需要告诉spring:
	(1)对哪个类增强(连接点)
	(2)对类中哪些方法增强(切入点)
	(3)在方法哪个位置增强(前、后..)(切面=通知+切入点)
	(4) 增强什么(增强的方法)(通知)
   spring AOP能做的事:在我们需要对某个方法增强时,告诉spring,spring会为我们创建代理对象,至于invoke中的方法需要我们
package com.itheima.factory;

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

import com.itheima.service.ICustomerService;
import com.itheima.service.impl.CustomerServiceImpl;
import com.itheima.utils.TransactionManager;

/**
 * 用于生成bean对象
 * @author zhy
 *
 */
public class BeanFactory {
	
	public static ICustomerService getCustomerService(){
		final ICustomerService cs = new CustomerServiceImpl();
		//创建业务层的代理对象
		ICustomerService proxyCs = (ICustomerService) Proxy.newProxyInstance(cs.getClass().getClassLoader(), 
				cs.getClass().getInterfaces(), 
				new InvocationHandler() {
					
					@Override
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						if("test".equals(method.getName())){
							return method.invoke(cs, args);
						}
						try{
							//开启事务
							TransactionManager.beginTransaction();
							//执行操作
							Object rtValue = method.invoke(cs, args);
							//提交事务
							TransactionManager.commit();
							//返回结果
							return rtValue;
						}catch(Exception e){
							//回滚事务
							TransactionManager.rollback();
							//处理异常
							throw new RuntimeException(e);
						}finally{
							//关闭资源
							//由于我们把session绑定到线程上了,hibernate就帮我们关闭了
						}
					}
				});
		return proxyCs;
	}
}
4.1 AOP 对代理方法的选择
在spring中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。
但是我们我也可以配置使用CGLIB来实现。CGLIB不管类是否实现接口。
4.2 相关术语
  • Joinpoint 连接点:业务的核心方法。代理对象(Service接口中所有的方法)中所有的方法

    ​ 所谓连接点是指那些被拦截到的点。在spring中,这些点值的是方法,因为spring只支持方法类型的连接点。

  • Pointcut 切入点:被增强的方法。代理对象中被增强的方法。哪些方法被增强了。

    ​ 所谓切入点是指我们要对哪些连接点进行拦截的定义。

    ​ 切入点一定是连接点,连接点不一定是切入点。

  • Advice 通知/增强:增强的代码所在的类。

    所谓的通知是指我们拦截到切入点后要做的事情。

    通知类型:前置通知、后置通知、异常通知、最终通知、环绕通知。

在这里插入图片描述

  • Instroducion 引介

  • Target(目标对象):代理的目标对象,被代理对象。

  • Weaving织入:在执行增强代码时称为织入。

    ​ 是指把增强 应用到目标对象来创建新的代理对象的过程。

  • Proxy 代理:一个类AOP织入增强后,就产生一个结果代理类。

  • Aspect 切面:是切入点和通知(引介)的结合。

5.配置AOP

  • 导入IOC的包,导入AOP的的四个包。

在这里插入图片描述

  • 创建一个业务层 com.zwd.service.impl.CustomerServiceImpl

    package com.zwd.service.impl;
    
    import org.springframework.stereotype.Service;
    
    import com.zwd.service.ICustomerService;
    
    /**
     * 模拟客户业务层的实现类
     * @author zhy
     *
     */
    @Service("customerService")
    public class CustomerServiceImpl implements ICustomerService {
    
    	@Override
    	public void saveCustomer() {
    		System.out.println("保存了客户");
    	}
    
    	@Override
    	public void updateCustomer(int i) {
    		System.out.println("更新了客户。。。"+i);
    	}
    
    	@Override
    	public int deleteCustomer() {
    		System.out.println("删除了客户");
    		return 0;
    	}
    }
    
  • 创建一个通知类 Logger

    package com.zwd.utils;
    
    /**
     * 一个用于记录日志的类
     * @author zhy
     *
     */
    public class Logger {
    
    	/**
    	 * 记录日志的操作
    	 * 计划让其在业务核心方法(切入点方法)执行之前执行
    	 */
    	public void printLog(){
    		System.out.println("Logger中的printLog方法开始记录日志了。。。。");
    	}
    }
    
    
  • 创建Spring配置文件 bean.xml

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

 <!-- 配置service -->
 <bean id="customerService" class="com.zwd.service.impl.CustomerServiceImpl"></bean>
<!-- 基于xml的aop配置步骤:要想使用spring的aop,必须aop的jar包 -->

<!-- 第一步:把通知类交给spring来管理 -->
<bean id="logger" class="com.zwd.utils.Logger"></bean>

<!-- 第二步:导入aop名称空间,并且使用aop:config开始aop配置 -->
<aop:config>

 <!-- 定义通用的切片表达式:如果是写在了aop:aspect标签外部,则表达式所有切面可用:需定义在使用之前 -->
 <aop:pointcut expression="execution(* com.zwd.service.impl.*.*(..))"  id="cut1"/>

<!-- 第三步:使用aop:aspect配置切面。(通知在切入点的执行顺序) 
        id属性:用于给切面提供一个唯一标识
        ref属性:用于应用通知Bean的id。         -->
<aop:aspect id="logAdvice" ref="logger">

<!-- 第四步:配置通知的类型,指定增强方法何时执行。
        method属性:用于指定增强的方法名称。
        pointcut属性:用于指定切入点表达式。
        切入点表达式:
            关键字:execution(表达式)
            表达式写法:访问修饰符  返回值  包名.包名.....类名.方法名(参数列表)  
            全匹配方式:
			   public void com.itheima.service.impl.CustomerServiceImpl.saveCustomer()
	        访问修饰符可以省略
			   void com.itheima.service.impl.CustomerServiceImpl.saveCustomer()
			返回值可以使用通配符,表示任意返回值。通配符是*
				* com.itheima.service.impl.CustomerServiceImpl.saveCustomer()
			包名可以使用通配符,表示任意包。但是,有几个包就需要写几个*
				*.*.*.*.CustomerServiceImpl.saveCustomer()
			包名可以使用..表示当前包及其子包
				* com..CustomerServiceImpl.saveCustomer()
			类名和方法名都可以使用通配符
				* com..*.*()
			参数列表可以使用具体类型,来表示参数类型
			基本类型直接写类型名称:int
			引用类型必须是包名.类名。 java.lang.Integer
			参数列表可以使用通配符,表示任意参数类型,但是必须有参数
				* com..*.*(*)
			参数列表可以使用..表示有无参数均可,有参数可以是任意类型
				* com..*.*(..)
			全通配方式:
				* *..*.*(..)
			实际开发中,我们一般情况下,我们都是对业务层方法进行增强:
				所以写法:* com.itheima.service.impl.*.*(..) -->
<!-- <aop:before method="printLog" pointcut="execution(public void com.zwd.service.impl.CustomerServiceImpl.saveCustomer())"/>
 -->
<!--  <aop:before method="printLog" pointcut="execution(* com.zwd.service.impl.*.*(..))"/>
  -->
  <aop:before method="printLog" pointcut-ref="cut1"/>
  <!-- 定义通用的切片表达式:如果是写在了aop:aspect标签内部,则表达式只有当前切面可用 -->
<!--  <aop:pointcut expression="execution(* com.zwd.service.impl.*.*(..))"  id="cut1"/>  -->


</aop:aspect>

 <!-- 定义通用的切片表达式:如果是写在了aop:aspect标签外部,则表达式所有切面可用:需定义在使用之前 -->
<!--  <aop:pointcut expression="execution(* com.zwd.service.impl.*.*(..))"  id="cut1"/>  -->

</aop:config>

</beans>

6.AOP的五种通知类型的配置

package com.zwd.utils;

import org.aspectj.lang.ProceedingJoinPoint;

/**
 * 一个用于记录日志的类
 * 计划让其在业务核心方法(切入点方法)之前执行
 * @author lenovo
 *
 */
public class Logger {
	/**
	 * 前置通知
	 */
	public void beforePrintLog() {
		System.out.println("Logger中的beforePrintLog方法开始记录日志了....");
	}
	
	/**
	 * 后置通知
	 */
	public void afterReturningPrintLog() {
		System.out.println("Logger中的afterReturningPrintLog方法开始记录日志了....");
	}
	
	/**
	 * 异常通知
	 */
	public void afterThrowingPrintLog() {
		System.out.println("Logger中的afterThrowingPrintLog方法开始记录日志了....");
	}
	
	/**
	 * 最终通知
	 */
	public void afterPrintLog() {
		System.out.println("Logger中的afterPrintLog方法开始记录日志了....");
	}
	
	/**
	 * 环绕通知
	 * 问题:当我们配置了环绕通知之后,切入点方法没有执行,而环绕通知里的代码执行了
	 * 分析:由动态代理可知,环绕通知指的是invoke方法,并且里面有明确的切入点方法调用。而我们现在的环绕通知没有明确切入点方法调用。
	 * 解决:spring为我们提供了一个接口:ProceedingJoinPoint.该接口可以作为环绕通知的方法参数来使用。
	 *       在程序运行时,spring框架会为我们提供该接口的实现类,供我们使用。
	 *       该接口中有一个方法,proceed(),它的作用就等同于method.invoke方法,就是明确调用业务层核心方法(切入点方法)
	 * 环绕通知:它是spring框架为我们提供的一种可以在代码中手动控制通知方法什么时候执行的方式。
	 */
	public Object aroundPrintLog(ProceedingJoinPoint pjp) {
		Object result = null;
		try {
			System.out.println("Logger中的aroundPrintLog方法开始记录日志了....前置");
			
			result = pjp.proceed();
			System.out.println("Logger中的aroundPrintLog方法开始记录日志了....后置");
			
		} catch (Throwable e) {
			System.out.println("Logger中的aroundPrintLog方法开始记录日志了....异常");
			
			e.printStackTrace();
		}finally {
			System.out.println("Logger中的aroundPrintLog方法开始记录日志了....最终");
		}
		return result;
	}
}

bean.xml

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

 <!-- 配置service -->
 <bean id="customerService" class="com.zwd.service.impl.CustomerServiceImpl"></bean>
<!-- 基于xml的aop配置步骤:要想使用spring的aop,必须aop的jar包 -->

<bean id="logger" class="com.zwd.utils.Logger"></bean>

<aop:config>
   <!-- 定义通用的切片表达式-->
   <aop:pointcut expression="execution(* com.zwd.service.impl.*.*(..))"  id="cut1"/> 
   <aop:aspect id="logAdvice" ref="logger">
   
       <!-- 配置前置通知:永远在切入点方法执行之前执行
       <aop:before method="beforePrintLog" pointcut-ref="cut1"/> -->
       <!-- 配置后置通知:切入点方法正常执行之后执行 
       <aop:after-returning method="afterReturningPrintLog" pointcut-ref="cut1"/> -->
       <!-- 异常通知:切入点方法执行产生异常之后执行。和后置通知永远只能执行一个 
       <aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="cut1"/> -->
       <!-- 最终通知:无论切入点方法是否正常执行,他都会在其后面执行 
       <aop:after method="afterPrintLog" pointcut-ref="cut1"/> -->
       
       <!-- 配置环绕通知 -->
       <aop:around method="aroundPrintLog" pointcut-ref="cut1"/>
      
   </aop:aspect>
</aop:config>
</beans>

环绕通知是spring提供的一种可以在代码中自己定义要增强的方法什么时候执行的方式。

7.注解AOP

  • 开启spring对注解AOP的支持

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xmlns:aop="http://www.springframework.org/schema/aop"
    	xmlns:context="http://www.springframework.org/schema/context"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans 
           					   http://www.springframework.org/schema/beans/spring-beans.xsd
           					   http://www.springframework.org/schema/aop 
           					   http://www.springframework.org/schema/aop/spring-aop.xsd
           					   http://www.springframework.org/schema/context 
           					   http://www.springframework.org/schema/context/spring-context.xsd">
    
    <!--配置spring注解需要扫描的包  -->
    <context:component-scan base-package="com.zwd"></context:component-scan>
    <!-- 开启spring对注解AOP的支持 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    
    </beans>
    

    Logger

    package com.zwd.utils;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Component;
    
    /**
     * 一个用于记录日志的类
     * 计划让其在业务核心方法(切入点方法)之前执行
     * @author lenovo
     *
     */
    @Component("logger")
    @Aspect//配置切面
    public class Logger {
    	
    	@Pointcut("execution(* com.zwd.service.impl.*.*(..))")
    	public void pc1() {}
    	/**
    	 * 前置通知
    	 */
    	//@Before("pc1()")
    	public void beforePrintLog() {
    		System.out.println("前置:Logger中的beforePrintLog方法开始记录日志了....");
    	}
    	
    	/**
    	 * 后置通知
    	 */
    	//@AfterReturning("pc1()")
    	public void afterReturningPrintLog() {
    		System.out.println("后置:Logger中的afterReturningPrintLog方法开始记录日志了....");
    	}
    	
    	/**
    	 * 异常通知
    	 */
    	//@AfterThrowing("pc1()")
    	public void afterThrowingPrintLog() {
    		System.out.println("异常:Logger中的afterThrowingPrintLog方法开始记录日志了....");
    	}
    	
    	/**
    	 * 最终通知
    	 */
    	//@After("pc1()")
    	public void afterPrintLog() {
    		System.out.println("最终:Logger中的afterPrintLog方法开始记录日志了....");
    	}
    	
    	/**
    	 * 环绕通知
    	 * 问题:当我们配置了环绕通知之后,切入点方法没有执行,而环绕通知里的代码执行了
    	 * 分析:由动态代理可知,环绕通知指的是invoke方法,并且里面有明确的切入点方法调用。而我们现在的环绕通知没有明确切入点方法调用。
    	 * 解决:spring为我们提供了一个接口:ProceedingJoinPoint.该接口可以作为环绕通知的方法参数来使用。
    	 *       在程序运行时,spring框架会为我们提供该接口的实现类,供我们使用。
    	 *       该接口中有一个方法,proceed(),它的作用就等同于method.invoke方法,就是明确调用业务层核心方法(切入点方法)
    	 * 环绕通知:它是spring框架为我们提供的一种可以在代码中手动控制通知方法什么时候执行的方式。
    	 */
    	@Around("pc1()")
    	public Object aroundPrintLog(ProceedingJoinPoint pjp) {
    		Object result = null;
    		try {
    			System.out.println("Logger中的aroundPrintLog方法开始记录日志了....前置");
    			
    			result = pjp.proceed();
    			System.out.println("Logger中的aroundPrintLog方法开始记录日志了....后置");
    			
    		} catch (Throwable e) {
    			System.out.println("Logger中的aroundPrintLog方法开始记录日志了....异常");
    			
    			e.printStackTrace();
    		}finally {
    			System.out.println("Logger中的aroundPrintLog方法开始记录日志了....最终");
    		}
    		return result;
    	}
    }
    

8.纯注解AOP

  • 创建spring的配置类 SpringConfig.java

在这里插入图片描述

  • 更改spring容器的加载方法

在这里插入图片描述

Spring第四天

1.Spring的 JDBC Template

它是spring框架中提供的一个对象,是对原始Jdbc API对象的简单封装。spring框架为我们提供了很多的操作模板类,如下图所示:

在这里插入图片描述

我们今天的主角在spring-jdbc-4.24.RELEASE.jar中,我们在导包的时候,除了要导入这个jar包外,还需要导入一个spring-tx-4.2.4.RELEASE.jar(它是和事务相关的)。

2.JDBC Template的基本用法。

  • 导包:还需要再导入IOC(core)的包。否则在执行 jt.execute("");的时候捕捉不到异常。

在这里插入图片描述

package _01.JDBCTemplate;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

/**
 * JDBCTemplate的基本用法
 * @author Administrator
 *
 */
public class JDBCTemplateDemo1 {
	
	public static void main(String[] args) {
		//1.定义数据源
		DriverManagerDataSource ds = new DriverManagerDataSource();
		ds.setDriverClassName("com.mysql.jdbc.Driver");
		ds.setUrl("jdbc:mysql://localhost:3306/spring");
		ds.setUsername("root");
		ds.setPassword("root");
		//2.获取对象
		//JdbcTemplate jt = new JdbcTemplate(ds);
		JdbcTemplate jt = new JdbcTemplate();
		jt.setDataSource(ds);
	
		//3.执行操作
		jt.execute("insert into cst_customer(cust_name) values('赵四')");
	}
}

3.JdbcTemplate的入门

  • 导包(IOC的包都要导)

在这里插入图片描述

  • bean.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans 
                               http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <!-- 配置数据源 -->
    <bean id = "ds" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    	<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
    	<property name="url" value="jdbc:mysql://localhost:3306/spring"></property>
    	<property name="username" value="root"></property>
    	<property name="password" value="root"></property>
    </bean>
    
    <bean id = "jt" class="org.springframework.jdbc.core.JdbcTemplate">
    	<property name="dataSource" ref="ds"></property>
    </bean>
    </beans>
    
  • 测试

    package com.zwd.JdbcTemplate;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.springframework.jdbc.core.JdbcTemplate;
    public class SpringJdbcTemplate {
    	public static void main(String[] args) {
    		//加载Spring容器
    		ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    		JdbcTemplate jt = (JdbcTemplate) ac.getBean("jt");
    		jt.execute("insert into cst_customer(cust_name) values('aaaa')");
    	}
    }
    

4.JdbcTemplate中的CRUD操作

package com.zwd.jdbcTemlateCRUD操作;


import java.util.List;

import javax.swing.tree.RowMapper;
import javax.swing.tree.TreePath;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

import com.zwd.domain.Customer;

public class JdbcTemplateDemo3 {

	public static void main(String[] args) {
		//1.获取容器
		ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
		//2.获取bean对象
		JdbcTemplate jt = (JdbcTemplate) ac.getBean("jt");
		//3.执行操作
		//保存
	//	jt.update("insert into cst_customer(cust_name) values(?)","customer2");
		//更新
	//	jt.update("update cst_customer set cust_name=? ", "customer33");
		//删除
	//	jt.update("delete from cst_customer where cust_name = ?","customer33");
		//查询所有
//		RowMapper rMapper = null;
//		List<Customer> customer = jt.query("select * from cst_customer", new BeanPropertyRowMapper<Customer>(Customer.class));
//	    for (Customer customer2 : customer) {
//			System.out.println(customer2);
//		}
	    
		//查询一个
	   // List customer1 = jt.query("select * from cst_customer where cust_id=?", new BeanPropertyRowMapper<Customer>(Customer.class),98);
		//System.out.println(customer1.isEmpty()?"没有结果":customer1.get(0));   
		//查询放回一行一列,聚合函数的使用
		Long count = jt.queryForObject("select count(*) from cst_customer",Long.class);
        System.out.println(count);
	}
}

5.JdbcTemplate 在DAO中的使用

package com.zwd.dao;

import com.zwd.domain.Account;
/**
 * 账户的持久层接口
 * @author Administrator
 *
 */
public interface IAccountDao {
	
	/**
	 * 根据id查询账户信息
	 * @param id
	 * @return
	 */
	Account findAccountById(Integer id);
	/**
	 * 更新账户信息
	 * @param account
	 */
	void updateAccount(Account account);
}
  • 方式一:

    ​ 问题:如果有很多的DAO,则对于jdbcTemplate 的创建是重复的。

  package com.zwd.dao.impl;
  
  import java.util.List;
  
  import org.springframework.jdbc.core.BeanPropertyRowMapper;
  import org.springframework.jdbc.core.JdbcTemplate;
  import org.springframework.jdbc.core.RowMapper;
  
  import com.zwd.dao.IAccountDao;
  import com.zwd.domain.Account;
  
  public class AccountDaoImpl implements IAccountDao{
  
  	private JdbcTemplate jdbcTemplate = null;
  	
  	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
  		this.jdbcTemplate = jdbcTemplate;
  	}
  
  	@Override
  	public Account findAccountById(Integer id) {
  	    List<Account> list = jdbcTemplate.query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class), id);
  	    return list.isEmpty() ? null :list.get(0);
  	}
  
  	@Override
  	public void updateAccount(Account account) {
  		jdbcTemplate.update("update account set name=?,money=? where id = ?", 
  				account.getName(),account.getMoney(),account.getId());
  	}
  }

bean.xml

  <?xml version="1.0" encoding="UTF-8"?>
  <beans xmlns="http://www.springframework.org/schema/beans"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns:context="http://www.springframework.org/schema/context"
         xsi:schemaLocation="http://www.springframework.org/schema/beans 
                             http://www.springframework.org/schema/beans/spring-beans.xsd
                             http://www.springframework.org/schema/context 
                             http://www.springframework.org/schema/context/spring-context.xsd
                             ">
  
  <!-- 配置数据源 -->
  <bean id = "ds" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  	<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
  	<property name="url" value="jdbc:mysql://localhost:3306/spring"></property>
  	<property name="username" value="root"></property>
  	<property name="password" value="root"></property>
  </bean>
  <bean id = "jt" class="org.springframework.jdbc.core.JdbcTemplate">
  	<property name="dataSource" ref="ds"></property>
  </bean>
  
  <!-- 配置DAO -->
  <bean id = "accountDao" class="com.zwd.dao.impl.AccountDaoImpl">
  	<property name="jdbcTemplate" ref="jt"></property>
  </bean>
  
  </beans>
  • 方式二:提取重复代码到一个类中,然后让DAO继承这个类。

    ​ 但是Spring提供了这个类 JdbcDaoSupport,我们不用自己写。

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

    方式二与方式一的区别在于:

    方式一可以用xml配置,也可以用于注解。但是方式二只能用于xml配置,不能用于注解。

    JdbcDaoSupport.java

    package com.zwd.dao.impl;
    
    import javax.sql.DataSource;
    
    import org.springframework.jdbc.core.JdbcTemplate;
    
    public class JdbcDaoSupport {
    	private JdbcTemplate jdbcTemplate;
    
    	public JdbcTemplate getJdbcTemplate() {
    		return jdbcTemplate;
    	}
    
    	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
    		this.jdbcTemplate = jdbcTemplate;
    	}
    	
    	public void setDataSource(DataSource ds){
    		if(jdbcTemplate != null){
    			jdbcTemplate = new JdbcTemplate(ds);
    		}
    	}
    }
    
package com.zwd.dao.impl;

import java.util.List;

import org.springframework.jdbc.core.BeanPropertyRowMapper;

import com.zwd.dao.IAccountDao;
import com.zwd.domain.Account;

public class AccountDaoImpl2  extends JdbcDaoSupport implements IAccountDao {
//问题:如果有很多dao:重复代码
//	private JdbcTemplate jdbcTemplate = null;
//	
//	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
//		this.jdbcTemplate = jdbcTemplate;
//	}

	@Override
	public Account findAccountById(Integer id) {
	    List<Account> list = getJdbcTemplate().query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class), id);
	    return list.isEmpty() ? null :list.get(0);
	}

	@Override
	public void updateAccount(Account account) {
		 getJdbcTemplate().update("update account set name=?,money=? where id = ?", 
				account.getName(),account.getMoney(),account.getId());
	}
}

6. 在Spring中使用其他数据源

  • 导c3p0和dpcp的包

在这里插入图片描述

<!-- 配置数据源:使用Spring的内置数据源
<bean id = "ds" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
	<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
	<property name="url" value="jdbc:mysql://localhost:3306/spring"></property>
	<property name="username" value="root"></property>
	<property name="password" value="root"></property>
</bean> -->

<!-- 配置数据源:使用dpcp -->
<bean id = "ds" class="org.apache.commons.dbcp.BasicDataSource">
	<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
	<property name="url" value="jdbc:mysql://localhost:3306/spring"></property>
	<property name="username" value="root"></property>
	<property name="password" value="root"></property>
</bean>

<!-- 配置数据源:使用c3p0 
<bean id = "ds" class="com.mchange.v2.c3p0.ComboPooledDataSource">
	<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
	<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring"></property>
	<property name="user" value="root"></property>
	<property name="password" value="root"></property>
</bean>-->

7.Spring和Hibernate的组合-编程式事务控制(需要JDK8以上)

  • 创建hibernate的环境:导入Hibernate的包

  • 在src下创建hibernate的配置文件:Hibernate.cfg.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-configuration PUBLIC
    	"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    	"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
    	
    <hibernate-configuration>
    	<session-factory>
    		<!-- 1.配置连接的数据库信息 -->
    		
    		<!-- 数据库方言 -->
    		<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
    		<!-- 连接数据的信息 -->
    		<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
    		<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/spring</property>
    		<property name="hibernate.connection.username">root</property>
    		<property name="hibernate.connection.password">root</property>
    		
    		<!-- 2.Hibernate的可选配置 -->
    		
    		<!-- 配置数据库连接池 c3p0 :记得导c3p0的包(在hibernate包的option中)-->
    		<property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
    		<!-- 配置session与线程绑定 -->
    		<property name="hibernate.current_session_context_class">thread</property>
    		<!-- 是否生成sql语句打印 -->
    		<property name="hibernate.show_sql">true</property>
    		<!-- 是否格式化生成sql语句 -->
    		<property name="hibernate.format_sql">false</property>
    		<!-- 采用怎样的方式处理ddl -->
    		<property name="hibernate.hbm2ddl.auto">update</property>
    		
    		<!-- 3.配置映射文件的位置 -->
    		<!-- <mapping resource="com/zwd/domain/Customer.xml" />
    		<mapping resource="com/zwd/domain/LinkMan.xml" /> -->
    		<mapping class="com.zwd.domain.Customer"/>
    		
    	</session-factory>
    </hibernate-configuration>
    
  • 创建Hibernate的工具类:HibernateUtil.java

    package com.zwd.utils;
    
    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.hibernate.cfg.Configuration;
    
    /**
     * Hibernate的工具类:
     * 		用于加载配置文件,以及获取session对象
     * @author Administrator
     *
     */
    public class HibernateUtil {
    	private static SessionFactory sessionFactory = null;
    	//加载配置文件
    	static{
    		Configuration cfg = new Configuration();
    		cfg.configure();//加载配置文件
    		sessionFactory = cfg.buildSessionFactory();
    	}
    	
    	public static Session getSeesion(){
    		return sessionFactory.openSession();
    	}
    	//获取一个线程的session对象,需配置xml文件,该session不需要手动close
    	public static Session getCurrentSession(){
    		return sessionFactory.getCurrentSession();
    	}
    	
    	public static void main(String[] args) {
    		getCurrentSession();
    	}
    }
    
  • 使用JPA配置实体类:Customer.java

    package com.zwd.domain;
    
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.Table;
    
    /*******************************************************************************
     * javaBeans
     * cst_customer --> Customer 
     * <table explanation>
     * @author 2019-08-12 14:43:39
     * 
     */	
    @Entity
    @Table(name="cst_customer")
    public class Customer implements java.io.Serializable {
    	@Id
    	@Column(name="cust_id")
    	@GeneratedValue(strategy=GenerationType.IDENTITY)
    	private int custId;
    	
    	@Column(name="cust_name")
    	private String custName;
    	
    	@Column(name="cust_source")
    	private String custSource;
    	
    	@Column(name="cust_industry")
    	private String custIndustry;
    	
    	@Column(name="cust_level")
    	private String custLevel;
    	
    	@Column(name="cust_address")
    	private String custAddress;
    	
    	@Column(name="cust_phone")
    	private String custPhone;
    	
    	//method
    	public int getCustId() {
    		return custId;
    	}
    	public void setCustId(int custId) {
    		this.custId = custId;
    	}
    	public String getCustName() {
    		return custName;
    	}
    	public void setCustName(String custName) {
    		this.custName = custName;
    	}
    	public String getCustSource() {
    		return custSource;
    	}
    	public void setCustSource(String custSource) {
    		this.custSource = custSource;
    	}
    	public String getCustIndustry() {
    		return custIndustry;
    	}
    	public void setCustIndustry(String custIndustry) {
    		this.custIndustry = custIndustry;
    	}
    	public String getCustLevel() {
    		return custLevel;
    	}
    	public void setCustLevel(String custLevel) {
    		this.custLevel = custLevel;
    	}
    	public String getCustAddress() {
    		return custAddress;
    	}
    	public void setCustAddress(String custAddress) {
    		this.custAddress = custAddress;
    	}
    	public String getCustPhone() {
    		return custPhone;
    	}
    	public void setCustPhone(String custPhone) {
    		this.custPhone = custPhone;
    	}
    	//override toString Method 
    	public String toString() {
    		StringBuffer sb=new StringBuffer();
    		sb.append("{");
    		sb.append("'custId':'"+this.getCustId()+"',");
    		sb.append("'custName':'"+this.getCustName()+"',");
    		sb.append("'custSource':'"+this.getCustSource()+"',");
    		sb.append("'custIndustry':'"+this.getCustIndustry()+"',");
    		sb.append("'custLevel':'"+this.getCustLevel()+"',");
    		sb.append("'custAddress':'"+this.getCustAddress()+"',");
    		sb.append("'custPhone':'"+this.getCustPhone());
    		sb.append("}");
    		return sb.toString();
    	}
    }
    
  • 创建dao、service

    package com.zwd.dao.impl;
    
    import java.util.List;
    
    import org.hibernate.Session;
    
    import com.zwd.dao.ICustomerDao;
    import com.zwd.domain.Customer;
    import com.zwd.utils.HibernateUtil;
    /**
     * 客户持久类的实现类
     * @author Administrator
     *
     */
    public class CustomerDaoImpl implements ICustomerDao {
    
    	private Session session;
    	
    	public void setSession(Session session) {
    		this.session = session;
    	}
    
    	@Override
    	public List<Customer> findAllCustomer() {
    //		Session session = HibernateUtil.getCurrentSession();
    //		Transaction transaction = session.beginTransaction();
    //		List<Customer> list  = session.createQuery("from Customer").list();
    //		transaction.commit();
    //		session.close();
    		return session.createQuery("from Customer").list();
    	}
    }
    
    
    package com.zwd.service.impl;
    
    import java.util.List;
    
    import com.zwd.dao.ICustomerDao;
    import com.zwd.dao.impl.CustomerDaoImpl;
    import com.zwd.domain.Customer;
    import com.zwd.service.ICustomerService;
    /**
     * 客户业务层的实现类
     * @author Administrator
     *
     */
    public class CustomerServiceImpl implements ICustomerService {
    
    	private ICustomerDao customerDao;
    	
    	public void setCustomerDao(ICustomerDao customerDao) {
    		this.customerDao = customerDao;
    	}
    
    	public List<Customer> findAllCusotmer() {
    		return customerDao.findAllCustomer();
    	}
    }
    
  • 创建事务通知类。TransactionManager.java

    package com.zwd.utils;
    
    
    import org.hibernate.Session;
    import org.hibernate.Transaction;
    
    /**
     * 事务管理类
     * @author Administrator
     *
     */
    public class TransactionManager {
    	
    	private Session session;
    	private Transaction tx;
    	
    	public void setSession(Session session) {
    		this.session = session;
    	}
    
    	/**
    	 * 开启事务
    	 */
    	public void beginTransaction(){
    		System.out.println("前置通知");
    		tx = session.beginTransaction();
    	}
    	
    	/**
    	 * 提交事务
    	 */
    	public void commit(){
    		System.out.println("后置通知");
    		tx.commit();
    	}
    	
    	/**
    	 * 回滚事务
    	 */
    	public void rollback(){
    		tx.rollback();
    	}
    }
    
  • 创建spring的环境:导入spring的包

  • 在src下创建spring的配置文件:bean.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans 
                               http://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/context 
                               http://www.springframework.org/schema/context/spring-context.xsd
                               http://www.springframework.org/schema/aop
                               http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <!-- 配置service -->
    <bean id="customerService" class="com.zwd.service.impl.CustomerServiceImpl">
    	<property name="customerDao" ref="customerDao"></property>
    </bean>
    <!-- 配置dao -->
    <bean id="customerDao" class="com.zwd.dao.impl.CustomerDaoImpl">
    	<property name="session" ref="session"></property>
    </bean>
    <!-- 配置session -->
    <bean id="session" class="com.zwd.utils.HibernateUtil" factory-method="getCurrentSession"></bean>
    
    <!-- 配置aop -->
    <!-- 把通知类也交给spring -->
    <bean id="transactionManager" class="com.zwd.utils.TransactionManager">
    	<property name="session" ref="session"></property>
    </bean>
    <aop:config >
    	<aop:pointcut expression="execution(* com.zwd.service.impl.*.*(..))" id="p1"/>
    	<aop:aspect id="advice" ref="transactionManager">
    		<aop:before method="beginTransaction" pointcut-ref="p1"/>
    		<aop:after-returning method="commit" pointcut-ref="p1"/>
    		<aop:after-throwing method="rollback" pointcut-ref="p1"/>
    	</aop:aspect>
    </aop:config>
    
    </beans>
    
  • 测试

    	@Test
    	public void test11(){
    	
    		ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    		ICustomerService service = (ICustomerService)ac.getBean("customerService");
    		List<Customer> allCusotmer = service.findAllCusotmer();
    		for (Customer customer : allCusotmer) {
    			System.out.println(customer);
    		}
    	}
    
  • 注解和纯注解代码省略。

遇到的问题:事务的回滚操作执行了但是实际并没有回滚
https://blog.csdn.net/iteye_10758/article/details/82583557

https://www.cnblogs.com/andy6/p/5789248.html

8.Spring的声明式事务控制

8.1.Spring事务控制我们要明确的
第一:
	JavaEE体系进行分层开发,事务处理位于业务层,Spring提供了分层设计业务层的事务处理解决方案。
第二:
	spring框架为我们提供了一组事务控制的接口。具体在后面的第二小节介绍。这组接口是在spring-tx-4.2.4.RELEASE.jar中。
第三:
	spring的事务控制都是基于AOP的,它既可以使用编程的方式实现,也可以使用配置的方式实现。我们学习的重点是使用配置的方式实现。
8.2.Spring中事务控制的API介绍
8.2.1.PlatformTransactionManager

​ 此接口是spring的事务管理器,它里面提供了我们常用的操作事务的方法,如下图:

在这里插入图片描述

​ 我们在开发中都是使用它的实现类,如下图:
在这里插入图片描述

真正管理事务的对象
org.springframework.jdbc.datasource.DataSourceTransactionManager	使用Spring JDBC或iBatis 进行持久化数据时使用
org.springframework.orm.hibernate3.HibernateTransactionManager		使用Hibernate版本进行持久化数据时使用
8.2.2.TransactionDefinition

​ 它是事务的定义信息对象,里面有如下方法:

在这里插入图片描述

事务的隔离级别

在这里插入图片描述

事务的传播行为

是否将操作看为是事务

REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常
REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起。
NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
NEVER:以非事务方式运行,如果当前存在事务,抛出异常
NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行REQUIRED类似的操作。
超时时间
默认值是-1,没有超时限制。如果有,以秒为单位进行设置。
是否是只读事务
建议查询时设置为只读。
8.2.3.TransactionStatus

​ 此接口提供的是事务具体的运行状态,方法介绍如下图:

在这里插入图片描述

8.3.Spring的事务控制:准备环境
  • 创建项目并导包:springIOC+AOP+Tx+jdbcDriver

  • 数据库表:注意存储引擎需要用:InnoDB,否则该表不支持事务。

    /*
    Navicat MySQL Data Transfer
    
    Source Server         : ZWD
    Source Server Version : 50711
    Source Host           : localhost:3306
    Source Database       : spring
    
    Target Server Type    : MYSQL
    Target Server Version : 50711
    File Encoding         : 65001
    
    Date: 2019-09-08 11:59:47
    */
    
    SET FOREIGN_KEY_CHECKS=0;
    
    -- ----------------------------
    -- Table structure for `account`
    -- ----------------------------
    DROP TABLE IF EXISTS `account`;
    CREATE TABLE `account` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(40) DEFAULT NULL,
      `money` float DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
    
    -- ----------------------------
    -- Records of account
    -- ----------------------------
    INSERT INTO `account` VALUES ('2', 'aaa', '800');
    INSERT INTO `account` VALUES ('3', 'bbb', '1100');
    
  • 实体类:Account.java

    package com.zwd.domain;
    
    /*******************************************************************************
     * javaBeans
     * account --> Account 
     * <table explanation>
     * @author 2019-09-08 11:00:28
     * 
     */	
    public class Account implements java.io.Serializable {
    	//field
    	/**  **/
    	private int id;
    	/**  **/
    	private String name;
    	/**  **/
    	private Float money;
    	//method
    	public int getId() {
    		return id;
    	}
    	public void setId(int id) {
    		this.id = id;
    	}
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public Float getMoney() {
    		return money;
    	}
    	public void setMoney(Float money) {
    		this.money = money;
    	}
    	//override toString Method 
    	public String toString() {
    		StringBuffer sb=new StringBuffer();
    		sb.append("{");
    		sb.append("'id':'"+this.getId()+"',");
    		sb.append("'name':'"+this.getName()+"',");
    		sb.append("'money':'"+this.getMoney()+"'");
    		sb.append("}");
    		return sb.toString();
    	}
    }
    
  • 持久层使用JdbcTemplate:编写dao、service、bean.xml、test(测试类)

    持久层DAO:

    IAccountDao.java

    package com.zwd.dao;
    
    import com.zwd.domain.Account;
    
    public interface IAccountDao {
    
    	/**
    	 * 根据id查询账户信息
    	 * @param accountId
    	 * @return
    	 */
    	Account findAccountById(Integer accountId);
    
    	/**
    	 * 根据名称查询账户信息
    	 * 
    	 */
    	Account findAccountByName(String accountName);
    
    	/**
    	 * 根据账户信息
    	 * @param source
    	 */
    	void update(Account account);
    }
    

    AccountDaoImpl.java

    package com.zwd.dao.impl;
    
    import java.util.List;
    
    import org.springframework.jdbc.core.BeanPropertyRowMapper;
    import org.springframework.jdbc.core.support.JdbcDaoSupport;
    
    import com.zwd.dao.IAccountDao;
    import com.zwd.domain.Account;
    
    public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {
    
    	@Override
    	public Account findAccountById(Integer accountId) {
    		List<Account> lists = getJdbcTemplate().query("select * from account where id  = ?", new BeanPropertyRowMapper<Account>(Account.class), accountId);
    		return lists.isEmpty() ? null : lists.get(0);
    	}
    
    	@Override
    	public Account findAccountByName(String accountName) {
    		List<Account> list = getJdbcTemplate().query("select * from account where name = ? ", new BeanPropertyRowMapper<Account>(Account.class),accountName);
    		if(list.isEmpty()){
    			return null;//没有这个名称的账户
    		}
    		if(list.size()>1){
    			//结果集不唯一,不符合我们的约定
    			throw new RuntimeException("结果集不唯一,请检查数据");
    		}
    		return list.get(0);
    	}
    
    	@Override
    	public void update(Account account) {
    		getJdbcTemplate().update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());
    	}
    }
    

    IAccountService.java

    package com.zwd.service;
    
    import com.zwd.domain.Account;
    
    /**
     * 账户的业务层接口
     * @author Administrator
     *
     */
    public interface IAccountService {
    
    	/**
    	 * 根据id查询账户
    	 * @param id
    	 * @return
    	 */
    	public Account findAccountById(Integer id);
    	
    	/**
    	 * 转账:
    	 * @param sourceName	转出账户名称
    	 * @param targetName	转入账户名称
    	 * @param money			转账金额
    	 */
    	public void transfer(String sourceName,String targetName,Float money);
    }
    

    AccountServiceImpl.java

    package com.zwd.service.impl;
    
    import com.zwd.dao.IAccountDao;
    import com.zwd.domain.Account;
    import com.zwd.service.IAccountService;
    /**
     * 账户的业务层实现类
     * @author Administrator
     *
     */
    public class AccountServiceImpl implements IAccountService {
    
    	private IAccountDao accountDao;
    	
    	public void setAccountDao(IAccountDao accountDao) {
    		this.accountDao = accountDao;
    	}
    
    	/**
    	 * 根据id查询账户信息
    	 */
    	@Override
    	public Account findAccountById(Integer accountId) {
    		return accountDao.findAccountById(accountId);
    	}
    
    	/**
    	 * 转账
    	 */
    	@Override
    	public void transfer(String sourceName, String targetName, Float money) {
    		//1.根据账户名称查询账户信息
    		Account source = accountDao.findAccountByName(sourceName);
    		Account target = accountDao.findAccountByName(targetName);
    		//2.转出账户减钱,转入账户加钱
    		source.setMoney(source.getMoney() - money);
    		target.setMoney(target.getMoney() + money);
    		//3.更新账户信息
    		accountDao.update(source);
    		int i = 1/0;
    		accountDao.update(target);
    	}
    }
    

    bean.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
    	xmlns:tx="http://www.springframework.org/schema/tx" 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
                               http://www.springframework.org/schema/aop 
        					   http://www.springframework.org/schema/aop/spring-aop.xsd
        					   http://www.springframework.org/schema/context
        					   http://www.springframework.org/schema/context/spring-context.xsd
        					   http://www.springframework.org/schema/tx 
        					   http://www.springframework.org/schema/tx/spring-tx.xsd">
    
    	<!-- 配置service -->
    	<bean id="accountService" class="com.zwd.service.impl.AccountServiceImpl">
    		<property name="accountDao" ref="accountDao"></property>
    	</bean>
    
    	<!-- 配置dao -->
    	<bean id="accountDao" class="com.zwd.dao.impl.AccountDaoImpl">
    		<property name="dataSource" ref="dataSource"></property>
    	</bean>
    	
    	<!-- 配置数据源:配置JdbcDaoSupport的JdbcTemplate,弄一个datasource就可以了 -->
    	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    		<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
    		<property name="url" value="jdbc:mysql://localhost:3306/spring"></property>
    		<property name="username" value="root"></property>
    		<property name="password" value="root"></property>
    	</bean>
    	<!-- 配置数据源:使用c3p0(导包)
    	<bean id = "ds" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    		<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
    		<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring"></property>
    		<property name="user" value="root"></property>
    		<property name="password" value="root"></property>
    	</bean>-->
    
    </beans>
    

    Client.java :测试类

    package com.zwd.test;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import com.zwd.domain.Account;
    import com.zwd.service.IAccountService;
    
    /**
     * 测试类
     * @author Administrator
     *
     */
    public class Client {
    	public static void main(String[] args) {
    		ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    		IAccountService accountService = (IAccountService) ac.getBean("accountService");
    //		Account account = accountService.findAccountById(2);
    //		System.out.println(account);
    		
    		accountService.transfer("aaa", "bbb",100f);
    	}
    }
    
8.4. Spring基于XML的声明式事务配置

bean.xml

<!-- 第四步:配置事务的属性
isolation="DEFAULT":配置事务的隔离级别。默认值:DEFAULT,表示使用数据库的默认隔离级别。mysql是						REPEATABLE_READ(可重复读);Oracle是READ-COMMITTED
propagation="REQUIRED":配置事务的传播行为。默认值:REQUIRED。增删改方法一般选择REQUIRED;当前是							查询方法时选择SUPPORTS。
timeout="-1":指定事务的超时时间。默认值:-1,表示永不超时。当指定其它值时,以秒为单位。
read-only="false":配置是否是否是只读事务。默认值:false,读写型事务;当指定为true时,表示只读,只  					能用于查询方法。
rollback-for="":用于指定一个异常,当执行产生该异常时,事务回滚。产生其它异常时,事务不回滚。没有指定    				时任何异常都回滚。
no-rollback-for="":用于指定一个异常,当执行产生该异常时,事务不回滚。产生其它异常时,事务回滚。没有						指定时任何异常都回滚。
-->
<!-- 配置Spring基于XML的声明式事务控制 -->
	<!-- 第一步:配置事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 第二步:配置事务的通知 -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<!-- 第四步:配置事务的属性 -->
		<tx:attributes>
			<tx:method name="*" isolation="DEFAULT" propagation="REQUIRED" timeout="-1"  read-only="false"/>
			<tx:method name="find*" isolation="DEFAULT" propagation="SUPPORTS" timeout="-1" read-only="true"/>
		</tx:attributes>
	</tx:advice>
	
	<!-- 配置AOP
			要配的是:切入点表达式的关联 -->
	<aop:config>
		<!-- 配置切入点表达式 -->
		<aop:pointcut expression="execution(* com.zwd.service.impl.*.*(..))" id="pt1"/>
		<!-- 配置事务通知和切入点表达式的关联 -->
		<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"/>
	</aop:config>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx" 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
                           http://www.springframework.org/schema/aop 
    					   http://www.springframework.org/schema/aop/spring-aop.xsd
    					   http://www.springframework.org/schema/context
    					   http://www.springframework.org/schema/context/spring-context.xsd
    					   http://www.springframework.org/schema/tx 
    					   http://www.springframework.org/schema/tx/spring-tx.xsd">

	<!-- 配置service -->
	<bean id="accountService" class="com.zwd.service.impl.AccountServiceImpl">
		<property name="accountDao" ref="accountDao"></property>
	</bean>

	<!-- 配置dao -->
	<bean id="accountDao" class="com.zwd.dao.impl.AccountDaoImpl">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 配置数据源:配置JdbcDaoSupport的JdbcTemplate,弄一个datasource就可以了 -->
	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
		<property name="url" value="jdbc:mysql://localhost:3306/spring"></property>
		<property name="username" value="root"></property>
		<property name="password" value="root"></property>
	</bean>
	<!-- 配置数据源:使用c3p0(导包)
	<bean id = "ds" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
		<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring"></property>
		<property name="user" value="root"></property>
		<property name="password" value="root"></property>
	</bean>-->



<!-- 配置Spring基于XML的声明式事务控制 -->
	<!-- 第一步:配置事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 第二步:配置事务的通知 -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<!-- 第四步:配置事务的属性
				isolation="DEFAULT":配置事务的隔离级别。默认值:DEFAULT,表示使用数据库的默认隔离级别。mysql是REPEATABLE_READ(可重复读);Oracle是READ-COMMITTED
				propagation="REQUIRED":配置事务的传播行为。默认值:REQUIRED。增删改方法一般选择REQUIRED;当前是查询方法时选择SUPPORTS。
				timeout="-1":指定事务的超时时间。默认值:-1,表示永不超时。当指定其它值时,以秒为单位。
				read-only="false":配置是否是否是只读事务。默认值:false,读写型事务;当指定为true时,表示只读,只能用于查询方法。
				rollback-for="":用于指定一个异常,当执行产生该异常时,事务回滚。产生其它异常时,事务不回滚。没有指定时任何异常都回滚。
				no-rollback-for="":用于指定一个异常,当执行产生该异常时,事务不回滚。产生其它异常时,事务回滚。没有指定时任何异常都回滚。
		 -->
		<tx:attributes>
			<tx:method name="*" isolation="DEFAULT" propagation="REQUIRED" timeout="-1"  read-only="false"/>
			<tx:method name="find*" isolation="DEFAULT" propagation="SUPPORTS" timeout="-1" read-only="true"/>
		</tx:attributes>
	</tx:advice>
	
	<!-- 配置AOP
			要配的是:切入点表达式的关联 -->
	<aop:config>
		<!-- 配置切入点表达式 -->
		<aop:pointcut expression="execution(* com.zwd.service.impl.*.*(..))" id="pt1"/>
		<!-- 配置事务通知和切入点表达式的关联 -->
		<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"/>
	</aop:config>
</beans>
8.5. Spring基于XML和Annotation的声明式事务配置
<!-- 配置Spring基于XML和注解的声明式事务控制 -->
	<!-- 第一步:配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
</bean>
	<!-- 第二步:配置Spring对注解事务的支持 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
	<!-- 第三步:在需要事务的地方使用@Transactional注解
			该注解可以写在接口上,类上和方法上。
			写在接口上:表示改接口的所有实现类都有事务
			写在类上:表示该类中的所有方法都有事务
			写在方法上:表示该方法有事务
			优先级:就近原则
	 -->
  • 首先对于AccountDaoImpl.java,由于它继承了类JdbcDaoSupport,无法对JdbcDaoSupport中的JdbcTemplate进行注解,所以需要修改该部分代码,删除对JdbcDaoSupport的继承,定义一个JdbcTemplate的成员变量。

    package com.zwd.dao.impl;
    
    import java.util.List;
    
    import javax.annotation.Resource;
    
    import org.springframework.jdbc.core.BeanPropertyRowMapper;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.jdbc.core.support.JdbcDaoSupport;
    import org.springframework.stereotype.Repository;
    
    import com.zwd.dao.IAccountDao;
    import com.zwd.domain.Account;
    
    @Repository("accountDao")
    public class AccountDaoImpl implements IAccountDao {
    	
    	@Resource(name = "jdbcTemplate")
    	private JdbcTemplate jdbcTemplate;
    
    	@Override
    	public Account findAccountById(Integer accountId) {
    		List<Account> lists = jdbcTemplate.query("select * from account where id  = ?", new BeanPropertyRowMapper<Account>(Account.class), accountId);
    		return lists.isEmpty() ? null : lists.get(0);
    	}
    
    	@Override
    	public Account findAccountByName(String accountName) {
    		List<Account> list = jdbcTemplate.query("select * from account where name = ? ", new BeanPropertyRowMapper<Account>(Account.class),accountName);
    		if(list.isEmpty()){
    			return null;//没有这个名称的账户
    		}
    		if(list.size()>1){
    			//结果集不唯一,不符合我们的约定
    			throw new RuntimeException("结果集不唯一,请检查数据");
    		}
    		return list.get(0);
    	}
    
    	@Override
    	public void update(Account account) {
    		jdbcTemplate.update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());
    	}
    }
    
  • 对service、dao进行注解

  • bean.xml配置:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
    	xmlns:tx="http://www.springframework.org/schema/tx" 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
                               http://www.springframework.org/schema/aop 
        					   http://www.springframework.org/schema/aop/spring-aop.xsd
        					   http://www.springframework.org/schema/context
        					   http://www.springframework.org/schema/context/spring-context.xsd
        					   http://www.springframework.org/schema/tx 
        					   http://www.springframework.org/schema/tx/spring-tx.xsd">
    
    
    	<!-- 配置spring在加载容器要扫描的包 -->
    	<context:component-scan base-package="com.zwd"></context:component-scan>
    	
    	<!-- 配置JdbcTemplate -->
    	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    		<property name="dataSource" ref="dataSource"></property>
    	</bean>
    	
    	<!-- 配置数据源 -->
    	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    		<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
    		<property name="url" value="jdbc:mysql://localhost:3306/spring"></property>
    		<property name="username" value="root"></property>
    		<property name="password" value="root"></property>
    	</bean>
    
    <!-- 配置Spring基于XML和注解的声明式事务控制 -->
    	<!-- 第一步:配置事务管理器 -->
    	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    		<property name="dataSource" ref="dataSource"></property>
    	</bean>
    	<!-- 第二步:配置Spring对注解事务的支持 -->
    	<tx:annotation-driven transaction-manager="transactionManager"/>
    	<!-- 第三步:在需要事务的地方使用@Transactional注解
    			该注解可以写在接口上,类上和方法上。
    			写在接口上:表示改接口的所有实现类都有事务
    			写在类上:表示该类中的所有方法都有事务
    			写在方法上:表示该方法有事务
    			优先级:就近原则
    	 -->
    </beans>
    
  • AccountServiceImpl.java中使用注解配置事务

    package com.zwd.service.impl;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Isolation;
    import org.springframework.transaction.annotation.Propagation;
    import org.springframework.transaction.annotation.Transactional;
    
    import com.zwd.dao.IAccountDao;
    import com.zwd.domain.Account;
    import com.zwd.service.IAccountService;
    /**
     * 账户的业务层实现类
     * @author Administrator
     *
     */
    @Service("accountService")
    @Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED,readOnly=false)//读写型
    public class AccountServiceImpl implements IAccountService {
    
    	@Autowired
    	@Qualifier("accountDao")
    	private IAccountDao accountDao;
    	
    	/**
    	 * 根据id查询账户信息
    	 */
    	@Override
    	@Transactional(propagation=Propagation.SUPPORTS,readOnly=true)//只读型
    	public Account findAccountById(Integer accountId) {
    		return accountDao.findAccountById(accountId);
    	}
    
    	/**
    	 * 转账
    	 */
    	@Override
    	public void transfer(String sourceName, String targetName, Float money) {
    		//1.根据账户名称查询账户信息
    		Account source = accountDao.findAccountByName(sourceName);
    		Account target = accountDao.findAccountByName(targetName);
    		//2.转出账户减钱,转入账户加钱
    		source.setMoney(source.getMoney() - money);
    		target.setMoney(target.getMoney() + money);
    		//3.更新账户信息
    		accountDao.update(source);
    		int i = 1/0;
    		accountDao.update(target);
    	}
    }
    
  • 测试:Client.java

    package com.zwd.test;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import com.zwd.domain.Account;
    import com.zwd.service.IAccountService;
    
    /**
     * 测试类
     * @author Administrator
     *
     */
    public class Client {
    	public static void main(String[] args) {
    		ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    		IAccountService accountService = (IAccountService) ac.getBean("accountService");
    //		Account account = accountService.findAccountById(2);
    //		System.out.println(account);
    		
    		accountService.transfer("aaa", "bbb",100f);
    	}
    }
    
8.6. Spring基于纯注解的声明式事务配置

根据如下的xml配置来创建相应的类。

<!-- 配置spring在加载容器要扫描的包 -->
	<context:component-scan base-package="com.zwd"></context:component-scan>
	
	<!-- 配置JdbcTemplate -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 配置数据源 -->
	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
		<property name="url" value="jdbc:mysql://localhost:3306/spring"></property>
		<property name="username" value="root"></property>
		<property name="password" value="root"></property>
	</bean>

<!-- 配置Spring基于XML和注解的声明式事务控制 -->
	<!-- 第一步:配置事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	<!-- 第二步:配置Spring对注解事务的支持 -->
	<tx:annotation-driven transaction-manager="transactionManager"/>
  • config包下创建SpringConfiguration.java配置类来取代bean.xml

    package config;
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    /**
     * Spring的配置类:代替bean.xml
     * @author Administrator
     *
     */
    @Configuration
    @ComponentScan("com.zwd")
    @Import({JdbcConfig.class,TransactionManager.class})
    @EnableTransactionManagement
    public class SpringConfiguration {
    
    }
    
  • 创建连接数据库的类:JdbcConfig.java:用来取代xml中的JdbcTemplate。

    package config;
    
    import javax.annotation.Resource;
    import javax.sql.DataSource;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.jdbc.datasource.DriverManagerDataSource;
    
    /**
     * 连接数据库的类
     * @author Administrator
     *
     */
    public class JdbcConfig {
    	@Bean(name="jdbcTemplate")
    	public JdbcTemplate getJdbcTemplate(@Qualifier("dataSource") DataSource dataSource){
    		return new JdbcTemplate(dataSource);
    	}
    	
    	@Bean(name="dataSource")
    	public DataSource getDataSource(){
    		DriverManagerDataSource ds = new DriverManagerDataSource();
    		ds.setDriverClassName("com.mysql.jdbc.Driver");
    		ds.setUrl("jdbc:mysql://localhost:3306/spring");
    		ds.setUsername("root");
    		ds.setPassword("root");
    		return ds;
    	}
    }
    
  • 创建spring的事务管理类:TransactionManager.java:用来取代xml中的transactionManager。

    package config;
    
    import javax.sql.DataSource;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.jdbc.datasource.DataSourceTransactionManager;
    import org.springframework.transaction.PlatformTransactionManager;
    /**
     * Spring的事务管理类
     * @author Administrator
     *
     */
    public class TransactionManager {
    	
    	@Bean(name="transactionManager")
    	public PlatformTransactionManager createTransactionManager(DataSource dataSource){
    		return new DataSourceTransactionManager(dataSource);
    	}
    }
    
  • 然后使用注解配置spring的配置类:SpringConfiguration.java,如上。

  • 测试:Client.java :用AnnotationConfigApplicationContext加载spring的容器。

    package com.zwd.test;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import com.zwd.service.IAccountService;
    
    import config.SpringConfiguration;
    
    /**
     * 测试类
     * @author Administrator
     *
     */
    public class Client {
    	public static void main(String[] args) {
    		ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
    		IAccountService accountService = (IAccountService) ac.getBean("accountService");
    //		Account account = accountService.findAccountById(2);
    //		System.out.println(account);
    		
    		accountService.transfer("aaa", "bbb",100f);
    	}
    }
    
  • 改进:对数据库的连接进行改进,使用配置文件配置数据库的连接信息,通过读取配置文件来获取连接信息。

    在类的根路径下创建db.properties

    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/spring
    jdbc.user=root
    jdbc.password=root
    

    在spring的配置类SpringConfiguration.java中添加注解,加载配置文件。@PropertySource("classpath:db.properties")//加载配置

    package config;
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    import org.springframework.context.annotation.PropertySource;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    /**
     * Spring的配置类:代替bean.xml
     * @author Administrator
     *
     */
    @Configuration
    @ComponentScan("com.zwd")
    @PropertySource("classpath:db.properties")//加载配置
    @Import({JdbcConfig.class,TransactionManager.class})
    @EnableTransactionManagement
    public class SpringConfiguration {
    
    }
    

    JdbcConfig.java中使用@Value + el表达式为连接数据的变量注入数据

    package config;
    
    import javax.annotation.Resource;
    import javax.sql.DataSource;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.jdbc.datasource.DriverManagerDataSource;
    
    /**
     * 连接数据库的类
     * @author Administrator
     *
     */
    public class JdbcConfig {
    	@Value("${jdbc.driver}")
    	private String jdbcDriver;
    	
    	@Value("${jdbc.url}")
    	private String jdbcUrl;
    	
    	@Value("${jdbc.user}")
    	private String jdbcUser;
    	
    	@Value("${jdbc.password}")
    	private String jdbcPassword;
    	
    	@Bean(name="jdbcTemplate")
    	public JdbcTemplate getJdbcTemplate(@Qualifier("dataSource") DataSource dataSource){
    		return new JdbcTemplate(dataSource);
    	}
    	
    //	@Bean(name="dataSource")
    //	public DataSource getDataSource(){
    //		DriverManagerDataSource ds = new DriverManagerDataSource();
    //		ds.setDriverClassName("com.mysql.jdbc.Driver");
    //		ds.setUrl("jdbc:mysql://localhost:3306/spring");
    //		ds.setUsername("root");
    //		ds.setPassword("root");
    //		return ds;
    //	}
    	
    	@Bean(name="dataSource")
    	public DataSource getDataSource(){
    		DriverManagerDataSource ds = new DriverManagerDataSource();
    		ds.setDriverClassName(jdbcDriver);
    		ds.setUrl(jdbcUrl);
    		ds.setUsername(jdbcUser);
    		ds.setPassword(jdbcPassword);
    		return ds;
    	}
    }
    
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值