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查找。
- 作用:为方法的形参注入数据。当一个带参数的方法使用@Bean将返回值注入到容器时,spring首先会根据形参的类型查找bean对象,如果指定了
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; } }