自己编写IOC控制反转及AOP面向切面

1.概念

IOC:Inversion of control 控制反转

  • 控制:指的是对象创建(实例化、管理)的权利
  • 反转:控制权交给外部环境了(spring框架、IoC容器)
  • 传统开发⽅式:⽐如类A依赖于类B,往往会在类A中new⼀个B的对象
  • IoC思想下开发方式:我们不⽤⾃⼰去new对象了,⽽是由IoC容器(Spring框架)去帮助我们实例化对象并且管理它,我们需要使⽤哪个对象,去问IoC容器要即可。
  • 解决的问题:解决对象之间的耦合问题,避免new关键字
  • Ioc和DI区别:IOC和DI是从不同角度描述同一件事情(对象实例化及依赖关系维护这件事情)。IOC是站在对象的角度,对象实例化及其管理的权力交给了(反转)容器。DI:Dependancy Injection(依赖注⼊),是站在容器的角度,容器会把对象依赖的其他对象注入(送进去),比如A对象实例化过程中因为声明了一个B类型的属性,那么就需要容器把B对象注入给A

AOP:Aspect oriented Programming 面向切面编程

  • 起源:aop是oop的延续,oop三大特征:封装、继承、多态。是一种垂直纵向的继承体系。OOP编程思想可以解决⼤多数的代码重复问题,但是有⼀些情况是处理不了的,⽐如在顶级⽗类中的多个⽅法中相同位置出现了重复代码,OOP就解决不了
  • 横切逻辑代码问题:1.横切代码重复问题。2.横切逻辑代码和业务代码混杂在⼀起,代码臃肿,维护不⽅便
  • AOP解决的问题:在不改变原有业务逻辑情况下,增强横切逻辑代码,根本上解耦合,避免横切逻辑代码重复
  • 面向切面编程理解:「切」:指的是横切逻辑,原有业务逻辑代码我们不能动,只能操作横切逻辑代码,所以⾯向横切逻辑。「⾯」:横切逻辑代码往往要影响的是很多个⽅法,每⼀个⽅法都如同⼀个点,多个点构成⾯,有⼀个⾯的概念在⾥⾯

2.通过银行转账案例手写IOC和AOP

2.1.表结构

