30个类仿真手写spring框架V2.0版本

相关内容:
架构师系列内容:架构师学习笔记(持续更新)
一步一步手绘Spring IOC运行时序图一(Spring 核心容器 IOC初始化过程)
一步一步手绘Spring IOC运行时序图二(基于XML的IOC容器初始化)
一步一步手绘Spring IOC运行时序图三(基于Annotation的IOC容器初始化)
一步一步手绘Spring DI运行时序图(Spring 自动装配之依赖注入)
一步一步手绘Spring AOP运行时序图(Spring AOP 源码分析)
一步一步手绘Spring MVC运行时序图(Spring MVC原理)
Spring MVC 使用优化建议
模仿SpringMVC的DispatcherServlet 手撸300行代码提炼精华设计思想并保证功能可用(1.0版本)
必读,代码只是模仿spring的一个大致设计思路,很多细节问题并没有考虑到,代码本身还存在很多的问题,只能供参考一下。

从 Servlet 到 ApplicationContext

我们已经了解 SpringMVC 的入口是 DispatcherSerlvet,我们实现了 DispatcherServlet 的 init()方法。在 init()方法中完成了 IOC 容器的初始化。而在我们使用Spring 的经验中,我们见得最多的是 ApplicationContext,似乎 Spring 托管的所有实例 Bean 都可以通过调用 getBean()方法来获得。那么 ApplicationContext 又是从何而来的呢?从 Spring 源码中我们可以看到,DispatcherServlet 的类图如下:
在这里插入图片描述
DispatcherServlet 继 承 了 FrameworkServlet , FrameworkServlet 继 承 了 HttpServletBean ,HttpServletBean 继承了 HttpServlet。在 HttpServletBean 的 init()方法中调用了 FrameworkServlet的 initServletBean()方法,在 initServletBean()方法中初始化 WebApplicationContext 实例。在initServletBean()方法中调用了 DispatcherServlet 重写的 onRefresh()方法。在 DispatcherServlet的 onRefresh()方法中又调用了 initStrategies()方法,初始化 SpringMVC 的九大组件。其实,上面复杂的调用关系,我们可以简单的得出一个结论:就是在 Servlet 的 init 方法中初始化了 IOC容器和 SpringMVC 所依赖的九大组件。

高仿真的类关系图
在这里插入图片描述

项目环境搭建

application.properties 配置
还是先从 application.properties 文件开始 , 用 application.properties 来代替application.xml,具体配置如下

#IOC部分  扫描包路径
scanPackage=com.jarvisy.mvc2.demo


#MVC部分
#页面模板路径
templateRoot=layouts


#AOP部分
#切面表达式#
pointCut=public .* com.jarvisy.mvc2.demo.service..*Service..*(.*)
#切面类#
aspectClass=com.jarvisy.mvc2.demo.aspect.LogAspect
#切面前置通知#
aspectBefore=before
#切面后置通知#
aspectAfter=after
#切面异常通知#
aspectAfterThrow=afterThrowing
#切面异常类型#
aspectAfterThrowingName=java.lang.Exception

pom.xml 配置
引入所需的依赖就可以了 此代码依赖了lombok 插件,请注意。

<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>4.0.0-b01</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.18.12</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>1.8.0-alpha0</version>
  <scope>test</scope>
</dependency>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">


    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>com.jarvisy.mvc2.framework.webmvc.servlet.MyDispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:application.properties</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>


    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>


</web-app>

IOC 和DI部分

annotation(自定义配置)模块
注解完整代码

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAutowired {
    String value() default "";
}
//----------------------------分割线,不同类---------------------------------------
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyController {
    String value() default "";
}
//----------------------------分割线,不同类---------------------------------------
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestMapping {
    String value() default "";
}
//----------------------------分割线,不同类---------------------------------------
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestParam {
    String value() default "";
}
//----------------------------分割线,不同类---------------------------------------
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyService {
    String value() default "";
}

core(顶层接口)模块
MyBeanFactory,MyFactoryBean完整代码

/**
* @author :Jarvisy
* @date :Created in 2020/10/20 22:13
* @description :单例工厂得顶层设计
*/
public interface MyBeanFactory {
    /**
     * 根据beanName从IOC容器中获得一个实例Bean
     *
     * @param beanName
     * @return
     */
    Object getBean(String beanName)  throws Exception;
    Object getBean(Class<?> beanClass)  throws Exception;


}

/**
* @author :Jarvisy
* @date :Created in 2020/10/20 22:13
* @description :  这里没有用到
*/
public interface MyFactoryBean {
}

beans(配置封装)模块
MyBeanDefinition完整代码

//用来存储配置文件中的信息
//相当于保存在内存中的配置
public class MyBeanDefinition {
    private String beanClassName;
    private boolean lazyInit = false;
    private String factoryBeanName;


    public String getBeanClassName() {
        return beanClassName;
    }


    public void setBeanClassName(String beanClassName) {
        this.beanClassName = beanClassName;
    }


    public boolean isLazyInit() {
        return lazyInit;
    }


    public void setLazyInit(boolean lazyInit) {
        this.lazyInit = lazyInit;
    }


    public String getFactoryBeanName() {
        return factoryBeanName;
    }


    public void setFactoryBeanName(String factoryBeanName) {
        this.factoryBeanName = factoryBeanName;
    }
}

MyBeanWrapper完整代码

/**
* @author :Jarvisy
* @date :Created in 2020/10/21 0:08
* @description :
*/
public class MyBeanWrapper {
    private Object wrappedInstance;
    private Class<?> wrappedClass;


    public MyBeanWrapper(Object wrappedInstance) {
        this.wrappedInstance = wrappedInstance;
    }


    public Object getWrappedInstance() {
        return this.wrappedInstance;
    }


    // 返回代理以后的Class
    // 可能会是这个 $Proxy0
    public Class<?> getWrappedClass() {
        return this.wrappedInstance.getClass();
    }
}

context(IOC 容器)模块
MyAbstractApplicationContext完整代码

/**
* @author :Jarvisy
* @date :Created in 2020/10/20 22:19
* @description :IOC容器实现得顶层设计
*/
public abstract class MyAbstractApplicationContext {
    /**
     * 受保护得,只提供给子类进行重写
     */
    public void refresh() throws Exception{
    }
}

MyDefaultListableBeanFactory完整代码

/**
* @author :Jarvisy
* @date :Created in 2020/10/20 22:27
* @description :
*/
public class MyDefaultListableBeanFactory extends MyAbstractApplicationContext {
    //存储注册信息的BeanDefinition
    protected final Map<String, MyBeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, MyBeanDefinition>();


}

MyApplicationContext完整代码

/**
* Created on Jarvisy by 2020/10/24
*/
public class MyApplicationContext extends MyDefaultListableBeanFactory implements MyBeanFactory {


