当前位置: 移动技术网 > IT编程>开发语言>Java > 注解实现Spring IOC与事务控制

注解实现Spring IOC与事务控制

2020年07月12日  | 移动技术网IT编程  | 我要评论
文章内容输出来源:拉勾教育Java高薪训练营;本篇文章是Spring学习课程中的一部分学习心得。1. 新建maven工程1.1 新建一个maven工程点击next点击finish1.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/X

文章内容输出来源:拉勾教育Java高薪训练营;

本篇文章是Spring学习课程中的一部分学习心得。

1. 新建maven工程

1.1 新建一个maven工程

[新建maven工程.png)]

点击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

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网