1 简单的 IOC

1.1 简单的 IOC 容器实现的步骤

  1. 加载 xml 配置文件,遍历其中的标签

  2. 获取标签中的 id 和 class 属性,加载 class 属性对应的类,并创建 bean

  3. 遍历标签中的标签,获取属性值,并将属性值填充到 bean中

  4. 将 bean 注册到 bean 容器中

1.2 代码结构

 SimpleIOC       // IOC 的实现类,实现了上面说的四个步骤
 SimpleIOCTest // IOC 的测试类
 Car // IOC 测试使用的bean
 Wheel // 同上
 ioc.xml(放到源代码目录下) // bean 配置文件

1.3 使用到的技术

  • 加载xml配置文件,使用org.w3c.dom包下的Document等类来遍历其中的标签

  • beanClass = Class.forName(className) ==> beanClass.newInstance();

  • 反射:利用反射将 bean 相关字段访问权限设为可访问,将属性值填充到相关字段中

  • 使用HashMap 模拟 bean 容器

1.4 代码

容器实现类 SimpleIOC 的代码:

 package ioc;
 ​
 public class SimpleIOC {
     private Map<String, Object> beanMap = new HashMap<String, Object>();
     
     public SimpleIOC(String location) throws Exception {
         loadBeans(location);
    }
     
     public Object getBean(String name) {
         Object bean = beanMap.get(name);
         if (bean == null) {
             throw new IllegalArgumentException("there is no bean with name: " + name);
        }
         return bean;
    }
     
     private void loadBeans(String location) throws Exception {
         // 加载 xml 配置文件
         System.out.println(location);
         InputStream inputStream = new FileInputStream(location);
         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
         DocumentBuilder docBuilder = factory.newDocumentBuilder();
         Document document = docBuilder.parse(inputStream);
         Element root = document.getDocumentElement();
         NodeList nodes = root.getChildNodes();
         
         // 遍历 <bean> 标签
         for (int i = 0; i < nodes.getLength(); i++) {
             Node node = nodes.item(i);
             if (node instanceof Element) {
                 Element ele = (Element) node;
                 String id = ele.getAttribute("id");
                 String className = ele.getAttribute("class");
                 
                 // 加载 beanClass
                 Class beanClass = null;
                 try {
                     beanClass = Class.forName(className);
                } catch (ClassNotFoundException e) {
                     e.printStackTrace();
                     return;
                }
                 
                 // 创建 bean
                 Object bean = beanClass.newInstance();
                 
                 // 遍历 <property> 标签
                 NodeList propertyNodes = ele.getElementsByTagName("property");
                 for (int j = 0; j < propertyNodes.getLength(); j++) {
                     Node propertyNode = propertyNodes.item(j);
                     if (propertyNode instanceof Element) {
                         Element propertyElement = (Element) propertyNode;
                         String name = propertyElement.getAttribute("name");
                         String value = propertyElement.getAttribute("value");
                         
                         // 利用反射将 bean 相关字段访问权限设为可访问
                         Field declaredField = bean.getClass().getDeclaredField(name);
                         declaredField.setAccessible(true);
                         
                         if (value != null && value.length() > 0) {
                             // 将属性值填充到相关字段中
                             declaredField.set(bean, value);
                        } else {
                             String ref = propertyElement.getAttribute("ref");
                             if (ref == null || ref.length() == 0) {
                                 throw new IllegalArgumentException("ref config error");
                            }
                             
                             // 将引用填充到相关字段中
                             declaredField.set(bean, getBean(ref));
                             
                        }
                         // 将 bean 注册到 bean 容器中
                         registerBean(id, bean);
                    }
                }
            }
        }
    }
 ​
 ​
     private void registerBean(String id, Object bean) {
         beanMap.put(id, bean);
    }
 }

容器测试类使用的 bean 代码

 package bean;
 ​
 public class Car {
     private String name;
     private String length;
     private String width;
     private String height;
     private Wheel wheel;
     
     // getter 和 setter 方法
     
     @Override
     public String toString() {
         return "Car [name=" + name + ", length=" + length + ", width=" + width + ", height=" + height + ", wheel="
                 + wheel + "]";
    }
     
 }
 ​
 package bean;
 ​
 public class Wheel {
     private String brand;
     private String specification;
     
     // getter 和 setter 方法
     @Override
     public String toString() {
         return "Wheel [brand=" + brand + ", specification=" + specification + "]";
    }
 }

bean 配置文件 bean.xml 内容:

 <?xml version="1.0" encoding="UTF-8"?>
 <beans>
     <bean id="wheel" class="bean.Wheel">
         <property name="brand" value="Michelin"/>
         <property name="specification" value="265/60 R18"/>
     </bean>
     
     <bean id="car" class="bean.Car">
         <property name="name" value="Mercedes Benz G 500"/>
         <property name="length" value="4717mm"/>
         <property name="width" value="1855mm"/>
         <property name="height" value="1949mm"/>
         <property name="wheel" ref="wheel"/>
     </bean>
 </beans>