    private String[] configLocations;
    private MyBeanDefinitionReader reader;


    //单例的IOC容器缓存
    private Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>();
    //通用的IOC容器
    private Map<String, MyBeanWrapper> factoryBeanInstanceCache = new ConcurrentHashMap<>();


    public MyApplicationContext(String... configLocations) {
        this.configLocations = configLocations;
        try {
            refresh();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public Object getBean(Class<?> beanClass) throws Exception {
        return getBean(beanClass.getName());
    }


    //依赖注入,从这里开始,通过读取BeanDefinition中的信息
    //然后,通过反射机制创建一个实例并返回
    //Spring做法是,不会把最原始的对象放出去,会用一个BeanWrapper来进行一次包装
    //装饰器模式:
    //1、保留原来的OOP关系
    //2、我需要对它进行扩展,增强(为了以后AOP打基础)
    @Override
    public Object getBean(String beanName) throws Exception {
        //1、初始化 初始化和注入不一起是为了解决循环依赖的问题
        MyBeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
        Object instance = null;
        //bean初始化前的通知和初始化后的通知
        MyBeanPostProcessor postProcessor = new MyBeanPostProcessor();
        postProcessor.postProcessBeforeInitialization(instance, beanName);


        instance = instantiateBean(beanName, beanDefinition);
        // 3、把这个对象封装到BeanWrapper
        MyBeanWrapper beanWrapper = new MyBeanWrapper(instance);


        // 4、拿到BeanWrapper之后,把BeanWrapper保存到IOC容器中去
        this.factoryBeanInstanceCache.put(beanName, beanWrapper);
        postProcessor.postProcessAfterInitialization(instance, beanName);
        // 5、注入
        populateBean(beanName, new MyBeanDefinition(), beanWrapper);
        return this.factoryBeanInstanceCache.get(beanName).getWrappedInstance();
    }


    private void populateBean(String beanName, MyBeanDefinition beanDefinition, MyBeanWrapper beanWrapper) {
        Object instance = beanWrapper.getWrappedInstance();
        Class<?> clazz = beanWrapper.getWrappedClass();
        //判断只有加了注解的类,才执行依赖注入
        if (!(clazz.isAnnotationPresent(MyController.class) || clazz.isAnnotationPresent(MyService.class))) return;
        //获得所有的fields
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (!field.isAnnotationPresent(MyAutowired.class)) continue;
            MyAutowired autowired = field.getAnnotation(MyAutowired.class);
            String autowiredBeanName = autowired.value().trim();
            if ("".equals(autowiredBeanName)) autowiredBeanName = field.getType().getName();
            //强制访问
            field.setAccessible(true);
            try {
                if (this.factoryBeanInstanceCache.get(autowiredBeanName) == null) continue;
                field.set(instance, this.factoryBeanInstanceCache.get(autowiredBeanName).getWrappedInstance());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }


        }
    }


    private Object instantiateBean(String beanName, MyBeanDefinition beanDefinition) {
        // 1、拿到要实例化的对象的类名
        String className = beanDefinition.getBeanClassName();
        // 2、反射实例化,得到一个对象
        Object instance = null;
        try {
            //假设默认就是单例,细节暂且不考虑,先把主线拉通
            if (this.factoryBeanObjectCache.containsKey(className))
                instance = this.factoryBeanObjectCache.get(className);
            else {
                Class<?> clazz = Class.forName(className);
                instance = clazz.newInstance();


                // AOP部分 start
                MyAdvisedSupport config = instantiateAopConfig(beanDefinition);
                config.setTargetClass(clazz);
                config.setTarget(instance);


                //符合PointCut的规则的话,闯将代理对象
                if (config.pointCutMatch())  instance = createProxy(config).getProxy();


                // AOP部分 end


                this.factoryBeanObjectCache.put(className, instance);
                this.factoryBeanObjectCache.put(beanDefinition.getFactoryBeanName(), instance);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }


        return instance;


    }


    @Override
    public void refresh() throws Exception {
        //1、定位配置文件
        reader = new MyBeanDefinitionReader(this.configLocations);


        //2、加载配置文件,扫描相关得类,把它们封装成BeanDefinition
        List<MyBeanDefinition> beanDefinitions = reader.loadBeanDefinitions();


        //3、注册,把配置信息放到容器里面(伪IOC容器)
        doRegisterBeanDefinition(beanDefinitions);
        //4、把不是延时加载得类,提前初始化
        doAutowired();
    }


    //只处理非延时加载的情况
    private void doAutowired() {
        for (Map.Entry<String, MyBeanDefinition> beanDefinitionEntry : super.beanDefinitionMap.entrySet()) {
            String beanName = beanDefinitionEntry.getKey();
            if (!beanDefinitionEntry.getValue().isLazyInit()) {
                try {
                    getBean(beanName);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }


    }


    private void doRegisterBeanDefinition(List<MyBeanDefinition> beanDefinitions) throws Exception {
        for (MyBeanDefinition beanDefinition : beanDefinitions) {
            if (super.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())) {
                throw new Exception("The “" + beanDefinition.getFactoryBeanName() + "” is exists!!");
            }
            super.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition);
        }
        //到这里为止,容器初始化完毕


    }


    public String[] getBeanDefinitionNames() {
        return this.beanDefinitionMap.keySet().toArray(new String[this.beanDefinitionMap.size()]);
    }


    public int getBeanDefinitionCount() {
        return this.beanDefinitionMap.size();
    }


    public Properties getConfig() {
        return this.reader.getConfig();
    }


    private MyAdvisedSupport instantiateAopConfig(MyBeanDefinition gpBeanDefinition) {
        MyAopConfig config = new MyAopConfig();
        config.setPointCut(this.reader.getConfig().getProperty("pointCut"));
        config.setAspectClass(this.reader.getConfig().getProperty("aspectClass"));
        config.setAspectBefore(this.reader.getConfig().getProperty("aspectBefore"));
        config.setAspectAfter(this.reader.getConfig().getProperty("aspectAfter"));
        config.setAspectAfterThrow(this.reader.getConfig().getProperty("aspectAfterThrow"));
        config.setAspectAfterThrowingName(this.reader.getConfig().getProperty("aspectAfterThrowingName"));
        return new MyAdvisedSupport(config);
    }


    private MyAopProxy createProxy(MyAdvisedSupport config) {


        Class targetClass = config.getTargetClass();
        if (targetClass.getInterfaces().length > 0) return new MyJdkDynamicAopProxy(config);
        return new MyCglibAopProxy(config);
    }
}

MyBeanDefinitionReader完整代码

/**
* @author :Jarvisy
* @date :Created in 2020/10/20 23:22
* @description :
*/
public class MyBeanDefinitionReader {
    private List<String> registerBeanClasses = new ArrayList<String>();


    private Properties config = new Properties();


    //固定配置文件中的key,相对于xml的规范
    private final String SCAN_PACKAGE = "scanPackage";


    public MyBeanDefinitionReader(String... locations) {
        //通过URL定位找到其所对应的文件,然后转换为文件流


        try (InputStream is = this.getClass().getClassLoader().getResourceAsStream(locations[0].replace("classpath:", ""))) {
            config.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
        doScanner(config.getProperty(SCAN_PACKAGE));
    }


    private void doScanner(String scanPackage) {
        //转换为文件路径,实际上就是把.替换为/就OK了
        URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));
        File classPath = new File(url.getFile());
        for (File file : classPath.listFiles()) {
            if (file.isDirectory()) doScanner(scanPackage + "." + file.getName());
            else {
                if (!file.getName().endsWith(".class")) continue;
                String className = (scanPackage + "." + file.getName().replace(".class", ""));
                registerBeanClasses.add(className);
            }
        }
    }


    public Properties getConfig() {
        return this.config;
    }


    public List<MyBeanDefinition> loadBeanDefinitions() {
        List<MyBeanDefinition> result = new ArrayList<MyBeanDefinition>();
        try {
            for (String className : registerBeanClasses) {
                Class<?> beanClass = Class.forName(className);
                //如果是一个接口,是不能实例化的
                //用它实现类来实例化
                if (beanClass.isInterface()) continue;


                //beanName有三种情况:
                //1、默认是类名首字母小写
                //2、自定义名字
                //3、接口注入
                result.add(doCreateBeanDefinition(toLowerFirstCase(beanClass.getSimpleName()), beanClass.getName()));
                result.add(doCreateBeanDefinition(beanClass.getName(),beanClass.getName()));
                Class<?>[] interfaces = beanClass.getInterfaces();
                for (Class<?> i : interfaces) {
                    //如果是多个实现类,只能覆盖
                    //为什么?因为Spring没那么智能,就是这么傻
                    //这个时候,可以自定义名字
                    result.add(doCreateBeanDefinition(i.getName(), beanClass.getName()));
                }


            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
    //把配置文件中扫描到得所有配置信息解析成一个BeanDefinition对象,以便于之后IOC操作
    private MyBeanDefinition doCreateBeanDefinition(String factoryBeanName, String beanClassName) {
        MyBeanDefinition beanDefinition = new MyBeanDefinition();
        beanDefinition.setBeanClassName(beanClassName);
        beanDefinition.setFactoryBeanName(factoryBeanName);
        return beanDefinition;
    }


    //转换首字母小写
    private String toLowerFirstCase(String beanName) {
        return beanName.replaceFirst("^.", String.valueOf(beanName.charAt(0)).toLowerCase());
    }
}

MyApplicationContextAware完整代码

/**
* @author :Jarvisy
* @date :Created in 2020/10/20 22:48
* @description :
* 通过解耦方式获得IOC容器的顶层设计
* 后面将通过一个监听器去扫描所有的类,只要实现了此接口,
* 将自动调用setApplicationContext()方法,从而将IOC容器注入到目标类中
*/
public interface MyApplicationContextAware {
    void setApplicationContext(MyApplicationContext applicationContext);
}

完成 DI 依赖注入功能

依赖注入的入口是从 getBean()方法开始的,前面的 IOC 手写部分基本流程已通。先在 GPApplicationContext 中定义好 IOC 容器,一个是 GPBeanWrapper,一个是单例对象缓存
完整代码从上面找

public class MyApplicationContext extends MyDefaultListableBeanFactory implements MyBeanFactory {


    private String[] configLocations;
    private MyBeanDefinitionReader reader;


    //单例的IOC容器缓存
    private Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>();
    //通用的IOC容器
    private Map<String, MyBeanWrapper> factoryBeanInstanceCache = new ConcurrentHashMap<>();
}

从getBean()开始
完善 getBean()方法 完整代码从上面找

//依赖注入,从这里开始,通过读取BeanDefinition中的信息
//然后,通过反射机制创建一个实例并返回
//Spring做法是,不会把最原始的对象放出去,会用一个BeanWrapper来进行一次包装
//装饰器模式:
//1、保留原来的OOP关系
//2、我需要对它进行扩展,增强(为了以后AOP打基础)
@Override
public Object getBean(String beanName) throws Exception {
    //1、初始化 初始化和注入不一起是为了解决循环依赖的问题
    MyBeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
    Object instance = null;
    //bean初始化前的通知和初始化后的通知
    MyBeanPostProcessor postProcessor = new MyBeanPostProcessor();
    postProcessor.postProcessBeforeInitialization(instance, beanName);


    instance = instantiateBean(beanName, beanDefinition);
    // 3、把这个对象封装到BeanWrapper
    MyBeanWrapper beanWrapper = new MyBeanWrapper(instance);


    // 4、拿到BeanWrapper之后,把BeanWrapper保存到IOC容器中去
    this.factoryBeanInstanceCache.put(beanName, beanWrapper);
    postProcessor.postProcessAfterInitialization(instance, beanName);
    // 5、注入
    populateBean(beanName, new MyBeanDefinition(), beanWrapper);
    return this.factoryBeanInstanceCache.get(beanName).getWrappedInstance();
}

MyBeanPostProcessor完整代码

/**
* Created on Jarvisy by 2020/10/26
*/
public class MyBeanPostProcessor {


    public Object postProcessBeforeInitialization(Object bean, String beanName) throws Exception {
        return bean;
    }


    public Object postProcessAfterInitialization(Object bean, String beanName) throws Exception {
        return bean;
    }
}

DI 部分完成

MVC 部分

MyDispatcherServlet完整代码

package com.jarvisy.mvc2.framework.webmvc.servlet;


import com.jarvisy.mvc2.framework.annotation.MyController;
import com.jarvisy.mvc2.framework.annotation.MyRequestMapping;
import com.jarvisy.mvc2.framework.context.MyApplicationContext;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerExceptionResolver;


/**
* Created on Jarvisy by 2020/10/28
*/
@Slf4j
public class MyDispatcherServlet extends HttpServlet {


    private final String CONTEXT_CONFIG_LOCATION = "contextConfigLocation";


    private MyApplicationContext context;




    private List<MyHandlerMapping> handlerMappings = new ArrayList<>();
    //
    private Map<MyHandlerMapping, MyHandlerAdapter> handlerAdapters = new HashMap<>();
    //
    private List<MyViewResolver> viewResolvers = new ArrayList<MyViewResolver>();


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }


    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            this.doDispatcher(req, resp);
        } catch (Exception e) {
            resp.getWriter().write("500 Exception,Details:\r\n" + Arrays.toString(e.getStackTrace()).replaceAll("\\[|\\]", "").replaceAll(",\\s", "\r\n"));
            e.printStackTrace();


        }
    }


    private void doDispatcher(HttpServletRequest request, HttpServletResponse response) throws Exception {
        //1、通过从request中拿到URL,去匹配一个HandlerMapping
        MyHandlerMapping handler = getHandler(request);
        if (null == handler) {
            processDispatchResult(request, response, new MyModelAndView("404"));
            return;
        }
        //2、准备调用前的参数
        MyHandlerAdapter ha = getHandleAdapter(handler);
        //3、真正的调用方法,返回ModelAndView存储了要穿页面上值,和页面模板的名称
        MyModelAndView mv = ha.handle(request, response, handler);
        //这一步才是真正的输出
        processDispatchResult(request, response, mv);


    }


    private MyHandlerAdapter getHandleAdapter(MyHandlerMapping handler) {
        if (this.handlerAdapters.isEmpty()) return null;
        MyHandlerAdapter ha = this.handlerAdapters.get(handler);
        if (ha.supports(handler)) return ha;
        return null;


    }


    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, MyModelAndView mv) throws Exception {
        //把给我的ModelAndView变成一个HTML、OutputStream、json、freemarker、velocity
        //ContextType
        if (null == mv || this.viewResolvers.isEmpty()) return;
//
//        //如果ModelAndView不为null,怎么办?
        for (MyViewResolver viewResolver : this.viewResolvers) {
            MyView view = viewResolver.resolveViewName(mv.getViewName(), null);
            if (view != null) {
                view.render(mv.getModel(), request, response);
                return;
            }
        }
        // do something
    }


