手写实现IoC和AOP

一、传统开发模式问题分析
1、new 关键字将service层的实现类和Dao层的实现类耦合在了一起,当业务逻辑需要切换Dao层实现类的时候必须得修改service代码,不符合面向接口开发的最有原则;
解决方案:
通过 反射 来代替 new 关键字 实例化对象。Class.forName(“全限定类名”),可以把全限定类名配置在xml中。
实现图例:在这里插入图片描述
代码实现:
创建bean.xml

<?xml version="1.0" encoding="UTF-8" ?>
<beans>
    <bean id="accountDao" class="com.spStudy.dao.impl.JdbcAccountDaoImpl"></bean>
    <bean id="transferService" class="com.spStudy.service.impl.TransferServiceImpl">
        <property name="AccountDao" ref="accountDao"></property>
    </bean>
</beans>

创建BeanFactory

/**
 * 工厂类,生产对象(使用反射技术)
 */
public class BeanFactory {
    //存储对象
    private static Map<String,Object> map = new HashMap<>();

    //静态方法,类被jvm虚拟机加载时执行
    //读取解析xml,通过反射技术实例化对象并且存储待用
    static {
        //加载xml
        InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
        //解析xml(dom4j)
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();
            List<Element> list = rootElement.selectNodes("//bean");
            //实例化bean对象
            for (Element element: list) {
                String id = element.attributeValue("id");
                String clazz = element.attributeValue("class");
                //通过反射技术实例化对象
                Class<?> aClass = Class.forName(clazz);
                Object o = aClass.newInstance();
                //将对象存储到map中
                map.put(id,o);
            }

            //维护对象的以来关系
            List<Element> properties = rootElement.selectNodes("//property");
            for (Element property: properties) {
                String name = property.attributeValue("name");
                String ref = property.attributeValue("ref");

                //找到当前需要被处理的bean
                Element parent = property.getParent();

                //调用父元素对象的反射功能
                String parentId = parent.attributeValue("id");
                Object parentObject = map.get(parentId);

                //遍历父对象中的所有方法,找到“set”+name
                Method[] methods = parentObject.getClass().getMethods();
                for (int i = 0; i < methods.length; i++) {
                    Method method = methods[i];
                    if(method.getName().equalsIgnoreCase("set"+name)){
                        method.invoke(parentObject,map.get(ref));
                    }
                }
                map.put(parentId,parentObject);
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    //对外提供获取实例对象的接口(根据id获取)
    public static Object getBean(String id){
        return map.get(id);
    }

}

通过BeanFactory获取实例化的对象

private TransferService transferService = (TransferService) BeanFactory.getBean("transferService");
private AccountDao accountDao;

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

2、service层没有添加业务控制,出现异常时可能导致数据错乱;
数据控制主要时在Connection的事务:

  • connection.commit(),事务的提交
  • connection,rollback(),事务的回滚
@Override
public void transfer(String fromCardNo, String toCardNo, int money) throws Exception {
    Account from = accountDao.queryAccountByCardNo(fromCardNo);
    Account to = accountDao.queryAccountByCardNo(toCardNo);
    from.setMoney(from.getMoney()-money);
    to.setMoney(to.getMoney()+money);
    
    //两次修改使用两个connection链接
    accountDao.updateAccountByCardNo(to);
    accountDao.updateAccountByCardNo(from);
}

事务控制目前是放在了Dao层,没有在service层

@Override
public int updateAccountByCardNo(Account account) throws Exception {
    Connection con = DruidUtils.getInstance().getConnection();
    String sql = "update account set money = ? where card_no = ?";
    PreparedStatement preparedStatement = con.prepareStatement(sql);
    preparedStatement.setInt(1,account.getMoney());
    preparedStatement.setString(2,account.getCardNo());
    int i = preparedStatement.executeUpdate();
    preparedStatement.close();
    return i;
}

解决思路
1.让两次update使用同一个connection连接,两次update属于同一个线程内的执行调用,我们可以给当前线程绑定一个Connection,和当前线程有关系的数据库操作都去使用这个Connection(从当前线程中去拿)。
2.把事务控制添加在service层

代码实现
创建ConnectionUtils,控制同一线程中是同一个Connection

public class ConnectionUtils {

    private static ConnectionUtils connectionUtils = new ConnectionUtils();

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

    private ConnectionUtils(){

    }

    public static ConnectionUtils getInstance(){
        return connectionUtils;
    }

    public Connection getCurrentConnection() throws SQLException {
        //判断当前线程是都已经绑定链接,如果没有绑定,从连接池获取一个链接绑定到当前线程
        Connection connection = threadLocal.get();
        if(connection == null){
             connection = DruidUtils.getInstance().getConnection();
             threadLocal.set(connection);
        }
        return connection;
    }
}

创建TransactionManager,事务管理器,负责手动事务的开启,提交,回滚。

/**
 * 事务管理器,负责手动事务的开启,提交,回滚
 */
public class TransactionManager {

    private TransactionManager(){

    }

    private static TransactionManager transactionManager = new TransactionManager();

    public static TransactionManager getInstance(){
        return transactionManager;
    }
    //开启手动事务控制
    public void beginTransaction() throws SQLException {
        ConnectionUtils.getInstance().getCurrentConnection().setAutoCommit(false);
    }

    //提交事务
    public void commit() throws SQLException {
        ConnectionUtils.getInstance().getCurrentConnection().commit();
    }

    //回滚事务
    public void rollback() throws SQLException {
        ConnectionUtils.getInstance().getCurrentConnection().rollback();
    }
}

在service层添加事务控制代码逻辑

    @Override
    public void transfer(String fromCardNo, String toCardNo, int money) throws Exception {

        try {
            //开启事务(关闭事务的自动提交)
            TransactionManager.getInstance().beginTransaction();

            Account from = accountDao.queryAccountByCardNo(fromCardNo);
            Account to = accountDao.queryAccountByCardNo(toCardNo);
            from.setMoney(from.getMoney()-money);
            to.setMoney(to.getMoney()+money);

            //两次修改使用两个connection链接
            accountDao.updateAccountByCardNo(to);
            int i = 1/0;//设置错误代码
            accountDao.updateAccountByCardNo(from);

            //提交事务
            TransactionManager.getInstance().commit();
        }catch (Exception e){
            e.printStackTrace();
            //回滚事务
            TransactionManager.getInstance().rollback();
            //抛出异常
            throw e;
        }
    }

3、事务控制代码与业务代码耦合,不易扩展
解决思路:
事务控制代码属于代码逻辑中的横切逻辑,使用动态代理模式进行优化,将事务控制代码与业务代码进行分离。

在实现代理模式前梳理类的依赖关系,整合代码
ConnectionUtils:确保一个线程使用一个链接,被JdbcAccountDaoImpl和TransactionManager使用;
TransactionManager:事务管理器,负责手动事务的开启,提交,回滚,将被代理工厂 类使用加强事务控制逻辑;
ProxyFacatory:代理工厂类,用来动态生成代理对象,使用TransactionManager类

根据类的依赖关系整合bean.xml

<?xml version="1.0" encoding="UTF-8" ?>
<beans>
    <bean id="accountDao" class="com.spStudy.dao.impl.JdbcAccountDaoImpl">
        <property name="ConnectionUtils" ref="connectionUtils"></property>
    </bean>
    <bean id="transferService" class="com.spStudy.service.impl.TransferServiceImpl">
        <property name="AccountDao" ref="accountDao"></property>
    </bean>

    <!-- 配置新增的三个组件 -->
    <!-- 获取数据库链接 -->
    <bean id="connectionUtils" class="com.spStudy.utils.ConnectionUtils"></bean>

    <!-- 事务管理器 -->
    <bean id="transactionManager" class="com.spStudy.utils.TransactionManager">
        <property name="ConnectionUtils" ref="connectionUtils"></property>
    </bean>

    <!-- 代理对象工厂 -->
    <bean id="proxyFacatory" class="com.spStudy.factory.ProxyFacatory">
        <property name="TransactionManager" ref="transactionManager" ></property>
    </bean>
</beans>

优化ConnectionUtils、TransactionManager和service

public class ConnectionUtils {

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

    public Connection getCurrentConnection() throws SQLException {
        //判断当前线程是都已经绑定链接,如果没有绑定,从连接池获取一个链接绑定到当前线程
        Connection connection = threadLocal.get();
        if(connection == null){
             connection = DruidUtils.getInstance().getConnection();
             threadLocal.set(connection);
        }
        return connection;
    }
}
public class TransactionManager {

    private ConnectionUtils connectionUtils;

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

    //开启手动事务控制
    public void beginTransaction() throws SQLException {
        connectionUtils.getCurrentConnection().setAutoCommit(false);
    }

    //提交事务
    public void commit() throws SQLException {
        connectionUtils.getCurrentConnection().commit();
    }

    //回滚事务
    public void rollback() throws SQLException {
        connectionUtils.getCurrentConnection().rollback();
    }
}
public class TransferServiceImpl implements TransferService {

    private AccountDao accountDao;

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

    @Override
    public void transfer(String fromCardNo, String toCardNo, int money) throws Exception {
        Account from = accountDao.queryAccountByCardNo(fromCardNo);
        Account to = accountDao.queryAccountByCardNo(toCardNo);
        from.setMoney(from.getMoney()-money);
        to.setMoney(to.getMoney()+money);

        //两次修改使用两个connection链接
        accountDao.updateAccountByCardNo(to);
        int i = 1/0;
        accountDao.updateAccountByCardNo(from);
    }
}

创建代理工厂

public class ProxyFacatory {

    private TransactionManager transactionManager;

    public void setTransactionManager(TransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }

    public Object getJDKProxy(Object object){

        return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //增强逻辑
                try {
                    Object o = null;

                    transactionManager.beginTransaction();
                    o = method.invoke(object, args);
                    transactionManager.commit();
                    return o;
                }catch (Exception e){
                    e.printStackTrace();
                    transactionManager.rollback();
                    throw e;
                }
            }
        });
    }

    public Object getCglibProxy(Object object){
        return Enhancer.create(object.getClass(), new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                try {
                    Object obj = null;
                    transactionManager.beginTransaction();
                    o = method.invoke(object,objects);
                    transactionManager.commit();
                    return o;
                }catch (Exception e){
                    e.printStackTrace();
                    transactionManager.rollback();
                    throw e;
                }
            }
        });
    }
}

servlet调用

//从BeanFactory获取到proxyFactory代理工厂的实例化对象
private ProxyFacatory proxyFacatory = (ProxyFacatory)BeanFactory.getBean("proxyFacatory");

private TransferService transferService = (TransferService) proxyFacatory.getJDKProxy(BeanFactory.getBean("transferService"));

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //设置请求体的字符编码
    req.setCharacterEncoding("UTF-8");

    //接收参数
    String fromCardNo = req.getParameter("fromCardNo");
    String toCardNo = req.getParameter("toCardNo");
    String moneyStr = req.getParameter("money");
    int money = Integer.parseInt(moneyStr);

    Result result = new Result();

    try {
        transferService.transfer(fromCardNo,toCardNo,money);
        result.setStatus("200");
        result.setMessage("转账成功!!");
    } catch (Exception e) {
        e.printStackTrace();
        result.setStatus("201");
        result.setMessage(e.toString());
    }

    // 响应
    resp.setContentType("application/json;charset=utf-8");
    resp.getWriter().print(JsonUtils.object2Json(result));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值