Spring5 - 30个类手写实战 - 打卡第四天(AOP)

相关文章:
Spring5 - 30个类手写实战 - 打卡第一天(V1版本)

Spring5 - 30个类手写实战 - 打卡第二天(IOC与DI)

Spring5 - 30个类手写实战 - 打卡第三天(MVC)

Spring5 - 30个类手写实战 - 打卡第五天(手绘IOC时序图)

1.AOP

底层技术用动态代理实现。
增强、解耦。
由Spring生成的Proxy类来完成织入一些新的代码,生成一个新的类(JDK,CGLib),
要么就跟目标实现一个相同的接口
要么就是直接继承目标类,覆盖目标类的方法
真正去调用代码逻辑的时候,直接用新生成的Proxy类的代码

例:日志监听的切面

  • befor:记录方法开始调用时间,输出日志

  • after : 记录方法结束调用的时间,输出日志

  • afterThrows:如统一到某一个页面

只要有共同特征的类或者方法,都可以称之为一个切面(Aspect)。

如下例,可以将公共的部分拆分出来放到,before/after/afterThrows,在实际编写代码中,只需要关注业务逻辑代码部分即可。

public void handler(){
	try {
		//--------------before-------------//
		long start = System.currentTimeMillis();
		//--------------------------------//
		... // 业务逻辑代码
		//--------------after-------------//
		long end = System.currentTimeMillis();
		log.debug((end - start) + "ms"+""...);
		//--------------------------------//
	}catch (Exception e){
		//--------------afterThrows-------------//
		resp.getWriter().println("500");
		//-------------------------------------//
	}
}

通常,放到Advice(通知)中,

一个方法,对应多个Advice。可以大致理解为Map<Method,List<Advice>>这样的结构。

AOP顶层设计

IOC==>AOP==>DI==>MVC

在这里插入图片描述
从调用getBean方法开始,进入到ApplicationContext,ApplicationContext内部读取配置BeanDifinition,封装成BeanWrapper。通过AopConfig判断BeanWrapper里面的类是不是要生成代理类。

可以采用注解或XML的方式配置,这里还是采用properties的方式进行配置

XML方式:

<bean id="xmlAspect" class="com.liulin.demo.aspect.LogAspect"></bean>
<aop:config>
	<aop:aspect ref="xmlAspect">
		<aop:before point-ref="simplePointcut" method="before"/>
		<aop:after point-ref="simplePointcut" method="after"/>
		<aop:after-returning point-ref="simplePointcut" method="afterReturn"/>
		<aop:after-throwing point-ref="simplePointcut" method="afterThrow" throwing="ex"/>
	</aop:aspect>
</aop:config>

本项目方式:

在这里插入图片描述

项目结构:

在这里插入图片描述

2.代码

LogAspect

package com.liulin.demo.aspect;

/**
 * Create by DbL on 2020/5/3 0003
 */
public class LogAspect {
    //在调用一个方法之前,执行before方法
    public void before(){
        //这个方法中的逻辑,是由我们自己写的
        System.out.println("Invoker Before Method!!!");
    }
    //在调用一个方法之后,执行after方法
    public void after(){
        System.out.println("Invoker After Method!!!");
    }

    public void afterThrowing(){
        System.out.println("出现异常");
    }
}

LAdvice

package com.liulin.spring.framework.aop.aspect;

import java.lang.reflect.Method;

/**
 * Create by DbL on 2020/5/3 0003
 */
public class LAdvice {
    private Object aspect;
    private Method adviceMethod;
    private String throwName;

    public LAdvice(Object aspect, Method adviceMethod) {
        this.aspect = aspect;
        this.adviceMethod = adviceMethod;
    }

    public Object getAspect() {
        return aspect;
    }

    public void setAspect(Object aspect) {
        this.aspect = aspect;
    }

    public Method getAdviceMethod() {
        return adviceMethod;
    }

    public void setAdviceMethod(Method adviceMethod) {
        this.adviceMethod = adviceMethod;
    }

    public String getThrowName() {
        return throwName;
    }

    public void setThrowName(String throwName) {
        this.throwName = throwName;
    }
}

LAopConfig

package com.liulin.spring.framework.aop.config;

import lombok.Data;

/**
 * Create by DbL on 2020/5/3 0003
 */
public class LAopConfig {
    private String pointCut;
    private String aspectClass;
    private String aspectBefore;
    private String aspectAfter;
    private String aspectAfterThrow;
    private String aspectAfterThrowingName;

    public String getPointCut() {
        return pointCut;
    }

    public void setPointCut(String pointCut) {
        this.pointCut = pointCut;
    }

    public String getAspectClass() {
        return aspectClass;
    }

    public void setAspectClass(String aspectClass) {
        this.aspectClass = aspectClass;
    }

    public String getAspectBefore() {
        return aspectBefore;
    }

    public void setAspectBefore(String aspectBefore) {
        this.aspectBefore = aspectBefore;
    }

    public String getAspectAfter() {
        return aspectAfter;
    }

    public void setAspectAfter(String aspectAfter) {
        this.aspectAfter = aspectAfter;
    }

    public String getAspectAfterThrow() {
        return aspectAfterThrow;
    }

    public void setAspectAfterThrow(String aspectAfterThrow) {
        this.aspectAfterThrow = aspectAfterThrow;
    }

    public String getAspectAfterThrowingName() {
        return aspectAfterThrowingName;
    }

    public void setAspectAfterThrowingName(String aspectAfterThrowingName) {
        this.aspectAfterThrowingName = aspectAfterThrowingName;
    }
}

LJdkDynamicAopProxy