    private MyHandlerMapping getHandler(HttpServletRequest req) throws Exception {
        if (this.handlerMappings.isEmpty()) return null;


        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replace(contextPath, "").replaceAll("/+", "/");


        for (MyHandlerMapping handler : this.handlerMappings) {
            Matcher matcher = handler.getPattern().matcher(url);
            //如果没有匹配上继续下一个匹配
            if (!matcher.matches()) continue;
            return handler;


        }
        return null;
    }




    @Override
    public void init(ServletConfig config) throws ServletException {
        //1、初始化ApplicationContext
        context = new MyApplicationContext(config.getInitParameter(CONTEXT_CONFIG_LOCATION));
        //2、初始化Spring MVC 九大组件
        initStrategies(context);
    }


    //初始化策略
    protected void initStrategies(MyApplicationContext context) {
        //有九种策略
        // 针对于每个用户请求, 都会经过一些处理的策略之后, 最终才能有结果输出
        // 每种策略可以自定义干预, 但是最终的结果都是一致
        // ============= 这里说的就是传说中的九大组件 ================
        initMultipartResolver(context);//文件上传解析, 如果请求类型是 multipart 将通过MultipartResolver 进行文件上传解析
        initLocaleResolver(context);//本地化解析
        initThemeResolver(context);//主题解析
        /** 我们自己会实现 */
        //GPHandlerMapping 用来保存 Controller 中配置的 RequestMapping 和 Method 的一个对应关系
        initHandlerMappings(context);//通过 HandlerMapping, 将请求映射到处理器
        /** 我们自己会实现 */
        //HandlerAdapters 用来动态匹配 Method 参数, 包括类转换, 动态赋值
        initHandlerAdapters(context);//通过 HandlerAdapter 进行多类型的参数动态匹配
        initHandlerExceptionResolvers(context);//如果执行过程中遇到异常, 将交给HandlerExceptionResolver 来解析
        initRequestToViewNameTranslator(context);//直接解析请求到视图名
        /** 我们自己会实现 */
        //通过 ViewResolvers 实现动态模板的解析
        //自己解析一套模板语言
        initViewResolvers(context);//通过 viewResolver 解析逻辑视图到具体视图实现
        initFlashMapManager(context);//flash 映射管理器
    }


