提示:该框架只是对的部分功能进行实现,并且没有考虑太多的细节 。
只是为了对框架的IOC,AOP实现原理进行更加深入的理解才决定用java来实现基于注解,实现无xml配置的一套框架 。(其实也支持无配置,全注解) 。
文章中有些问题描述的解决方案可能描述的不够准确,甚至是错误的,欢迎各位指出 。
用java实现纯注解框架的部分内容[包括IOC,AOP,定时器,后端]一、源码下载地址()二、IOC--源码及其实现原理 三、AOP--源码及其实现原理 四、该框架对IOC和AOP整合实现原理及源码(该框架解决的核心问题) 五、定时器--源码及其实现原理 六、(处理http请求)--源码及其实现原理 七、其他
前言 1、手写框架IOC,AOP的目的2、该框架的实现内容
该框架主要实现了框架的几个主要部分,并且进行了拓展
IOC,即依赖注入,以bean工厂的方式,对所有被@Bean标注的的类在进行注册 , 使用时只需要在进行获取 。AOP,即面向切面编程,通过不侵入式的编程 , 只需要对相关的类和方法加注解,就可实现对特定的类进行增强 。定时器,该框架是以多线程的方式,来实现定时任务 。,使用原生的通信来进行通信 , 优点是可以支持各种类型请求 。该框架定义了一个@注解,可以配合该框架来进行类似于框架的后端框架的开发 。
【用java实现纯注解Spring框架的部分内容(包括IOC,AOP,定时器】提示:以下是本篇文章正文内容
一、源码下载地址()
项目地址:
二、IOC–源码及其实现原理 1、该框架IOC的实现原理
(1)、IOC的实现原理是反射,那么什么是反射,通过下面的例子来进行说明
简单创建一个类
package com.mabo;public class Student {private String name="mabo";private int age=22;public String getName() {return name;}public int getAge() {return age;}}
通过反射来获取的对象
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 关键字来生成对象,但是最终通过反射的方式帮我们生成了一个对象 。
现在可以大概明白在中xml文件注入的时候带包名和类名是用来经行反射生成bean对象的 。并且我们也会常常在写的时候会给类写一个无参构造方法就是这个原因 。
在该框架中,底层原理也是采用该方法来对Bean进行第三层缓存的初始化
(2)、用来存储Bean
bean的底层存储是用来进行存储的,这样可以保证一个Id,只对应一个bean
2、注入一个Bean的全过程
一个bean的生成过程经历大概以下的步骤
以上是一个类正常情况下的生成过程,但是有以下情况我们也会遇到
三层缓存解决循环依赖(只解决循环依赖实际上2层缓存足够)
这是的三层缓存,实际上就是三个Map用来存放对象
不考虑循环依赖中有AOP的情况
解决循环依赖的原理(默认A , B,C一直都没有被初始化)
假设A依赖B,B依赖C,C依赖A
在进行初始化的时候,
在一级缓存查找A,不存在,利用A的无参构造,通过反射的方式生成A对象,并且在三级缓存中注入bean A对A进行属性的初始化,发现A依赖B,查找B发现不存在 , 通过反射的方式生成B对象,并且在三级缓存中注入beanB,对B进行属性的初始化,发现B依赖C,查找C发现不存在,通过反射的方式生成C对象,并且在三级缓存中注入beanC,对C进行初始化,发现C依赖A,一级缓存中不存在,将三级缓存中A注入到C的属性中,C初始化完成,将C保存到一级缓存C初始化完成,将C注入到B中,B初始化完成 。B初始化完成,将B注入到A中,到这里A,B,C三个类的实例化对象都完成了 。
最后的效果如图
如果直接new出来A的对象再使用对象,对象的属性将是空指针
三、AOP–源码及其实现原理 1、该框架AOP的实现原理
实现动态代理有两种方式JDK动态代理和CGLIB动态代理,两者的简单区别就是JDK动态代理的类必须实现了接口,而CGLIB不需要实现接口,但是CGLIB会加大系统开销 , 所以在中两者都使用,有类实现接口就用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接口中重写的方法** 回调方法*/@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的情况 。
当循环以来的类A又实现了AOP , 那么就在发现出现循环依赖的时候,将A类的三级缓存的bean进行增强,生成代理类 , 放到二级缓存 。当C类依赖A的时候,就利用A列的增强类在 还未注入属性的时候,利用构造方法生成代理对象,给C 。C初始化完成,放到一级缓存 , C初始化完成 , B也可以完成初始化 。B完成初始化 , A再去调用代理对象,注入属性b.到这里,就完成了A,B,C循环依赖的注入,并且对A进行了增强
实现的结果就是下图,并且可以看到类A的前面出现$$符号,证明A类的字节码文件已经不是A本身了,而是代理类的字节码文件
2、IOC和AOP整合的源码
其中最重要的是()方法
/*** @Author mabo* @Descriptionbean工厂(单例)* bean的扫描和初始化*/public class BeanFactory {//系统日志private static Log log=new Log(BeanFactory.class);//扫描所有的类和方法private static List
文章插图
五、定时器–源码及其实现原理 1、定时器的原理
定时器的原理比较简单 , 简单理解就是在所有的Bean完成初始化以后,利用多线程来经行定时执行遗传代码
下图可以看到定时器启动在的启动时在类完成初始化后执行的
2、定时器的源码
采用异步线程的方式实现定时器,利用反射获取需要定时执行的方法
public class QuartzReflect {private static Log log=new Log(QuartzReflect.class);/*** @Author mabo* @Description用于反射定时器*/public static void reflect(){List 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("多线程定时器初始化成功");}}
六、(处理http请求)–源码及其实现原理 1、后端的实现原理
这里所说的后端其实就是利用通信,来实现和前端的交互 。
使用的原因时不受请求类型的限制,并且可以自定义任意的请求类型和请求头内容
框架内已经实现了的请求和反射过程,当我们需要对一各请求进行响应,只需要在方法上面加@注解,参数为请求类型,例如"/login",当前端发起了/login的请求,后端就会执行该注解下面的方法 。
实现原理
启动通信,接收请求,解析请求的url和参数 。
根据url地址 , 取查找对应的@注解的方法,并执行
将方法直接结果返回,发送给前端服务器(也可以时其他服务器)
反射执行方法的源码:
/*** @Author mabo* @Description* String requestMapping: RequestMapping接口的反射类的请求类型* RequestType requestType: 发送的是什么类型的请求POST/GET/PUT*/public static Object getReflect(String requestMapping,RequestType requestType, Map map) {List 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 = http://www.kingceram.com/post/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 即可,执行()服务器就启动成功了
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);returnJSONObject.toJSON(json);}}
利用向后端发起请求,返回了响应结果
后端响应结果
七、其他
项目地址:
- 中药七厘子的功效与作用 七厘子中药有什么功效
- 川乌的功效与作用及禁忌 中药制川乌的功效与作用及禁忌
- 胡黄连的功效与作用及禁忌
- 升麻的功效与作用及禁忌 升麻的功效与作用
- 大皂角的功效与作用的功能与主治 大皂角的功效与作用
- 用蒲公英怎么洗脸 蒲公英洗脸方法
- 毛毡布的用途 毛毡布作用
- 潜精研思用法 潜精研思是什么意思
- 哪种月季可以吃 哪种月季不能吃
- 鹌鹑用途与价值有哪些 鹌鹑有什么价值