CREATE TABLE `account` (
  `name` varchar(255) DEFAULT NULL COMMENT '用户名',
  `money` varchar(255) DEFAULT NULL COMMENT '账户金额',
  `cardNo` varchar(255) NOT NULL COMMENT '银行卡号'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2.2.银行转账调用关系

自己编写IOC控制反转及AOP面向切面_第1张图片

2.3.分析存在的问题

  • (1)问题⼀:在上述案例实现中,service 层实现类在使⽤ dao 层对象时,直接在TransferServiceImpl 中通过 AccountDao accountDao = new JdbcAccountDaoImpl() 获得了 dao层对象,然⽽⼀个 new 关键字却将 TransferServiceImpl 和 dao 层具体的⼀个实现类JdbcAccountDaoImpl 耦合在了⼀起,如果说技术架构发⽣⼀些变动,dao 层的实现要使⽤其它技术,⽐如 Mybatis,思考切换起来的成本?每⼀个 new 的地⽅都需要修改源代码,重新编译,⾯向接⼝开发的意义将⼤打折扣?
  • (2)问题⼆:service 层代码没有竟然还没有进⾏事务控制 ?!如果转账过程中出现异常,将可能导致数据库数据错乱,后果可能会很严重,尤其在⾦融业务

2.4.解决问题思路

  • 实例化对象的⽅式除了 new 之外,还有什么技术?反射 (需要把类的全限定类名配置在xml中)
  • 考虑使⽤设计模式中的⼯⼚模式解耦合,另外项⽬中往往有很多对象需要实例化,那就在⼯⼚中使⽤反 射技术实例化对象,⼯⼚模式很合适
  • service 层没有添加事务控制,怎么办?没有事务就添加上事务控制,⼿动控制 JDBC 的Connection 事务,但要注意将Connection和当前线程绑定(即保证⼀个线程只有⼀个Connection,这样操作才针对的是同⼀个 Connection,进⽽控制的是同⼀个事务)。分析:数据库的事务归根结底是Connection的事务connection.commit();提交事务 connection.rollback();回滚事务

2.5.通过IOC及AOP进行改造

2.5.0.pom.xml




  4.0.0

  com.lagou.edu
  lagou-transfer
  1.0-SNAPSHOT
  war

  lagou-transfer Maven Webapp
  
  http://www.example.com

  
    UTF-8
    11
    11
  


  
    
    
      junit
      junit
      4.12
    

    
    
      mysql
      mysql-connector-java
      5.1.35
    
    
    
      com.alibaba
      druid
      1.1.21
    

    
    
      javax.servlet
      javax.servlet-api
      3.1.0
      provided
    

    
    
      com.fasterxml.jackson.core
      jackson-databind
      2.9.6
    

    
    
      dom4j
      dom4j
      1.6.1
    
    
    
      jaxen
      jaxen
      1.1.6
    
    
    
      cglib
      cglib
      2.1_2
    
  

  
    
      
      
        org.apache.maven.plugins
        maven-compiler-plugin
        3.2
        
          11
          11
          UTF-8
        
      

      
      
      
        org.apache.tomcat.maven
        tomcat7-maven-plugin
        2.2
        
          8080
          /
        
      
    
  

2.5.1.index.xml




    
    转账汇款

    


    
    




2.5.2.beans.xml



    
    
        
    
    
        
        
    

    
    
    

    
    
        
    

    
    
        
    


2.5.3.工具类

  • ConnectionUtils
package com.lagou.edu.utils;

import com.alibaba.druid.pool.DruidPooledConnection;

import java.sql.Connection;
import java.sql.SQLException;

/**
 * 获取数据库连接工具类
 */
public class ConnectionUtils {

/*    private ConnectionUtils(){}

    public static ConnectionUtils getInstance(){
        return new ConnectionUtils();
    }*/

    // 1.单例,保证线程获取到的连接是同一个。(每次新new ConnectionUtils,那么里面的threadlocal也是新的,connection也是新的)
    private ThreadLocal threadLocal = new ThreadLocal<>();

    public Connection getCurrentThreadConn() throws SQLException {
        Connection connection = threadLocal.get();
        if (connection == null){
            connection = DruidUtils.getInstance().getConnection();
            // 创建完成后一定要设置回去
            threadLocal.set(connection);
        }
        return connection;
    }

}
  • DruidUtils
package com.lagou.edu.utils;

import com.alibaba.druid.pool.DruidDataSource;


public class DruidUtils {

    private DruidUtils(){
    }

    private static DruidDataSource druidDataSource = new DruidDataSource();


    static {
        druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        druidDataSource.setUrl("jdbc:mysql://localhost:3306/bank");
        druidDataSource.setUsername("root");
        druidDataSource.setPassword("123456");

    }

    public static DruidDataSource getInstance() {
        return druidDataSource;
    }

}
  • TransactionManager
package com.lagou.edu.utils;

import java.sql.Connection;
import java.sql.SQLException;

/**
 * 事务管理器
 */
public class TransactionManager {

    private ConnectionUtils connectionUtils;

    public void setConnectionUtils(ConnectionUtils connectionUtils) {
        this.connectionUtils = connectionUtils;
    }
    /*    private TransactionManager(){}

    private static TransactionManager transactionManager = new TransactionManager();

    public static TransactionManager getInstance(){
        return transactionManager;
    }*/

    public void beginTranscation() throws SQLException {
        Connection conn = connectionUtils.getCurrentThreadConn();
        conn.setAutoCommit(false);
        System.out.println(conn.getAutoCommit() + ":开启事务的连接:"+conn);
    }

    public void commit() throws SQLException {
        connectionUtils.getCurrentThreadConn().commit();
        System.out.println("提交的连接:"+connectionUtils.getCurrentThreadConn());
    }

    public void rollback() throws SQLException {
        connectionUtils.getCurrentThreadConn().rollback();
        System.out.println("回滚的连接:"+connectionUtils.getCurrentThreadConn());
    }

}
  • JsonUtils
package com.lagou.edu.utils;

import java.util.List;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * JSON工具类(使用的是jackson实现的)
 */
public class JsonUtils {

    private static final ObjectMapper MAPPER = new ObjectMapper();

    /**
     * 将对象转换成json字符串。
     * @param data
     * @return
     */
    public static String object2Json(Object data) {
    	try {
			String string = MAPPER.writeValueAsString(data);
			return string;
		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}
    	return null;
    }
    
    /**
     * 将json结果集转化为对象
     * 
     * @param jsonData json数据
     * @param beanType 对象中的object类型
     * @return
     */
    public static  T json2Pojo(String jsonData, Class beanType) {
        try {
            T t = MAPPER.readValue(jsonData, beanType);
            return t;
        } catch (Exception e) {
        	e.printStackTrace();
        }
        return null;
    }
    
    /**
     * 将json数据转换成pojo对象list
     * @param jsonData
     * @param beanType
     * @return
     */
    public static List json2List(String jsonData, Class beanType) {
    	JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType);
    	try {
    		List list = MAPPER.readValue(jsonData, javaType);
    		return list;
		} catch (Exception e) {
			e.printStackTrace();
		}
    	
    	return null;
    }
    
}