    private void initFlashMapManager(MyApplicationContext context) { }


    private void initViewResolvers(MyApplicationContext context) {
        //解决页面名字和模板文件关联的问题
        String templateRoot = context.getConfig().getProperty("templateRoot");
        String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();
        File templateRootDir = new File(templateRootPath);
        String[] templates = templateRootDir.list();
        for (int i = 0; i < templates.length; i++) {
            //这里主要是为了兼容多模板,所有模仿Spring用List保存
            //在代码中简化了,其实只有需要一个模板就可以搞定,这里只是为了仿真,所有还是搞了个List
            this.viewResolvers.add(new MyViewResolver(templateRoot));
        }
    }


    private void initRequestToViewNameTranslator(MyApplicationContext context) {


    }


    private void initHandlerExceptionResolvers(MyApplicationContext context) {
    }


    private void initHandlerAdapters(MyApplicationContext context) {
        //把一个request请求变成一个handler,参数都是字符串的,自动配到handler中的形参


        //可想而知,他要拿到HandlerMapping才能干活
        //就意味着,有几个HandlerMapping就有几个HandlerAdapter


        //在初始化阶段, 我们能做的就是, 将这些参数的名字或者类型按一定的顺序保存下来
        //因为后面用反射调用的时候, 传的形参是一个数组
        //可以通过记录这些参数的位置 index,挨个从数组中填值, 这样的话, 就和参数的顺序无关了
        for (MyHandlerMapping handlerMapping : this.handlerMappings) {
            //每一个方法有一个参数列表, 那么这里保存的是形参列表
            this.handlerAdapters.put(handlerMapping, new MyHandlerAdapter());
        }


    }
    //将 Controller 中配置的 RequestMapping 和 Method 进行一一对应
    private void initHandlerMappings(MyApplicationContext context) {
        //按照我们通常的理解应该是一个 Map
        //Map<String,Method> map;
        //map.put(url,Method)


        //首先从容器中取到所有的实例
        String[] beanNames = context.getBeanDefinitionNames();
        try {
            for (String beanName : beanNames) {
                //到了 MVC 层, 对外提供的方法只有一个 getBean 方法
                Object controller = context.getBean(beanName);
                Class<?> clazz = controller.getClass();
                if (!clazz.isAnnotationPresent(MyController.class)) continue;
                String baseUrl = "";
                //获取Controller的url配置
                if (clazz.isAnnotationPresent(MyRequestMapping.class)) {
                    MyRequestMapping requestMapping = clazz.getAnnotation(MyRequestMapping.class);
                    baseUrl = requestMapping.value();
                }


                //扫描所有的 public 方法
                //获取Method的url配置
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {


                    //没有加RequestMapping注解的直接忽略
                    if (!method.isAnnotationPresent(MyRequestMapping.class)) continue;


                    //映射URL
                    MyRequestMapping requestMapping = method.getAnnotation(MyRequestMapping.class);


                    String regex = ("/" + baseUrl + "/" + requestMapping.value().replaceAll("\\*", ".*")).replaceAll("/+", "/");
                    Pattern pattern = Pattern.compile(regex);


                    this.handlerMappings.add(new MyHandlerMapping(pattern, controller, method));
                    log.info("Mapped " + regex + "," + method);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }


    }


    private void initThemeResolver(MyApplicationContext context) {


    }


    private void initLocaleResolver(MyApplicationContext context) {


    }


    private void initMultipartResolver(MyApplicationContext context) {


    }




}

MyHandlerMapping完整代码

/**
* Created on Jarvis.y by 2020/10/28
*/
public class MyHandlerMapping {
    private Object controller;    //保存方法对应的实例
    private Method method;        //保存映射的方法
    private Pattern pattern;    //URL的正则匹配
    public MyHandlerMapping(Pattern pattern, Object controller, Method method) {
        this.controller = controller;
        this.method = method;
        this.pattern = pattern;
    }
    public Object getController() {
        return controller;
    }
    public void setController(Object controller) {
        this.controller = controller;
    }
    public Method getMethod() {
        return method;
    }
    public void setMethod(Method method) {
        this.method = method;
    }
    public Pattern getPattern() {
        return pattern;
    }
    public void setPattern(Pattern pattern) {
        this.pattern = pattern;
    }

}

MyHandlerAdapter完整代码

/**
* Created on Jarvisy by 2020/10/28
*/
public class MyHandlerAdapter {
    public boolean supports(Object handler) {
        return (handler instanceof MyHandlerMapping);
    }


    //进行数据类型转换
    //Spring 做了顶层转换策略  public interface Converter<S, T> 实现了很多转换类型
    private Object convert(Class<?> type, String[] value) {
        //如果是int
        if (Integer.class == type) return Integer.valueOf(value[0]);
        else if (Integer[].class == type) return null; // do something
        else if (String.class == type) return value[0];
        else if (String[].class == type) return value;
        //如果还有double或者其他类型,继续加if
        //这时候,我们应该想到策略模式了,在这里暂时不实现
        return value;
    }


    MyModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        MyHandlerMapping handlerMapping = (MyHandlerMapping) handler;
        //把方法的形参列表和request的参数列表所在顺序进行一一对应
        //每一个方法有一个参数列表, 那么这里保存的是形参列表
        Map<String, Integer> paramIndexMapping = new HashMap<String, Integer>();


        Method method = handlerMapping.getMethod();


        //获取方法参数的真实名称
        DefaultParameterNameDiscoverer discover = new DefaultParameterNameDiscoverer();
        String[] parameterNames = discover.getParameterNames(method);


        //提取方法中加了注解的参数


        //根据用户请求的参数信息, 跟 method 中的参数信息进行动态匹配
        //resp 传进来的目的只有一个: 只是为了将其赋值给方法参数, 仅此而已
        //只有当用户传过来的 ModelAndView 为空的时候, 才会 new 一个默认的
        //1、 要准备好这个方法的形参列表
        //方法重载: 形参的决定因素: 参数的个数、 参数的类型、 参数顺序、 方法的名字
        //只处理 Request 和 Response


        Annotation[][] pa = method.getParameterAnnotations();
        Class<?>[] paramTypes = method.getParameterTypes();
        for (int i = 0; i < paramTypes.length; i++) {
            Class<?> type = paramTypes[i];
            if (type == HttpServletRequest.class || type == HttpServletResponse.class) {
                paramIndexMapping.put(type.getName(), i);
                continue;
            }
            String paramName = "";
            for (Annotation a : pa[i]) {
                if (a instanceof MyRequestParam) {
                    paramName = ((MyRequestParam) a).value();
                    break;
                }
            }
            if ("".equals(paramName)) paramName = parameterNames[i];
            paramIndexMapping.put(paramName, i);
        }
        //2、 拿到自定义命名参数所在的位置
        //用户通过 URL 传过来的参数列表
        Map<String, String[]> params = request.getParameterMap();
        //3、 构造实参列表
        Object[] paramValues = new Object[paramTypes.length];
        for (Map.Entry<String, String[]> param : params.entrySet()) {
            if (!paramIndexMapping.containsKey(param.getKey())) continue;
            Integer index = paramIndexMapping.get(param.getKey());
            //因为页面上传过来的值都是 String 类型的, 而在方法中定义的类型是千变万化的
            //要针对我们传过来的参数进行类型转换
            paramValues[index] = convert(paramTypes[index], param.getValue());
        }


        if (paramIndexMapping.containsKey(HttpServletRequest.class.getName()))
            paramValues[paramIndexMapping.get(HttpServletRequest.class.getName())] = request;
        if (paramIndexMapping.containsKey(HttpServletResponse.class.getName()))
            paramValues[paramIndexMapping.get(HttpServletResponse.class.getName())] = response;
        //4、 从 handler 中取出 controller、 method, 然后利用反射机制进行调用
        Object result = method.invoke(handlerMapping.getController(), paramValues);
        if (result == null || result instanceof Void) return null;


        boolean isModelAndView = method.getReturnType() == MyModelAndView.class;
        if (isModelAndView) return (MyModelAndView) result;
        return null;
    }


}

MyModelAndView完整代码

/**
* Created on Jarvis.y by 2020/10/28
*/
public class MyModelAndView {
    private String viewName;
    private Map<String, ?> model;
    public MyModelAndView(String viewName) {
        this.viewName = viewName;
    }
    public MyModelAndView(String viewName, Map<String, ?> model) {
        this.viewName = viewName;
        this.model = model;
    }
    public String getViewName() {
        return viewName;
    }
    public Map<String, ?> getModel() {
        return model;
    }
}

MyViewResolver完整代码

/**
* Created on Jarvisy by 2020/10/28
* 设计这个类的主要目的是:
* 1、 讲一个静态文件变为一个动态文件
* 2、 根据用户传送参数不同, 产生不同的结果
* 最终输出字符串, 交给 Response 输出
*/
public class MyViewResolver {
    private final String DEFAULT_TEMPLATE_SUFFIX = ".html";
    private File templateRootDir;
    public MyViewResolver(String templateRoot) {
        String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();
        templateRootDir = new File(templateRootPath);
    }
    public MyView resolveViewName(String viewName, Locale locale) throws Exception {
        if (null == viewName || "".equals(viewName.trim()))  return null;
        viewName = viewName.endsWith(DEFAULT_TEMPLATE_SUFFIX) ? viewName : (viewName + DEFAULT_TEMPLATE_SUFFIX);
        File templateFile = new File((templateRootDir.getPath() + "/" + viewName).replaceAll("/+", "/"));
        return new MyView(templateFile);
    }
}

MyView完整代码

/**
* Created on Jarvisy by 2020/10/28
*/
public class MyView {
    public final String DEFAULT_CONTENT_TYPE = "text/html;charset=utf-8";
    private File viewFile;

