spring之动态代理实战

1、搭建dao层
 

package com.proxy.dao;



import com.proxy.entity.Account;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import java.util.List;
public class AccountDaoImpl implements AccountDao {
    private QueryRunner queryRunner;
    public Account findAccountByName(String accountName) {
        try{
            List<Account> query = queryRunner.query(" select id,account_name,money from ac where account_name = ?", new BeanListHandler<Account>(Account.class), accountName);
        if(query == null || query.size() == 0){
            return null;
        }
        if(query.size() >= 2){
            throw  new RuntimeException("数据大约2条");
        }
            Account account = query.get(0);
            account.setAccountName(accountName);
       return  account;
        }catch (Exception e){
            throw  new RuntimeException(e);
        }
    }

    public void updateAccount(Account account) {
        try{
          queryRunner.update("update ac set money = ? where account_name = ?", account.getMoney(), account.getAccountName());
        }catch (Exception e){
            throw  new RuntimeException(e);
        }
    }
    public void setQueryRunner(QueryRunner queryRunner) {
        this.queryRunner = queryRunner;
    }
}

2、搭建service层
 

package com.proxy.service.impl;
import com.proxy.dao.AccountDao;
import com.proxy.entity.Account;
import com.proxy.service.AccountService;

/**
 * 问题:每个方法都写txManagement 事务方法
 *          代码比较臃肿,不易维护
 */

public class AccountServiceImpl implements AccountService {
    private AccountDao accountDao;
    public Account findAccountByName(String accountName) {
            Account accountByName = accountDao.findAccountByName(accountName);
            return accountByName;
    }
    public void updateAccount(Account account) {
            accountDao.updateAccount(account);
        }
        
    public void transfer(String sourceName, String targetName, Float money) {

            //根据A名称查询出账户
            Account source = accountDao.findAccountByName(sourceName);
            //根据B名称查询出账户
            Account target = accountDao.findAccountByName(targetName);
            //转出账户减钱
            source.setMoney(source.getMoney() - money);
            //转入账户加钱
            target.setMoney(target.getMoney() + money);
            //更新转出账户
            accountDao.updateAccount(source);
            //更新转入账户
            accountDao.updateAccount(target);

    }
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
}

3、配置bean.xml
 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    <!--配置service层-->
    <bean id="accountService" class="com.proxy.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>
    <!--配置dao层-->
    <bean id="accountDao" class="com.proxy.dao.AccountDaoImpl">
        <property name="queryRunner" ref="queryRunner"></property>
    </bean>
    <!--配置QueryRunner-->
    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
     <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>
    <!--配置数据源-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://120.25.236.19:3306/practice?useSSL=false"></property>
        <property name="user" value="root"></property>
        <property name="password" value="123456"></property>
    </bean>
</beans>

4、测试
 


import com.proxy.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")  //classes = BeanConfiguration.class
public class TestMain {


    @Autowired
    private AccountService accountService;

    @Test
    public void test(){
       System.out.println(accountService.findAccountByName("aaa").getId());

    }

}


改造成代理模式,对业务层方法进行增强

5、创建ConnectionUtils
 

package com.spring.utils;


import javax.sql.DataSource;
import java.sql.Connection;

/**
 * 链接的工具类,它用于从数据源中获取一个链接,并且实现和线程的绑定
 */
public class ConnectionUtils {

    private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();

    private DataSource dataSource;

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    /**
     * 获取当前线程上的链接
     */
    public Connection getThreadConnection(){
        //1、先从ThreadLocal上进行获取
        Connection connection = tl.get();
        //2、判断当前线程上是否有链接
        try{
            if(connection == null){
                //3、从数据源中获取一个链接,并存入ThreadLocal中
                connection = dataSource.getConnection();
                tl.set(connection);
            }
        }catch (Exception e){
            throw new RuntimeException(e);
        }
        return  connection;
    }


    public void removeConnection(){
        tl.remove();
    }

}

 

6、创建TransactionManagement
 

package com.spring.utils;

/**
 * 和事务相关的工具类,它3包含了,开启事务,提交事务、回滚事务、释放事务
 */
public class TransactionManagement {

    private ConnectionUtils connectionUtils;

