一只会飞的旺旺
文章152
标签131
分类7
Spring初探-手写IOC与AOP

Spring初探-手写IOC与AOP

小卖铺上新啦!ChatGPT账号大甩卖! 一键直达

Spring核心结构

image-20220928203138888

Spring的核心思想

AOP

  • OOP:面向对象编程,特征是继承,封装,多态,解决垂直体系的代码重复

image-20220928203255111

  • AOP:面向切面,解决横切代码的重复问题,以及抽取非业务代码,明确业务逻辑.常见应用有事务控制,日志记录,权限控制等

image-20220928203303141

IOC

控制反转: 对象创建交由外部容器实现,解决对象间的耦合.

image-20220928203236405

DI

依赖注入:与IOC角度不同,IOC是站在对象角度上,对象的创建交由容器维护,DI是站在容器的角度上,容器会提供对象依赖的其他对象,供给其使用

image-20220928203246302

MVC的发展

传统MVC

// Controller持有Service对象
public class TransferServlet extends HttpServlet {
    private TransferService transferService = new TransferServiceImpl();
}
// Service持有Dao对象
public class TransferServiceImpl implements TransferService {
    private AccountDao accountDao = new JdbcAccountDaoImpl();
}

问题

1.代码耦合,每层都持有下一层的实现类对象
2.service层没有进行事务控制,方法执行中出现异常时,可能会导致数据库数据错乱

解决方案

问题1:
除了通过new创建对象外,我们还可以通过反射的方式创建,可以将对象创建提取到一个统一的地方执行,并提供一个方法去获取对象.如工厂类
问题2:
为Service层添加事务控制,但是JDBC的事务是控制在dao层,若一个service层中调用dao层多个db方法,这样就会导致出现多个事务,我们需要让这多个db方法属于一个Connection,这样才能实现Service层的事务控制.

代码示例

1.提取对象到XML

<beans>
    <bean id="transferService"
          class="com.ww.transfer.service.impl.TransferServiceImpl">
        <property name="AccountDao" ref="accountDao"/>
    </bean>
    <bean id="accountDao"
          class="com.ww.transfer.dao.impl.JdbcAccountDaoImpl">
    </bean>
</beans>

2.通过工厂类创建对象

public class BeanFactory {
    /**
     * ⼯⼚类的两个任务
     * 任务⼀:加载解析xml,读取xml中的bean信息,通过反射技术实例化bean对象,然后放⼊
     * map待⽤
     * 任务⼆:提供接⼝⽅法根据id从map中获取bean(静态⽅法)
     */
    private static final Map<String, Object> map = new HashMap<>();

    static {
        // 读取配置对象信息的XML文件
        InputStream resourceAsStream =
                BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();
            // 获取所有bean节点
            List list = rootElement.selectNodes("//bean");
            // 实例化bean对象
            for (Object value : list) {
                Element element = (Element) value;
                // 获取ID
                String id = element.attributeValue("id");
                // 获取类全限定名
                String clazz = element.attributeValue("class");
                Class<?> aClass = Class.forName(clazz);
                Object o = aClass.newInstance();
                map.put(id, o);
            }
            // 维护bean之间的依赖关系
            List propertyNodes =
                    rootElement.selectNodes("//property");
            for (Object propertyNode : propertyNodes) {
                Element element = (Element) propertyNode;
                // 处理property元素
                String name = element.attributeValue("name");
                String ref = element.attributeValue("ref");

                // 获取外层Bean的对象
                String parentId =
                        element.getParent().attributeValue("id");
                Object parentObject = map.get(parentId);

                // 调用setter方法
                Method[] methods = parentObject.getClass().getMethods();
                for (Method method : methods) {
                    if (("set" + name).equalsIgnoreCase(method.getName())) {
                        // bean之间的依赖关系(注⼊bean)
                        Object propertyObject = map.get(ref);
                        method.invoke(parentObject, propertyObject);
                    }
                }
                // 维护依赖关系后重新将bean放⼊map中
                map.put(parentId, parentObject);
            }
        } catch (DocumentException | ClassNotFoundException | IllegalAccessException | InstantiationException |
                 InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    public static Object getBean(String id) {
        return map.get(id);
    }
}

3.增加ConnectionUtils维护唯一Connection

public class ConnectionUtils {

    // 存储当前线程的连接
    private ThreadLocal<Connection> threadLocal = new ThreadLocal<>();


    /**
     * 从当前线程获取连接
     */
    public Connection getCurrentThreadConn() throws SQLException {
        /**
         * 判断当前线程中是否已经绑定连接,如果没有绑定,需要从连接池获取⼀个连接绑定到
         当前线程
         */

        Connection connection = threadLocal.get();
        if (connection == null) {
            // 从连接池拿连接并绑定到线程
            connection = DruidUtils.getInstance().getConnection();
            // 绑定到当前线程
            threadLocal.set(connection);
        }
        return connection;
    }

}

4.增加ProxyFactory生成代理类,插入事务处理逻辑

public class ProxyFactory {

    private TransactionManager transactionManager;

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

    // 生成代理类
    public Object getProxy(Object target) {
        
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(), (proxy, method, args) -> {
                    Object result;
                    try {
                        // 开启事务
                        transactionManager.beginTransaction();
                        // 调⽤原有业务逻辑
                        result = method.invoke(target, args);
                        // 提交事务
                        transactionManager.commit();
                    } catch (Exception e) {
                        e.printStackTrace();
                        // 回滚事务
                        transactionManager.rollback();
                        // 异常向上抛出,便于servlet中捕获
                        throw e.getCause();
                    }
                    return result;
                });
    }

}
微信支付码 微信支付
支付宝支付码 支付宝支付