注解实现Spring IOC与事务控制
文章内容输出来源:拉勾教育Java高薪训练营;
本篇文章是Spring学习课程中的一部分学习心得。
1. 新建maven工程
1.1 新建一个maven工程
点击next
点击finish
1.2 修改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.snowsea</groupId>
<artifactId>my_spring</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- 单元测试Junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- mysql数据库驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.35</version>
</dependency>
<!--druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>
<!-- servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- jackson依赖 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.6</version>
</dependency>
<!--dom4j依赖-->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<!--xpath表达式依赖-->
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.6</version>
</dependency>
<!--引入cglib依赖包-->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.1_2</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 配置Maven的JDK编译级别 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>8</source>
<target>8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- tomcat7插件 -->
<!-- 注意:目前来说,maven*仓库还没有tomcat8的插件 -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8080</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
1.3 新建applicationContext.xml配置文件
在resources目录下添加配置文件applicationContext.xml,注意,这里的applicationContext.xml配置文件和spring的配置文件并无任何关联。
<?xml version="1.0" encoding="UTF-8" ?>
<beans>
<!-- base-package:扫描包
exclude-package:排除扫描包
-->
<component-scan base-package="com.snowsea" exclude-package="com.snowsea.web"/>
</beans>
1.4 准备工作
1.4.1 自定义注解类
- Component
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
String value() default "";
}
- Repostory
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Repostory {
String value() default "";
}
- Service
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {
String value() default "";
}
- Autowired
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
String value() default "";
}
- Transactional
@Documented
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Transactional {
boolean required() default true;
}
1.4.2 资源加载器
- ResourceLoader
public interface ResourceLoader {
/**
* 读取类文件资源并将类文件封装为BeanDefinition对象
* @param basePackage 基本包
* @param excludePackage 排除包
* @return {@link List<BeanDefinition>}
*/
List<BeanDefinition> reader(String basePackage, String excludePackage) throws ClassNotFoundException;
}
- DefaultResourceLoader
public class DefaultResourceLoader implements ResourceLoader {
@Override
public List<BeanDefinition> reader(String basePackage, String excludePackage) throws ClassNotFoundException {
List<String> classNameList = PackageScanUtils.doScanner(basePackage, excludePackage);
List<BeanDefinition> beanDefinitions = new ArrayList<>();
for (String className : classNameList) {
Class<?> clazz = Class.forName(className);
// 是否使用了Component注解
if (clazz.isAnnotationPresent(Component.class)) {
// 获取该注解的值
Component component = clazz.getAnnotation(Component.class);
String beanName = component.value();
if ("".equals(component.value())) {
beanName = ResolveUtils.lowerFirstCase(beanName);
}
parseBeanDefinition(beanDefinitions, className, clazz, beanName);
} else if (clazz.isAnnotationPresent(Repostory.class)) {
// 获取该注解的值
Repostory repostory = clazz.getAnnotation(Repostory.class);
String beanName = repostory.value();
if ("".equals(repostory.value())) {
beanName = ResolveUtils.lowerFirstCase(beanName);
}
parseBeanDefinition(beanDefinitions, className, clazz, beanName);
} else if (clazz.isAnnotationPresent(Service.class)) {
// 获取该注解的值
Service service = clazz.getAnnotation(Service.class);
String beanName = service.value();
if ("".equals(service.value())) {
beanName = ResolveUtils.lowerFirstCase(beanName);
}
parseBeanDefinition(beanDefinitions, className, clazz, beanName);
}
}
return beanDefinitions;
}
/**
* 将类文件封装为BeanDefinition
* @param beanDefinitions beanDefinitions集合
* @param className 全限定类名
* @param clazz class类
* @param beanName bean名称
*/
private void parseBeanDefinition(List<BeanDefinition> beanDefinitions, String className, Class<?> clazz, String beanName) {
BeanDefinition beanDefinition = new BeanDefinition();
List<PropertyValue> propertyValueList = new ArrayList<>();
beanDefinition.setClassName(className);
beanDefinition.setBeanClass(clazz);
beanDefinition.setBeanName(beanName);
// 获取类属性值
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
// 获取Autowired注解的属性
Autowired annotation = field.getAnnotation(Autowired.class);
PropertyValue propertyValue = new PropertyValue();
propertyValue.setName(field.getName());
propertyValue.setType(field.getType());
if (annotation != null) {
propertyValue.setRefFlag(true);
// 获取注解的值
String value = annotation.value();
if ("".equals(value)) {
value = ResolveUtils.lowerFirstCase(field.getType().getSimpleName());
}
propertyValue.setBeanName(value);
} else {
propertyValue.setRefFlag(false);
}
propertyValueList.add(propertyValue);
}
// 装配属性
beanDefinition.setPropertyValues(propertyValueList);
beanDefinitions.add(beanDefinition);
}
}
1.4.3 BeanDefinition对象及PropertyValue属性对象
- BeanDefinition
public class BeanDefinition {
// bean名称
private String beanName;
// bean类
private Class<?> beanClass;
// 全限定类名
private String className;
// 属性值
private List<PropertyValue> propertyValues;
public String getBeanName() {
return beanName;
}
public void setBeanName(String beanName) {
this.beanName = beanName;
}
public Class<?> getBeanClass() {
return beanClass;
}
public void setBeanClass(Class<?> beanClass) {
this.beanClass = beanClass;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public List<PropertyValue> getPropertyValues() {
return propertyValues;
}
public void setPropertyValues(List<PropertyValue> propertyValues) {
this.propertyValues = propertyValues;
}
}
- PropertyValue
public class PropertyValue implements Serializable {
// 属性名称
private String name;
// 属性类型
private Class<?> type;
// 依赖标识
private boolean refFlag;
// bean名称
private String beanName;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Class<?> getType() {
return type;
}
public void setType(Class<?> type) {
this.type = type;
}
public boolean isRefFlag() {
return refFlag;
}
public void setRefFlag(boolean refFlag) {
this.refFlag = refFlag;
}
public String getBeanName() {
return beanName;
}
public void setBeanName(String beanName) {
this.beanName = beanName;
}
}
1.4.4 包扫描工具及字符串转换等工具
- PackageScanUtils
public class PackageScanUtils {
// 用于存放全限定类名的集合
private static List<String> classNameList = new ArrayList<>();
/**
* 扫描包下的所有类文件
* @param basePackage 扫描包
* @param excludePackage 排除扫描包
* @return {@link List<String>}
*/
public static List<String> doScanner(String basePackage, String excludePackage){
// 加载资源
URL resource = PackageScanUtils.class.getClassLoader().getResource(basePackage.replaceAll("\\.", "/"));
File file = new File(resource.getFile());
for (File f : file.listFiles()) {
if (f.isDirectory()){
doScanner(basePackage + "." + f.getName(), excludePackage);
} else {
String className = (basePackage + "." + f.getName()).replace(".class", "");
// 排除部分包下的类文件
if (className.contains(excludePackage)){
continue;
}
classNameList.add(className);
}
}
return classNameList;
}
}
- ResolveUtils
public class ResolveUtils {
/**
* 字符串首字母小写
* 如果类名首字母包含I,做特殊处理
* 例如:IAccountDao—>accountDao
* @param str str
* @return {@link String}
*/
public static String lowerFirstCase(String str){
String newStr = str.substring(str.lastIndexOf(".") + 1, str.length());
if (newStr.contains("I")) {
newStr = newStr.replaceFirst("I", "");
}
char[] chars = newStr.toCharArray();
// 大写转小写
chars[0] += 32;
return String.valueOf(chars);
}
}
- DruidUtils
public class DruidUtils {
private DruidUtils(){
}
private static DruidDataSource druidDataSource = new DruidDataSource();
static {
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
druidDataSource.setUrl("jdbc:mysql:///test?useSSL=false&characterEncoding=utf-8");
druidDataSource.setUsername("root");
druidDataSource.setPassword("123456");
}
public static DruidDataSource getInstance() {
return druidDataSource;
}
}
- ConnectionUtils
public class ConnectionUtils {
private ThreadLocal<Connection> threadLocal = new ThreadLocal<>(); // 存储当前线程的连接
private ConnectionUtils(){}
private static ConnectionUtils connectionUtils = new ConnectionUtils();
public static ConnectionUtils newInstance(){
return connectionUtils;
}
/**
* 从当前线程获取连接
*/
public Connection getCurrentThreadConn() throws SQLException {
/**
* 判断当前线程中是否已经绑定连接,如果没有绑定,需要从连接池获取一个连接绑定到当前线程
*/
Connection connection = threadLocal.get();
if(connection == null) {
// 从连接池拿连接并绑定到线程
connection = DruidUtils.getInstance().getConnection();
// 绑定到当前线程
threadLocal.set(connection);
}
return connection;
}
}
- JsonUtils
public class JsonUtils {
private static final ObjectMapper MAPPER = new ObjectMapper();
/**
* 将对象转换成json字符串。
* @param data
* @return
*/
public static String object2Json(Object data) {
try {
String string = MAPPER.writeValueAsString(data);
return string;
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
/**
* 将json结果集转化为对象
*
* @param jsonData json数据
* @param beanType 对象中的object类型
* @return
*/
public static <T> T json2Pojo(String jsonData, Class<T> beanType) {
try {
T t = MAPPER.readValue(jsonData, beanType);
return t;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 将json数据转换成pojo对象list
* @param jsonData
* @param beanType
* @return
*/
public static <T>List<T> json2List(String jsonData, Class<T> beanType) {
JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType);
try {
List<T> list = MAPPER.readValue(jsonData, javaType);
return list;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
- TransactionManager
public class TransactionManager {
private ConnectionUtils connectionUtils;
private TransactionManager(){
connectionUtils = ConnectionUtils.newInstance();
}
private static TransactionManager transactionManager = new TransactionManager();
public static TransactionManager newInstance(){
return transactionManager;
}
// 开启手动事务控制
public void beginTransaction() throws SQLException {
connectionUtils.getCurrentThreadConn().setAutoCommit(false);
}
// 提交事务
public void commit() throws SQLException {
connectionUtils.getCurrentThreadConn().commit();
}
// 回滚事务
public void rollback() throws SQLException {
connectionUtils.getCurrentThreadConn().rollback();
}
}
- Result
public class Result {
private String status;
private String message;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
return "Result{" +
"status='" + status + '\'' +
", message='" + message + '\'' +
'}';
}
}
1.4.5 数据库文件
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
`name` varchar(32) DEFAULT NULL,
`card_no` varchar(64) NOT NULL,
`money` decimal(10,0) DEFAULT NULL,
PRIMARY KEY (`card_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of account
-- ----------------------------
INSERT INTO `account` VALUES ('李大雷', '6029621011000', '10000');
INSERT INTO `account` VALUES ('韩梅梅', '6029621011001', '10000');
1.4.6 Account实体类
public class Account {
private String name;
private String cardNo;
private BigDecimal money;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCardNo() {
return cardNo;
}
public void setCardNo(String cardNo) {
this.cardNo = cardNo;
}
public BigDecimal getMoney() {
return money;
}
public void setMoney(BigDecimal money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"name='" + name + '\'' +
", cardNo='" + cardNo + '\'' +
", money=" + money +
'}';
}
}
1.5 新建BeanFactory接口
1.5.1 BeanFactory
这里提供一个方法,用来获取bean实例
public interface factory {
// 根据bean名称获取实例对象
Object getBean(String beanName);
}
1.5.2 DefaultBeanFactory
创建类DefaultBeanFactory,并实现BeanFactory接口,作用:初始化xml配置文件并解析,根据配置包扫描来达到根据包路径进行扫描,将包路径下的java类文件加载到内存中,对使用注解的类文件进行封装为一个个的BeanDefinition对象,对其进行实例化、属性装填、扩展增强,这一过程称之为bean的初始化过程,具体内容如下:
public class DefaultBeanFactory implements BeanFactory {
// 一级缓存
private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
// 二级缓存
private Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();
// 三级缓存:用于解决循环依赖问题
private Map<String, Object> singletonFactories = new ConcurrentHashMap<>();
// BeanDefinition集合
private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
private Properties properties = new Properties();
private final String BASE_PACKAGE = "base-package";
private final String EXCLUDE_PACKAGE = "exclude-package";
// 配置文件路径 如:applicationContext.xml
private String configLocation;
public DefaultBeanFactory(String configLocation) throws DocumentException {
this.configLocation = configLocation;
// 加载配置文件并解析
loadConfigLocation(configLocation);
// 对bean进行实例化、属性填充等一系列操作
refresh();
}
public void loadConfigLocation(String configLocation) throws DocumentException {
// 加载配置文件为输入流
InputStream inputStream = DefaultBeanFactory.class.getClassLoader().getResourceAsStream(configLocation);
// 使用dom4j技术解析
Document root = new SAXReader().read(inputStream);
Element rootElement = root.getRootElement();
List<Element> list = rootElement.selectNodes("//component-scan");
for (Element element : list) {
String basePackage = element.attributeValue("base-package");
String excludePackage = element.attributeValue("exclude-package");
properties.setProperty(BASE_PACKAGE, basePackage);
properties.setProperty(EXCLUDE_PACKAGE, excludePackage);
}
}
public void refresh() {
try {
ResourceLoader resourceLoader = new DefaultResourceLoader();
List<BeanDefinition> beanDefinitions = resourceLoader.reader(properties.getProperty(BASE_PACKAGE), properties.getProperty(EXCLUDE_PACKAGE));
// 将bean注册为beanDefinition对象
registerBeanDefinitions(beanDefinitions);
// 注册bean
registerBean(beanDefinitions);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
/**
* 将bean注册为beanDefinition对象
* @param beanDefinitions beanDefinitions集合
*/
private void registerBeanDefinitions(List<BeanDefinition> beanDefinitions) {
for (BeanDefinition beanDefinition : beanDefinitions) {
beanDefinitionMap.put(beanDefinition.getBeanName(), beanDefinition);
}
}
/**
* 注册bean
* @param beanDefinitions beanDefinitions集合
*/
private void registerBean(List<BeanDefinition> beanDefinitions) throws IllegalAccessException,
InstantiationException, NoSuchFieldException {
for (BeanDefinition beanDefinition : beanDefinitions) {
getBean(beanDefinition.getBeanName());
}
}
/**
* 获取bean
* @param beanName bean的名字
* @return {@link Object}
*/
@Override
public Object getBean(String beanName) throws InstantiationException, IllegalAccessException, NoSuchFieldException {
return doGetBean(beanName);
}
private Object doGetBean(String beanName) throws IllegalAccessException, InstantiationException,
NoSuchFieldException {
// 首先从缓存中获取
Object singletonBean = getSingleton(beanName);
if (singletonBean != null) {
return singletonBean;
}
singletonBean = getSingleton(beanName, () -> {
return doCreateBean(beanName);
});
return singletonBean;
}
private Object getSingleton(String beanName) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.singletonFactories.get(beanName);
if (singletonObject != null) {
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
private Object getSingleton(String beanName, ObjectFactory<?> objectFactory) throws InstantiationException, IllegalAccessException, NoSuchFieldException {
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
//从二级缓存中取出bean并将bean放入单例池中
singletonObject = objectFactory.getObject();
addSingleton(beanName, singletonObject);
}
return singletonObject;
}
}
/**
* 添加bean到单例池
* @param beanName bean的名字
* @param singletonObject 单例对象
*/
private void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects){
this.singletonObjects.put(beanName, singletonObject);
this.earlySingletonObjects.remove(beanName);
this.singletonFactories.remove(beanName);
}
}
private Object doCreateBean(String beanName) throws InstantiationException, IllegalAccessException,
NoSuchFieldException {
// 创建 Bean 实例,仅仅调用构造方法,但是尚未设置属性
Object exposedBean = instanceBean(beanName);
// 放入三级缓存
addSingletonFactory(beanName, exposedBean);
// Bean属性填充
populateBean(beanName, exposedBean);
// 调用初始化方法
exposedBean = initializeBean(beanName, exposedBean);
return exposedBean;
}
/**
* 实例化bean
* @param beanName bean的名字
* @return {@link Object}* @throws IllegalAccessException 非法访问异常
* @throws InstantiationException 实例化异常
*/
private Object instanceBean(String beanName) throws IllegalAccessException, InstantiationException {
BeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
if (beanDefinition != null) {
Class<?> beanClass = beanDefinition.getBeanClass();
return beanClass.newInstance();
}
return null;
}
/**
* 将bean提前暴露出来
* @param beanName bean的名字
* @param exposedBean 暴露的bean
*/
private void addSingletonFactory(String beanName, Object exposedBean) {
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, exposedBean);
}
}
}
/**
* 填充Bean
* @param beanName bean的名字
* @param exposedBean 暴露的bean
* @throws NoSuchFieldException 没有这样的磁场异常
* @throws IllegalAccessException 非法访问异常
* @throws InstantiationException 实例化异常
*/
private void populateBean(String beanName, Object exposedBean) throws IllegalAccessException,
InstantiationException, NoSuchFieldException {
BeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
if (beanDefinition != null && !beanDefinition.getPropertyValues().isEmpty()) {
List<PropertyValue> propertyValues = beanDefinition.getPropertyValues();
for (PropertyValue propertyValue : propertyValues) {
if (propertyValue.isRefFlag()) {
// 根据依赖的beanName获取对应的bean
Object bean = this.getBean(propertyValue.getBeanName());
// 获取暴露的bean的属性
Field field = exposedBean.getClass().getDeclaredField(propertyValue.getName());
field.setAccessible(true);
// 装配属性
field.set(exposedBean, bean);
}
}
}
}
/**
* bean扩展
* @param beanName
* @param exposedBean
* @return {@link Object}
*/
private Object initializeBean(String beanName, Object exposedBean) {
BeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
if (beanDefinition != null) {
Class<?> beanClass = beanDefinition.getBeanClass();
if (isTransactionalAnno(beanClass)) {
// 返回代理类:添加事务逻辑
return ProxyFactory.getInstance().getJdkProxy(exposedBean);
}
}
return exposedBean;
}
/**
* 是否包含事务注解
* @param beanClass bean类
* @return boolean
*/
private boolean isTransactionalAnno(Class<?> beanClass) {
Transactional transactional = beanClass.getAnnotation(Transactional.class);
if (transactional != null) return true;
// 获取方法
Method[] methods = beanClass.getDeclaredMethods();
for (Method method : methods) {
// 获取有Transactional注解的方法
Transactional methodTransactional = beanClass.getAnnotation(Transactional.class);
if (methodTransactional != null) return true;
}
return false;
}
}
1.5.3 ObjectFactory
public interface BeanFactory {
// 根据bean名称获取实例对象
Object getBean(String beanName) throws InstantiationException, IllegalAccessException, NoSuchFieldException;
}
1.5.4 ProxyFactory
此类是对使用@Transactional注解的方法或类创建代理对象,从而达到对事务的控制目的,此处使用的是JDK动态代理,如有需要,也可改成CGLIB动态代理。
public class ProxyFactory {
private TransactionManager transactionManager;
private ProxyFactory(){transactionManager = TransactionManager.newInstance();}
private static ProxyFactory proxyFactory = new ProxyFactory();
public static ProxyFactory getInstance(){
return proxyFactory;
}
public Object getJdkProxy(Object target){
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try {
// 开启事务
transactionManager.beginTransaction();
// 执行原方法
result = method.invoke(target, args);
// 提交事务
transactionManager.commit();
} catch (Exception e) {
e.printStackTrace();
// 事务回滚
transactionManager.rollback();
throw e;
}
return result;
}
});
}
}
1.5.5 IAccountDao接口
public interface IAccountDao {
/**
* 根据帐号查询账户信息
* @param cardNo 帐号
* @return {@link Account}* @throws SQLException SQLException异常
*/
Account getAccountByCardNo(String cardNo) throws SQLException;
/**
* 更新帐户信息
* @param account 账户信息
* @return int* @throws SQLException SQLException异常
*/
int updateAccountByCardNo(Account account) throws SQLException;
}
1.5.6 AccountDaoImpl实现类
@Repostory("accountDao")
public class AccountDaoImpl implements IAccountDao {
private ConnectionUtils connectionUtils = ConnectionUtils.newInstance();
@Override
public Account getAccountByCardNo(String cardNo) throws SQLException {
// 获取数据库连接
Connection conn = connectionUtils.getCurrentThreadConn();
String sql = "select name, card_no, money from account where card_no = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
// 设置参数
pstmt.setString(1, cardNo);
pstmt.execute();
// 获取结果集
ResultSet resultSet = pstmt.getResultSet();
Account account = new Account();
while (resultSet.next()) {
account.setCardNo(resultSet.getString("card_no"));
account.setName(resultSet.getString("name"));
account.setMoney(resultSet.getBigDecimal("money"));
}
//关闭资源
resultSet.close();
pstmt.close();
return account;
}
@Override
public int updateAccountByCardNo(Account account) throws SQLException {
// 获取数据库连接
Connection conn = connectionUtils.getCurrentThreadConn();
String sql = "update account set money = ? where card_no = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
// 设置参数
pstmt.setBigDecimal(1, account.getMoney());
pstmt.setString(2, account.getCardNo());
pstmt.execute();
// 受影响行数
int rowNum = pstmt.getUpdateCount();
//关闭资源
pstmt.close();
return rowNum;
}
}
1.5.7 ITransferService接口
public interface ITransferService {
/**
* 转账
* @param fromCardNo 转账账户
* @param toCardNo 到账账户
* @param money 金额
* @return boolean
*/
void transfer(String fromCardNo, String toCardNo, BigDecimal money) throws SQLException;
}
1.5.8 TransferServiceImpl实现类
@Service("transferService")
@Transactional
public class TransferServiceImpl implements ITransferService {
@Autowired
private IAccountDao accountDao;
@Override
public void transfer(String fromCardNo, String toCardNo, BigDecimal money) throws SQLException {
// 获取转账账户信息
Account fromAccount = accountDao.getAccountByCardNo(fromCardNo);
// 获取到账账户信息
Account toAccount = accountDao.getAccountByCardNo(toCardNo);
BigDecimal fromMoney = fromAccount.getMoney().subtract(money);
BigDecimal toMoney = toAccount.getMoney().add(money);
fromAccount.setMoney(fromMoney);
toAccount.setMoney(toMoney);
// 更新账户信息
accountDao.updateAccountByCardNo(fromAccount);
int i = 1 / 0;
accountDao.updateAccountByCardNo(toAccount);
}
}
1.5.9 TransferServlet
@WebServlet(name = "/transferServlet", urlPatterns = "/transferServlet")
public class TransferServlet extends HttpServlet {
private ITransferService transferService = null;
@Override
public void init() throws ServletException {
try {
BeanFactory beanFactory = new DefaultBeanFactory("applicationContext.xml");
transferService = (ITransferService) beanFactory.getBean("transferService");
} catch (DocumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String fromCardNo = req.getParameter("fromCardNo");
String toCardNo = req.getParameter("toCardNo");
String money = req.getParameter("money");
Result result = new Result();
try {
transferService.transfer(fromCardNo, toCardNo, new BigDecimal(money));
result.setStatus("200");
result.setMessage("转账成功~");
} catch (SQLException e) {
e.printStackTrace();
result.setStatus("201");
result.setMessage(e.getMessage());
}
// 响应
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().write(JsonUtils.object2Json(result));
}
}
ok了,代码编写完成,接下来启动项目进行测试
2. 启动项目
点击tomcat7:run,启动项目。
这里可以看到,项目启动并无异常。可以用接口工具调用接口测试代码是否有问题。
3. 使用postman工具测试
3.1 发送post请求
转账成功了,看下数据库数据有没有更新成功。
再来演示个转账失败操作,首先找到TransferServiceImpl中更新账户信息的代码,在中间添加
// 更新账户信息
accountDao.updateAccountByCardNo(fromAccount);
int count = 1 / 0;
accountDao.updateAccountByCardNo(toAccount);
重新启动项目,再次发送post请求
可以看到,这里抛出java.lang.ArithmeticException: / by zero算术异常,再次查看数据库。
数据并未发生改变,说明通过@Transactional注解完成的声明式事务成功了,未发生异常时,事务正常提交,发生异常,事务回滚。
写在最后
工作 3 年多了,总感觉什么都会一点,但是什么都不够精,实际开发中遇到困难的问题就不知道怎么解决,技术上得不到提升。考虑参加拉钩训练营之前,参考了很多,马士兵教育、奈学教育等等几个大的培训机构,这些机构的高级课程看着也确实高大上,但就一个字,贵,两三万的学费,让人望而却步。
如果没有碰到拉钩训练营,我大概率就每天浑水摸鱼,下班到点就走,技术得不到提升,以后跳槽肯定又是到处碰壁,但幸好碰到了这个如果。
在拉钩训练营坚持学了半个多月,收获确实颇多,对Mybatis、Spring源码有了一定的了解和深入以及阅读源码的技巧,而且班班妹子非常负责任,每周会督促学习。
为避免太过广告,就不写太多了,只是想让看到这篇博文的同学,如果也想系统深入的学习一下 Java 常用框架、源码以及其他常用组件,拉钩训练营是个不错的选择,而且学费相比其他机构便宜不少,只要你学得够好,毕业还有内推大厂的机会。
本文地址:https://blog.csdn.net/qq_35983669/article/details/107287697
上一篇: python实现动态规划全局比对
下一篇: 三十行代码教你批量爬取某网站妹纸图