    public MyView(File viewFile) {
        this.viewFile = viewFile;
    }
    public void render(Map<String, ?> model,
                       HttpServletRequest request, HttpServletResponse response) throws Exception {
        StringBuffer sb = new StringBuffer();
        RandomAccessFile ra = new RandomAccessFile(this.viewFile, "r");
        String line = null;
        while (null != (line = ra.readLine())) {
            line = new String(line.getBytes("ISO-8859-1"), "utf-8");
            Pattern pattern = Pattern.compile("¥\\{[^\\}]+\\}", Pattern.CASE_INSENSITIVE);
            Matcher matcher = pattern.matcher(line);
            while (matcher.find()) {
                String paramName = matcher.group();
                paramName = paramName.replaceAll("¥\\{|\\}", "");
                Object paramValue = model.get(paramName);
                if (null == paramValue)  continue;
                //要把¥{}中间的这个字符串给取出来
                line = matcher.replaceFirst(makeStringForRegExp(paramValue.toString()));
                matcher = pattern.matcher(line);
            }
            sb.append(line);
        }


        response.setCharacterEncoding("utf-8");
//        response.setContentType(DEFULAT_CONTENT_TYPE);
        response.getWriter().write(sb.toString());
    }
    //处理特殊字符  可能存在有问题
    // FIXME: 2020/10/31 这个正则可能存在问题
    public static String makeStringForRegExp(String str) {
        return str.replace("\\", "\\\\").replace("*", "\\*")
                .replace("+", "\\+").replace("|", "\\|")
                .replace("{", "\\{").replace("}", "\\}")
                .replace("(", "\\(").replace(")", "\\)")
                .replace("^", "\\^").replace("$", "\\$")
                .replace("[", "\\[").replace("]", "\\]")
                .replace("?", "\\?").replace(",", "\\,")
                .replace(".", "\\.").replace("&", "\\&");
    }
}

业务代码实现

MyAction完整代码

/**
* 公布接口url
*/
@MyController
@MyRequestMapping("/web")
public class MyAction {


