老冯课堂笔记Spring
1.简单自己实现IOC
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.darksnow</groupId>
<artifactId>SpringDemo01</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>7</source>
<target>7</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.6</version>
</dependency>
</dependencies>
</project>
beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="userService" class="com.darksnow.service.impl.UserServiceImpl"/>
</beans>
service层代码
package com.darksnow.service.impl;
import com.darksnow.service.UserService;
public class UserServiceImpl implements UserService {
}
----------------------------------
package com.darksnow.service;
public interface UserService {
}
Beanfactory
package com.darksnow.factory;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class BeanFactory {
private static final Map<String,Object> map = new HashMap<>();
//用Map来存储对象,因为有查找需求,相当于一个容器对象,包含了所有对象
static {
//获取配置文件的输入流对象
InputStream inputStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
//解析xml,获取xml中的信息
SAXReader reader = new SAXReader();
try {
//创建文档对象
Document doc = reader.read(inputStream);
//获取根节点
Element root = doc.getRootElement();
//获取根节点中的所有子节点
List<Element> beanList = root.elements("bean");
//遍历获取每一个bean的id和class
for(Element element : beanList) {
String id = element.attributeValue("id");
String className = element.attributeValue("class");
//通过className全限类名创建对象
//获取字节码文件对象
Class<?> clazz = Class.forName(className);
//通过反射创建对象
Object obj = clazz.newInstance();
//存储到Map集合中
map.put(id,obj);
}
} catch (Exception e) {
e.printStackTrace();
}
}
//需要实现的功能:传入一个id值,获取对应的Bean对象
public static Object getBean(String id){
return map.get(id);
}
}
测试类
package com.darksnow.test;
import com.darksnow.factory.BeanFactory;
public class TestIoc {
public static void main(String[] args) {
Object userService = BeanFactory.getBean("userService");
System.out.println(userService);
Object main = BeanFactory.getBean("main");
System.out.println(main);
}
}
2.Spring入门案例
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.darksnow</groupId>
<artifactId>SpringDemo01</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
配置文件
<?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">
<!--
scope属性取值:
prototype:对象的创建是多例模式
singleton:对象的创建是单例模式(默认的)
-->
<bean id="userDao" class="com.darksnow.dao.impl.UserDaoImpl" scope="singleton" init-method="init" destroy-method="destroy"/>
</beans>
接口与实现类
package com.darksnow.dao;
public interface UserDao {
}
-----------------------------------------------------------
package com.darksnow.dao.impl;
import com.darksnow.dao.UserDao;
public class UserDaoImpl implements UserDao {
public void init() {
System.out.println("userDao的初始化....");
}
public void destroy() {
System.out.println("userDao的销毁....");
}
}
测试类
package com.darksnow.test;
import com.darksnow.dao.UserDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestSpringIoc {
public static void main(String[] args) {
//1.创建Spring的IOC容器
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//创建容器对象时,创建对象,只创建单例模式的对象,多例模式的对象在获取时创建
//单例模式的对象在创建容器时创建,销毁容器时销毁
//多例模式的对象在获取时创建,即使容器被销毁,对象也不见得立马被销毁,当对象长时间不再被引用,则被垃圾回收机制回收
Object userDao = ac.getBean("userDao");
System.out.println(userDao);
UserDao userDao1 = ac.getBean(UserDao.class);
System.out.println(userDao1);
UserDao userDao2 = ac.getBean("userDao", UserDao.class);
System.out.println(userDao2);
//ac.close(); 销毁容器,ClassPathXmlApplicationContext里面的方法
}
}
3.实例化Bean的三种方式
3.1 实例工厂
package com.darksnow.factory;
import com.darksnow.dao.UserDao;
import com.darksnow.dao.impl.UserDaoImpl;
public class InstanceFactory {
public UserDao getUserDao() {
return new UserDaoImpl();
}
}
3.2 静态工厂
package com.darksnow.factory;
import com.darksnow.dao.UserDao;
import com.darksnow.dao.impl.UserDaoImpl;
public class StaticFactory {
public static UserDao getUserDao() {
return new UserDaoImpl();
}
}
3.3 applicationContext.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">
<!-- 方式一:通过xml方式配置 -->
<bean id="userDao1" class="com.darksnow.dao.impl.UserDaoImpl" />
<!-- 方式二:实例工厂 -->
<!-- 创建实例工厂 -->
<bean id="instanceFactory" class="com.darksnow.factory.InstanceFactory"/>
<!-- 通过实例工厂创建UserDaoImpl对象 -->
<bean id="userDao2" factory-bean="instanceFactory" factory-method="getUserDao"/>
<!-- 方式三:静态工厂 -->
<bean id="userDao3" class="com.darksnow.factory.StaticFactory" factory-method="getUserDao"/>
</beans>
3.4 测试类
package com.darksnow.test;
import com.darksnow.dao.UserDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestSpringIoc {
public static void main(String[] args) {
//创建Spring的IOC容器
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//方式一:通过xml方式来实例化Bean
UserDao userDao1 = ac.getBean("userDao1", UserDao.class);
System.out.println(userDao1);
//方式二:通过实例工厂来实例化Bean
UserDao userDao2 = ac.getBean("userDao2", UserDao.class);
System.out.println(userDao2);
//方式三:通过静态工厂来实例化Bean
UserDao userDao3 = ac.getBean("userDao3", UserDao.class);
System.out.println(userDao3);
}
}
4.Spring的依赖注入
4.1 构造方法注入
User实体
package com.darksnow.pojo;
import java.util.Date;
public class User {
private Integer id;
private String username;
private Character sex;
private Date birthday;
public User() {
}
public User(Integer id, String username, Character sex, Date birthday) {
this.id = id;
this.username = username;
this.sex = sex;
this.birthday = birthday;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", sex=" + sex +
", birthday=" + birthday +
'}';
}
}
配置文件
<?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="user" class="com.darksnow.pojo.User">
<!-- 通过构造方法上参数的索引进行赋值 -->
<constructor-arg index="0" value="1"/>
<constructor-arg index="1" value="darksnow"/>
<!-- 通过构造方法上参数的类型进行赋值 -->
<constructor-arg type="java.lang.Character" value="男"/>
<!--
value:该属性只能赋值简单的类型和String类型
ref:pojo等复杂类型,可以关联创建好的对象
-->
<constructor-arg name="birthday" ref="birthday"/>
</bean>
<bean id="birthday" class="java.util.Date"/>
</beans>
测试类
package com.darksnow.test;
import com.darksnow.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Date;
public class TestSpringDi {
public static void main(String[] args) {
//创建Spring的IOC容器
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = ac.getBean("user", User.class);
System.out.println(user);
}
}
4.2 set方法注入和p名称空间注入
User实体
package com.darksnow.pojo;
import java.util.Date;
public class User {
private Integer id;
private String username;
private Character sex;
private Date birthday;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Character getSex() {
return sex;
}
public void setSex(Character sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", sex=" + sex +
", birthday=" + birthday +
'}';
}
}
配置文件
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="birthday" class="java.util.Date"/>
<!-- 通过set方法注入 -->
<bean id="user" class="com.darksnow.pojo.User">
<property name="id" value="110"/>
<property name="username" value="安欣"/>
<property name="sex" value="男" />
<property name="birthday" ref="birthday" />
</bean>
<!-- 使用p名称空间注入(基于set方法) -->
<bean id="user2" class="com.darksnow.pojo.User" p:id="520" p:username="孟钰" p:sex="女" p:birthday-ref="birthday"/>
</beans>
测试类
package com.darksnow.test;
import com.darksnow.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestSpringDi {
public static void main(String[] args) {
//创建Spring的IOC容器
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = ac.getBean("user", User.class);
System.out.println(user);
User user2 = ac.getBean("user2", User.class);
System.out.println(user2);
}
}
4.3 集合注入
User实体
package com.darksnow.pojo;
import java.util.*;
public class User {
private List<String> list;
private Set<String> set;
private String[] strs;
private Map<String,String> map;
private Properties prop;
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
public Set<String> getSet() {
return set;
}
public void setSet(Set<String> set) {
this.set = set;
}
public String[] getStrs() {
return strs;
}
public void setStrs(String[] strs) {
this.strs = strs;
}
public Map<String, String> getMap() {
return map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public Properties getProp() {
return prop;
}
public void setProp(Properties prop) {
this.prop = prop;
}
}
配置文件
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="birthday" class="java.util.Date"/>
<bean id="user" class="com.darksnow.pojo.User">
<!-- array,list,set结构相同,标签可以混用 -->
<!-- list集合注入 -->
<property name="list">
<list>
<value>强哥</value>
<value>刀哥</value>
<value>小江</value>
</list>
</property>
<!-- set集合注入 -->
<property name="set">
<set>
<value>书婷</value>
<value>黄瑶</value>
<value>老默</value>
</set>
</property>
<!-- 数组注入 -->
<property name="strs">
<array>
<value>高启强</value>
<value>高启盛</value>
<value>高启兰</value>
</array>
</property>
<!-- map集合注入,map和properties结构相同,可以混用 -->
<property name="map">
<map>
<entry key="1" value="李响"/>
<entry key="2" value="谭思言"/>
</map>
</property>
<!-- properties注入 -->
<property name="prop">
<props>
<prop key="666">李有田</prop>
<prop key="777">李宏伟</prop>
</props>
</property>
</bean>
</beans>
测试类
package com.darksnow.test;
import com.darksnow.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Arrays;
public class TestSpringDi {
public static void main(String[] args) {
//创建Spring的IOC容器
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = ac.getBean("user", User.class);
System.out.println(user.getList());
System.out.println(user.getSet());
System.out.println(Arrays.toString(user.getStrs()));
System.out.println(user.getMap());
System.out.println(user.getProp());
}
}
5.CRUD案例
配置文件
<?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:p="http://www.springframework.org/schema/p"
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 https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 需要扫描包才能够使用@Controller,@Service,@Repository,@Component -->
<context:component-scan base-package="com.darksnow" />
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
<constructor-arg ref="comboPooledDataSource"/>
</bean>
<bean id="comboPooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql:///darksnow?CharacterEncoding=utf-8"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
</bean>
</beans>
实体类
package com.darksnow.pojo;
public class Account {
private Integer id;
private String name;
private Float money;
public Integer getId() {
return id;
}
public void setId(Integer 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
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
dao层
package com.darksnow.dao;
import com.darksnow.pojo.Account;
import java.sql.SQLException;
import java.util.List;
public interface AccountDao {
List<Account> findAll() throws SQLException;
Account findById(Integer id) throws SQLException;
void save(Account account) throws SQLException;
void update(Account account) throws SQLException;
void delete(Integer id) throws SQLException;
}
-----------------------------------------
package com.darksnow.dao.impl;
import com.darksnow.dao.AccountDao;
import com.darksnow.pojo.Account;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import java.sql.SQLException;
import java.util.List;
@Repository
public class AccountDaoImpl implements AccountDao {
@Autowired
private QueryRunner runner;
public List<Account> findAll() throws SQLException {
String sql = "select * from account";
return runner.query(sql,new BeanListHandler<Account>(Account.class));
}
public Account findById(Integer id) throws SQLException {
String sql = "select * from account where id = ?";
return runner.query(sql,new BeanHandler<Account>(Account.class),id);
}
public void save(Account account) throws SQLException {
String sql = "insert into account values(?,?,?)";
runner.update(sql,null,account.getName(),account.getMoney());
}
public void update(Account account) throws SQLException {
String sql = "update account set name = ? where id = ?";
runner.update(sql,account.getName(),account.getId());
}
public void delete(Integer id) throws SQLException {
String sql = "delete from account where id = ?";
runner.update(sql,id);
}
}
service层
package com.darksnow.service;
import com.darksnow.pojo.Account;
import java.sql.SQLException;
import java.util.List;
public interface AccountService {
List<Account> findAll() throws SQLException;
Account findById(Integer id) throws SQLException;
void save(Account account) throws SQLException;
void update(Account account) throws SQLException;
void delete(Integer id) throws SQLException;
}
-----------------------------------------
package com.darksnow.service.impl;
import com.darksnow.dao.AccountDao;
import com.darksnow.pojo.Account;
import com.darksnow.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.sql.SQLException;
import java.util.List;
/*
* @Component只要标记了该注解,并且标记该注解的类在扫描范围内,就会创建对象
* 需要在xml配置文件中配置 <context:component-scan base-package="你要扫描的包路径,该路径下的所有类都会被扫描到"/>
*
* 除了上述注解还有三个
* @Controller 一般用于web层
* @Service 一般用于service层
* @Repository 一般用于dao层
*/
//@Service //如果没有指定名称,默认是简单类名(但是首字母小写),比如:类名为UserServiceImpl,getBean的时候就用userServiceImpl
@Service("accountService") //主句话相当于,你在xml中配置<bean id="accountService" class="com.darksnow.service.impl.AccountServiceImpl">
public class AccountServiceImpl implements AccountService {
/*
必须与@Autowired结合使用
作用:如果自动注入按照类型注入失败,则按照指定的名称注入
不是很常用,了解
*/
//@Qualifier("accountDao")
/*
@Autowired 可以自动按照类型注入
当属性或者set方法标记了@Autowired,会自动在容器中查找该属性类型的对象,如果有多个则会选择名字相同的
*/
//@Autowired
/*
一样可以自动按照类型注入,但是不同于@Autowired的是,它是不是有Spring提供的而是JDK
而且它还能够按照指定的名称注入,但是@Autowired不行!
*/
//@Resource(name = "accountDaoImpl")
@Resource
private AccountDao accountDao;
public List<Account> findAll() throws SQLException {
System.out.println(accountDao);
return accountDao.findAll();
}
public Account findById(Integer id) throws SQLException {
return accountDao.findById(id);
}
public void save(Account account) throws SQLException {
accountDao.save(account);
}
public void update(Account account) throws SQLException {
accountDao.update(account);
}
public void delete(Integer id) throws SQLException {
accountDao.delete(id);
}
}
测试类
package com.darksnow.test;
import com.darksnow.dao.AccountDao;
import com.darksnow.pojo.Account;
import com.darksnow.service.AccountService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.sql.SQLException;
import java.util.List;
public class TestCrud {
private ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
private AccountService accountService = ac.getBean("accountService", AccountService.class);
@Test
public void testFindAll() throws SQLException {
List<Account> accountList = accountService.findAll();
for (Account account : accountList) {
System.out.println(account);
}
}
@Test
public void testFindById() throws SQLException {
Account account = accountService.findById(1);
System.out.println(account);
}
@Test
public void testSave() throws SQLException {
Account account = new Account();
account.setName("康健");
account.setMoney(1500f);
accountService.save(account);
}
@Test
public void testUpdate() throws SQLException {
Account account = new Account();
account.setId(3);
account.setName("康康建");
accountService.update(account);
}
@Test
public void testDelete() throws SQLException {
accountService.delete(3);
}
}
6.纯注解CRUD案例(无xml)
基于原来的CRUD案例,dao层和service层,pojo代码没有做任何修改
配置类
package com.darksnow.config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
@Configuration
//加载properties配置文件
@PropertySource({"jdbc.properties"})
public class JdbcConfiguration {
//给属性赋值,只能给简单类型赋值
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
//@Bean("name"),用在方法上,用来指定方法创建的对象存储到容器中,name就是在容器中的名称
@Bean("queryRunner")
public QueryRunner createQueryRunner(DataSource dataSource) {
return new QueryRunner(dataSource);
}
@Bean("dataSource")
public DataSource createDataSource() {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
try {
dataSource.setDriverClass(driver);
dataSource.setJdbcUrl(url);
dataSource.setUser(username);
dataSource.setPassword(password);
}catch (PropertyVetoException e){
e.printStackTrace();
}
return dataSource;
}
}
-----------------------------------
package com.darksnow.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
//该注解用于标记指定类为配置文件类
@Configuration
//相当于<context:component-scan base-package="com.darksnow" />
@ComponentScan({"com.darksnow"})
//引入其他配置类
@Import({JdbcConfiguration.class})
public class SpringConfiguration {
}
测试类
package com.darksnow.test;
import com.darksnow.config.SpringConfiguration;
import com.darksnow.pojo.Account;
import com.darksnow.service.AccountService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import java.sql.SQLException;
import java.util.List;
public class TestCrud {
@Test
public void testFindAll() throws SQLException {
//存注解创建容器对象
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
AccountService accountService = ac.getBean("accountService", AccountService.class);
List<Account> accountList = accountService.findAll();
for (Account account : accountList) {
System.out.println(account);
}
}
}
其他注解
//配置对象的模式,默认是单例,配置在类上
@Scope("singleton")
//写在方法上,相当于bean标签内的属性:init-method,指定初始化方法
@PostConstruct
//写在方法上,相当于bean标签内的属性:destroy-method,指定对象的销毁方法
@PreDestroy
7.xml加载properties
<?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:p="http://www.springframework.org/schema/p"
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 https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 在xml中引入其他的配置文件 -->
<!-- <import resource="applicationContext-dao.xml"/> -->
<!-- 加载properties配置文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 需要扫描包才能够使用@Controller,@Service,@Repository,@Component -->
<context:component-scan base-package="com.darksnow" />
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner" >
<constructor-arg ref="comboPooledDataSource"/>
</bean>
<bean id="comboPooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- ${jdbc.driver} 获取的是properties中的配置 -->
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>
8.Spring与Junit整合
<!-- 引入依赖坐标 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
8.1 xml的形式
package com.darksnow.test;
import com.darksnow.pojo.Account;
import com.darksnow.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.sql.SQLException;
import java.util.List;
//替换Junit的运行器为spring与junit整合后的运行器
@RunWith(SpringJUnit4ClassRunner.class)
//指定配置文件路径,会自动创建容器对象
@ContextConfiguration({"classpath:applicationContext.xml"})
public class TestCrud {
@Autowired
private AccountService accountService;
@Test
public void testFindAll() throws SQLException {
List<Account> accountList = accountService.findAll();
for (Account account : accountList) {
System.out.println(account);
}
}
}
8.2 纯注解的形式
package com.darksnow.test;
import com.darksnow.config.SpringConfiguration;
import com.darksnow.pojo.Account;
import com.darksnow.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.sql.SQLException;
import java.util.List;
//替换Junit的运行器为spring与junit整合后的运行器
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {SpringConfiguration.class})
public class TestCrud {
@Autowired
private AccountService accountService;
@Test
public void testFindAll() throws SQLException {
List<Account> accountList = accountService.findAll();
for (Account account : accountList) {
System.out.println(account);
}
}
}
9.动态代理
9.1 JDK动态代理
package com.darksnow.proxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.junit.Test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
class ProductFactory {
//制造产品
public void make() {
System.out.println("生产了一个产品");
}
}
//建立一个销售的网点:直营
class OldSale {
public void sale(Float money) {
System.out.println("正在以" + money + "价格卖出");
}
}
//总经销商
interface NewSale {
//卖产品
void sale(Float money);
}
//经销商创建的销售网点
class NewSaleImpl implements NewSale {
public void sale(Float money) {
System.out.println("正在以" + money + "价格卖出");
}
}
public class TestProxy {
//JDK动态代理:基于接口的动态代理
@Test
public void testJDKProxy() {
//代理之前对象
NewSale newSale = new NewSaleImpl();
//newSale.sale(2000F);
/*
对卖出方法进行增强,可以判断我以某个价格卖出是亏了还是赚了
TestProxy.class.getClassLoader():类加载器
newSale.getClass().getInterfaces():实现类的接口数组
new InvocationHandler():以匿名内部类的方式实现了InvocationHandler接口,具体的增强逻辑写在覆盖该接口中的invoke方法中
*/
NewSale sale = (NewSale) Proxy.newProxyInstance(TestProxy.class.getClassLoader(), newSale.getClass().getInterfaces(), new InvocationHandler() {
/*
Object proxy:代理对象
Method method:代理的方法,原来未增强的方法
Object[] args:代理的方法的参数
返回值:代理的方法的返回值
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//生产产品
ProductFactory productFactory = new ProductFactory();
productFactory.make();
/*
开始销售
invoke方法的参数
参数1:要增强的方法所在的对象
参数2:要增强的方法的参数
*/
method.invoke(newSale, args); //调用的是原来没有增强的方法
if ((Float) args[0] > 1500) {
System.out.println("卖的价格高于成本,挣了,可卖");
} else {
System.out.println("卖的价格低于成本,赔了,不可卖");
}
return null;
}
});
sale.sale(2000F);
}
}
9.2 cglib动态代理
//cglib动态代理:基于类的动态代理
@Test
public void testCglibProxy() {
//cglib动态代理,代理对象是要增强的方法所在的对象的一个子类
//要增强的方法所在的对象,要增强的方法为OldSale对象中的sale(Float money)方法
OldSale oldSale = new OldSale();
//创建cglib代理对象
//1.创建增强类对象
Enhancer enhancer = new Enhancer();
//2.指定代理对象的父类
enhancer.setSuperclass(OldSale.class);
//3.指定增强的内容
//MethodInterceptor接口是方法拦截器,拦截方法进行增强
enhancer.setCallback(new MethodInterceptor() {
/*
参数说明
Object o:代理对象,增强后的对象
Method method:代理的方法
Object[] objects:代理的方法的参数
MethodProxy methodProxy:代理方法,增强后的方法
*/
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//生产产品
ProductFactory productFactory = new ProductFactory();
productFactory.make();
//开始销售,执行原来要增强的方法
method.invoke(oldSale,objects);
if ((Float) objects[0] > 1500) {
System.out.println("卖的价格高于成本,挣了,可卖");
} else {
System.out.println("卖的价格低于成本,赔了,不可卖");
}
return null;
}
});
//4.创建代理对象
OldSale oldSaleProxy = (OldSale) enhancer.create();
oldSaleProxy.sale(500F);
}
10.动态代理增强转账业务
配置文件
<?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:p="http://www.springframework.org/schema/p"
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 https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 加载properties配置文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 需要扫描包才能够使用@Controller,@Service,@Repository,@Component -->
<context:component-scan base-package="com.darksnow" />
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner" >
<constructor-arg ref="dataSource"/>
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- ${jdbc.driver} 获取的是properties中的配置 -->
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>
工具类
package com.darksnow.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
/*
一个管理连接的工具类,用于实现连接和线程的绑定
*/
@Component
public class ConnectionUtil {
private final ThreadLocal<Connection> tl = new ThreadLocal<>();
@Autowired
private DataSource dataSource;
/*
获取当前线程上绑定的连接
*/
public Connection getThreadConnection() {
try {
Connection conn = tl.get();
//判断线程上是否绑了
if (conn == null) {
//从数据源中获取一个连接
conn = dataSource.getConnection();
//和线程绑定
tl.set(conn);
}
//返回线程上的连接
return tl.get();
}catch (SQLException e){
throw new RuntimeException(e);
}
}
/*
把连接和当前线程解绑
*/
public void remove() {
tl.remove();
}
}
package com.darksnow.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.sql.SQLException;
/*
事务管理器
*/
@Component
public class TransactionManager {
@Autowired
private ConnectionUtil connectionUtil;
//开始事务
public void beginTransaction() {
//从当前线程上获取连接,实现开启事务
try {
connectionUtil.getThreadConnection().setAutoCommit(false);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
//提交事务
public void commit() {
try {
connectionUtil.getThreadConnection().commit();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
//回滚事务
public void rollback() {
try {
connectionUtil.getThreadConnection().rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
//释放连接
public void release() {
try {
connectionUtil.getThreadConnection().setAutoCommit(true);
//关闭连接
connectionUtil.getThreadConnection().close();
//解绑线程
connectionUtil.remove();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
实体类
package com.darksnow.pojo;
public class Account {
private Integer id;
private String name;
private Float money;
public Integer getId() {
return id;
}
public void setId(Integer 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
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
dao层
package com.darksnow.dao;
import com.darksnow.pojo.Account;
import java.sql.SQLException;
public interface AccountDao {
Account findByName(String fromName) throws SQLException;
void update(Account account) throws SQLException;
}
-----------------------------------------
package com.darksnow.dao.impl;
import com.darksnow.dao.AccountDao;
import com.darksnow.pojo.Account;
import com.darksnow.utils.ConnectionUtil;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.sql.Connection;
import java.sql.SQLException;
@Repository
public class AccountDaoImpl implements AccountDao {
@Autowired
private ConnectionUtil connectionUtil;
@Autowired
private QueryRunner queryRunner;
//根据账户名查询账户
@Override
public Account findByName(String fromName) throws SQLException {
String sql = "select * from account where name = ?";
//从线程中获取一个连接对象
Connection conn = connectionUtil.getThreadConnection();
return queryRunner.query(conn,sql,new BeanHandler<Account>(Account.class),fromName);
}
//更新账户
@Override
public void update(Account account) throws SQLException {
String sql = "update account set money = ? where name = ?";
Connection conn = connectionUtil.getThreadConnection();
queryRunner.update(conn, sql, account.getMoney(), account.getName());
}
}
service层
package com.darksnow.service;
public interface AccountService {
void transfer(String fromName, String toName, Float money);
}
------------------------------------
package com.darksnow.service.impl;
import com.darksnow.dao.AccountDao;
import com.darksnow.pojo.Account;
import com.darksnow.service.AccountService;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.sql.SQLException;
/*
一个事务必须在一个Connection中完成
ThreadLocal:线程绑定,绑定Connection对象,业务层和持久层需要Connection就从ThreadLocal中获取
*/
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Override
public void transfer(String fromName, String toName, Float money) {
try {
//查询要转出的账号
Account fromAccount = accountDao.findByName(fromName);
//查询要转入的账号
Account toAccount = accountDao.findByName(toName);
//修改要转出的账户余额
fromAccount.setMoney(fromAccount.getMoney() - money);
//修改要转入的账户余额
toAccount.setMoney(toAccount.getMoney() + money);
//持久化到数据库
accountDao.update(fromAccount);
// int i = 1/0;
accountDao.update(toAccount);
}catch (SQLException e) {
e.printStackTrace();
}
}
}
测试类
package com.darksnow.test;
import com.darksnow.service.AccountService;
import com.darksnow.utils.TransactionManager;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestTransfer {
@Autowired
private AccountService accountService;
@Autowired
private TransactionManager txManager;
@Test
public void transfer() {
accountService.transfer("张三","李四",500f);
}
@Test
public void testJDKProxyService() {
AccountService accountServiceProxy = (AccountService) Proxy.newProxyInstance(accountService.getClass().getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//开启事务
txManager.beginTransaction();
//执行未增强之前的方法
method.invoke(accountService, args);
//提交事务
txManager.commit();
} catch (Exception e) {
//事务回滚
txManager.rollback();
e.printStackTrace();
} finally {
//还原状态
txManager.release();
}
return null;
}
});
accountServiceProxy.transfer("张三", "李四", 500f);
}
@Test
public void testCglibProxyService() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(accountService.getClass());
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
try {
txManager.beginTransaction();
method.invoke(accountService,objects);
txManager.commit();
}catch (Exception e) {
txManager.rollback();
e.printStackTrace();
}finally {
txManager.release();
}
return null;
}
});
AccountService as = (AccountService) enhancer.create();
as.transfer("张三","李四",500f);
}
}
11.AOP
11.1 概念
AOP:全称为Aspect Oriented Programming 即:面向切面编程
简单说它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对我们已有的方法进行增强,所以底层基于jdk动态代理 + cglib动态代理的
作用:在程序运行期间,不修改源码对已有方法进行增强
优势:减少重复代码,松耦合,提高开发效率,维护起来比较便捷
注意:aop由多个oop组成(oop为面向对象编程)
11.2 AOP相关术语
Joinpoint(连接点):所谓的连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
Pointcut(切入点):所谓的切入点是指我们要对那些Joinpoint进行拦截的定义
Advice(通知/增强):所谓的通知是指拦截到Joinpoint之后所要做的事情就是通知
Target(目标对象):代理的目标对象
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程,spring采用动态代理织入,而aspect采用编译期织入和类装载期织入。
Proxy(代理):一个类被Aop织入增强后,就产生一个结果代理类
Introduction(引介):引介是一种特殊的通知,在不修改代码的前提下,引介可以在运行期为类动态地添加一些方法或字段
Aspect(切面):它是切入点和通知(引介)的结合
11.3 AOP的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:p="http://www.springframework.org/schema/p"
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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 需要扫描包才能够使用@Controller,@Service,@Repository,@Component -->
<context:component-scan base-package="com.darksnow" />
<bean id="logger" class="com.darksnow.log.Logger" />
<!-- 配置Aop -->
<aop:config>
<!-- 配置切面 = 切入点 + 通知 -->
<aop:aspect ref="logger"> <!-- 指定通知的对象是谁 -->
<!--
配置切入点
属性说明:
id:唯一标识
expression:表达式
表达式说明:
* com.darksnow.service.impl.*.*(..)
第一个*:代表方法的返回值类型任意
第二个*:代表类名任意,impl包中的所有的类
第三个*:代表方法名任意
(..):参数任意,个数任意,类型任意,顺序任意
表达式其他的配置方式:
* com.darksnow.service..*.*(..)
public void com.darksnow.service.impl.AccountService.findAll()
void com.darksnow.service.impl.AccountService.findAll()
* com.darksnow.service.impl.AccountService.findAll()
* com.darksnow.service..AccountService.findAll() 代表service包及其子包里的所有包都算
* *。。*。*(..):全通配方式
-->
<aop:pointcut id="pointcut" expression="execution(* com.darksnow.service.impl.*.*(..))"/>
<!--
配置通知
织入:告诉通知对象执行,具体执行哪一个方法
通知的类型:
前置通知:方法之前执行
后置通知:方法执行完毕之后,返回之前执行,如果遇到异常则不执行
最终通知:方法执行完之后总会执行
异常通知:方法出现异常则执行
环绕通知:前置通知 + 后置通知 + 最终通知 + 异常通知
-->
<!-- 前置通知 -->
<!-- <aop:before method="before" pointcut-ref="pointcut"/>-->
<!-- 后置通知 -->
<!-- <aop:after-returning method="afterReturning" pointcut-ref="pointcut"/>-->
<!-- 最终通知 -->
<!-- <aop:after method="after" pointcut-ref="pointcut"/>-->
<!-- 异常通知 -->
<!-- <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="e"/>-->
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pointcut" />
</aop:aspect>
</aop:config>
</beans>
service
package com.darksnow.service;
public interface AccoutService {
void print();
Integer add(int a,int b);
}
---------------------------------------------
package com.darksnow.service.impl;
import com.darksnow.service.AccoutService;
import org.springframework.stereotype.Service;
@Service
public class AccountServiceImpl implements AccoutService {
@Override
public void print() {
// int i = 1/0;
System.out.println("打印了......");
}
@Override
public Integer add(int a,int b) {
return a+b;
}
}
Logger类
package com.darksnow.log;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
//日志类,记录什么时间访问什么类,什么方法,操作什么功能
public class Logger {
public void before(JoinPoint joinPoint){
//获取被代理的对象
Object target = joinPoint.getTarget();
//被拦截的类的名称
String className = target.getClass().getName();
System.out.println("被拦截到的类名:" + className);
//获取方法对象
Signature signature = joinPoint.getSignature();
//获取被拦截的方法名
String methodName = signature.getName();
System.out.println("被拦截到的方法名:" + methodName);
System.out.println("前置通知......");
}
public void afterReturning() {
System.out.println("后置通知......");
}
public void after() {
System.out.println("最终通知......");
}
public void afterThrowing(Exception e) {
System.out.println("执行的方法的异常为:" + e);
System.out.println("异常通知......");
}
//ProceedingJoinPoint:可以执行拦到截的方法
public void around(ProceedingJoinPoint joinPoint) {
try {
System.out.println("前置通知......");
//执行原来要增强的方法,可以得到原来方法的返回值
Object result = joinPoint.proceed();
System.out.println(result);
System.out.println("后置增强......");
}catch (Throwable e) {
System.out.println("异常通知.....");
e.printStackTrace();
}finally {
System.out.println("最终通知......");
}
}
}
测试类
package com.darksnow.test;
import com.darksnow.service.AccoutService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestAopXml {
@Autowired
private AccoutService accoutService;
@Test
public void testAop() {
accoutService.print();
System.out.println("-------------------");
accoutService.add(1314,520);
}
}
11.4 AOP的注解形式
其他的和11.3的内容一样,以下为更改部分
配置文件
<?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:p="http://www.springframework.org/schema/p"
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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 需要扫描包才能够使用@Controller,@Service,@Repository,@Component -->
<context:component-scan base-package="com.darksnow" />
<!-- 开启AOP的自动代理,注解方式代理 -->
<aop:aspectj-autoproxy />
</beans>
Logger类
package com.darksnow.log;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
//日志类,记录什么时间访问什么类,什么方法,操作什么功能
@Component //创建对象交给容器管理
@Aspect //配置该类为切面,切面 = 切入点 + 通知
public class Logger {
//配置切入点
@Pointcut("execution(* com.darksnow.service.impl.*.*(..))")
public void pointcut() {
}
//JoinPoint:连接点 --- 拦截到的方法
@Before("pointcut()") //织入
public void before(JoinPoint joinPoint){
//获取被代理的对象
Object target = joinPoint.getTarget();
//被拦截的类的名称
String className = target.getClass().getName();
System.out.println("被拦截到的类名:" + className);
//获取方法对象
Signature signature = joinPoint.getSignature();
//获取被拦截的方法名
String methodName = signature.getName();
System.out.println("被拦截到的方法名:" + methodName);
System.out.println("前置通知......");
}
@AfterReturning("pointcut()")
public void afterReturning() {
System.out.println("后置通知......");
}
@After("pointcut()")
public void after() {
System.out.println("最终通知......");
}
@AfterThrowing(value = "pointcut()",throwing = "e")
public void afterThrowing(Exception e) {
System.out.println("执行的方法的异常为:" + e);
System.out.println("异常通知......");
}
//ProceedingJoinPoint:可以执行拦到截的方法
// @Around("pointcut()")
public void around(ProceedingJoinPoint joinPoint) {
try {
System.out.println("前置通知......");
//执行原来要增强的方法,可以得到原来方法的返回值
Object result = joinPoint.proceed();
System.out.println(result);
System.out.println("后置增强......");
}catch (Throwable e) {
System.out.println("异常通知.....");
e.printStackTrace();
}finally {
System.out.println("最终通知......");
}
}
}
11.5 AOP配置式解决事务问题
照抄10.动态代理增强转账业务的代码,只是去掉动态代理增强的那两个测试方法
配置文件
<?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:p="http://www.springframework.org/schema/p"
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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 加载properties配置文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 需要扫描包才能够使用@Controller,@Service,@Repository,@Component -->
<context:component-scan base-package="com.darksnow" />
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner" >
<constructor-arg ref="dataSource"/>
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- ${jdbc.driver} 获取的是properties中的配置 -->
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--
使用aop解决事务问题
-->
<aop:config>
<!-- 配置切面 -->
<aop:aspect ref="transactionManager"> <!-- 指定通知对象是谁 , 这里明显通知对象是 事务管理类 -->
<!-- 切入点 -->
<aop:pointcut id="pointcut" expression="execution(* com.darksnow.service.impl.*.*(..))"/>
<!-- 织入 -->
<!-- 前置通知(增强):开启事务 -->
<aop:before method="beginTransaction" pointcut-ref="pointcut" />
<!-- 后置增强:提交事务 -->
<aop:after-returning method="commit" pointcut-ref="pointcut" />
<!-- 异常增强:回滚事务 -->
<aop:after-throwing method="rollback" pointcut-ref="pointcut" />
<!-- 最终增强:释放资源 -->
<aop:after method="release" pointcut-ref="pointcut" />
</aop:aspect>
</aop:config>
</beans>
测试类
package com.darksnow.test;
import com.darksnow.service.AccountService;
import com.darksnow.utils.TransactionManager;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestTransfer {
@Autowired
private AccountService accountService;
@Autowired
private TransactionManager txManager;
@Test
public void transfer() {
accountService.transfer("张三","李四",500f);
}
}
11.6 AOP注解式解决事务问题
照抄10.动态代理增强转账业务的代码,然后做如下修改
配置文件
<?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:p="http://www.springframework.org/schema/p"
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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 加载properties配置文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 需要扫描包才能够使用@Controller,@Service,@Repository,@Component -->
<context:component-scan base-package="com.darksnow" />
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner" >
<constructor-arg ref="dataSource"/>
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- ${jdbc.driver} 获取的是properties中的配置 -->
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 开启aop自动代理注解 -->
<aop:aspectj-autoproxy />
</beans>
事务管理类
package com.darksnow.utils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.sql.SQLException;
/*
事务管理器
*/
@Component
@Aspect //配置切面
public class TransactionManager {
@Autowired
private ConnectionUtil connectionUtil;
@Pointcut("execution(* com.darksnow.service.impl.*.*(..))")
public void pointcut(){
}
//环绕通知(增强)
@Around("pointcut()")
public void around(ProceedingJoinPoint joinPoint) {
try {
//前置增强
beginTransaction(); //开启事务
//执行原来未增强的方法
joinPoint.proceed();
//后置增强
commit(); //提交事务
}catch (Throwable throwable) {
//异常增强
rollback(); //回滚事务
throwable.printStackTrace();
}finally {
//最终增强
release(); //释放资源
}
}
//开始事务
public void beginTransaction() {
//从当前线程上获取连接,实现开启事务
try {
connectionUtil.getThreadConnection().setAutoCommit(false);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
//提交事务
public void commit() {
try {
connectionUtil.getThreadConnection().commit();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
//回滚事务
public void rollback() {
try {
connectionUtil.getThreadConnection().rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
//释放连接
public void release() {
try {
connectionUtil.getThreadConnection().setAutoCommit(true);
//关闭连接
connectionUtil.getThreadConnection().close();
//解绑线程
connectionUtil.remove();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
测试类
package com.darksnow.test;
import com.darksnow.service.AccountService;
import com.darksnow.utils.TransactionManager;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestTransfer {
@Autowired
private AccountService accountService;
@Autowired
private TransactionManager txManager;
@Test
public void transfer() {
accountService.transfer("张三","李四",500f);
}
}
12.JDBCTemplate的使用
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.darksnow</groupId>
<artifactId>SpringDemo01</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.18</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
</dependencies>
</project>
配置文件
<?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:p="http://www.springframework.org/schema/p"
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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 加载properties配置文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 需要扫描包才能够使用@Controller,@Service,@Repository,@Component -->
<context:component-scan base-package="com.darksnow" />
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner" >
<constructor-arg ref="c3p0DataSource"/>
</bean>
<!-- 配置c3p0的数据源 -->
<bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- ${jdbc.driver} 获取的是properties中的配置 -->
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- Spring自带的数据源 -->
<bean id="springDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 创建JdbcTemplate模板对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="springDataSource" />
</bean>
</beans>
实体类
package com.darksnow.pojo;
public class Account {
private Integer id;
private String name;
private Float money;
//省略getter,setter,toString
}
结果集对象
package com.darksnow.mapper;
import com.darksnow.pojo.Account;
import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
//结果集对象,只包含一行数据
public class AccountRomMapper implements RowMapper<Account> {
@Override
public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
Account account = new Account();
int id = rs.getInt("id");
account.setId(id);
account.setName(rs.getString("name"));
account.setMoney(rs.getFloat("money"));
return account;
}
}
测试类
package com.darksnow.test;
import com.darksnow.mapper.AccountRomMapper;
import com.darksnow.pojo.Account;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestJdbcTemplate {
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
public void testFindAll() {
String sql = "select * from account";
//方式一:
// List<Map<String, Object>> accoutList = jdbcTemplate.queryForList(sql);
// for (Map<String, Object> map : accoutList) {
// System.out.println(map);
// }
//方式二:
//query方法参数一:sql语句,参数二:自定义结果集对象
List<Account> accountList = jdbcTemplate.query(sql, new AccountRomMapper());
for (Account account : accountList) {
System.out.println(account);
}
}
@Test
public void testFindById() {
String sql = "select * from account where id = ?";
//方式一:
// List<Account> accountList = jdbcTemplate.query(sql, new AccountRomMapper(), 20);
// System.out.println(accountList.size() == 1 ? accountList.get(0) : "结果为:null");
//方式二:
// Account account = jdbcTemplate.queryForObject(sql, new AccountRomMapper(), 2);
// System.out.println(account);
//方式三:
//BeanPropertyRowMapper对象会帮你做映射,但是使用的时候要保证属性和表中字段名一致
Account account = jdbcTemplate.queryForObject(sql, new Object[]{2}, new BeanPropertyRowMapper<>(Account.class));
System.out.println(account);
}
@Test
public void testSave() {
String sql = "insert into account values(null,?,?)";
jdbcTemplate.update(sql,"坤坤","0.01");
}
@Test
public void testUpdate() {
String sql = "update account set name = ? where id = ?";
jdbcTemplate.update(sql,"爱坤",3);
}
@Test
public void testDelete() {
String sql = "delete from account where id = ?";
jdbcTemplate.update(sql, 3);
}
@Test
public void testGetTotalCount() {
String sql = "select count(*) from account";
//queryForObject(sql语句,返回值类型)
Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
System.out.println(count);
}
}
13.JDBCTemplate在dao层的使用
13.1 方式一
dao层
package com.darksnow.dao.impl;
import com.darksnow.dao.AccountDao;
import com.darksnow.mapper.AccountRomMapper;
import com.darksnow.pojo.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public class AccountDaoImpl implements AccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public List<Account> findAll() {
String sql = "select * from account";
return jdbcTemplate.query(sql,new AccountRomMapper());
}
@Override
public Account findById(Integer id) {
String sql = "select * from account where id = ?";
return jdbcTemplate.queryForObject(sql,new AccountRomMapper(),id);
}
}
测试类
package com.darksnow.test;
import com.darksnow.dao.AccountDao;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestJdbcTemplate {
@Autowired
private AccountDao accountDao;
@Test
public void testFindAll() {
System.out.println(accountDao.findAll());
}
@Test
public void testFindById() {
System.out.println(accountDao.findById(1));
}
}
13.2 方式二
dao层代码
package com.darksnow.dao.impl;
import com.darksnow.dao.AccountDao;
import com.darksnow.mapper.AccountRomMapper;
import com.darksnow.pojo.Account;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import java.util.List;
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
@Override
public List<Account> findAll() {
String sql = "select * from account";
return getJdbcTemplate().query(sql,new AccountRomMapper());
}
@Override
public Account findById(Integer id) {
String sql = "select * from account where id = ?";
return getJdbcTemplate().queryForObject(sql,new AccountRomMapper(),id);
}
}
测试类
package com.darksnow.test;
import com.darksnow.dao.AccountDao;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestJdbcTemplate {
@Autowired
private AccountDao accountDao;
@Test
public void testFindAll() {
System.out.println(accountDao.findAll());
}
@Test
public void testFindById() {
System.out.println(accountDao.findById(1));
}
}
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:p="http://www.springframework.org/schema/p"
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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 加载properties配置文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 需要扫描包才能够使用@Controller,@Service,@Repository,@Component -->
<context:component-scan base-package="com.darksnow" />
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner" >
<constructor-arg ref="c3p0DataSource"/>
</bean>
<!-- 配置c3p0的数据源 -->
<bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- ${jdbc.driver} 获取的是properties中的配置 -->
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- Spring自带的数据源 -->
<bean id="springDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 创建JdbcTemplate模板对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="springDataSource" />
</bean>
<!-- 创建dao层对象,交给容器管理 -->
<bean id="accountDao" class="com.darksnow.dao.impl.AccountDaoImpl">
<!-- 通过set方法注入数据源 -->
<property name="dataSource" ref="springDataSource"/>
</bean>
</beans>
14.Spring事务管理
脏读:
脏读又称无效数据读出(读出了脏数据)。一个事务读取另外一个事务还没有提交的数据叫脏读。
例如:事务T1修改了某个表中的一行数据,但是还没有提交,这时候事务T2读取了被事务T1修改后的数据,
之后事务T1因为某种原因回滚(Rollback)了,那么事务T2读取的数据就是脏的(无效的)。
幻读:
幻读的重点在于新增或者删除 (数据条数变化), 同样的条件, 第1次和第2次读出来的记录数不一样。
例如:系统事务A将数据库中所有数据都删除的时候,但是事务B就在这个时候新插入了一条记录,
当事务A删除结束后发现还有一条数据,就好像发生了幻觉一样。这就叫幻读。
不可重复读:
不可重复读与脏读逻辑类似。主要在于事务2在事务1第二次读取时,提交了数据。导致事务1前后两次读取的数据不一致。
不可重复读的重点是修改并提交: 同样的条件, 你读取过的数据, 再次读取出来发现值不一样了
不可重复读是指在同一个事务内,两次相同的查询返回了不同的结果。
例如:事务T1会读取两次数据,在第一次读取某一条数据后,事务T2修改了该数据并提交了事务,
T1此时再次读取该数据,两次读取便得到了不同的结果。
-------------------------------------------------------------------
事务的四个特性(ACID):
原子性(atomicity):不可再分隔
一致性(consistency):要么全部完成,要么全部不完成
隔离性(isolation):事务之间的隔离级别
持久性(durability):一旦提交,持久化到数据中
事务隔离级别:它反映了事务提交并发访问时的处理态度
(1)可以读取未提交数据(读未提交):read_uncommitted
产生的问题:脏读,幻读,不可重复读
(2)只能读取已提交数据,解决脏读问题(Oracle默认级别,读已提交):red_committed
产生的问题:幻读,不可重复读
解决的问题:脏读
(3)是否读取其他事务提交修改后的数据(Mysql默认级别,重复读):repeatable_red
产生的问题:幻读
解决的问题:脏读,不可重复读
(4)是否读取其他事务提交添加后的数据(串行化):serializable
产生的问题:无
解决的问题:脏读,幻读,不可重复读
隔离级别最高,效率最低
事务的传播行为:
(1)REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务,那么加入到这个事务中(默认值)
(2)MANDATORY:使用当前的事务,如果没有事务,就抛出异常
(3)NESTED:如果当前存在事务,则在嵌套事务内执行,如果当前没有事务,则执行REQUIRED类似的操作。
(4)NEVER:以非事务方式运行,如果当前存在事务,就抛出异常
(5)NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
(6)REQUIRES_NEW:新建事务,如果在事务中,把当前事务挂起
(7)SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行
14.1 Spring配置式事务管理
下面仅放修改了的代码,其他代码参考笔记"10.动态代理增强转账业务的代码"
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.darksnow</groupId>
<artifactId>SpringDemo01</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.18</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
</dependencies>
</project>
配置文件
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 加载properties配置文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 需要扫描包才能够使用@Controller,@Service,@Repository,@Component -->
<context:component-scan base-package="com.darksnow" />
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner" >
<constructor-arg ref="c3p0DataSource"/>
</bean>
<!-- 配置c3p0的数据源 -->
<bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- ${jdbc.driver} 获取的是properties中的配置 -->
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- Spring自带的数据源 -->
<bean id="springDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 创建JdbcTemplate模板对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="springDataSource" />
</bean>
<!-- 创建事务管理对象 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- set注入数据源属性:事务在连接中,连接在连接池(数据源)中 -->
<property name="dataSource" ref="springDataSource" />
</bean>
<!--
配置事务的增强:过滤方法是否需要拦截
id:唯一标识
transaction-manager:指定事务管理对象
-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- 增强:方法过滤 -->
<tx:attributes>
<!--
指定需要拦截的方法
name属性:匹配方法名,*代表通配符匹配,比如find*,就代表find开头的都会匹配
isolation属性:设置事务的隔离级别,一般选默认
propagation属性:事务传播行为
read-only属性:是否为只读的事务,增删该设为非只读事务(false),查询设为只读事务(true)
timeout属性:设置超时时间,默认为-1,意味着没有超时限制,单位为秒
-->
<tx:method name="insert*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
<tx:method name="add*" />
<tx:method name="update*"/>
<tx:method name="del*" />
<tx:method name="delete*"/>
<tx:method name="save*"/>
<tx:method name="transfer*"/>
<!-- 查询 -->
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="select*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
<aop:config>
<!--
配置切面
advice-ref:关联通知对象
pointcut:切入点
-->
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.darksnow.service.impl.*.*(..))" />
</aop:config>
</beans>
dao层代码
package com.darksnow.dao;
import com.darksnow.pojo.Account;
import java.sql.SQLException;
public interface AccountDao {
Account findByName(String fromName) throws SQLException;
void update(Account account) throws SQLException;
}
----------------------------------------------------------
package com.darksnow.dao.impl;
import com.darksnow.dao.AccountDao;
import com.darksnow.mapper.AccountRomMapper;
import com.darksnow.pojo.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.sql.SQLException;
@Repository
public class AccountDaoImpl implements AccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
//根据账户名查询账户
@Override
public Account findByName(String fromName) throws SQLException {
String sql = "select * from account where name = ?";
//从线程中获取一个连接对象
return jdbcTemplate.queryForObject(sql,new AccountRomMapper(),fromName);
}
//更新账户
@Override
public void update(Account account) throws SQLException {
String sql = "update account set money = ? where name = ?";
jdbcTemplate.update(sql,account.getMoney(),account.getName());
}
}
映射结果集对象
package com.darksnow.mapper;
import com.darksnow.pojo.Account;
import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
//结果集对象,只包含一行数据
public class AccountRomMapper implements RowMapper<Account> {
@Override
public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
Account account = new Account();
int id = rs.getInt("id");
account.setId(id);
account.setName(rs.getString("name"));
account.setMoney(rs.getFloat("money"));
return account;
}
}
测试类
package com.darksnow.test;
import com.darksnow.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestXmlTransaction {
@Autowired
private AccountService accountService;
@Test
public void testTransfer() {
accountService.transfer("张三","李四",1000f);
}
}
14.2 Spring注解式事务管理
下面仅放修改了的代码,其他代码参考笔记"14.1 Spring配置式事务管理"
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 加载properties配置文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 需要扫描包才能够使用@Controller,@Service,@Repository,@Component -->
<context:component-scan base-package="com.darksnow" />
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner" >
<constructor-arg ref="c3p0DataSource"/>
</bean>
<!-- 配置c3p0的数据源 -->
<bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- ${jdbc.driver} 获取的是properties中的配置 -->
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- Spring自带的数据源 -->
<bean id="springDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 创建JdbcTemplate模板对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="springDataSource" />
</bean>
<!-- 创建事务管理对象 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- set注入数据源属性:事务在连接中,连接在连接池(数据源)中 -->
<property name="dataSource" ref="springDataSource" />
</bean>
<!-- transaction-manager:关联事务管理器对象 -->
<tx:annotation-driven transaction-manager="txManager" />
</beans>
service层实现类代码
package com.darksnow.service.impl;
import com.darksnow.dao.AccountDao;
import com.darksnow.pojo.Account;
import com.darksnow.service.AccountService;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.sql.SQLException;
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
//放在类上就是整个类都有事务管理,放在方法上就只作用于该方法上
@Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED, readOnly = false, timeout = -1)
@Override
public void transfer(String fromName, String toName, Float money) {
try {
//查询要转出的账号
Account fromAccount = accountDao.findByName(fromName);
//查询要转入的账号
Account toAccount = accountDao.findByName(toName);
//修改要转出的账户余额
fromAccount.setMoney(fromAccount.getMoney() - money);
//修改要转入的账户余额
toAccount.setMoney(toAccount.getMoney() + money);
//持久化到数据库
accountDao.update(fromAccount);
int i = 1/0;
accountDao.update(toAccount);
}catch (SQLException e) {
e.printStackTrace();
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY