手写IOC及AOP

Posted 怀瑾Hello World

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手写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.银行转账调用关系

转账页面 TransferServlet TransferService AccountDao JdbcAccountDaoImpl MybatisAccountDaoImpl 点击'转出',页面发起ajax,请求到TransferServlet TransferServlet中实例化service层对象,并调用service层方法- TransferService transferService = new TransferServiceImpl() TransferService中实例化dao层对象,并调用dao层方- AccountDao dao=new JdbcAccountDaoImpl() Dao层实现原生JDBC 如果要切换成Mybatis实现呢? 转账页面 TransferServlet TransferService AccountDao JdbcAccountDaoImpl MybatisAccountDaoImpl

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

<?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;以上是关于手写IOC及AOP的主要内容,如果未能解决你的问题,请参考以下文章

手写IOC及AOP

手写实现自定义简易版Spring (实现IoC 和 AOP)

手写实现自定义简易版Spring (实现IoC 和 AOP)

03Spring源码-手写篇-手写AOP实现(上)

03Spring源码-手写篇-手写AOP实现(上)

01Spring源码-手写篇-手写IoC实现