简单实现Spring中的IOC及AOP思想

本文通过一个简单的A向B转账的业务,简单实现Spring中的核心思想,IOC及AOP;

IOC:控制反转,将对象创建(实例化、管理)的权利交给了IOC容器;

AOP: 面向切面编程,通过横向抽取机制,将业务代码与横切逻辑分离

1. IOC思想实现

1.1 定义一个XML文件,在文件中,定义需要实例化的类全限定名、ID及类中其他属性

1.2 定义BeanFactory工厂类

作用

A. 读取XML中类全限定名,通过反射机制生成实例化对象,通过获取对象中的set方法,注入所需属性对象(eg: TransferService中的TransferDao),存放在HashMap集合中

B.提供一个getBean(id)方法,返回实例化对象

    <bean id="accountDao" class="com.kay.dao.impl.JdbcAccountDaoImpl">
        <property name="ConnectionUtils" ref="connectionUtils"></property>
    </bean>

    <bean id="transferService" class="com.kay.service.impl.TransferServiceImpl">
<!--        TransferServiceImpl对象中需要使用到accountDao对象 ref 中配置引用的 bean标签对应的id-->
        <property name="AccountDao"  ref="accountDao" ></property>
    </bean>

    <bean id="connectionUtils" class="com.kay.utils.ConnectionUtils"></bean>

    <bean id="transactionManager" class="com.kay.utils.TransactionManager">
        <property name="ConnectionUtils" ref="connectionUtils"></property>
    </bean>

    <bean id="proxyFactory" class="com.kay.factory.ProxyFactory">
        <property name="TransactionManager" ref="transactionManager"></property>
    </bean>
</beans>
private static Map<String, Object> beanMap = new HashMap<>();

    /**
     * 读取bean.xml中的标签,解析成对象存放到 beanMap 集合中
     */
    static {
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml"));
            Element rootElement = document.getRootElement();
            List<Element> beanList = rootElement.selectNodes("//bean");
            if (beanList != null) {
                for (Element element : beanList) {
                    // 获取 <bean> 标签中对应的 id 及 实现类类路径
                    String id = element.attributeValue("id"); // transferService
                    String classPath = element.attributeValue("class"); //com.kay.service.impl.TransferServiceImpl

                    // 反射实例化类对象
                    Object object = Class.forName(classPath).newInstance();
                    // 将对象存放到map集合中
                    beanMap.put(id, object);
                }
            }
            // 处理 属性标签 <property> 为需要配置的对象注入对象(即 TransferServiceImpl对象中,需要注入 AccountDao )
            List<Element> propertyList = rootElement.selectNodes("//property");
            if (propertyList != null) {
                for (Element element : propertyList) {
                    // 通过set+Id注入对象里面的引用
                    String methodName = "set" + element.attributeValue("name");//set +  AccountDao
                    String ref = element.attributeValue("ref");// accountDao

                    Object obj = beanMap.get(ref); //需要被注入的对象  AccountDao
                    // 获取当前element的父级节点
                    Element parentElement = element.getParent();
                    String parentId = parentElement.attributeValue("id"); // transferService
                    Object parentObject = beanMap.get(parentId);

                    // 获取父节点所有方法,符合 methodName 的注入对应的属性对象
                    Method[] methods = parentObject.getClass().getMethods();
                    for (Method method : methods) {
                        if (method.getName().equals(methodName)){ // setAccountDao
                            // 调用该方法,将 需要被注入的对象(AccountDao)传入
                            method.invoke(parentObject, obj);
                        }
                    }
                    // parentObject被重新赋值了,需要覆盖beanMap中原值
                    beanMap.put(parentId, parentObject);
                }
            }
        } catch (DocumentException | ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据传入的类ID 返回实体类
     * @param id
     * @return
     */
    public static Object getBean(String id) {
        return beanMap.get(id);
    }

2.AOP思想实现 (实现service层的事务管理) 动态代理实现

2.1.定义 ConnectionUtils获取当前线程Connection实例 (事务管理需要放在Service层,JDBC自带的事务管理只对一个Connection请求有用,一个业务代码涉及多个JDBC的执行请求,必须保证同一个业务代码,使用的是同一个Connection对象;此处通过同一个线程限定一个Connection对象)

2.2.定义TransactionManager操作事务的管理(事务开启、提交、回滚等)

public class ConnectionUtils {

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

    /**
     * 从当前线程获取连接
     * @return
     * @throws SQLException
     */
    public Connection getCurrentThreadCon() 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.getCurrentThreadCon().setAutoCommit(false);
    }

    /**
     * 提交事务
     * @throws SQLException
     */
    public void commitTransaction() throws SQLException {
        connectionUtils.getCurrentThreadCon().commit();
    }

    /**
     * 回滚事务
     */
    public void rollBack() throws SQLException {
        connectionUtils.getCurrentThreadCon().rollback();
    }
}

通过上面两步,初步实现service的事务管理,如下代码,在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);

            accountDao.updateAccountByCardNo(to);
//            int i = 1 / 0; // 制造异常
            accountDao.updateAccountByCardNo(from);
            // 提交
            TransactionManager.getInstance().commitTransaction();
        }catch (Exception e){
            e.printStackTrace();
            // 异常回滚
            TransactionManager.getInstance().rollBack();
            throw e;
        }
    }

通过动态代理避免以上问题,对service对象进行动态代理,在service中方法执行前开启事务,执行后提交事务,出现异常时回滚事务

2.3.定义 ProxyFactory 代理对象工程,用于生产代理对象

public class ProxyFactory {
    private TransactionManager transactionManager;

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

    public Object getProxyObject(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 {
                Object result = null;
                try {
                    // 开启事务
                    transactionManager.beginTransaction();
                    // 执行原方法
                    result =  method.invoke(object, args);
                    // 提交事务
                    transactionManager.commitTransaction();
                }catch (Exception e){
                    transactionManager.rollBack();
                    // 向上抛出异常
                    throw e;
                }
                return result;
            }
        });
    }
}

2.4. 在Servlet层调用Service对象时,将从BeanFactory获得到的对象实例,传入到ProxyFactory中进行增强

    private ProxyFactory proxyFactory = (ProxyFactory) BeanFactory.getBean("proxyFactory");
    // 1. 获取TransferService实例化对象 并通过 ProxyFactory 动态代理类工厂将实例化后的TransferService进行增强
    private TransferService transferService = (TransferService) proxyFactory.getProxyObject(BeanFactory.getBean("transferService"));

至此完成spring 的基本思想IOC及AOP思想

完整代码:https://gitee.com/sunshinekay/iaop-iocin-transfer.git

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值