(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; }*/ }在单例模式中:如果类中存在类成员变量,则不是线程安全的。因为单例中只会创建一个对象,每个线程都用的是一个对象,前一个线程更改了类成员变量,后一个线程则访问到的是前一个线程修改后的值。可以将类成员变量改为方法成员变量解决线程安全问题。
在多例模式中:是线程安全的。每个线程都会创建自己的对象,每个线程之间的对象互不影响。
获取对象的方式:
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(); } }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(); } }涉及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(); } }注入的方式有三种:
构造函数注入
涉及的标签: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)第三类:复杂类型(集合类型)通过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>数据库:
/* 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创建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); } }@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;@Value("")
作用:它可以借助spring的el表达式读取properties文件中的配置。
属性:value:用于指定要注入的数据。
@Scope("")
作用:用于改变bean的作用范围属性: value:用于指定范围的取值。
取值:和xml中scope属性的取值是一样的。singleton、prototype、request、session、globalsession。
@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查找。解决Runner和dataSource
测试:将加载配置文件的方式该为使用注解加载
ApplicationContext ac= new AnnotationConfigApplicationContext(SpringConfiguration.class);步骤:
(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); } } }动态代理: 作用:不改变源码的基础上,对已有方法增强。(他是AOP思想的实现技术) 分类:基于接口的动态代理、基于子类的动态代理。
要求:被代理类最少实现一个接口。 提供者: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; } }基于子类的动态代理: 要求:被代理类不能是最终类。不能被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); } }增强代码的时候可以使用装 饰者模式 和 动态代理 。
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=rootJDBCUtil.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); } } } 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; } }Joinpoint 连接点:业务的核心方法。代理对象(Service接口中所有的方法)中所有的方法
所谓连接点是指那些被拦截到的点。在spring中,这些点值的是方法,因为spring只支持方法类型的连接点。
Pointcut 切入点:被增强的方法。代理对象中被增强的方法。哪些方法被增强了。
所谓切入点是指我们要对哪些连接点进行拦截的定义。
切入点一定是连接点,连接点不一定是切入点。
Advice 通知/增强:增强的代码所在的类。
所谓的通知是指我们拦截到切入点后要做的事情。
通知类型:前置通知、后置通知、异常通知、最终通知、环绕通知。
Instroducion 引介:
Target(目标对象):代理的目标对象,被代理对象。
Weaving织入:在执行增强代码时称为织入。
是指把增强 应用到目标对象来创建新的代理对象的过程。
Proxy 代理:一个类AOP织入增强后,就产生一个结果代理类。
Aspect 切面:是切入点和通知(引介)的结合。
创建一个业务层 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>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提供的一种可以在代码中自己定义要增强的方法什么时候执行的方式。
开启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; } }它是spring框架中提供的一个对象,是对原始Jdbc API对象的简单封装。spring框架为我们提供了很多的操作模板类,如下图所示:
我们今天的主角在spring-jdbc-4.24.RELEASE.jar中,我们在导包的时候,除了要导入这个jar包外,还需要导入一个spring-tx-4.2.4.RELEASE.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"> <!-- 配置数据源 --> <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')"); } }方式一:
问题:如果有很多的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()); } }创建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); } }注解和纯注解代码省略。
此接口是spring的事务管理器,它里面提供了我们常用的操作事务的方法,如下图:
我们在开发中都是使用它的实现类,如下图:
真正管理事务的对象 org.springframework.jdbc.datasource.DataSourceTransactionManager 使用Spring JDBC或iBatis 进行持久化数据时使用 org.springframework.orm.hibernate3.HibernateTransactionManager 使用Hibernate版本进行持久化数据时使用 它是事务的定义信息对象,里面有如下方法:
是否将操作看为是事务
REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值) SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务) MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常 REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起。 NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起 NEVER:以非事务方式运行,如果当前存在事务,抛出异常 NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行REQUIRED类似的操作。 此接口提供的是事务具体的运行状态,方法介绍如下图:
创建项目并导包: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); } }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>首先对于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); } }根据如下的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; } }