2.5.4.pojo

  • Account
package com.lagou.edu.pojo;


public class Account {

    private String cardNo;
    private String name;
    private int money;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }

    public String getCardNo() { return cardNo; }

    public void setCardNo(String cardNo) { this.cardNo = cardNo;}

    @Override
    public String toString() {
        return "Account{" +
                "cardNo='" + cardNo + '\'' +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}

2.5.5.工厂类

  • BeanFactory
package com.lagou.edu.factory;

import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 工厂类
 * 作用1:解析xml文件,使用反射技术实例化bean对象,放入map中待用;
 * 作用2:提供接口方法根据id从map中获取bean(静态方法)
 *
 */
public class BeanFactory {

    private static Map map = new HashMap<>();

   // 0.服务一启动,就将对象加载至容器中,这里使用静态代码块
    static{
        // 1.解析对象配置文件
       InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
       try {
           // 使用dom4j技术,首先获取根节点 
           Element rootElement = new SAXReader().read(resourceAsStream).getRootElement();
           // 使用xpath,寻找  节点
           List beanList = rootElement.selectNodes("//bean");
           for (Element element : beanList) {
               String id = element.attributeValue("id");
               String aClass = element.attributeValue("class");
               // 2.使用反射技术,根据类的全路径创建对象
               Class aClass1 = Class.forName(aClass);
               Object o = aClass1.newInstance();

               // 3.将解析后的对象放入容器中(map)
               map.put(id,o);
           }

           // 遍历property标签,将属性注入,维护bean之间的依赖关系
           List propertyList = rootElement.selectNodes("//property");
           for (Element element : propertyList) {
               String name = element.attributeValue("name");
               String ref = element.attributeValue("ref");

               // 使用反射技术,设置属性
               Element parent = element.getParent();
               String parentId = parent.attributeValue("id");
               Object parentObj = map.get(parentId);
               Method[] methods = parentObj.getClass().getMethods();
               // 获取所有方法,寻找set+name,将ref设置
               for (Method method : methods) {
                   if(method.getName().equalsIgnoreCase("set"+name)){
                       Object propertyObj = map.get(ref);
                       method.invoke(parentObj,propertyObj);
                   }
               }
               // 维护依赖关系后重新将bean放入map中
               map.put(parentId,parentObj);
           }





       } catch (DocumentException | ClassNotFoundException e) {
           e.printStackTrace();
       } catch (IllegalAccessException e) {
           e.printStackTrace();
       } catch (InstantiationException e) {
           e.printStackTrace();
       } catch (InvocationTargetException e) {
           e.printStackTrace();
       }


   }

    // 3.提供获取对象的方法
    public static Object getBean(String id){
        return map.get(id);
    }


}
  • ProxyFactory
