six-finger-web
一个Web后端框架的轮子从处理Http请求【基于Netty的请求级Web服务器】 到mvc【接口封装转发)】,再到ioc【依赖注入】,aop【切面】,再到 rpc【远程过程调用】最后到orm【数据库操作】全部自己撸一个(简易)的轮子。
为啥要写这个轮子
其实是这样的,小六六自己平时呢?有时候喜欢看看人家的源码比如Spring,但是小六六的水平可能不怎么样,每次看都看得晕头转向,然后就感觉里面的细节太难了,然后我就只能观其总体的思想,然后我就想我如果可以根据各位前辈的一些思考,自己撸一个简单的轮子出来,那我后面去理解作者的思想是不是简单点呢?于是呢 six-finger-web就面世了,它其实就是我的一个学习过程,然后我把它开源出来,希望能帮助那些对于学习源码有困难的同学。还有就是可以锻炼一下自己的编码能力,因为平时我们总是crud用的Java api都是那些,久而久之,很多框架类的api我们根本就不熟练了,所以借此机会,锻炼一下。
特点
- 内置由 Netty 编写 HTTP 服务器,无需额外依赖 Tomcat 之类的 web 服务(刚好小六六把Netty系列写完,顺便用下)
- 代码简单易懂(小六六自己写不出框架大佬那种高类聚,低耦合的代码),能力稍微强一点看代码就能懂,弱点的也没关系,小六六有配套的从0搭建教程。
- 支持MVC相关的注解确保和SpringMVC的用法类似
- 支持Spring IOC 和Aop相关功能
- 支持类似于Mybatis相关功能
- 支持类似于Dubbo的rpc相关功能
- 对于数据返回,只支持Json格式
絮叨
前面是已经写好的章节,下面我给大家来一一走一遍搭建流程
- 适合初中级Java程序员修炼手册从0搭建整个Web项目(一)
- 适合初中级Java程序员修炼手册从0搭建整个Web项目(二)
- 适合初中级Java程序员修炼手册从0搭建整个Web项目(三)
- 适合初中级Java程序员修炼手册从0搭建整个Web项目(四)
我来总结一下前面我们已经完成的,我们已经完成了基于Netty的Http服务器,和springmvc spring ioc相关的功能,今天呢?我们尝试着来写写spring的aop
总的结构
上面就是写完之后的结构,我们下面来一一查看我们的过程
其实这边小六六遇到了一些问题,但是没能解决,就先这样了吧。
JoinPoint
通知方法的其中一个入参就是JoinPoint,通过它我们可以拿到当前被代理的对象、方法、参数等,甚至可以设置一个参数用于上下文传递,从接口方法就能判断出来了。
package com.xiaoliuliu.six.finger.web.spring.aop.aspect; import java.lang.reflect.Method; /** * @author 小六六 * @version 1.0 * @date 2020/10/26 16:26 *通知方法的其中一个入参就是<kbd>JoinPoint</kbd>,通过它我们可以拿到当前被代理的对象、方法、参数等,甚至可以设置一个参数用于上下文传递,从接口方法就能判断出来了。 */ public interface JoinPoint { Object getThis(); Object[] getArguments(); Method getMethod(); void setUserAttribute(String key, Object value); Object getUserAttribute(String key); } 复制代码
它的实现类就是前言中提到的外层拦截器对象,负责执行整个拦截器链,主要逻辑是:先遍历执行完拦截器链,最后才执行被代理的方法。这其实就是责任链模式的实现。
package com.xiaoliuliu.six.finger.web.spring.aop.intercept; import com.xiaoliuliu.six.finger.web.spring.aop.aspect.JoinPoint; import java.lang.reflect.Method; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; /** * @author 小六六 * @version 1.0 * @date 2020/10/26 17:10 */ public class MethodInvocation implements JoinPoint { /** * @author: linliangkun * @Date: 2020/10/26 17:12 * @Description: 被代理对象 */ private Object proxy; /**被代理的对象*/ private Object target; /**被代理对象的class*/ private Class<?> targetClass; /**被代理的方法*/ private Method method; /**被代理的方法的入参*/ private Object [] arguments; /**拦截器链*/ private List<Object> interceptorsAndDynamicMethodMatchers; /**用户参数*/ private Map<String, Object> userAttributes; /**记录当前拦截器执行的位置*/ private int currentInterceptorIndex = -1; public MethodInvocation(Object proxy, Object target, Method method, Object[] arguments, Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) { this.proxy = proxy; this.target = target; this.targetClass = targetClass; this.method = method; this.arguments = arguments; this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers; } @Override public Object getThis() { return this.target; } @Override public Object[] getArguments() { return this.arguments; } @Override public Method getMethod() { return this.method; } @Override public void setUserAttribute(String key, Object value) { if (value != null) { if (this.userAttributes == null) { this.userAttributes = new HashMap<>(); } this.userAttributes.put(key, value); } else { if (this.userAttributes != null) { this.userAttributes.remove(key); } } } @Override public Object getUserAttribute(String key) { return (this.userAttributes != null ? this.userAttributes.get(key) : null); } public Object proceed() throws Throwable{ //拦截器执行完了,最后真正执行被代理的方法 if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { return this.method.invoke(this.target,this.arguments); } //获取一个拦截器 Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); if (interceptorOrInterceptionAdvice instanceof MethodInterceptor) { MethodInterceptor mi = (MethodInterceptor) interceptorOrInterceptionAdvice; //执行通知方法 return mi.invoke(this); } else { //跳过,调用下一个拦截器 return proceed(); } } } 复制代码
MethodInterceptor
新建拦截器MethodInterceptor接口
package com.xiaoliuliu.six.finger.web.spring.aop.intercept; /** * @author 小六六 * @version 1.0 * @date 2020/10/26 17:19 */ public interface MethodInterceptor { Object invoke(MethodInvocation invocation) throws Throwable; } 复制代码
Advice
在实现拦截器MethodInterceptor的子类前,先新建Advice,作为不同通知方法的顶层接口。
package com.xiaoliuliu.six.finger.web.spring.aop.aspect; /** * @author 小六六 * @version 1.0 * @date 2020/10/26 17:22 * 在实现拦截器<kbd>MethodInterceptor</kbd>的子类前,先新建<kbd>Advice</kbd>,作为不同通知方法的顶层接口。 */ public interface Advice { } 复制代码
接着写一个抽象子类来封装不同的通知类型的共同逻辑:调用通知方法前先将入参赋值,这样用户在写通知方法时才能拿到实际值。
package com.xiaoliuliu.six.finger.web.spring.aop.aspect; import java.lang.reflect.Method; /** * @author 小六六 * @version 1.0 * @date 2020/10/26 17:23 */ public abstract class AbstractAspectAdvice implements Advice{ /**通知方法*/ private Method aspectMethod; /**切面类*/ private Object aspectTarget; public AbstractAspectAdvice(Method aspectMethod, Object aspectTarget) { this.aspectMethod = aspectMethod; this.aspectTarget = aspectTarget; } /** * 调用通知方法 */ public Object invokeAdviceMethod(JoinPoint joinPoint, Object returnValue, Throwable tx) throws Throwable { Class<?>[] paramTypes = this.aspectMethod.getParameterTypes(); if (null == paramTypes || paramTypes.length == 0) { return this.aspectMethod.invoke(aspectTarget); } else { Object[] args = new Object[paramTypes.length]; for (int i = 0; i < paramTypes.length; i++) { if (paramTypes[i] == JoinPoint.class) { args[i] = joinPoint; } else if (paramTypes[i] == Throwable.class) { args[i] = tx; } else if (paramTypes[i] == Object.class) { args[i] = returnValue; } } return this.aspectMethod.invoke(aspectTarget, args); } } } 复制代码
实现多种通知类型
拦截器本质上就是各种通知方法的封装,因此继承AbstractAspectAdvice,实现MethodInterceptor。下面分别实现前置通知、后置通知和异常通知。
前置通知
package com.xiaoliuliu.six.finger.web.spring.aop.aspect; /** * @author 小六六 * @version 1.0 * @date 2020/10/26 17:37 */ import com.xiaoliuliu.six.finger.web.spring.aop.intercept.MethodInterceptor; import com.xiaoliuliu.six.finger.web.spring.aop.intercept.MethodInvocation; import java.lang.reflect.Method; /** * 前置通知 */ public class MethodBeforeAdviceInterceptor extends AbstractAspectAdvice implements MethodInterceptor { private JoinPoint joinPoint; public MethodBeforeAdviceInterceptor(Method aspectMethod, Object aspectTarget) { super(aspectMethod, aspectTarget); } private void before(Method method, Object[] args, Object target) throws Throwable { super.invokeAdviceMethod(this.joinPoint, null, null); } @Override public Object invoke(MethodInvocation mi) throws Throwable { this.joinPoint = mi; //在调用下一个拦截器前先执行前置通知 before(mi.getMethod(), mi.getArguments(), mi.getThis()); return mi.proceed(); } } 复制代码
后置通知
package com.xiaoliuliu.six.finger.web.spring.aop.aspect; /** * @author 小六六 * @version 1.0 * @date 2020/10/26 17:40 */ import com.xiaoliuliu.six.finger.web.spring.aop.intercept.MethodInterceptor; import com.xiaoliuliu.six.finger.web.spring.aop.intercept.MethodInvocation; import java.lang.reflect.Method; /** * 后置通知 */ public class AfterReturningAdviceInterceptor extends AbstractAspectAdvice implements MethodInterceptor { private JoinPoint joinPoint; public AfterReturningAdviceInterceptor(Method aspectMethod, Object aspectTarget) { super(aspectMethod, aspectTarget); } @Override public Object invoke(MethodInvocation mi) throws Throwable { //先调用下一个拦截器 Object retVal = mi.proceed(); //再调用后置通知 this.joinPoint = mi; this.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis()); return retVal; } private void afterReturning(Object retVal, Method method, Object[] arguments, Object aThis) throws Throwable { super.invokeAdviceMethod(this.joinPoint, retVal, null); } } 复制代码
异常通知
package com.xiaoliuliu.six.finger.web.spring.aop.aspect; /** * @author 小六六 * @version 1.0 * @date 2020/10/26 17:40 */ import com.xiaoliuliu.six.finger.web.spring.aop.intercept.MethodInterceptor; import com.xiaoliuliu.six.finger.web.spring.aop.intercept.MethodInvocation; import java.lang.reflect.Method; /** * 异常通知 */ public class AfterThrowingAdviceInterceptor extends AbstractAspectAdvice implements MethodInterceptor { private String throwingName; public AfterThrowingAdviceInterceptor(Method aspectMethod, Object aspectTarget) { super(aspectMethod, aspectTarget); } @Override public Object invoke(MethodInvocation mi) throws Throwable { try { //直接调用下一个拦截器,如果不出现异常就不调用异常通知 return mi.proceed(); } catch (Throwable e) { //异常捕捉中调用通知方法 invokeAdviceMethod(mi, null, e.getCause()); throw e; } } public void setThrowName(String throwName) { this.throwingName = throwName; } } 复制代码