    public void setConnectionUtils(ConnectionUtils connectionUtils) {
        this.connectionUtils = connectionUtils;
    }
    /**
     * 开启事务
     */
    public void beginTransaction(){

        try{
            connectionUtils.getThreadConnection().setAutoCommit(false);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 提交事务
     */
    public void commit(){
        try{
            connectionUtils.getThreadConnection().commit();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 回滚事务
     */
    public void rollback(){
        try{
            connectionUtils.getThreadConnection().rollback();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 释放连接
     */
    public void release(){
        try{
            connectionUtils.getThreadConnection().close();
            connectionUtils.removeConnection();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

}

 

7、改造dao
 

package com.spring.dao;

import com.spring.entity.Account;

import com.spring.utils.ConnectionUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;


import java.util.List;


public class AccountDaoImpl implements AccountDao {

    private QueryRunner queryRunner;
    private ConnectionUtils connectionUtils;

    public Account findAccountByName(String accountName) {
        try{

            List<Account> query = queryRunner.query(connectionUtils.getThreadConnection()," select id,account_name,money from ac where account_name = ?", new BeanListHandler<Account>(Account.class), accountName);
        if(query == null || query.size() == 0){
            return null;
        }
        if(query.size() >= 2){
            throw  new RuntimeException("数据大约2条");
        }
            Account account = query.get(0);
            account.setAccountName(accountName);
       return  account;
        }catch (Exception e){
            throw  new RuntimeException(e);
        }
    }

    public void updateAccount(Account account) {
        try{
          queryRunner.update(connectionUtils.getThreadConnection(),"update ac set money = ? where account_name = ?", account.getMoney(), account.getAccountName());
        }catch (Exception e){
            throw  new RuntimeException(e);
        }
    }

    public QueryRunner getQueryRunner() {
        return queryRunner;
    }

    public void setQueryRunner(QueryRunner queryRunner) {
        this.queryRunner = queryRunner;
    }

    public ConnectionUtils getConnectionUtils() {
        return connectionUtils;
    }

    public void setConnectionUtils(ConnectionUtils connectionUtils) {
        this.connectionUtils = connectionUtils;
    }
}


8、创建代理对象的工厂
 

package com.spring.proxy;

import com.spring.service.AccountService;
import com.spring.utils.TransactionManagement;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 *
 * 用于创建代理对象的工厂
 */
public class ProxyBeanFactory {

    private AccountService accountService;
    private  TransactionManagement txManagement;

    /**
     * 生成accountService的代理对象
     */
   public AccountService getAccountService(){
     return  (AccountService)Proxy.newProxyInstance(
             accountService.getClass().getClassLoader(),
                accountService.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     * 添加事务的支持
                     * @param proxy
                     * @param method
                     * @param args
                     * @return
                     * @throws Throwable
                     */
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                        Object returnValue = null;

                        try {
                            txManagement.beginTransaction();
                            returnValue = method.invoke(accountService, args);
                            txManagement.commit();
                            return returnValue;
                        } catch (Exception e) {
                            txManagement.rollback();
                            throw new RuntimeException(e);
                        } finally {
                            txManagement.release();
                        }
                    }
                });
    }

    public void setAccountService(AccountService accountService) {
        this.accountService = accountService;
    }

    public TransactionManagement getTxManagement() {
        return txManagement;
    }

    public void setTxManagement(TransactionManagement txManagement) {
        this.txManagement = txManagement;
    }
}

 

9、改造bean.xml
 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">



    <!--配置service层-->
    <bean id="accountService" class="com.spring.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>
    <!--配置dao层-->
    <bean id="accountDao" class="com.spring.dao.AccountDaoImpl">
        <property name="queryRunner" ref="queryRunner"></property>
        <property name="connectionUtils" ref="connectionUtils"></property>

    </bean>



    <!--配置代理的service-->
    <!--proxyAccountService 代理了 accountService  所以在control层调用,就调用代理-->
    <bean id="proxyAccountService" factory-bean="proxyBeanFactory" factory-method="getAccountService"></bean>

    <!--配置ProxyBeanFactory-->
    <bean id="proxyBeanFactory" class="com.spring.proxy.ProxyBeanFactory">
        <property name="txManagement" ref="transactionManagement"></property>
        <property name="accountService" ref="accountService"></property>
    </bean>

    <!--配置QueryRunner-->
    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"></bean>

    <!--配置数据源-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://120.25.236.19:3306/practice?useSSL=false"></property>
        <property name="user" value="root"></property>
        <property name="password" value="123456"></property>
    </bean>

    <!--配置Connection的工具类-->
    <bean id="connectionUtils" class="com.spring.utils.ConnectionUtils">
        <property name="dataSource" ref="dataSource"></property>
    </bean>


    <!--配置事务管理器-->
    <bean id="transactionManagement" class="com.spring.utils.TransactionManagement">
        <property name="connectionUtils" ref="connectionUtils"></property>
    </bean>

</beans>

 

10、测试
        在service设置报错,会正常回滚代码,

package com;

import com.spring.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;



@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")  //classes = BeanConfiguration.class
public class TestMain {


    @Autowired
    @Qualifier("proxyAccountService")
    private AccountService accountService;

    @Test
    public void test(){
        accountService.transfer("aaa","bbb", 100f);

    }

}

 

11、总结
  本次应用的代理是接口代理。将事务特性:创建、提交、回滚、释放做了代理,也是将事务直接切入到方法中,从而体现出了切面的特性,也就是AOP。

 

 

git地址:https://gitee.com/Xiaokeworksveryhard/dynamic_proxy.git

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值