    @MyAutowired
    IQueryService queryService;
    @MyAutowired
    IModifyService modifyService;


    @MyRequestMapping("/query.json")
    public MyModelAndView query(HttpServletRequest request, HttpServletResponse response,
                                @MyRequestParam("name") String name) {
        String result = queryService.query(name);
        return out(response, result);
    }


    @MyRequestMapping("/add*.json")
    public MyModelAndView add(HttpServletRequest request, HttpServletResponse response,
                              @MyRequestParam("name") String name, @MyRequestParam("addr") String addr) {
        String result = null;
        try {
            result = modifyService.add(name, addr);
            return out(response, result);
        } catch (Exception e) {
//       e.printStackTrace();
            Map<String, Object> model = new HashMap<String, Object>();
            model.put("detail", e.getMessage());
//       System.out.println(Arrays.toString(e.getStackTrace()).replaceAll("\\[|\\]",""));
            model.put("stackTrace", Arrays.toString(e.getStackTrace()).replaceAll("\\[|\\]", ""));
            return new MyModelAndView("500", model);
        }
    }


    @MyRequestMapping("/remove.json")
    public void remove(HttpServletRequest request, HttpServletResponse response,
                       @MyRequestParam("id") Integer id) {
        String result = modifyService.remove(id);
        out(response, result);
    }


    @MyRequestMapping("/edit.json")
    public void edit(HttpServletRequest request, HttpServletResponse response,
                     @MyRequestParam("id") Integer id,
                     @MyRequestParam("name") String name) {
        String result = modifyService.edit(id, name);
        out(response, result);
    }




    private MyModelAndView out(HttpServletResponse resp, String str) {
        try {
            resp.getWriter().write(str);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }


}

IModifyService,IQueryService完整代码

//增删改业务
public interface IModifyService {
    //增加
    public String add(String name, String addr)  throws Exception;
    //修改
    public String edit(Integer id, String name);
    //删除
    public String remove(Integer id);
}

//查询业务
public interface IQueryService {
    //查询
    public String query(String name);
}

ModifyService,QueryService完整代码

@MyService
public class ModifyService implements IModifyService {
    public String add(String name, String addr) throws Exception {
        throw new Exception("这是故意抛的异常!!");
    }
    public String edit(Integer id, String name) {
        return "modifyService edit,id=" + id + ",name=" + name;
    }
    public String remove(Integer id) {
        return "modifyService id=" + id;
    }
}

@MyService
@Slf4j
public class QueryService implements IQueryService {
    
    public String query(String name) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String time = sdf.format(new Date());
        String json = "{name:\"" + name + "\",time:\"" + time + "\"}";
        log.info("这是在业务方法中打印的:" + json);
        return json;
    }
}

定制模板页面

在resources 目录下新建layouts文件夹加入以下文件
fisrst.html

<!DOCTYPE html>
<html lang="zh-cn">
<head>
   <meta charset="utf-8">
   <title>demo</title>
</head>
<center>
   <h1>大家好,我是¥{teacher}<br/>欢迎大家一起来探索Spring的世界</h1>
   <h3>Hello,My name is ¥{teacher}</h3>
   <div>¥{data}</div>
   Token值:¥{token}
</center>
</html>

500.html

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="utf-8">
    <title>服务器好像累了</title>
</head>
<body>
    <font size='25' color='blue'>500 服务器好像有点累了,需要休息一下</font><br/>
    <b>Message:¥{detail}</b><br/>
    <b>StackTrace:¥{stackTrace}</b><br/>
    <br/><font color='green'><i>jarvisy@www.jarvisy.com</i></font>
</body>
</html>

400.html

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="utf-8">
    <title>页面去火星了</title>
</head>
<body>
    <font size='25' color='red'>404 Not Found</font><br/><font color='green'><i>jarvisy@www.jarvisy.com</i></font>
</body>
</html>

mvc部分完成 至此可以运行看效果了

AOP部分

在 application.properties 中增加如下自定义配置

#AOP部分
#切面表达式#
pointCut=public .* com.jarvisy.mvc2.demo.service..*Service..*(.*)
#切面类#
aspectClass=com.jarvisy.mvc2.demo.aspect.LogAspect
#切面前置通知#
aspectBefore=before
#切面后置通知#
aspectAfter=after
#切面异常通知#
aspectAfterThrow=afterThrowing
#切面异常类型#
aspectAfterThrowingName=java.lang.Exception

MyAopProxy完整代码

/**
* Created on Jarvisy by 2020/10/29
*/
public interface MyAopProxy {
    Object getProxy();
    Object getProxy(ClassLoader classLoader);
}

MyCglibAopProxy完整代码 未实现逻辑

public class MyCglibAopProxy implements MyAopProxy {
    public MyCglibAopProxy(MyAdvisedSupport config) {
    }
    @Override
    public Object getProxy() {
        return null;
    }
    @Override
    public Object getProxy(ClassLoader classLoader) {
        return null;
    }
}

MyJdkDynamicAopProxy完整代码

public class MyJdkDynamicAopProxy implements MyAopProxy, InvocationHandler {


