欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

注解实现Spring IOC与事务控制

程序员文章站 2022-07-02 22:54:28
文章内容输出来源:拉勾教育Java高薪训练营;本篇文章是Spring学习课程中的一部分学习心得。1. 新建maven工程1.1 新建一个maven工程点击next点击finish1.2 修改pom.xml文件

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

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

1. 新建maven工程

1.1 新建一个maven工程

注解实现Spring IOC与事务控制

点击next

注解实现Spring IOC与事务控制
点击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. 启动项目

注解实现Spring IOC与事务控制

点击tomcat7:run,启动项目。

注解实现Spring IOC与事务控制

这里可以看到,项目启动并无异常。可以用接口工具调用接口测试代码是否有问题。

3. 使用postman工具测试

3.1 发送post请求

注解实现Spring IOC与事务控制

转账成功了,看下数据库数据有没有更新成功。

注解实现Spring IOC与事务控制

再来演示个转账失败操作,首先找到TransferServiceImpl中更新账户信息的代码,在中间添加

// 更新账户信息
accountDao.updateAccountByCardNo(fromAccount);
int count = 1 / 0;
accountDao.updateAccountByCardNo(toAccount);

注解实现Spring IOC与事务控制

重新启动项目,再次发送post请求

注解实现Spring IOC与事务控制

可以看到,这里抛出java.lang.ArithmeticException: / by zero算术异常,再次查看数据库。

注解实现Spring IOC与事务控制

数据并未发生改变,说明通过@Transactional注解完成的声明式事务成功了,未发生异常时,事务正常提交,发生异常,事务回滚。

写在最后

工作 3 年多了,总感觉什么都会一点,但是什么都不够精,实际开发中遇到困难的问题就不知道怎么解决,技术上得不到提升。考虑参加拉钩训练营之前,参考了很多,马士兵教育、奈学教育等等几个大的培训机构,这些机构的高级课程看着也确实高大上,但就一个字,贵,两三万的学费,让人望而却步。
如果没有碰到拉钩训练营,我大概率就每天浑水摸鱼,下班到点就走,技术得不到提升,以后跳槽肯定又是到处碰壁,但幸好碰到了这个如果。
在拉钩训练营坚持学了半个多月,收获确实颇多,对Mybatis、Spring源码有了一定的了解和深入以及阅读源码的技巧,而且班班妹子非常负责任,每周会督促学习。

为避免太过广告,就不写太多了,只是想让看到这篇博文的同学,如果也想系统深入的学习一下 Java 常用框架、源码以及其他常用组件,拉钩训练营是个不错的选择,而且学费相比其他机构便宜不少,只要你学得够好,毕业还有内推大厂的机会。

本文地址:https://blog.csdn.net/qq_35983669/article/details/107287697