该框架主要实现了Spring框架的几个主要部分,并且进行了拓展
提示:以下是本篇文章正文内容,下面案例中会有关于如何使用该框架的demo
Github项目地址:https://github.com/MaBo2420935619/framework
(1)、IOC的实现原理是反射,那么什么是反射,通过下面的例子来进行说明
简单创建一个类Student
package com.mabo; public class Student { private String name="mabo"; private int age=22; public String getName() { return name; } public int getAge() { return age; } }
通过反射来获取Student的对象
package com.mabo; public class StudentReflect { public static void main(String[] args) { Student student = null; try { Class<?> aClass = Class.forName("com.mabo.Student"); //利用无参构造方法反射生成对象 student = (Student)aClass.newInstance(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } int age = student.getAge(); String name = student.getName(); System.out.println(name); System.out.println(age); } }
执行结果
可以看到,在main方法中,没有使用new 关键字来生成对象,但是最终通过反射的方式帮我们生成了一个对象。
现在可以大概明白在Spring中xml文件注入的时候带包名和类名是用来经行反射生成bean对象的。并且我们也会常常在写的时候会给类写一个无参构造方法就是这个原因。
在该框架中,底层原理也是采用该方法来对Bean进行第三层缓存的初始化,初始化过程如下
(2)、HashMap用来存储Bean
bean的底层存储是用HashMap来进行存储的,这样可以保证一个Id,只对应一个bean
一个bean的生成过程经历大概以下的步骤
以上是一个类正常情况下的生成过程,但是有以下情况我们也会遇到
三层缓存解决循环依赖(只解决循环依赖实际上2层缓存足够)
这是BeanFactory的三层缓存,实际上就是三个Map用来存放对象
不考虑循环依赖中有AOP的情况
解决循环依赖的原理(默认A,B,C一直都没有被初始化)
假设A依赖B,B依赖C,C依赖A
在进行BeanFactory初始化的时候,
最后的效果如图
如果直接new出来A的对象再使用对象,对象的属性将是空指针
实现动态代理有两种方式JDK动态代理和CGLIB动态代理,两者的简单区别就是JDK动态代理的类必须实现了接口,而CGLIB不需要实现接口,但是CGLIB会加大系统开销,所以在Spring中两者都使用,有类实现接口就用JDK的动态代理,否则不用
该框架只使用了CGLIB的动态代理,其中需要
下面来看AOP的主要实现源码,可以看到,
public class SayHelloProxy implements MethodInterceptor { private Object target; public Object getInstance(Object target) { this.target = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.target.getClass()); // 设置回调方法 enhancer.setCallback(this); // 创建代理对象 return enhancer.create(); } /** * 实现MethodInterceptor接口中重写的方法 * * 回调方法 */ @Override public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("前置增强。。。"); Object result = proxy.invokeSuper(object, args); System.out.println("后置增强。。。"); return result; }
主要实现增强的地方是下图的位置,我们只需要结合Bean根据注解去遍历Bean,在类调用方法的前后,各做其他的事情,就可以实现对类的增强。
如何使用该增强类
public class SayHelloService { public void sayHello(){ System.out.println("Hello,我再执行sayHello()方法"); } public static void main(String[] args) { SayHelloService sayHelloService = new SayHelloService(); SayHelloProxy sayHelloProxy = new SayHelloProxy(); SayHelloService sayHelloService1 = (SayHelloService) sayHelloProxy.getInstance(sayHelloService); sayHelloService1.sayHello(); } }
测试结果,不仅仅执行了我们定义的方法,还在方法前后执行了其他的内容。
前文中提到,IOC在实现的时候,采用了三层缓存的原理,当IOC结合AOP的时候,实际上大部分情况也可以利用双重缓存解决。使用三层和缓存的原因是为了解决依赖循环中的类存在AOP的问题。
前面已经提到了循环依赖的解决过程。
下面再复制过来
三层缓存,就是在二级缓存的基础上,当一个bean在三级缓存中初始化过,不断递归去实现他的属性的时候,最后回到了去一级缓存中寻找自己bean,这个时候,我们就可以判断出来我们的bean注入过程 出现了循环依赖。如果只是出现循环依赖,上文中是可以解决的。下面讨论循环依赖中出现AOP的情况。
实现的结果就是下图,并且可以看到类A的前面出现$$符号,证明A类的字节码文件已经不是A本身了,而是代理类的字节码文件
定时器的原理比较简单,简单理解就是在所有的Bean完成初始化以后,利用多线程来经行定时执行遗传代码
下图可以看到定时器启动在BeanFatory的启动时在类完成初始化后执行的
采用异步线程的方式实现定时器,利用反射获取需要定时执行的方法
public class QuartzReflect { private static Log log=new Log(QuartzReflect.class); /** * @Author mabo * @Description 用于反射定时器 */ public static void reflect(){ List<Method> methods = InitInterface.annotationOnMethod(Quartz.class); for (Method method : methods) { method.setAccessible(true);//设置方法为可执行的 if (method.isAnnotationPresent(Quartz.class)) { Class declaringClass = method.getDeclaringClass(); String simpleName =null; Bean bean = (Bean)declaringClass.getAnnotation(Bean.class); if(bean==null||bean.value().equals("")){ simpleName = StringUtil.toLowerCaseFirstOne(declaringClass.getSimpleName()); }else{ simpleName=bean.value(); } Object o = BeanFactory.getBean(simpleName); //获取注解的接口 Quartz mt = method.getAnnotation(Quartz.class); //获取注解的参数 Object finalO = o; Runnable runnable = new Runnable() { public void run() { try { method.invoke(finalO); } catch (Exception e) { e.printStackTrace(); log.error(method.getName()+"()方法执行失败"); } } }; ScheduledExecutorService service = Executors .newSingleThreadScheduledExecutor(); // 第二个参数为首次执行的延时时间,第三个参数为定时执行的间隔时间 service.scheduleAtFixedRate(runnable, mt.waitSecond(), mt.time(), TimeUnit.SECONDS); } } log.info("多线程定时器初始化成功"); } }
这里所说的JavaWeb后端其实就是利用Socket通信,来实现和前端的交互。
使用socket的原因时不受请求类型的限制,并且可以自定义任意的请求类型和请求头内容
框架内已经实现了Socket的请求和反射过程,当我们需要对一各请求进行响应,只需要在方法上面加@RequestMaping注解,参数为请求类型,例如"/login",当前端发起了/login的请求,后端就会执行该注解下面的方法。
实现原理
启动socket通信,接收请求,解析请求的url和参数。
根据url地址,取查找对应的@RequestMaping注解的方法,并执行
将方法直接结果返回socket,发送给前端服务器(也可以时其他服务器)
反射执行方法的源码:
/** * @Author mabo * @Description * String requestMapping: RequestMapping接口的反射类的请求类型 * RequestType requestType: 发送的是什么类型的请求POST/GET/PUT */ public static Object getReflect(String requestMapping,RequestType requestType, Map map) { List<Method> methods = InitInterface.annotationOnMethod(RequestMapping.class); for (Method method : methods) { method.setAccessible(true);//设置方法为可执行的 if (method.isAnnotationPresent(RequestMapping.class)) { Class declaringClass = method.getDeclaringClass(); Object o = null; //获取注解的接口 RequestMapping mt = method.getAnnotation(RequestMapping.class); //获取注解的参数 String value = mt.value(); RequestType rt = mt.requestType(); if (requestMapping.equals(value)&&requestType.equals(rt)) { //实例化类 try { o = declaringClass.newInstance(); } catch (Exception e) { log.info(declaringClass.getName()+"()类实例化失败"); } //反射执行方法 try { //重要 //获取bean工厂的bean Bean annotation = o.getClass().getAnnotation(Bean.class); String value1 = annotation.value(); String name =null; if (!value1.equals("")) name=value1; else{ name = o.getClass().getSimpleName(); name = StringUtil.toLowerCaseFirstOne(name); } Object bean = BeanFactory.getBean(name); Object invoke = method.invoke(bean, map); log.info(method.getName()+"()方法执行成功"); return invoke; } catch (Exception e) { log.info(method.getName()+"()方法执行失败"); } } } } return null; }
利用postman向后端发起请求,和响应结果
后端响应结果
Github项目地址:https://github.com/MaBo2420935619/framework
数据库的配置文件
redis配置文件