package com.liulin.spring.framework.aop.support;

import com.liulin.spring.framework.aop.LAdvisedSupport;
import com.liulin.spring.framework.aop.aspect.LAdvice;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;

/**
 * Create by DbL on 2020/5/3 0003
 */
public class LJdkDynamicAopProxy implements InvocationHandler {
    private LAdvisedSupport config;

    public LJdkDynamicAopProxy(LAdvisedSupport config) {
        this.config = config;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // key存储 befor、after、throw ...
        Map<String, LAdvice> advices = config.getAdvices(method, null);
        Object returnValue;
        try {
            invokeAdvice(advices.get("before"));

            returnValue = method.invoke(this.config.getTarget(), args);

            invokeAdvice(advices.get("after"));
        } catch (Exception e) {
            invokeAdvice(advices.get("afterThrow"));
            throw e;
        }
        return returnValue;
    }

    private void invokeAdvice(LAdvice advice) {
        try {
            advice.getAdviceMethod().invoke(advice.getAspect());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), this.config.getTargetClass().getInterfaces(), this);
    }
}

LAdvisedSupport

package com.liulin.spring.framework.aop;

import com.liulin.spring.framework.aop.aspect.LAdvice;
import com.liulin.spring.framework.aop.config.LAopConfig;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Create by DbL on 2020/5/3 0003
 */
public class LAdvisedSupport {
    private LAopConfig config;
    private Class<?> targetClass;
    private Object target;
    private Map<Method, Map<String, LAdvice>> methodCache;
    private Pattern pointCutClassPattern;

    public LAdvisedSupport(LAopConfig config) {
        this.config = config;
    }

    // 解析配置文件的方法
    private void parse() {
        //pointCut=public .* com.liulin.demo.service..*Service..*(.*)  改为正则表达式
        String pointCut = config.getPointCut()
                .replaceAll("\\.", "\\\\.")
                .replaceAll("\\\\.\\*", ".*")
                .replaceAll("\\(", "\\\\(")
                .replaceAll("\\)", "\\\\)");
        // 1.方法的修饰符和返回值
        // 2.类名
        // 3.方法的名称和形参列表

        // 生成匹配class的正则
        String pointCutForClassRegex = pointCut.substring(0, pointCut.lastIndexOf("\\(") - 4);
        pointCutClassPattern = Pattern.compile("class " + pointCutForClassRegex.substring(pointCutForClassRegex.lastIndexOf(" ") + 1));

        methodCache = new HashMap<Method, Map<String, LAdvice>>();
        // 专门匹配方法的正则
        Pattern pointCutPattern = Pattern.compile(pointCut);
        try {
            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);
            }
            // 以上都是初始化工作,准备阶段

            //  从这里开始封装LAopAdvice
            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, LAdvice> advices = new HashMap<String, LAdvice>();
                    if (!(null == config.getAspectBefore() || "".equals(config.getAspectBefore()))) {
                        advices.put("before", new LAdvice(aspectClass.newInstance(), aspectMethods.get(config.getAspectBefore())));
                    }
                    if (!(null == config.getAspectAfter() || "".equals(config.getAspectAfter()))) {
                        advices.put("after", new LAdvice(aspectClass.newInstance(), aspectMethods.get(config.getAspectAfter())));
                    }
                    if (!(null == config.getAspectAfterThrow() || "".equals(config.getAspectAfterThrow()))) {
                        LAdvice advice = new LAdvice(aspectClass.newInstance(), aspectMethods.get(config.getAspectAfterThrow()));
                        advice.setThrowName(config.getAspectAfterThrowingName());
                        advices.put("afterThrow", advice);
                    }
                    // 跟目标代理类的业务方法和Advices建立一对多的关联关系,以便在Proxy中获得
                    methodCache.put(method,advices);
                }
            }
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    // 根据一个目标代理类方法获得其对应的通知
    public Map<String, LAdvice> getAdvices(Method method, Object o) throws Exception {
        Map<String, LAdvice> cache = methodCache.get(method);
        if (null == cache) {
            Method m = targetClass.getMethod(method.getName(), method.getParameterTypes());
            cache = methodCache.get(m);
            this.methodCache.put(m, cache);
        }
        return cache;
    }

    // 给ApplicationContext首先IOC中的对象初始化时调用,决定要不要生成代理类的逻辑
    public boolean pointCutMatch() {
        return pointCutClassPattern.matcher(this.targetClass.toString()).matches();
    }

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

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

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

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

LApplicationContext

仅修改以下方法:

private Object instantiateBean(String beanName, LBeanDefinition beanDefinition) {
        String className = beanDefinition.getBeanClassName();
        Object instance = null;
        try {
            if(this.factoryBeanObjectCache.containsKey(beanName)){
                instance =  factoryBeanObjectCache.get(beanName);
            }else {
                Class<?> aClass = Class.forName(className);
                instance = aClass.newInstance();
                //====================AOP START==================//
                // 如果满足条件,就指直接返回Proxy对象
                // 1.加载AOP配置文件
                LAdvisedSupport config = instantionAopConfig(beanDefinition);
                config.setTargetClass(aClass);
                config.setTarget(instance);
                // 判断规则,要不要生成代理类,如果要就覆盖原生对象,如果不要就不做任何处理,返回原生对象
                if(config.pointCutMatch()){
                    instance = new LJdkDynamicAopProxy(config).getProxy();
                }
                //====================AOP END==================//
                this.factoryBeanObjectCache.put(beanName, instance);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }

3.测试

例:
http://localhost:8080/web/query.json?name=DBL
http://localhost:8080/web/add.json?name=DBL&addr=chongqing
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值