【Spring】重构--仿写Spring核心逻辑(一)目标及框架结构

之前我们用300行代码手写过 Spring 核心逻辑 【Spring】仿写Spring核心逻辑–实现IOC、DI、MVC,虽然也达到了我们想要的目的,实现了Spring的最基本功能,但是它把所有功能都放在一个类(MYDispatchServlet)中,没有用到任何设计模式。另外,它也没有实现Spring另一核心 aop 相关逻辑。

所以,本篇我们就开始对它进行重构与扩充,原则是尽量模仿着Spring原本的样子,达到高仿真的目的。

1.要实现的目标

跟上篇一样,我们还是先来明确一下要实现的目标:

目标一:IOC。可以通过注解 @MYController 将Bean交给IOC容器去管理

目标二:DI。可以通过注解 @MYAutowired 将相应Bean依赖注入进来

目标三:MVC。可以通过注解 @MYRequestMapping 和 @MYRequestParam 完成请求的分发处理

@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("/first.html")
	// 相比于mini,重构版可以通过 ModelAndView返回我们定义好的页面,然后通过模板版引擎解析model中的数据
    public MYModelAndView query(@MYRequestParam("name") String name) {
        String result = queryService.query(name);
        // 构建model
        Map<String, Object> model = new HashMap<String, Object>();
        model.put("name", name);
        model.put("data", result);
        model.put("token", 123456);
        // 返回我们自定义的ModelAndView,并指定页面
        // 注:这里我们既可以写 first.html 也可以直接写 first(框架内部会做处理)
        return new MYModelAndView("first", model);
    }
	
	@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) {
			// 将异常信息保存在Map中,然后放入Model
			Map<String,Object> model = new HashMap<String,Object>();
			// 注:这里在单独测 mvc 模块时要去掉getCause
			model.put("detail",e.getMessage());
			model.put("stackTrace", Arrays.toString(e.getStackTrace()).replaceAll("\\[|\\]",""));
			
			return new MYModelAndView("500",model);
		}
	}

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

}

下面只给出500.html,关于Service和页面模板这里就不全部展示了。

<!-- 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/>
    <font color='green'><i>Copyright@YZH</i></font>
</body>
</html>

在这里插入图片描述

目标四:AOP。除了上面三个基本的目标外,我们还需要实现 AOP,即可以通过切面实现对方法的增强

@Slf4j // 借助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());
    }
}

配置文件 application.properties:

# 要扫描的包
scanPackage=com.xupt.yzh.demo
# 模板的位置
templateRoot=layouts
# 切点表达式
pointCut=public .* com.xupt.yzh.demo.service..*Service..*(.*)
# 切面类
aspectClass=com.xupt.yzh.demo.aspect.LogAspect
# 切面前置通知
aspectBefore=before
# 切面后置通知
aspectAfter=after
# 切面异常通知
aspectAfterThrow=afterThrowing
# 切面	异常类型
aspectAfterThrowingName=java.lang.Exception

2.框架结构

我们先来看看整体的项目结构,见下图:

问题一:可以看到中间有一个 framework,但我们还没有说过它是干什么的?

没错,它就是需要我们去实现的框架内容。为了实现高仿的目的,我们也按照 Spring 的方式创建出 annotation、aop、beans、context、webmvc.这几个包:

在这里插入图片描述

问题二:那这五个包里面都有些什么类?

首先我们来看 annotation 包,它里面的内容就是第一部分我们说过的那五个注解

在这里插入图片描述

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MYController {
    String value() default "";
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MYService {
    String value() default "";
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MYAutowired {
    String value() default "";
}

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MYRequestMapping {
    String value() default "";
}

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MYRequestParam {
    String value() default "";

    boolean required() default true;
}

剩下的四个包我们放在后面的文章中,按照 IOC/DI --> AOP --> MVC 的顺序去逐一实现:

完整代码我放到 GitHub 上了,可以点击这里跳转…

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

A minor

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值