package com.lagou.edu.factory;

import com.lagou.edu.utils.TransactionManager;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

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

/**
 *
 * 代理对象工厂:生成代理对象的
 */

public class ProxyFactory {


    private TransactionManager transactionManager;

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

     /*private ProxyFactory(){

    }

   private static ProxyFactory proxyFactory = new ProxyFactory();

    public static ProxyFactory getInstance() {
        return proxyFactory;
    }*/



    /**
     * Jdk动态代理
     * @param obj  委托对象
     * @return   代理对象
     */
    public Object getJdkProxy(Object obj) {

        // 获取代理对象
        return  Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Object result = null;

                        try{
                            // 开启事务(关闭事务的自动提交)
                            transactionManager.beginTranscation();

                            result = method.invoke(obj,args);

                            // 提交事务

                            transactionManager.commit();
                        }catch (Exception e) {
                            e.printStackTrace();
                            // 回滚事务
                            transactionManager.rollback();

                            // 抛出异常便于上层servlet捕获
                            throw e;

                        }

                        return result;
                    }
                });

    }


    /**
     * 使用cglib动态代理生成代理对象
     * @param obj 委托对象
     * @return
     */
    public Object getCglibProxy(Object obj) {
        return  Enhancer.create(obj.getClass(), new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                Object result = null;
                try{
                    // 开启事务(关闭事务的自动提交)
                    transactionManager.beginTranscation();

                    result = method.invoke(obj,objects);

                    // 提交事务
                    transactionManager.commit();
                }catch (Exception e) {

                    // 回滚事务
                    transactionManager.rollback();

                    e.printStackTrace();

                    // 抛出异常便于上层servlet捕获
                    throw e;

                }
                return result;
            }
        });
    }
}

2.5.6.dao层

  • AccountDao
    package com.lagou.edu.dao;
    
    import com.lagou.edu.pojo.Account;
    
    
    public interface AccountDao {
    
        Account queryAccountByCardNo(String cardNo) throws Exception;
    
        int updateAccountByCardNo(Account account) throws Exception;
    }
  • JdbcAccountDaoImpl
    package com.lagou.edu.dao.impl;
    
    import com.lagou.edu.pojo.Account;
    import com.lagou.edu.dao.AccountDao;
    import com.lagou.edu.utils.ConnectionUtils;
    import com.lagou.edu.utils.DruidUtils;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    
    
    public class JdbcAccountDaoImpl implements AccountDao {
    
        private ConnectionUtils connectionUtils;
    
        public void setConnectionUtils(ConnectionUtils connectionUtils) {
            this.connectionUtils = connectionUtils;
        }
    
    
        public void init() {
            System.out.println("初始化方法.....");
        }
    
        public void destory() {
            System.out.println("销毁方法......");
        }
    
        @Override
        public Account queryAccountByCardNo(String cardNo) throws Exception {
            //从连接池获取连接
    //         Connection con = DruidUtils.getInstance().getConnection();
            // 从当前线程中获取连接池对象
            Connection con = connectionUtils.getCurrentThreadConn();
            String sql = "select * from account where cardNo=?";
            PreparedStatement preparedStatement = con.prepareStatement(sql);
            preparedStatement.setString(1,cardNo);
            ResultSet resultSet = preparedStatement.executeQuery();
    
            Account account = new Account();
            while(resultSet.next()) {
                account.setCardNo(resultSet.getString("cardNo"));
                account.setName(resultSet.getString("name"));
                account.setMoney(resultSet.getInt("money"));
            }
    
            resultSet.close();
            preparedStatement.close();
    //        con.close(); // 不能将当前线程的连接关闭了,不然同个线程同个业务中其他更新方法获取的连接就不是同一个
    
            return account;
        }
    
        @Override
        public int updateAccountByCardNo(Account account) throws Exception {
    
            // 从连接池获取连接
            // 改造为:从当前线程当中获取绑定的connection连接
    //        Connection con = DruidUtils.getInstance().getConnection();
            // 从当前线程中获取连接池对象
            Connection con = connectionUtils.getCurrentThreadConn();
            String sql = "update account set money=? where cardNo=?";
            PreparedStatement preparedStatement = con.prepareStatement(sql);
            preparedStatement.setInt(1,account.getMoney());
            preparedStatement.setString(2,account.getCardNo());
            int i = preparedStatement.executeUpdate();
    
            preparedStatement.close();
    //        con.close();
            return i;
        }
    }

