admin 管理员组

文章数量: 887031

用java实现纯注解Spring框架的部分内容(包括IOC,AOP,定时器,javaWeb)

提示:该框架只是对Spring的部分功能进行实现,并且没有考虑太多的细节。
只是为了对Spring框架的IOC,AOP实现原理进行更加深入的理解才决定用java来实现基于注解,实现无xml配置的一套框架。(其实Spring也支持无配置,全注解)。
文章中有些问题描述的解决方案可能描述的不够准确,甚至是错误的,欢迎各位指出。

用java实现纯注解Spring框架的部分内容[包括IOC,AOP,定时器,javaWeb后端]

  • 前言
    • 1、手写Spring框架IOC,AOP的目的
    • 2、该框架的实现内容
  • 一、源码下载地址(github)
  • 二、IOC--源码及其实现原理
    • 1、该框架IOC的实现原理
    • 2、注入一个Bean的全过程
  • 三、AOP--源码及其实现原理
    • 1、该框架AOP的实现原理
    • 2、AOP的测试Demo
  • 四、该框架对IOC和AOP整合实现原理及源码(该框架解决的核心问题)
    • 1、IOC和AOP整合的实现原理
    • 2、IOC和AOP整合的源码
  • 五、定时器--源码及其实现原理
    • 1、定时器的原理
    • 2、定时器的源码
  • 六、JavaWeb(处理http请求)--源码及其实现原理
    • 1、JavaWeb后端的实现原理
    • 2、演示示例
  • 七、其他


前言

1、手写Spring框架IOC,AOP的目的

  • 各个公司的框架几乎都离不开Spring框架,众所周知Spring框架几乎现在已经成为了Java事实上的标准,那么Spring框架到底为我们做了什么。
  • 不论是什么项目,为什么Spring可以做到不侵入,只需要引入该框架,Spring就可以帮你完成很多的东西,几乎所有场景都可以使用Spring框架。
  • 为什么Spring在进行配置文件编写的时候,类名前面写的是包名,为什么有的Bean有他的Id,这个Id在什么时候进行了使用。
  • 为什么三个类进行了循环依赖,用纯java在new对象的时候报错,用bean进行获取的时候就不会报错
  • 为什么类继承了另外一个类,要给他在配置文件注入的时候要加parent,否则在使用父类的public对象的时候会空指针呢。
    有了以上的问题,就有了下面的内容

2、该框架的实现内容

该框架主要实现了Spring框架的几个主要部分,并且进行了拓展

  1. IOC,即依赖注入,以bean工厂的方式,对所有被@Bean标注的的类在BeanFactory进行注册,使用时只需要在BeanFactory进行获取。
  2. AOP,即面向切面编程,通过不侵入式的编程,只需要对相关的类和方法加注解,就可实现对特定的类进行增强。
  3. 定时器,该框架是以多线程的方式,来实现定时任务。
  4. JavaWeb,使用原生的socket通信来进行通信,优点是可以支持各种类型请求。该框架定义了一个@RequestMaping注解,可以配合该框架来进行类似于Spring框架的后端框架的开发。

提示:以下是本篇文章正文内容

一、源码下载地址(github)

Github项目地址:

二、IOC–源码及其实现原理

1、该框架IOC的实现原理

