【Spring】手动实现简易AOP和IOC

news/2024/11/8 14:52:39/

前言

XML:通过Dom4j对XML进行解析和验证。
IOC:通过获取要创建对象的Class类型、构造函数后,通过反射来实现。
AOP:通过使用JDK动态代理Cglib动态代理实现。

一、解析XML

1.1、解析bean标签

/*** 解析bean标签* @param xmlBean bean标签*/private void parseBeanDefinitionXML(Element xmlBean){String beanId = xmlBean.attributeValue("id");if (beanDefinitionMap.containsKey(beanId)) {return;}String beanClass = xmlBean.attributeValue("class");BeanDefinition beanDefinition = new BeanDefinition();beanDefinition.setId(beanId);beanDefinition.setClassName(beanClass);List<Element> xmlConstructor = xmlBean.elements();//  无参bean,则解析完成if (UsualUtil.isNull(xmlConstructor)) {beanDefinitionMap.put(beanId, beanDefinition);return;}List<BeanDefinition.ConstructArg> constructArgs = new ArrayList<>();//  解析参数for (Element xmlConstructArg : xmlConstructor) {//  目前仅解析constructor-argif ("constructor-arg".equals(xmlConstructArg.getName())) {String typeClass = xmlConstructArg.attributeValue("type");String value = xmlConstructArg.attributeValue("value");String refName = xmlConstructArg.attributeValue("ref");BeanDefinition.ConstructArg constructArg = new BeanDefinition.ConstructArg();constructArg.setType(typeClass);constructArg.setValue(value);constructArg.setRef(refName);//  非引用其他beanif (UsualUtil.isNull(refName)) {if (typeClass.endsWith("List")) {Element xmlCollection = xmlConstructArg.elements().get(0);String valueTypeClass = xmlCollection.attributeValue("value-type");constructArg.setItemType(valueTypeClass);List<String> itemValue = new ArrayList<>();for (Element xmlItem : xmlCollection.elements()) {itemValue.add(xmlItem.getStringValue());}constructArg.setItemValue(itemValue);}}constructArgs.add(constructArg);}}beanDefinition.setConstructArgs(constructArgs);beanDefinitionMap.put(beanId, beanDefinition);}

1.2、解析aspect标签

/*** 解析aspect标签* @param xmlAspect aspect标签* @param useCglib 是否使用cglib*/private void parseAspectXML(Element xmlAspect, boolean useCglib) {
//    <aop:config>
//        <aop:aspect ref="aspect">
//            <aop:before method="run" pointcut="execution(public void org.tree.aspect.AspectProcessor.before())"/>
//            <aop:after method="run" pointcut="execution(public void org.tree.aspect.AspectProcessor.after())"/>
//        </aop:aspect>
//    </aop:config>String refName = xmlAspect.attributeValue("ref");if (aspectDefinitionMap.containsKey(refName)) {return;}AspectDefinition aspectDefinition = new AspectDefinition();aspectDefinition.setUseCglib(useCglib);aspectDefinition.setRef(refName);List<AspectDefinition.Advice> advices = new ArrayList<>();for (Element xmlAdvice : xmlAspect.elements()) {AspectDefinition.Advice advice = new AspectDefinition.Advice();advice.setType(xmlAdvice.getName());//  execution(public void org.tree.aspect.AspectProcessor.before())String pointcutStr = xmlAdvice.attributeValue("pointcut");pointcutStr = pointcutStr.replace("execution", "");pointcutStr = pointcutStr.substring(0, pointcutStr.length() - 1);//  public void org.tree.aspect.AspectProcessor.before()String[] strings = pointcutStr.split(" ");advice.setMethod(xmlAdvice.attributeValue("method"));advice.setProxyMethod(strings[2].substring(strings[2].lastIndexOf('.') + 1, strings[2].length() - 2));advice.setProxyClass(strings[2].substring(0, strings[2].lastIndexOf('.')));advices.add(advice);}aspectDefinition.setAdvices(advices);aspectDefinitionMap.put(refName, aspectDefinition);}

二、加载Bean

加载Bean时,本文实现的是通过构造函数注入参数,所以无法解决循环依赖的问题。
浅说下发现构造函数循环依赖的原理:当要加载一个bean时,记录下当前bean的加载状态为“正在创建中”,准备好构造函数所需要的参数,如果参数是未加载的,则取加载其参数,当加载的参数发现自己正处于“正在创建中”的状态时,则此刻这两个bean之间发生循环依赖了。

2.1、getBean

/*** 通过bean实例名字获取实例* @param id bean实例名字* @return bean实例*/public Object getBean(String id) throws Exception {if (beanFactory.containsKey(id)) {return beanFactory.get(id);}if (!beanDefinitionMap.containsKey(id)) {return null;}BeanDefinition beanDefinition = beanDefinitionMap.get(id);List<BeanDefinition.ConstructArg> constructArgs = beanDefinition.getConstructArgs();if (UsualUtil.isNull(constructArgs)) {//  创建bean前操作if (beanPostProcessor != null) {beanPostProcessor.preInitBean(objectFactory.get(id), id);}Object beanObj = Class.forName(beanDefinition.getClassName()).getConstructor().newInstance();//  如果该bean被aop引用beanObj = doEnhanceBean(beanDefinition, beanObj);//  创建bean后操作if (beanPostProcessor != null) {beanPostProcessor.postInitBean(beanObj, id);}beanFactory.put(id, beanObj);return beanObj;}return doCreateBean(beanDefinition);}

2.2、doCreateBean

/*** 创建bean* @param beanDefinition bean标签* @return bean实例* @throws Exception 空*/private Object doCreateBean(BeanDefinition beanDefinition) throws Exception{//  标记该bean正在创建实例中objectFactory.put(beanDefinition.getId(), Class.forName(beanDefinition.getClassName()).getConstructor().newInstance());List<BeanDefinition.ConstructArg> constructArgs = beanDefinition.getConstructArgs();Class<?>[] paramTypes = new Class[constructArgs.size()];Object[] paramValues= new Object[constructArgs.size()];int paramIndex = 0;for (BeanDefinition.ConstructArg constructArg : constructArgs) {//  构造函数参数为引用if (!UsualUtil.isNull(constructArg.getRef())) {//  该引用bean已经创建实例if (beanFactory.containsKey(constructArg.getRef())) {Object refBeanObj = beanFactory.get(constructArg.getRef());paramTypes[paramIndex] = refBeanObj.getClass();paramValues[paramIndex] = refBeanObj;paramIndex++;//  该引用bean正在创建实例中,循环引用} else if (objectFactory.containsKey(constructArg.getRef())) {throw new Exception("Both id '" + beanDefinition.getId() + "' and '" + constructArg.getRef() + "' are circular reference");//  该引用bean未创建实例} else {Object refBeanObj = doCreateBean(beanDefinitionMap.get(constructArg.getRef()));paramTypes[paramIndex] = refBeanObj.getClass();paramValues[paramIndex] = refBeanObj;paramIndex++;}continue;}paramTypes[paramIndex] = Class.forName(constructArg.getType());//  构造函数参数为非引用类型, 且是非集合类型if (!(constructArg.getType().endsWith("List"))) {paramValues[paramIndex++] = Class.forName(constructArg.getType()).getConstructor(String.class).newInstance(constructArg.getValue());continue;}List<Object> objParamList = new ArrayList<>();for (String itemValue : constructArg.getItemValue()) {objParamList.add(Class.forName(constructArg.getItemType()).getConstructor(String.class).newInstance(itemValue));}paramValues[paramIndex++] = objParamList;}//  创建bean前操作if (beanPostProcessor != null) {beanPostProcessor.preInitBean(objectFactory.get(beanDefinition.getId()), beanDefinition.getId());}Object beanObj = Class.forName(beanDefinition.getClassName()).getConstructor(paramTypes).newInstance(paramValues);//  如果该bean被aop引用beanObj = doEnhanceBean(beanDefinition, beanObj);//  创建bean后操作if (beanPostProcessor != null) {beanPostProcessor.postInitBean(beanObj, beanDefinition.getId());}beanFactory.put(beanDefinition.getId(), beanObj);//  标记该bean没有正在创建objectFactory.remove(beanDefinition.getId());return beanObj;}

三、增强Bean

3.1、doEnhanceBean

/*** 创建增强bean* @param beanDefinition bean标签* @param beanObj bean对象* @return 增强bean* @throws Exception 空*/private Object doEnhanceBean(BeanDefinition beanDefinition, Object beanObj) throws Exception {if (aspectDefinitionMap.containsKey(beanDefinition.getId())) {AspectDefinition aspectDefinition = aspectDefinitionMap.get(beanDefinition.getId());for (AspectDefinition.Advice advice : aspectDefinition.getAdvices()) {//  增强beanObjbeanObj = doCreateProxy(beanObj, advice.getMethod(), advice.getProxyClass(), advice.getProxyMethod(), "before".equals(advice.getType()), aspectDefinition.isUseCglib());}}return beanObj;}

3.2、doCreateProxy

/*** 创建代理对象* @param beanObj bean实例* @param joinPointMethod 要代理的bean方法* @param strProxyClass 代理类* @param strProxyMethod 代理类的方法* @param isBefore 通知类型* @param useCglib 是否使用cglib* @return 代理对象* @throws Exception 空*/private Object doCreateProxy(Object beanObj, String joinPointMethod, String strProxyClass, String strProxyMethod, boolean isBefore, boolean useCglib) throws Exception {Class<?> clazz = Class.forName(strProxyClass);Object proxyObject = clazz.getConstructor().newInstance();Method proxyMethod = clazz.getMethod(strProxyMethod);//  如果使用cglib动态代理if (useCglib) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(beanObj.getClass());/** 设置被代理类执行方法时的逻辑* o 被代理类生成的对象实例(子类)* method 被代理类的方法* args 方法的参数* methodProxy 被代理类的方法的代理方法*/enhancer.setCallback((MethodInterceptor) (o, method, args, methodProxy) -> {//  当方法是我们要代理的方法时if (method.getName().equals(joinPointMethod)) {if (isBefore) {proxyMethod.invoke(proxyObject);}//  调用父类的方法methodProxy.invokeSuper(o, args); // 使用beanObj会报错,因为参数需要的是生成的子类if (!isBefore) {proxyMethod.invoke(proxyObject);}} else { // 如果不写,则是全放空(由于生成的是子类,其他方法全空,不调用父类的【被代理类】)methodProxy.invokeSuper(o, args); // 调用父类的方法}return o;});return enhancer.create();}/** clazz 加载被代理类的加载器* interfaces 被代理类实现的所有接口* 增强方法*  o 被代理类的对象实例*  method 被代理类实现的所有接口的方法,且要执行*  args 方法需要的参数*/// 代理一个类,该类通过loader加载,且实现interfaces接口return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), beanObj.getClass().getInterfaces(), (o, method, args) -> {//  当方法为我们要代理的方法时,执行特定增强if (method.getName().equals(joinPointMethod)) {if (isBefore) {proxyMethod.invoke(proxyObject);}//  调用被代理类实际的方法method.invoke(beanObj, args); // 这里写beanObj不报错是因为它是AspectInterface的子类, 但不能写o,会死循环(调用了代理的自己,又再次调用)if (!isBefore) {proxyMethod.invoke(proxyObject);}} else {method.invoke(beanObj, args);}//  返回值就是代理类执行代理方法的返回值return o;});

四、类图在这里插入图片描述

gitee地址:https://gitee.com/sir-tree/my-spring.git


http://www.ppmy.cn/news/25714.html

相关文章

【C++】string类(下)

文章目录1.迭代器(正向遍历)begin有两个版本2.反向迭代器(反向遍历)rbegin由两个版本3. at4. insert ——头插在pos位置前插入一个字符串在pos位置前插入n个字符在迭代器前插入一个字符5. erase从pos位置开始删除len个字符从迭代器位置开始删除6. replace——替换从pos位置开始…

空间复杂度与时间复杂度

1、时间复杂度和空间复杂度 &#xff08;1&#xff09;时间复杂度、空间复杂度是什么&#xff1f; 算法效率分析分为两种&#xff1a;第一种是时间效率&#xff0c;第二种是空间效率。时间效率被称为时间复杂度&#xff0c;空间效率被称作空间复杂度时间复杂度主要衡量的是一…

2022黑马Redis跟学笔记.实战篇(四)

2022黑马Redis跟学笔记.实战篇 四4.3.秒杀优惠券功能4.3.1.秒杀优惠券的基本实现一、优惠卷秒杀1.1 全局唯一ID1.2 Redis实现全局唯一Id1.3 添加优惠卷1.4 实现秒杀下单4.3.2.超卖问题4.3.3.基于乐观锁解决超卖问题1. 悲观锁2. 乐观锁3. 乐观锁解决超卖问题4.4 秒杀的一人一单限…

【MySQL】数据库基础

目录 1、什么是数据库 2、 数据库基本操作 2.1 查看当前数据库 2.2 创建一个数据库 2.3 选中数据库 2.4 删除数据库 3、常见的数据类型 3.1 数值类型 3.2 字符串类型 3.3 日期类型 4、表的操作 4.1 创建表 4.2 查看指定数据库下的所有表 4.3 查看表的结构 4.…

【MT7628】固件开发-SDK4320添加MT7612E WiFi驱动操作说明

解压5G WiFi MT7612E驱动1.1解压指令 tar -xvf MT76x2E_MT7620_LinuxAP_V3.0.4.0_P2_DPA_20160308.tar.bz2 1.2解压之后会出现以下两个目录 rlt_wifi rlt_wifi_ap 1.3将解压后的文件拷贝到系统下 拷贝路径 RT288x_SDK/source/linux-2.6.36.x/drivers/net/wireless 内核中打开驱…

极验3代 加密分析

目标链接 aHR0cHM6Ly93d3cuZ2VldGVzdC5jb20vZGVtby9zbGlkZS1mbG9hdC5odG1s接口分析 极验参数重要信息 gt和challenge&#xff1b;gt是固定的&#xff0c;但是challenge每次请求会产生不同的&#xff0c;这里的请求的并没有什么加密参数。 下一个请求 gettype.php&#xff0c…

element-ui中el-table点击其他自定义按钮展开table中某一行

element-ui中el-table点击其他自定义按钮展开table中某一行 在日常开发中&#xff0c;我们遇见了会有点击某些按钮&#xff0c;使得表格行展开的需求&#xff0c;这时候去查看文档 element-ui&#xff08;table&#xff09; 这里官方提供了示例为在行最左侧有一个展开合并ico…

深入理解MySQLⅢ -- 锁与InnoDB引擎

文章目录锁概述全局锁表级锁表锁元数据锁意向锁行级锁行锁间隙锁&临键锁InnoDB引擎逻辑存储结构架构内存结构磁盘结构后台线程事务原理redo logundo logMVCC锁 概述 锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中&#xff0c;除传统的计算资源&#x…