手写Spring框架(四)

本文详细介绍了AOP的实现步骤,包括获取Bean、使用AdviseSupport解析配置、配置文件中的逻辑转换,以及如何将通知与目标类方法关联。重点讲解了PIGAdvisedSupport的关键作用和如何根据配置动态织入切面。测试案例展示了LogAspect在方法调用前后如何执行通知。
摘要由CSDN通过智能技术生成

逻辑梳理

这部分完成AOP部分。
先梳理AOP的步骤:
getBean()
getBean()方法作为入口,而后是几个关键的类。Context在前文都有提到,现在解释一下其他的类。

AdviseSupport:通知的工具类

  • 完成配置文件的解析
  • 将Advise和目标类的方法建立关系——通过map结构
Map<Method,Map<String,Advice>>

例如:
在这里插入图片描述

AopConfig:调用方法时,判断方法的名称是否符合切面规则,如果符合就要触发通知
Advice:该类包含的属性:aspect的实例、method的回调
JdkDynamicAopProxy:生成一个全新的,类字节码重组,用代码生成代码,在内存中编译,在内存中完成实例化,返回的新的代理类,和原始类处于同一个继承体系(可赋值给目标类,对于用户来说,是无感知的,但是实际上在内部完成了偷梁换柱)

骨架与填空

先从context入手,在对象实例化之后进行织入:
在这里插入图片描述
上面相当于搭好了骨架,接着分别填空。

    //从配置文件读取信息保存到config对象
    private PIGAdvisedSupport instantionAopConfig(PigBeanDefinition beanDefinition) {
        PIGAopConfig config = new PIGAopConfig();
        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 PIGAdvisedSupport(config);
    }

配置文件可能长这样:
在这里插入图片描述
PIGAdvisedSupport比较关键,它负责根据以上解析出来的config,将Advise和目标类的方法建立关系。
也就是config.setTargetClass(clazz);这个方法

public void setTargetClass(Class targetClass) {
        this.targetClass = targetClass;
        //解析表达式
        parse();
    }

    public Class getTargetClass() {
        return targetClass;
    }


    private void parse() {

        //对配置文件中的特殊字符进行转义
        String pointCut = config.getPointCut()
                .replaceAll("\\.","\\\\.")
                .replaceAll("\\\\.\\*",".*")
                .replaceAll("\\(","\\\\(")
                .replaceAll("\\)","\\\\)");

        // pointCut=public .* com.gupaoedu.vip.demo.service..*Service..*(.*)
        String pointCutForClassRegex = pointCut.substring(0,pointCut.lastIndexOf("\\(") - 4);
        //提取class的全名
        pointCutClassPattern = Pattern.compile("class " + pointCutForClassRegex.substring(pointCutForClassRegex.lastIndexOf(" ") + 1));

        try {
            //开始映射目标类方法和通知的关系
            methodCache = new HashMap<Method, Map<String, PIGAdvice>>();

            //开始匹配目标类的方法
            Pattern pointCutPattern = Pattern.compile(pointCut);

            //先把要织入的切面的方法缓存起来
            Class aspectClass = Class.forName(this.config.getAspectClass());
            Map<String,Method> aspectMethods = new HashMap<String, Method>();
            for (Method method : aspectClass.getMethods()) {
                aspectMethods.put(method.getName(),method);
            }

            //扫描目标类的所有的方法
            for (Method method : this.targetClass.getMethods()) {
                //包括了修饰符、返回值、方法名、形参列表
                String methodString = method.toString();
                //把异常去掉
                if(methodString.contains("throws")){
                    methodString = methodString.substring(0,methodString.lastIndexOf("throws")).trim();
                }

                Matcher matcher = pointCutPattern.matcher(methodString);
                if(matcher.matches()){
                    Map<String,PIGAdvice> advices = new HashMap<String, PIGAdvice>();

                    //前置通知
                    if(!(null == config.getAspectBefore() || "".equals(config.getAspectBefore()))){
                        advices.put("before",
                                new PIGAdvice(aspectClass.newInstance(),aspectMethods.get(config.getAspectBefore())));
                    }

                    //后置通知
                    if(!(null == config.getAspectAfter() || "".equals(config.getAspectAfter()))){
                        advices.put("after",
                                new PIGAdvice(aspectClass.newInstance(),aspectMethods.get(config.getAspectAfter())));
                    }

                    //异常通知
                    if(!(null == config.getAspectAfterThrow() || "".equals(config.getAspectAfterThrow()))){
                        advices.put("afterThrowing",
                                new PIGAdvice(aspectClass.newInstance(),aspectMethods.get(config.getAspectAfterThrow())));
                    }

                    methodCache.put(method,advices);
                }

            }


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

    }

测试

这是用户侧的代码(切面):

@Slf4j
public class LogAspect {

    //在调用一个方法之前,执行before方法
    public void before(){
        //这个方法中的逻辑,是由我们自己写的
        log.info("Invoker Before Method!!!");
    }

    //在调用一个方法之后,执行after方法
    public void after(){
        log.info("Invoker After Method!!!");
    }

    public void afterThrowing(){
        log.info("出现异常");
    }

}

(请求):

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

在这里插入图片描述

总结

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值