    private MyAdvisedSupport advised;


    public MyJdkDynamicAopProxy(MyAdvisedSupport config) {
        this.advised = config;
    }

    @Override
    public Object getProxy() {
        return getProxy(this.advised.getTargetClass().getClassLoader());
    }

    @Override
    public Object getProxy(ClassLoader classLoader) {
        return Proxy.newProxyInstance(classLoader, this.advised.getTargetClass().getInterfaces(), this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        List<Object> interceptorsAndDynamicMethodMatchers = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, this.advised.getTargetClass());
        MyMethodInvocation invocation = new MyMethodInvocation(proxy, this.advised.getTarget(), method, args, this.advised.getTargetClass(), interceptorsAndDynamicMethodMatchers);
        return invocation.proceed();
    }
}

MyAdvisedSupport完整代码

/**
* Created on Jarvisy by 2020/10/29
*/
public class MyAdvisedSupport {
    private Class<?> targetClass;
    private Object target;
    private MyAopConfig config;
    private Pattern pointCutClassPattern;
    private transient Map<Method, List<Object>> methodCache;


    public MyAdvisedSupport(MyAopConfig config) {
        this.config = config;
    }


    public Class<?> getTargetClass() {
        return this.targetClass;
    }


    public void setTargetClass(Class<?> targetClass) {
        this.targetClass = targetClass;
        parse();
    }


    private void parse() {
        String pointCut = config.getPointCut().replaceAll("\\.", "\\\\.")
                .replaceAll("\\\\.\\*", ".*").replaceAll("\\(", "\\\\(").replaceAll("\\)", "\\\\)");
        String pointCutForClassRegex = pointCut.substring(0, pointCut.lastIndexOf("\\(") - 4);
        pointCutClassPattern = Pattern.compile("class " + pointCutForClassRegex.substring(
                pointCutForClassRegex.lastIndexOf(" ") + 1));


        try {
            methodCache = new HashMap<Method, List<Object>>();
            Pattern pattern = Pattern.compile(pointCut);


            Class aspectClass = Class.forName(this.config.getAspectClass());
            Map<String, Method> aspectMethods = new HashMap<String, Method>();
            for (Method m : aspectClass.getMethods()) {
                aspectMethods.put(m.getName(), m);
            }


            for (Method m : this.targetClass.getMethods()) {
                String methodString = m.toString();
                if (methodString.contains("throws"))
                    methodString = methodString.substring(0, methodString.lastIndexOf("throws")).trim();


                Matcher matcher = pattern.matcher(methodString);
                if (matcher.matches()) {
                    //执行器链
                    List<Object> advices = new LinkedList<Object>();
                    //把每一个方法包装成 MethodInterceptor
                    //before
                    if (!(null == config.getAspectBefore() || "".equals(config.getAspectBefore()))) {
                        //创建一个Advice
                        advices.add(new MyMethodBeforeAdviceInterceptor(aspectMethods.get(config.getAspectBefore()), aspectClass.newInstance()));
                    }
                    //after
                    if (!(null == config.getAspectAfter() || "".equals(config.getAspectAfter()))) {
                        advices.add(new MyAfterReturningAdviceInterceptor(aspectMethods.get(config.getAspectAfter()), aspectClass.newInstance()));
                    }
                    //afterThrowing
                    if (!(null == config.getAspectAfterThrow() || "".equals(config.getAspectAfterThrow()))) {
                        MyAfterThrowingAdviceInterceptor throwingAdvice =
                                new MyAfterThrowingAdviceInterceptor(
                                        aspectMethods.get(config.getAspectAfterThrow()),
                                        aspectClass.newInstance());
                        throwingAdvice.setThrowName(config.getAspectAfterThrowingName());
                        advices.add(throwingAdvice);
                    }
                    methodCache.put(m, advices);
                }


            }
        } catch (Exception e) {
            e.printStackTrace();
        }


    }


    public Object getTarget() {
        return this.target;
    }


    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) throws Exception {
        List<Object> cached = methodCache.get(method);
        if (cached == null) {
            Method m = targetClass.getMethod(method.getName(), method.getParameterTypes());


            cached = methodCache.get(m);


            //底层逻辑,对代理方法进行一个兼容处理
            this.methodCache.put(m, cached);
        }


        return cached;
    }


    public void setTarget(Object target) {
        this.target = target;
    }


    public boolean pointCutMatch() {
        return pointCutClassPattern.matcher(this.targetClass.toString()).matches();
    }
}

MyMethodInvocation完整代码

/**
* Created on Jarvisy by 2020/10/29
*/
public class MyMethodInvocation implements MyJoinPoint {
    private Object proxy;
    private Method method;
    private Object target;
    private Object[] arguments;
    private List<Object> interceptorsAndDynamicMethodMatchers;
    private Class<?> targetClass;


    private Map<String, Object> userAttributes;


    //定义一个索引,从-1开始来记录当前拦截器执行的位置
    private int currentInterceptorIndex = -1;


    public MyMethodInvocation(Object proxy, Object target, Method method, Object[] arguments, Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {
        this.proxy = proxy;
        this.method = method;
        this.target = target;
        this.arguments = arguments;
        this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
        this.targetClass = targetClass;
    }


    public Object proceed() throws Throwable {
        //如果Interceptor执行完了,则执行joinPoint
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
            return this.method.invoke(this.target, this.arguments);
        }


        Object interceptorOrInterceptionAdvice =
                this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
        //如果要动态匹配joinPoint
        if (interceptorOrInterceptionAdvice instanceof MyMethodInterceptor) {
            MyMethodInterceptor mi =
                    (MyMethodInterceptor) interceptorOrInterceptionAdvice;
            return mi.invoke(this);
        } else {
            //动态匹配失败时,略过当前Interceptor,调用下一个Interceptor
            return proceed();
        }
    }


    @Override
    public Object getThis() {
        return this.target;
    }


    @Override
    public Object[] getArguments() {
        return this.arguments;
    }


    @Override
    public Method getMethod() {
        return this.method;
    }


    public void setUserAttribute(String key, Object value) {
        if (value != null) {
            if (this.userAttributes == null) {
                this.userAttributes = new HashMap<String,Object>();
            }
            this.userAttributes.put(key, value);
        }
        else {
            if (this.userAttributes != null) {
                this.userAttributes.remove(key);
            }
        }
    }




    public Object getUserAttribute(String key) {
        return (this.userAttributes != null ? this.userAttributes.get(key) : null);
    }
}

MyMethodInterceptor完整代码

