java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > 手写IOC和AOP代码

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

作者:怀瑾Hello World

本文展示通过一个案例来自己手写IOC和AOP代码,通过银行转账案例详细的代码编写和文档解释来说明IOC和AOP的思想,会分享存在的问题和解决问题的思路

1.概念

IOC:Inversion of control 控制反转

AOP:Aspect oriented Programming 面向切面编程

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.银行转账调用关系

在这里插入图片描述

2.3.分析存在的问题

2.4.解决问题思路

2.5.通过IOC及AOP进行改造

2.5.0.pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.lagou.edu</groupId>
  <artifactId>lagou-transfer</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>lagou-transfer Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
  </properties>


  <dependencies>
    <!-- 单元测试Junit -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
    </dependency>

    <!-- mysql数据库驱动包 -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.35</version>
    </dependency>
    <!--druid连接池-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.21</version>
    </dependency>

    <!-- servlet -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>

    <!-- jackson依赖 -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.6</version>
    </dependency>

    <!--dom4j依赖-->
    <dependency>
      <groupId>dom4j</groupId>
      <artifactId>dom4j</artifactId>
      <version>1.6.1</version>
    </dependency>
    <!--xpath表达式依赖-->
    <dependency>
      <groupId>jaxen</groupId>
      <artifactId>jaxen</artifactId>
      <version>1.1.6</version>
    </dependency>
    <!--引入cglib依赖包-->
    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>2.1_2</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <!-- 配置Maven的JDK编译级别 -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.2</version>
        <configuration>
          <source>11</source>
          <target>11</target>
          <encoding>UTF-8</encoding>
        </configuration>
      </plugin>

      <!-- tomcat7插件 -->
      <!-- 注意:目前来说,maven中央仓库还没有tomcat8的插件 -->
      <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <version>2.2</version>
        <configuration>
          <port>8080</port>
          <path>/</path>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

2.5.1.index.xml

<!doctype html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>转账汇款</title>

    <script type="text/javascript" src="js/jquery-3.4.1.min.js"></script>


    <style type="text/css">
        body {
            background-color:#00b38a;
            text-align:center;
        }

        .lp-login {
            position:absolute;
            width:500px;
            height:300px;
            top:50%;
            left:50%;
            margin-top:-250px;
            margin-left:-250px;
            background: #ffffff;
            border-radius: 4px;
            box-shadow: 0 0 10px #12a591;
            padding: 57px 50px 35px;
            box-sizing: border-box
        }


        .lp-login .submitBtn {
            display:block;
            text-decoration:none;
            height: 48px;
            width: 150px;
            line-height: 48px;
            font-size: 16px;
            color: #fff;
            text-align: center;
            background-image: -webkit-gradient(linear, left top, right top, from(#09cb9d), to(#02b389));
            background-image: linear-gradient(90deg, #09cb9d, #02b389);
            border-radius: 3px
        }


        input[type='text'] {
            height:30px;
            width:250px;
        }

        span {
            font-style: normal;
            font-variant-ligatures: normal;
            font-variant-caps: normal;
            font-variant-numeric: normal;
            font-variant-east-asian: normal;
            font-weight: normal;
            font-stretch: normal;
            font-size: 14px;
            line-height: 22px;
            font-family: "Hiragino Sans GB", "Microsoft Yahei", SimSun, Arial, "Helvetica Neue", Helvetica;
        }

    </style>
    <script type="text/javascript">
        $(function(){
            $(".submitBtn").bind("click",function(){
                var fromAccount = $("#fromAccount").val();
                var toAccount = $("#toAccount").val();
                var money = $("#money").val();

                if(money == null || $.trim(money).length == 0){
                    alert("sorry,必须输入转账金额~");
                    return;
                }

                $.ajax({
                    url:'/transferServlet',
                    type:'POST',    //GET
                    async:false,    //或false,是否异步
                    data:{
                        fromCardNo:fromAccount.split(' ')[1],
                        toCardNo:toAccount.split(' ')[1],
                        money:money
                    },
                    timeout:5000,    //超时时间
                    dataType:'json', //返回的数据格式:json/xml/html/script/jsonp/text
                    success:function(data){
                        if("200" == data.status){
                            alert("转账成功~~~");
                        }else{
                            alert("转账失败~~~,message:" + data.message);
                        }
                    }
                })
            })
        })

        //检查输入值是否为整数
        function checkFormat(obj){
            var reg = /^[0-9]+[0-9]*]*$/;
            if($.trim($(obj).val()).length>0){
                if(!reg.test($(obj).val())){
                    alert("输入格式错误!请输整数!");
                    $(obj).val("");
                }else{
                    $(obj).val(parseInt($(obj).val()));
                }
            }
        }
    </script>
</head>
<body>


<form>
    <table class="lp-login">
        <tr>
            <td align="right"><span>收款账户</span></td>
            <td align="center">
                <input type="text" id="toAccount" value="韩梅梅 6029621011001" disabled></input>
            </td>
        </tr>
        <tr>
            <td align="right"><span>付款账户</span></td>
            <td align="center">
                <input type="text" id="fromAccount" value="李大雷 6029621011000" disabled></input>
            </td>
        </tr>
        <tr>
            <td align="right"><span>转账金额</span></td>
            <td align="center">
                <input type="text" id="money" onblur="checkFormat(this)"></input>
            </td>
        </tr>
        <tr align="center">
            <td colspan="2">
                <a href="javasrcipt:void(0)" rel="external nofollow"  class="submitBtn"><span>转 出</span></a>
            </td>
        </tr>
    </table>
</form>

</body>
</html>

2.5.2.beans.xml

<?xml version="1.0" encoding="UTF-8" ?>
<beans>
    <!--id:唯一标识, class类的全路径-->
    <bean id="accountDao" class="com.lagou.edu.dao.impl.JdbcAccountDaoImpl">
        <property name="ConnectionUtils" ref="connectionUtils"></property>
    </bean>
    <bean id="transferService" class="com.lagou.edu.service.impl.TransferServiceImpl">
        <!--set+ name,将属性注入-->
        <property name="AccountDao" ref="accountDao"></property>
    </bean>

    <!--配置新增的三个组件-->
    <!--连接池工具类-->
    <bean id="connectionUtils" class="com.lagou.edu.utils.ConnectionUtils"></bean>

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

    <!--代理对象工厂-->
    <bean id="proxyFactory" class="com.lagou.edu.factory.ProxyFactory">
        <property name="TransactionManager" ref="transactionManager"></property>
    </bean>


</beans>

2.5.3.工具类

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<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;
    }

}
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;
    }

}
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());
    }

}
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> T json2Pojo(String jsonData, Class<T> 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 <T>List<T> json2List(String jsonData, Class<T> beanType) {
    	JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType);
    	try {
    		List<T> list = MAPPER.readValue(jsonData, javaType);
    		return list;
		} catch (Exception e) {
			e.printStackTrace();
		}
    	
    	return null;
    }
    
}

2.5.4.pojo

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.工厂类

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<String,Object> map = new HashMap<>();

   // 0.服务一启动,就将对象加载至容器中,这里使用静态代码块
    static{
        // 1.解析对象配置文件
       InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
       try {
           // 使用dom4j技术,首先获取根节点 <beans>
           Element rootElement = new SAXReader().read(resourceAsStream).getRootElement();
           // 使用xpath,寻找 <bean > 节点
           List<Element> 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<Element> 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);
    }


}
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层

2.5.7.service层

2.5.8.controller层

2.5.9.注意事项

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

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

总结

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

您可能感兴趣的文章:
阅读全文