spring学习笔记(一)自定义spring

spring学习笔记(一)自定义spring

  • IOC 与 AOP
    • IOC
    • AOP
    • 自定义IOC和AOP
      • 自定义IOC
        • xml
        • BeanFactory解析
      • 自定义AOP
        • 数据库事务

IOC 与 AOP

在spring中老生常谈的就是IOC与AOP。IOC即控制反转,AOP即面向切面编程。IOC与AOP这两种理念其实出现在spring出现之前,spring框架只是将其付之实现。

IOC

谈到IOC的同时也要引入另外一个概念DI,DI是依赖注入。在某些方面我们可以说IOC与DI是同一个东西,但是细分下来还有稍有区别。IOC和DI主要是讲述了一个对象创建和对象管理权利转移给IOC容器(即spring框架)。但是IOC是站在对象角度来说,把对象的创建管理权限交给了容器。而DI是从容器看向对象的角度。举个例子,加入A依赖于B,那么再DI角度来说,发现A依赖于B那么便会创建B,同时将B注入到A中。
控制反转之控制:主要是指对象的创建,管理的权利
控制反转之反转:主要是指将控制的权利反转给IOC容器

AOP

AOP 是面向切面编程。讲到AOP不免就要提起OOP。OOP是面向对象编程,三大特性是封装继承多态,已经解决了大部分的代码重复问题。但是也有OOP解决不了的部分,那么此时就需要借助AOP来实现。OOP是垂直继承体系,而AOP是横向抽取机制。

自定义IOC和AOP

自定义IOC

原生代码存在耦合问题,不符合面向接口开发的原则。

public class TransferServiceImpl implements TransferService {

    private AccountDao accountDao = new JdbcAccountDaoImpl();
}

由上面的代码可以看出,TransferServiceImpl 依赖与AccountDao ,在调用AccountDao 的方法时,需要new该对象进行使用,这时就存在了类与类之间的耦合问题。
我们可以将AccountDao 使用java中的反射进行实例化,即Class.forName(“全限定类名”),由于需要全限定类名的字符串,如果直接写入代码中,又存在了硬编码问题,我们可以使用xml来存放字符串。同时在TransferServiceImpl 中列出AccountDao 的set方法,使用set方法为AccountDao 实例化。

xml

beans.xml




    
    
    
        
        
        
    

BeanFactory解析

创建BeanFactory类解析xml文件,并对TransferServiceImpl 中的AccountDao 进行实例化。

private static Map map=new HashMap<>();//用于存储对象

    static{
        //执行任务1
        //加载xml
        InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
        //通过dom4j解析
        SAXReader saxReader=new SAXReader();
        try {
            Document read = saxReader.read(resourceAsStream);
            Element rootElement = read.getRootElement();
            List list = rootElement.selectNodes("//bean");
            for (Element element : list) {
                //处理每个bean元素,获取该元素的id和class属性
                String id = element.attributeValue("id");//获取id
                String clazz = element.attributeValue("class");//获取全限类名
                //通过反射技术实例化对象
                Class aClass = Class.forName(clazz);
                Object o = aClass.newInstance();
                //放入map中
                map.put(id,o);
            }

            //实例化完成之后,检查那些对象需要传值,根据配置进行传值
            //实例化完成之后,维护对象依赖关系
            //有property属性的元素,其父元素需要传值
            List propertyList = rootElement.selectNodes("//property");
            for (Element element : propertyList) {
                //找到当前需要处理依赖关系的bean
                Element parent = element.getParent();
                String name = element.attributeValue("name");
                String ref = element.attributeValue("ref");
                //调用父元素的反射方法进行赋值
                String parentId = parent.attributeValue("id");
                Object parentObject = map.get(parentId);
                Method[] methods = parentObject.getClass().getMethods();
                for (Method method : methods) {
                    boolean equals = method.getName().equals("set" + name);
                    if(equals){
                        method.invoke(parentObject,map.get(ref));
                    }
                }
                //把处理好的parentElement重新放入map中
                map.put(parentId,parentObject);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static Object getBean(String id){
        return map.get(id);
    }

通过该步骤之后,在TransferServiceImpl 中便可以直接通过其中的setAccountDao方法进行AccountDao 的实例化了。

自定义AOP

关于一些项目中存在一个方法中有两个甚至更多操作数据库的代码,如果在执行中出现bug那么程序便会终止,此时如果操作数据库的代码没有执行完成,则会出现十分严重的问题。此时在多个操作数据库代码之上添加事务就十分有必要。

数据库事务

数据库事务归根结底是Connection的事务,多个操作数据库的代码使用的是多个Connection,且事务在dao层已经进行了提交。
在同一个方法执行过程中,多个操作数据库代码都属于同一线程,我们可以给当前线程绑定一个Connection,和当前线程有关的数据库操作都使用该Connection。
具体代码实现如下,使用创建单例模式的ConnectionUtils

public class ConnectionUtils {

    private ConnectionUtils() {
    }
    private static ConnectionUtils connectionUtils=new ConnectionUtils();
    public static ConnectionUtils getInstance(){
        return connectionUtils;
    }
    private ThreadLocal threadLocal= new ThreadLocal<>();//当前线程连接
    /**
     * 从当前线程获取连接
     * @return
     */
    public Connection getCurrentThreadConn() throws SQLException {
        //判断当前线程是否绑定连接,如果没有,则获取连接并绑定
        Connection connection=threadLocal.get();
        if(connection==null){
            connection=DruidUtils.getInstance().getConnection();
            threadLocal.set(connection);
        }
        return connection;
    }
}

创建一个事务管理工具类TransactionManager,同样使用单例模式

public class TransactionManager {
    private static TransactionManager transactionManager=new TransactionManager();
    private TransactionManager() {
    }
    public static TransactionManager getInstance(){
        return transactionManager;
    }
    /**
     * 开启事务
     */
    public void beginTransaction() throws SQLException {
        ConnectionUtils.getInstance().getCurrentThreadConn().setAutoCommit(false);
    }

    /**
     * 提交事务
     */
    public void commit() throws SQLException {
        ConnectionUtils.getInstance().getCurrentThreadConn().commit();
    }
    /**
     *
     * 回滚事务
     */
    public void rollback() throws SQLException {
        ConnectionUtils.getInstance().getCurrentThreadConn().rollback();
    }
}

之后在service层使用事务控制

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

        try {
            //开启事务
            TransactionManager.getInstance().beginTransaction();
           ······(多个操作数据库代码)
            //提交事务
            TransactionManager.getInstance().commit();
        } catch (Exception e) {
            e.printStackTrace();
            //回滚事务
            TransactionManager.getInstance().rollback();
            throw e;
        }
    }

通过以上代码可以进行手写AOP的实现,该代码可以进行优化,在proxyFactory中加入TransactionManager的实例化对象,以及在TransactionManager和dao层代码中添加ConnectionUtils 的实例化对象,以及修改beans.xml如下

 
    
        
    
    
        
        
        
    

    
    

    
    
        
    
    
        
    

至此,手写IOC和AOP以及完成

你可能感兴趣的:(spring)