(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

2、注入一个Bean的全过程

一个bean的生成过程经历大概以下的步骤

  • 利用该类,利用反射调用类的无参构造生成类对象,将该类放入三级缓存
  • 该类是否需要增强AOP,如果增强,该框架使用CGLIB的底层的字节码技术进行增强
  • 遍历所有生成的对象,利用反射,用类对象属性的set方法为对象注入属性
  • 该类成功实现,并且填充了属性,放入一级缓存,可以直接调用使用

以上是一个类正常情况下的生成过程,但是有以下情况我们也会遇到

  • 该类继承另外一个类,在填充属性的时候,优先实现父类,对子类实现填充的时候,调用父类的相关 属性的get,set方法进行填充。
  • 循环依赖的时候,将使用二级缓存的方式来进行实现,如果循环依赖的时候,涉及到AOP,需要三级缓存。

三层缓存解决循环依赖(只解决循环依赖实际上2层缓存足够)
这是BeanFactory的三层缓存,实际上就是三个Map用来存放对象

不考虑循环依赖中有AOP的情况
解决循环依赖的原理(默认A,B,C一直都没有被初始化)
假设A依赖B,B依赖C,C依赖A
在进行BeanFactory初始化的时候,

  1. 在一级缓存查找A,不存在,利用A的无参构造,通过反射的方式生成A对象,并且在三级缓存中注入bean A
  2. 对A进行属性的初始化,发现A依赖B,查找B发现不存在,通过反射的方式生成B对象,并且在三级缓存中注入beanB,
  3. 对B进行属性的初始化,发现B依赖C,查找C发现不存在,通过反射的方式生成C对象,并且在三级缓存中注入beanC,
  4. 对C进行初始化,发现C依赖A,一级缓存中不存在,将三级缓存中A注入到C的属性中,C初始化完成,将C保存到一级缓存
  5. C初始化完成,将C注入到B中,B初始化完成。
  6. B初始化完成,将B注入到A中,到这里A,B,C三个类的实例化对象都完成了。

最后的效果如图

如果直接new出来A的对象再使用对象,对象的属性将是空指针

三、AOP–源码及其实现原理

1、该框架AOP的实现原理

实现动态代理有两种方式JDK动态代理和CGLIB动态代理,两者的简单区别就是JDK动态代理的类必须实现了接口,而CGLIB不需要实现接口,但是CGLIB会加大系统开销,所以在Spring中两者都使用,有类实现接口就用JDK的动态代理,否则不用
该框架只使用了CGLIB的动态代理,其中需要

  • AOP的实现原理是依赖注入,有两种实现方式JDK和CGLIB
  • cglib实际上是继承了该类,如果同时该类需要代理,其方法必须是public修饰,否则子类无法实现正常代理
  • cglib是通过字节码技术创建这个类的子类,实现动态代理。
  • AOP的根本原理是新建了一个class文件,是继承了代理类,实现了对代理类的增强

下面来看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接口中重写的方法** 回调方法*/@Overridepublic 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,在类调用方法的前后,各做其他的事情,就可以实现对类的增强。

2、AOP的测试Demo

如何使用该增强类

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和AOP整合实现原理及源码(该框架解决的核心问题)

1、IOC和AOP整合的实现原理

前文中提到,IOC在实现的时候,采用了三层缓存的原理,当IOC结合AOP的时候,实际上大部分情况也可以利用双重缓存解决。使用三层和缓存的原因是为了解决依赖循环中的类存在AOP的问题。
前面已经提到了循环依赖的解决过程。
下面再复制过来

三层缓存,就是在二级缓存的基础上,当一个bean在三级缓存中初始化过,不断递归去实现他的属性的时候,最后回到了去一级缓存中寻找自己bean,这个时候,我们就可以判断出来我们的bean注入过程 出现了循环依赖。如果只是出现循环依赖,上文中是可以解决的。下面讨论循环依赖中出现AOP的情况。

  1. 当循环以来的类A又实现了AOP,那么就在发现出现循环依赖的时候,将A类的三级缓存的bean进行增强,生成代理类,放到二级缓存。
  2. 当C类依赖A的时候,就利用A列的增强类在 还未注入属性的时候,利用构造方法生成代理对象,给C。C初始化完成,放到一级缓存,
  3. C初始化完成,B也可以完成初始化。
  4. B完成初始化,A再去调用代理对象,注入属性b.
  5. 到这里,就完成了A,B,C循环依赖的注入,并且对A进行了增强

实现的结果就是下图,并且可以看到类A的前面出现$$符号,证明A类的字节码文件已经不是A本身了,而是代理类的字节码文件

2、IOC和AOP整合的源码

其中最重要的是initBeanByValue()方法

/*** @Author mabo* @Description   bean工厂(单例)* bean的扫描和初始化*/
public class BeanFactory {//系统日志private static Log log=new Log(BeanFactory.class);//扫描所有的类和方法private static List<InitClass> initAllClass;//扫描获取所有的beanprivate static List<InitClass> allBeans=new ArrayList<>();//标注当前bean被有依赖未成功创建/*** @Author mabo* @Description   一级缓存* 生成所有实例化完成的bean*/private static Map<String, Object>  firstBeans=new HashMap<>();/*** @Author mabo* @Description   二级缓存* 存放产生循环依赖的bean*/private static Map<String, Object>  secondBeans=new HashMap<>();/*** @Author mabo* @Description   存放刚刚生成实例,未完全初始化的bean*/private static Map<String, Object>  thirdBeans=new HashMap<>();/*** @Author mabo* @Description   beanFactory的初始化过程*/static {//遍历类文件initAllClass = InitClass.getInitAllClass();//获取所有被Bean注解的列getInitAllBeans();//利用三级缓存初始化beaninitBeans();//初始化定时器,并启动QuartzReflect.reflect();}/*** @Author mabo* @Description   获取bean的id*/private static String getBeanValue(Class clazz){if (clazz.isAnnotationPresent(Bean.class)){//是否被Bean标注Bean bean = (Bean) clazz.getAnnotation(Bean.class);//获取被注解的bean的id  valueString value = bean.value();if (value.equals("")){value = StringUtil.toLowerCaseFirstOne(clazz.getSimpleName());}return value;}return null;}/*** @Author mabo* @Description   三层缓存初始化bean*/private static void initBeans(){for (InitClass  initClass:allBeans) {Class clazz = initClass.getClazz();String value = getBeanValue(clazz);//如果为空,需要初始化,否则跳过if (firstBeans.get(value)==null){//通过id初始化beanObject o = initBeanByValue(value);if (firstBeans.get(value)==null){firstBeans.put(value,o);}}}}/*** @Author mabo* @Description 对需要进行增强bean经行增强*/public static Object strongerBean(Object bean){Class<?> clazz = bean.getClass();if (clazz.isAnnotationPresent(Aop.class)){Aop aop = clazz.getAnnotation(Aop.class);CreateCglibBeanProxy createBeanProxy=new CreateCglibBeanProxy();Object instance = createBeanProxy.getInstance(bean,aop);return instance;}return null;}/*** @Author mabo* @Description   根据value生成并返回bean实例*/private static Object initBeanByValue(String value){for (InitClass  initClass:allBeans) {//该Class是一个类if (!initClass.isAnInterface() && !initClass.isAnEnum() && !initClass.isAnAnnotation()) {Class clazz = initClass.getClazz();Field[] fields = initClass.getDeclaredFields();Method[] methods = initClass.getMethods();String beanValue = getBeanValue(clazz);//找value到对应的beanif (beanValue.equals(value)){//一级缓存中不存在bean,该bean需要初始化,开始if (firstBeans.get(value)==null){if (secondBeans.get(value)==null){Object o1 = thirdBeans.get(value);if (o1!=null){//三级缓存中存在bean,一级不存在,说名该bean被循环依赖//判断被循环的bean是否需要增强//先注入,再增强Object o = strongerBean(o1);if (o!=null){secondBeans.put(value,o);return o;}else {return o1;}}//三级缓存中都不存在该bean,去初始化该beanelse {//根据class生成实例Object object = createBeanByClass(clazz);//先生成父类及获取其相关属性getParentField(clazz,object);//立刻写入三级缓存//所有缓存都不存在该bean,向三级缓存写入beanthirdBeans.put(value,object);if (object!=null){//对bean注入属性for (Field field : fields) {//如果当前属性是一个bean,不是跳过if(field.isAnnotationPresent(SetBean.class)){SetBean annotation = field.getAnnotation(SetBean.class);String fieldId = annotation.value();//先判断一级级缓存是否存在if (firstBeans.get(fieldId)!=null){//存在,根据set方法,注入beanfor (Method method:methods) {if (method.getName().equals("set" + StringUtil.toUpperCaseFirstOne(fieldId))){try {method.invoke(object, firstBeans.get(fieldId));} catch (Exception e) {e.printStackTrace();log.error(method.getName()+"()方法执行失败");}}}}//一级缓存不存在,去根据id生成该bean,在进行set注入else{Object o = initBeanByValue(fieldId);if (secondBeans.get(fieldId)==null){firstBeans.put(fieldId,o);}//存在,根据set方法,注入beanfor (Method method:methods) {if (method.getName().equals("set" + StringUtil.toUpperCaseFirstOne(fieldId))){try {method.invoke(object, o);} catch (Exception e) {e.printStackTrace();log.error(method.getName()+"()方法执行失败");}}}}}}//如果出现循环依赖,重新获取进行注入if (secondBeans.get(value)!=null){initBeanByValue(value);}//如果二级缓存不存在,再写入该beanif (secondBeans.get(value)==null){if (firstBeans.get(value)==null){Object o = strongerBean(object);if (o!=null){firstBeans.put(value,o);}}}}return object;}}//二级缓存中存在改bean,去完成初始化else{Object object = secondBeans.get(value);Field[] fields1 = object.getClass().getFields();Method[] methods1 = object.getClass().getMethods();if (object!=null){//对bean注入属性for (Field field : fields1) {//如果当前属性是一个bean,不是跳过if(field.isAnnotationPresent(SetBean.class)){SetBean annotation = field.getAnnotation(SetBean.class);String fieldId = annotation.value();//先判断一级级缓存是否存在if (firstBeans.get(fieldId)!=null){//存在,根据set方法,注入beanfor (Method method:methods1) {if (method.getName().equals("set" + StringUtil.toUpperCaseFirstOne(fieldId))){try {method.invoke(object, firstBeans.get(fieldId));} catch (Exception e) {e.printStackTrace();log.error(method.getName()+"()方法执行失败");}}}}}}}//对二级缓存进行注入属性//注入后放到一级缓存secondBeans.remove(value);firstBeans.put(value,object);return object;}}//一级缓存中存在存在bean,该bean不需要初始化跳过else {Object o = firstBeans.get(value);Object o1 = strongerBean(o);if (o1!=null){return o1;}else return o;}}}}return null;}/*** @Author mabo* @Description   对父级bean进行初始化*/private static void getParentField(Class clazz, Object son){//该类有继承,先实现父类Object parentObject=null;if(clazz.isAnnotationPresent(Parent.class)){Parent parent = (Parent) clazz.getAnnotation(Parent.class);//获取被注解的bean的id  valueString parentValue = parent.value();parentObject = initBeanByValue(parentValue);firstBeans.put(parentValue,parentObject);}//获取父类的属性,给注入分类的属性子类//通过父类的set方法,给子类赋值//通过父类的get方法,获取属性值if (parentObject!=null){//获取子类的属性Field[] fields = clazz.getFields();//获取父类的方法Method[] methods = parentObject.getClass().getMethods();for (Field field: fields) {String fieldName = field.getName();Object getObject=null;//根据get方法,获取父类的属性值for (Method method:methods) {if (method.getName().equals("get" + StringUtil.toUpperCaseFirstOne(fieldName))){try {//获取到父类的属性值getObject = method.invoke(parentObject);} catch (Exception e) {e.printStackTrace();log.error(method.getName()+"()方法执行失败");}}}//如果获取到父类的属性值,调用父类的set方法给子类注入值if (getObject!=null){for (Method method:methods) {if (method.getName().equals("set" + StringUtil.toUpperCaseFirstOne(fieldName))){try {//调用父类的set方法对子类进行注入method.invoke(son,getObject);} catch (Exception e) {e.printStackTrace();log.error(method.getName()+"()方法执行失败");}}}}}}}/*** @Author mabo* @Description   根据class生成并返回bean实例*/private static Object createBeanByClass(Class clazz){Object o = null;try {o = clazz.newInstance();} catch (Exception e) {e.printStackTrace();log.error(clazz.getName()+"类生成错误,请检查是否有无参构造方法");}return o;}/*** @Author mabo* @Description   获取所有被bean标注的initClassBean*/private static void getInitAllBeans(){HashMap<String, Boolean> map = new HashMap<>();for (InitClass  initClass:initAllClass) {if (!initClass.isAnInterface()&&!initClass.isAnEnum()&&!initClass.isAnAnnotation()){Class clazz = initClass.getClazz();if(clazz.isAnnotationPresent(Bean.class)){Bean bean = (Bean) clazz.getAnnotation(Bean.class);String value = bean.value();if (value.equals("")){value = StringUtil.toLowerCaseFirstOne(clazz.getSimpleName());}if (map.get(value)==null){allBeans.add(initClass);map.put(value,true);}else {log.error("Bean"+clazz.getName()+"注入失败,原因是重复注入");allBeans=null;}}}}}/*** @Author mabo* @Description   根据id获取Bean*/public static Object getBean(String value){Object o = firstBeans.get(value);return o;}/*** @Author mabo* @Description   根据id获取写入bean*/public static void setBean(String value, Object object){firstBeans.put(value,object);}}

五、定时器–源码及其实现原理

1、定时器的原理

定时器的原理比较简单,简单理解就是在所有的Bean完成初始化以后,利用多线程来经行定时执行遗传代码
下图可以看到定时器启动在BeanFatory的启动时在类完成初始化后执行的

2、定时器的源码

采用异步线程的方式实现定时器,利用反射获取需要定时执行的方法

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(处理http请求)–源码及其实现原理

1、JavaWeb后端的实现原理

  1. 这里所说的JavaWeb后端其实就是利用Socket通信,来实现和前端的交互。

  2. 使用socket的原因时不受请求类型的限制,并且可以自定义任意的请求类型和请求头内容

  3. 框架内已经实现了Socket的请求和反射过程,当我们需要对一各请求进行响应,只需要在方法上面加@RequestMaping注解,参数为请求类型,例如"/login",当前端发起了/login的请求,后端就会执行该注解下面的方法。
    实现原理

  4. 启动socket通信,接收请求,解析请求的url和参数。

  5. 根据url地址,取查找对应的@RequestMaping注解的方法,并执行

  6. 将方法直接结果返回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工厂的beanBean 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;}

2、演示示例

启动后端服务器,只需要获取bean socketServer即可,执行startServer()服务器就启动成功了

public class WebTest {public static void main(String[] args) {//浏览器启动后输入 http://localhost:8888/login?uname=mabo&upwd=123456//就可以看到请求成功,证明web项目建立成功SocketServer server= (SocketServer) BeanFactory.getBean("socketServer");server.startServer();}
}

新建一个处理 /login请求的方法


/*** @Author mabo* @Description   在这里写请求方法*/
@Bean("loginController")
public class LoginController{private Log log=new Log(LoginController.class);@SetBean("loginService")private LoginService loginService;public void setLoginService(LoginService loginService) {this.loginService = loginService;}/*** @Author mabo* @Description   登录操作*/@RequestMapping("/login")public Object login(Map map){String uname = MapUtil.getMapString(map,"uname");String upwd = MapUtil.getMapString(map,"upwd");JSONObject json=new JSONObject();json.put("收到的姓名为:",uname);json.put("收到的密码为:",upwd);System.out.println("即将返回给前端的数据为: "+json);return  JSONObject.toJSON(json);}
}

利用postman向后端发起请求,返回了响应结果

后端响应结果

七、其他

Github项目地址:

  • 如何使用该框架可以去github下载具体的代码作为参考
  • 配置文件内相关内容都做了注释,非常容易理解

  • 数据库的配置文件

  • redis配置文件

  • 该框架也内置了一些用的工具类,可以进行使用

本文标签: 用java实现纯注解Spring框架的部分内容(包括IOC AOP 定时器 javaWeb)