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. Qt按ESC关闭模态对话框不触发closeEvent()问题解析(ESC默认调用的是reject()函数,所以必须覆盖这个函数才会有效果)good

    事情是这样的:今天调试窗体,突然发现按ESC键居然跳过closeEvent()关闭了对话框!我的关闭判断都在closeEvent()里,这直接导致非正常关闭了正在进行的工作.先重建下场景: 调用处: ...

  2. 浏览器引擎-phantomjs初次认识

    最近没什么重要的任务,就抽空看了看项目组爬虫小组的代码,因为我们的爬虫主要是以python的scrapy框架为主,看起来比较方便.在看代码的时候看到一个叫phantomjs的东西,蛮新鲜的,就去问了下 ...

  3. 【转载】Chrome使用自定义协议打开本地程序并运行IE打开网页

    部分内容转载自: http://blog.sina.com.cn/s/blog_e2b8213a0102wqby.html 项目中遇到某需求:chorme要运行IE并打开网页.解决方案之一就是通过自定 ...

  4. Python基础,day3

    本节内容 1. 函数基本语法及特性 2. 参数与局部变量 3. 返回值 嵌套函数 4.递归 5.匿名函数 6.函数式编程介绍 7.高阶函数 8.内置函数 1.函数基本语法及特性 如何不重复代码,其实很 ...

  5. hgoi#20190519

    更好的阅读体验 来我的博客观看 T1-求余问题 Abu Tahun很喜欢回文. 一个数组若是回文的,那么它从前往后读和从后往前读都是一样的,比如数组{1},{1,1,1},{1,2,1},{1,3,2 ...

  6. SpringBoot(十九)_spring.profiles.active=@profiles.active@ 的使用

    现在在的公司用spring.profiles.active=@profiles.active@ 当我看到这个的时候,一脸蒙蔽,这个@ 是啥意思. 这里其实是配合 maven profile进行选择不同 ...

  7. Spring Boot:集成Druid数据源

    综合概述 数据库连接池负责分配.管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个:释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据 ...

  8. 「玩转树莓派」树莓派 3B+ 配置无线WiFi

    前言 网线不方便还花钱,有自带的无线 WiFi 模块为啥不用. 网络模式 这里我们先介绍两种网络模式,WPA-Personal 与 WPA-Enterprise. WPA-Personal 大多数家庭 ...

  9. 全自动Landsat影像温度反演软件开发

    许久没有更新遥感类软件开发了,都有点生疏了,这一次我带来了一个老的算法,新的东西, 为什么这么说呢,我们知道Landat8.Landsat5等影像,单个影像去做温度反演,并没有什么太大的难度, 但是呢 ...

  10. 记录一次关于Cookie、Json中文乱码的解决方法

    今天工作上遇到一个问题,需要把一个对象集合List<Model>存入一个Cookie,按照原来都封装方法存入都ok,但是到取值都时候中文会变成乱码. 首先,我们可以确认Json和Cooki ...