IOC 测试类 SimpleIOCTest:

 class SimpleIOCTest {
     public static void main(String[] args) throws Exception {
         // 注意项目名或工作目录不能带有空格
         System.out.println(SimpleIOC.class.getClassLoader().getResource("bean.xml").getFile());
         String location = SimpleIOC.class.getClassLoader().getResource("bean.xml").getFile();
         SimpleIOC bf = new SimpleIOC(location);
         Wheel wheel = (Wheel) bf.getBean("wheel");
         System.out.println(wheel);
         Car car = (Car) bf.getBean("car");
         System.out.println(car);
    }
 }

测试结果

2 简单的AOP

2.1 Spring AOP 的基本概念

通知(Advice):通知定义了要织入对象的逻辑,以及执行时机。Spring中对应了 5 种 不同类型的通知。

  • 前置通知(Before):在目标方法执行前,执行通知

  • 后置通知(After):在目标方法执行后,执行通知,此时不关系目标方法会返回什么

  • 返回通知(After-returning):在目标方法执行返回后,执行通知

  • 异常通知(After-throwing):在目标方法抛出异常后执行通知

  • 环绕通知(Around):目标方法被通知包裹,通知在目标方法执行前和执行后都会被调用

切点( PointCut):定义了在何处执行通知。作用:通过匹配规则查找合适的连接点(JoinPoint),AOP 会在这些连接点上织入通知。

切面(Aspect):切面包含了通知和切点,通知和切点共同定义了切面是什么,在何时何处执行切面逻辑。

2.2 简单 AOP 实现的步骤

基于 JDK 动态代理实现,需要以下三步:

  1. 定义一个包含切面逻辑的对象

  2. 定义一个Advice 对象(实现了 InvocationHandler 接口),并将上面定义的对象和目标对象传入

  3. 将上面的 Advice 对象和目标对象传给 JDK 动态代理方法,为目标对象生成代理。

2.3 Java SDK 动态代理原理

Java SDK代理面向的是一组接口,只能为接口创建代理。实现原理如下:

  1. 通过Proxy.newProxyInstance 创建代理类对象,需要三个参数:一个是ClassLoader,一个是接口数组,还有一个是InvocationHandler 对象

  2. ClassLoader是类加载器,使用本类即SimpleAOP 的类加载器即可

  3. 接口数组是我们需要为其创建代理的接口,即该数组中包含 HelloService

  4. InvocationHandler:对代理接口所有方法的调用都会转给该对象。可以由我们自己实现,在本例中即是 BeforeAdvice

  5. BeforeAdvice 实现了 InvocationHandler ,它的 invoke 方法处理所有的接口调用。(主要逻辑在 invoke 方法中)

2.4 简单 AOP 的代码结构

  • MethodInvocation 接口:实现类包含了切面逻辑

  • Advice 接口:继承了 InvocationHandler 接口

  • BeforeAdvice 类:实现了 Advice 接口,是一个前置通知

  • SimpleAOP 类:生成代理类

  • SimpleAOPTest 类:测试类

  • HelloService 接口:目标对象接口

  • HelloServiceImpl类:目标对象

2.5 代码

MethodInvocation 接口代码:

 public interface MethodInvocation {
     void invoke();
 }

Advice 接口代码:

 public interface Advice extends InvocationHandler {
 }

BeforeAdvice 实现代码:

 public class BeforeAdvice implements Advice {
     private Object bean;
     private MethodInvocation methodInvocation;
     
     public BeforeAdvice(Object bean, MethodInvocation methodInvocation) {
         this.bean = bean;
         this.methodInvocation = methodInvocation;
    }
     @Override
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         // 在目标方法执行前调用通知
         methodInvocation.invoke();
         return method.invoke(bean, args);
    }
 }

SimpleAOP 实现代码:

 public class SimpleAOP {
     public static Object getProxy(Object bean, Advice advice) {
         return Proxy.newProxyInstance(SimpleAOP.class.getClassLoader(),
                 bean.getClass().getInterfaces(), advice);
    }
 }

HelloService 接口,及其实现类代码:

 public interface HelloService {
     public void sayHello();
 }
 ​
 public class HelloServiceImpl implements HelloService {
 ​
     @Override
     public void sayHello() {
         System.out.println("hello");
    }
 }

SimpleAOPTest 代码:

 public class SimpleAOPTest {
     public static void main(String[] args) {
         // 1.创建一个 MethodInvocation 实现类
         MethodInvocation logTask = () -> System.out.println("log task start");
         HelloServiceImpl helloService = new HelloServiceImpl();
         
         // 2.创建一个 Advice
         Advice advice = new BeforeAdvice(helloService, logTask);
         
         // 3.为目标对象生成代理
         HelloService proxy = (HelloService) SimpleAOP.getProxy(helloService, advice);
         // 4.使用代理对象运行目标对象的方法
         proxy.sayHello();
    }
 }