public interface MyMethodInterceptor {
    Object invoke(MyMethodInvocation invocation) throws Throwable;
}

MyAdvice完整代码

public interface MyAdvice {
}

MyJoinPoint完整代码

public interface MyJoinPoint {


    Object getThis();


    Object[] getArguments();


    Method getMethod();


    void setUserAttribute(String key, Object value);


    Object getUserAttribute(String key);
}

MyAopConfig完整代码

@Data
public class MyAopConfig {
    private String pointCut;
    private String aspectBefore;
    private String aspectAfter;
    private String aspectClass;
    private String aspectAfterThrow;
    private String aspectAfterThrowingName;
}

MyAbstractAspectAdvice完整代码

/**
* Created on Jarvisy by 2020/10/30
*/
public abstract class MyAbstractAspectAdvice implements MyAdvice {


    private Method aspectMethod;
    private Object aspectTarget;


    public MyAbstractAspectAdvice(Method aspectMethod, Object aspectTarget) {
        this.aspectMethod = aspectMethod;
        this.aspectTarget = aspectTarget;
    }


    public Object invokeAdviceMethod(MyJoinPoint 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] == MyJoinPoint.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);
        }
    }
}

MyMethodBeforeAdviceInterceptor完整代码

//前置通知
public class MyMethodBeforeAdviceInterceptor extends MyAbstractAspectAdvice implements MyAdvice, MyMethodInterceptor {
    private MyJoinPoint joinPoint;
    public MyMethodBeforeAdviceInterceptor(Method aspectMethod, Object aspectTarget) {
        super(aspectMethod, aspectTarget);
    }
    private void before(Method method, Object[] args, Object target) throws Throwable {
        //传送了给织入参数
        //method.invoke(target);
        super.invokeAdviceMethod(this.joinPoint, null, null);
    }
    @Override
    public Object invoke(MyMethodInvocation mi) throws Throwable {
        //从被织入的代码中才能拿到,JoinPoint
        this.joinPoint = mi;
        before(mi.getMethod(), mi.getArguments(), mi.getThis());
        return mi.proceed();
    }
}

MyAfterReturningAdviceInterceptor完整代码

// 后置通知
public class MyAfterReturningAdviceInterceptor extends MyAbstractAspectAdvice implements MyAdvice, MyMethodInterceptor {
    private MyJoinPoint joinPoint;
    public MyAfterReturningAdviceInterceptor(Method aspectMethod, Object aspectTarget) {
        super(aspectMethod, aspectTarget);
    }
    @Override
    public Object invoke(MyMethodInvocation 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);
    }
}

MyAfterThrowingAdviceInterceptor完整代码

//异常通知
public class MyAfterThrowingAdviceInterceptor extends MyAbstractAspectAdvice implements MyAdvice, MyMethodInterceptor {
    private String throwingName;
    public MyAfterThrowingAdviceInterceptor(Method aspectMethod, Object aspectTarget) {
        super(aspectMethod, aspectTarget);
    }
    @Override
    public Object invoke(MyMethodInvocation 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;
    }
}

接入 getBean()方法, 找到 GPApplicationContext 的 getBean()方法,我们知道 getBean()中负责 Bean 初始化的方法其实就是 instantiateBean(),我们在初始化时就可以确定是否返回原生 Bean 还是 Proxy Bean。代码实现

private Object instantiateBean(String beanName, MyBeanDefinition beanDefinition) {
    // 1、拿到要实例化的对象的类名
    String className = beanDefinition.getBeanClassName();
    // 2、反射实例化,得到一个对象
    Object instance = null;
    try {
        //假设默认就是单例,细节暂且不考虑,先把主线拉通
        if (this.factoryBeanObjectCache.containsKey(className))
            instance = this.factoryBeanObjectCache.get(className);
        else {
            Class<?> clazz = Class.forName(className);
            instance = clazz.newInstance();
            // AOP部分 start
            MyAdvisedSupport config = instantiateAopConfig(beanDefinition);
            config.setTargetClass(clazz);
            config.setTarget(instance);
            //符合PointCut的规则的话,闯将代理对象
            if (config.pointCutMatch())  instance = createProxy(config).getProxy();
            // AOP部分 end
            this.factoryBeanObjectCache.put(className, instance);
            this.factoryBeanObjectCache.put(beanDefinition.getFactoryBeanName(), instance);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return instance;
}

private MyAdvisedSupport instantiateAopConfig(MyBeanDefinition gpBeanDefinition) {
    MyAopConfig config = new MyAopConfig();
    config.setPointCut(this.reader.getConfig().getProperty("pointCut"));
    config.setAspectClass(this.reader.getConfig().getProperty("aspectClass"));
    config.setAspectBefore(this.reader.getConfig().getProperty("aspectBefore"));
    config.setAspectAfter(this.reader.getConfig().getProperty("aspectAfter"));
    config.setAspectAfterThrow(this.reader.getConfig().getProperty("aspectAfterThrow"));
    config.setAspectAfterThrowingName(this.reader.getConfig().getProperty("aspectAfterThrowingName"));
    return new MyAdvisedSupport(config);
}


private MyAopProxy createProxy(MyAdvisedSupport config) {


    Class targetClass = config.getTargetClass();
    if (targetClass.getInterfaces().length > 0) return new MyJdkDynamicAopProxy(config);
    return new MyCglibAopProxy(config);
}

业务代码

LogAspect完整代码

@Slf4j
public class LogAspect {
    //在调用一个方法之前,执行before方法
    public void before(MyJoinPoint joinPoint) {
        joinPoint.setUserAttribute("startTime_" + joinPoint.getMethod().getName(), System.currentTimeMillis());
        //这个方法中的逻辑,是由我们自己写的
        log.info("Invoker Before Method!!!" +
                "\nTargetObject:" + joinPoint.getThis() +
                "\nArgs:" + Arrays.toString(joinPoint.getArguments()));
    }
    //在调用一个方法之后,执行after方法
    public void after(MyJoinPoint joinPoint) {
        log.info("Invoker After Method!!!" +
                "\nTargetObject:" + joinPoint.getThis() +
                "\nArgs:" + Arrays.toString(joinPoint.getArguments()));
        long startTime = (Long) joinPoint.getUserAttribute("startTime_" + joinPoint.getMethod().getName());
        long endTime = System.currentTimeMillis();
        System.out.println("use time :" + (endTime - startTime));
    }
    public void afterThrowing(MyJoinPoint joinPoint, Throwable ex) {
        log.info("出现异常" +
                "\nTargetObject:" + joinPoint.getThis() +
                "\nArgs:" + Arrays.toString(joinPoint.getArguments()) +
                "\nThrows:" + ex.getMessage());
    }
}

AOP完成

最后附上项目包结构

在这里插入图片描述
在这里插入图片描述

spring 总结

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值