2.5.7.service层

  • TransferService
    package com.lagou.edu.service;
    
    
    public interface TransferService {
    
        void transfer(String fromCardNo,String toCardNo,int money) throws Exception;
    }
  • TransferServiceImpl
    package com.lagou.edu.service.impl;
    
    import com.lagou.edu.dao.AccountDao;
    import com.lagou.edu.pojo.Account;
    import com.lagou.edu.service.TransferService;
    import com.lagou.edu.utils.TransactionManager;
    
    
    public class TransferServiceImpl implements TransferService {
    
    //    private AccountDao accountDao = new JdbcAccountDaoImpl();
    
    //     private AccountDao accountDao = (AccountDao) BeanFactory.getBean("accountDao");
    
        // 最佳状态
        private AccountDao accountDao;
    
        // 构造函数传值/set方法传值
    
        public void setAccountDao(AccountDao accountDao) {
            this.accountDao = accountDao;
        }
    
    
    
        @Override
        public void transfer(String fromCardNo, String toCardNo, int money) throws Exception {
    
    //        try {
    //            // 开启事务(设置自动提交关闭)
    //            TransactionManager.getInstance().beginTranscation();
                Account from = accountDao.queryAccountByCardNo(fromCardNo);
                Account to = accountDao.queryAccountByCardNo(toCardNo);
    
                from.setMoney(from.getMoney()-money);
                to.setMoney(to.getMoney()+money);
    
                accountDao.updateAccountByCardNo(to);
                int c = 1/0;
                accountDao.updateAccountByCardNo(from);
                // 事务提交
    //            TransactionManager.getInstance().commit();
    //        }catch (Exception e){
    //            // 事务回滚
    //            TransactionManager.getInstance().rollback();
    //            throw e;
    //        }
    
    
        }
    }

2.5.8.controller层

  • TransferServlet
    package com.lagou.edu.servlet;
    
    import com.lagou.edu.factory.BeanFactory;
    import com.lagou.edu.factory.ProxyFactory;
    import com.lagou.edu.service.impl.TransferServiceImpl;
    import com.lagou.edu.utils.JsonUtils;
    import com.lagou.edu.pojo.Result;
    import com.lagou.edu.service.TransferService;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    
    @WebServlet(name="transferServlet",urlPatterns = "/transferServlet")
    public class TransferServlet extends HttpServlet {
    
        // 1. 实例化service层对象
    //    private TransferService transferService = new TransferServiceImpl();
    //    private TransferService transferService = (TransferService) BeanFactory.getBean("transferService");
    
        // 从工厂获取委托对象,使用代理对象,主要增加了事务控制
        private ProxyFactory proxyFactory = (ProxyFactory) BeanFactory.getBean("proxyFactory");
        private TransferService transferService = (TransferService) proxyFactory.getJdkProxy((TransferService) BeanFactory.getBean("transferService"));
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doPost(req,resp);
        }
    
        @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 {
    
                // 2. 调用service层方法
                transferService.transfer(fromCardNo,toCardNo,money);
                result.setStatus("200");
            } 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));
        }
    }

2.5.9.注意事项

com.lagou.edu.utils.ConnectionUtils#getCurrentThreadConn中一定要注意,第一次获取连接为空时,创建连接后要设置到当前线程中。

if (connection == null){
            connection = DruidUtils.getInstance().getConnection();
            // 创建完成后一定要设置回去
            threadLocal.set(connection);
        }

总结

本篇介绍了IOC和AOP的思想,以及通过一个案例来进行解释说明,希望对大家有所帮助,更多相关手写IOC和AOP代码内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

你可能感兴趣的:(自己编写IOC控制反转及AOP面向切面)