实现简单的 IOC 和 AOP的更多相关文章

  1. Spring框架-IOC和AOP简单总结

    参考博客: https://blog.csdn.net/qq_22583741/article/details/79589910 1.Spring框架是什么,为什么,怎么用 1.1 Spring框架是 ...

  2. java简单例子介绍IOC和AOP

    IOC和AOP的一些基本概念 介绍 IOC 一.什么是IOC IoC就是Inversion of Control,控制反转.在Java开发中,IoC意味着将你设计好的类交给系统去控制,而不是在你的类内 ...

  3. 简单理解Spring之IOC和AOP及代码示例

    Spring是一个开源框架,主要实现两件事,IOC(控制反转)和AOP(面向切面编程). IOC 控制反转,也可以称为依赖倒置. 所谓依赖,从程序的角度看,就是比如A要调用B的方法,那么A就依赖于B, ...

  4. Spring IOC DI AOP 的简单理解及应用

    Spring两大特性:IOC 和AOP.IOC 控制反转,AOP 面向切面编程 spring 核心容器的主要组件时Bean工厂(BeanFactory) ,Bean 工厂使用控制反转模式来降低程序代码 ...

  5. 简单易用的AOP/IOC框架

    Source: http://www.codeproject.com/KB/Articles/684613/Working/AopIoc.zip Introduction Supper framewo ...

  6. Spring的IOC和AOP之深剖

    今天,既然讲到了Spring 的IOC和AOP,我们就必须要知道 Spring主要是两件事: 1.开发Bean:2.配置Bean.对于Spring框架来说,它要做的,就是根据配置文件来创建bean实例 ...

  7. spring - ioc和aop

    1.程序中为什么会用到spring的ioc和aop 2.什么是IOC,AOP,以及使用它们的好处,即详细回答了第一个问题 3.原理 关于1: a:我们平常使用对象的时候,一般都是直接使用关键字类new ...

  8. spring的IOC和AOP

     spring的IOC和AOP 1.解释spring的ioc? 几种注入依赖的方式?spring的优点? IOC你就认为他是一个生产和管理bean的容器就行了,原来需要在调用类中new的东西,现在都是 ...

  9. Castle框架中的IOC和AOP机制

    反转控制(IOC)和面向切面编程(AOP)技术作为当前比较流行的技术,其优势已受到广泛关注,但是这两项新技术在实际项目上的应用研究却很落后,而且在.NET平台下实现这两项技术没有形成可以广泛套用的框架 ...

随机推荐

  1. QList使用下标[index]才可以获得可修改的item的引用(估计QStringList也是如此)

    QList算是最常用的集合了,今儿偶然间需要修改QList中的值,结果郁闷了.QList中提供了replace函数来替换item,但不是修改.而at().value()操作均返回的是const的ite ...

  2. win7、win8客户端 连接win2003上的sqlserver2005 速度超慢

    解决办法:开始运行,执行netsh interface tcp set global rss=disabled autotuninglevel=disabled.关闭TCP/IP协议的自动调谐功能.

  3. 面试官:你了解过Redis对象底层实现吗

    上一章我们讲了Redis的底层数据结构,不了解的人可能会有疑问:这个和平时用的五大对象有啥关系呢?这一章我们就主要解释他们所建立的联系. 看这个文件之前,如果对ziplist.skiplist.int ...

  4. 浅谈我在.net core一年里的收获

    前言:以前一直在winserver的环境里从事web工作,安装一个sqlserver,iis,把项目部署上面就OK了,简单轻松一.结缘nginx以前一直听说nginx这个反向代理的web服务器,当玩n ...

  5. 04 Javascript的运算符

    js中的运算符跟python中的运算符有点类似,但也有不同.所谓运算,在数学上,是一种行为,通过已知量的可能的组合,获得新的量. 1.赋值运算符 以var x = 12,y=5来演示示例| 2.算数运 ...

  6. List集合总结,对比分析ArrayList,Vector,LinkedList

    前面已经写了三篇关于Java集合的文章,包括: Java集合 ArrayList原理及使用 再说Java集合,subList之于ArrayList Java集合 LinkedList的原理及使用 关于 ...

  7. spring 5.x 系列第1篇 —— springmvc基础 (xml配置方式)

    文章目录 一.搭建hello spring工程 1.1 项目搭建 1.2 相关配置讲解 二.配置自定义拦截器 三.全局异常处理 四.参数绑定 4.1 参数绑定 4.2 关于日期格式转换的三种方法 五. ...

  8. 超级实用的表格树控件--QtTreePropertyBrowser

    目录 一.源码下载 二.代码编译 1.intersect函数替换为intersected 2.移除UnicodeUTF8 3.QtGui模块拆分 4.Q_TYPENAME错误 5.qVariantVa ...

  9. 安装mysql apache php smb

    1 SMB LinuX下SMB的配置 使用Smb进行连接的命令: smbclient //192.168.128.1/Share 今天要在LINUX之间以及LINUX与WINDOWS之间互相传送文件, ...

  10. java中方法的重载和覆盖

    java中方法的重载和覆盖 先来了解一下这两个名词的含义. 重载: 在一个类当中才可以重载,方法名相同,参数个数不同或参数个数相同而参数类型不同. 覆盖: 又称重写,在派生类(子类)中重写基类(父类) ...