暑假最后几天!肝爆(手写)一个Spring之Day5


title: 暑假最后几天!肝爆(手写)一个Spring之Day5
date: 2022-08-17 23:26:59
categories:

  • 手写spring

Day05—AOP代码织入

基础配置

  • 因为使用properties代替了xml,所以在application.properties中加入,简化操作

    #多切面配置可以在key前面加前缀
    #例如 aspect.logAspect.
    
    #切面表达式,expression#
    pointCut=public .* com.czh.spring.demo.service..*ServiceImpl..*(.*)
    #切面类#
    aspectClass=com.czh.spring.demo.aspect.LogAspect
    #切面前置通知#
    aspectBefore=before
    #切面后置通知#
    aspectAfter=after
    #切面异常通知#
    aspectAfterThrow=afterThrowing
    #切面异常类型#
    aspectAfterThrowingName=java.lang.Exception
    

完成AOP顶层设计

  • JoinPoint,定义一个切点的抽象,这是AOP的基础组成单元。可以理解为是业务方法的附加信息。

    package com.czh.spring.framework.aop.aspect;
    
    import java.lang.reflect.Method;
    
    /**
     * 回调连接点,通过它可以获得被代理的业务方法的所有信息
     */
    public interface MyJoinPoint {
    
        Method getMethod();//业务方法本身
    
        Object[] getArguments();//该方法的实参列表
    
        Object getThis();//该方法所属的实例对象
    
        //在JoinPoint中添加自定义属性
        void setUserAttribute(String key,Object value);
        //从已添加的自定义属性中获取一个属性
        Object getUserAttribute(String key);
    }
    
  • MethodInterceptor,方法拦截器,AOP代码增强的基本组成单元,子类主要有MethodBeforeAdvice,AfterReturningAdvice,AfterThrowingAdvice.

    package com.czh.spring.framework.aop.intercept;
    
    /**
     * 方法拦截器顶层接口
     */
    public interface MyMethodInterceptor {
        Object invoke(MyMethodInvocation mi)throws Throwable;
    }
    
  • AopConfig,定义AOP的配置信息的封装对象,以方便在之后的代码中相互传递。

    package com.czh.spring.framework.aop.config;
    
    import lombok.Data;
    
    /**
     * AOP配置封装
     */
    @Data
    public class MyAopConfig {
        /*与properties文件中的属性一一对应*/
        
        //切面表达式
        private String pointCut;
        //前置通知方法名
        private String aspectBefore;
        //后置通知方法名
        private String aspectAfter;
        //要植入的切面类
        private String aspectClass;
        //异常通知方法名
        private String aspectAfterThrow;
        //需要通知的异常类型
        private String aspectAfterThrowingName;
    }
    
  • AdvisedSupport,主要完成对AOP配置的解析。其中PointCutMatch()方法用来判断目标类是否符合切面规则,从而决定是否生成代理类,对目标方法进行增强。getInterceptorAndDynamicInterceptionAdvice()则主要根据AOP配置,将需要回调的方法封装成一个拦截器链并返回提供给外部获取。

    package com.czh.spring.framework.aop.support;
    
    import com.czh.spring.framework.aop.config.MyAopConfig;
    
    import java.lang.reflect.Method;
    import java.util.HashMap;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Map;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    /**
     * 主要用来解析和封装AOP配置
     */
    public class MyAdvisedSupport {
        private Class targetClass;
        private Object target;
        private Pattern pointCutClassPattern;
    
        private transient Map<Method, List<Object>> methodCache;
    
        private MyAopConfig aopConfig;
    
        public MyAdvisedSupport(MyAopConfig aopConfig) {
            this.aopConfig = aopConfig;
        }
    
        public Class getTargetClass() {
            return targetClass;
        }
    
        public void setTargetClass(Class targetClass) {
            this.targetClass = targetClass;
            parse();
        }
    
        public Object getTarget() {
            return target;
        }
    
        public void setTarget(Object target) {
            this.target = target;
        }
      
        public List<Object> getInterceptorAndDynamicInterceptionAdvice(Method method,Class<?> targetClass)throws Exception{
            List<Object> cached = methodCache.get(method);
            
            //缓存未命中,则进行下一步处理
            if(null == cached){
                Method m = targetClass.getMethod(method.getName(), method.getParameterTypes());
                cached = methodCache.get(m);
                //存入缓存
                this.methodCache.put(m,cached);
            }
            return cached;
        }
        
        public boolean pointCutMatch(){
            return pointCutClassPattern.matcher(this.targetClass.toString()).matches();
        }
        
        private void parse(){
            //pointCut表达式
            String pointCut = aopConfig.getPointCut()
                    .replaceAll("\\.","\\\\.")
                    .replaceAll("\\\\.\\*",".*")
                    .replaceAll("\\(","\\\\(")
                    .replaceAll("\\)","\\\\)");
    
            String pointCutForClass = pointCut.substring(0, pointCut.lastIndexOf("\\(") - 4);
            pointCutClassPattern = Pattern.compile("class " + pointCutForClass.substring(pointCutForClass.lastIndexOf(" ") + 1));
            
            methodCache = new HashMap<Method,List<Object>>();
            Pattern pattern = Pattern.compile(pointCut);
    
            try {
                Class<?> aspectClass = Class.forName(aopConfig.getAspectClass());
                HashMap<String, Method> aspectMethods = new HashMap<>();
                for (Method m : aspectClass.getMethods()) {
                    aspectMethods.put(m.getName(),m);
                }
                
                //在这里得到的都是原生方法
                for (Method m : targetClass.getMethods()) {
                    String methodString = m.toString();
                    if(methodString.contains("throw")){
                        methodString = methodString.substring(0,methodString.lastIndexOf("throws")).trim();
                    }
                    Matcher matcher = pattern.matcher(methodString);
                    if(matcher.matches()){
                        //能够满足切面规则的类,添加到AOP配置中
                        LinkedList<Object> advices = new LinkedList<>();
                        //前置通知
                        if(!(null == aopConfig.getAspectBefore() || "".equals(aopConfig.getAspectBefore().trim()))){
                            advices.add(new MyMethodBeforeAdvice(aspectMethods.get(aopConfig.getAspectBefore()),aspectClass.newInstance()));
                        }
                        //后置通知
                        if(!(null == aopConfig.getAspectAfter() || "".equals(aopConfig.getAspectAfter().trim()))){
                            advices.add(new MyAfterReturningAdvice(aspectMethods.get(aopConfig.getAspectAfter()),aspectClass.newInstance()));
                        }
                        //异常通知
                        if(!(null == aopConfig.getAspectAfterThrow() || "".equals(aopConfig.getAspectAfterThrow().trim()))){
                            MyAfterThrowingAdvice afterThrowingAdvice = new MyAfterThrowingAdvice(aspectMethods.get(aopConfig.getAspectAfterThrow()), aspectClass.newInstance());
                            afterThrowingAdvice.setThrowingName(aopConfig.getAspectAfterThrowingName());
                            advices.add(afterThrowingAdvice);
                        }
                        methodCache.put(m,advices);
                    }
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    
    
  • AopProxy,代理工厂的顶层接口,主要是CglibAopProxy和JdkDynamicAopProxy这二个子类分别实现CGlib和JDK Proxy的代理

    package com.czh.spring.framework.aop;
    
    /**
     * 代理工厂的顶层接口,提供获取代理对象的顶层入口
     * 默认就使用JDK的动态代理
     */
    public interface MyAopProxy {
        //获取一个代理对象
        Object getProxy();
        //通过自定义类加载器获得一个代理对象
        Object getProxy(ClassLoader classLoader);
    }
    
  • CGlibAopProxy,暂不做实现

    package com.czh.spring.framework.aop;
    
    import com.czh.spring.framework.aop.support.MyAdvisedSupport;
    
    /**
     * TODO 使用CGlib生成代理类
     */
    public class MyCglibAopProxy implements MyAopProxy{
        private MyAdvisedSupport config;
        
        public MyCglibAopProxy(MyAdvisedSupport advisedSupport){
            this.config = advisedSupport;
        }
        
        @Override
        public Object getProxy() {
            return null;
        }
    
        @Override
        public Object getProxy(ClassLoader classLoader) {
            return null;
        }
    }
    
  • JdkDynamicAopProxy,主要功能在invoke()方法中。主要是调用AdvisedSupport的getInterceptorAndDynamicInterceptionAdvice()方法获得拦截器链。在目标类中,每一个被增强的目标方法都对应一个拦截器链。

    package com.czh.spring.framework.aop;
    
    import com.czh.spring.framework.aop.support.MyAdvisedSupport;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.util.List;
    
    /**
     * 使用JDK Proxy API来生成代理类
     */
    public class MyJdkDynamicAopProxy implements MyAopProxy, InvocationHandler {
        private MyAdvisedSupport config;
    
        public MyJdkDynamicAopProxy(MyAdvisedSupport config) {
            this.config = config;
        }
    
        //传入原生对象
        @Override
        public Object getProxy() {
            return getProxy(this.config.getTargetClass().getClassLoader());
        }
    
        @Override
        public Object getProxy(ClassLoader classLoader) {
            return Proxy.newProxyInstance(classLoader,this.config.getTargetClass().getInterfaces(),this);
        }
    
        //invoke()方法是执行代理的关键入口
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //将每一个JoinPoint也就是被代理的业务方法(Method)封装成一个拦截器,组合成一个拦截器链
            List<Object> interceptorAndDynamicMethodMatchers = 
                    config.getInterceptorAndDynamicInterceptionAdvice(method, this.config.getTargetClass());
            //交给拦截器链MethodInvocation的proceed()方法执行
            MyMethodInvocation invocation = 
                    new MyMethodInvocation(proxy, this.config.getTarget(), method, args, this.config.getTargetClass(), interceptorAndDynamicMethodMatchers);
            return invocation.proceed();
        }
    }
    
    
  • MethodInvocation:关键方法是proceed(),先进行判断,如果拦截器为空,则说明目标方法无需增强,直接调用目标方法并返回。如果拦截器链不为空,则将拦截器链中的方法按顺序执行,直到拦截器链中所有方法全部执行完毕。

    package com.czh.spring.framework.aop.intercept;
    
    import com.czh.spring.framework.aop.aspect.MyJoinPoint;
    
    import java.lang.reflect.Method;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * 执行拦截器链,相当于Spring中ReflectiveMethodInvocation的功能
     */
    public class MyMethodInvocation implements MyJoinPoint {
        
        private Object proxy;//代理对象
        private Method method;//代理的目标方法
        private Object target;//代理的目标对象
        private Class<?> targetClass;//代理的目标类
        private Object[] arguments;//代理的方法的实参列表
        private List<Object> interceptorsAndDynamicMethodMatchers;//回调方法链
        
        //保存自定义属性
        private Map<String,Object> userAttributes;
        
        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.targetClass = targetClass;
            this.arguments = arguments;
            this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
        }
        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
                return proceed();
            }
        }
    
        @Override
        public Method getMethod() {
            return this.method;
        }
    
        @Override
        public Object[] getArguments() {
            return this.arguments;
        }
    
        @Override
        public Object getThis() {
            return this.target;
        }
    
        @Override
        public void setUserAttribute(String key, Object value) {
            if(null != value){
                if(null == this.userAttributes){
                    this.userAttributes = new HashMap<String,Object>();
                }
                this.userAttributes.put(key, value);
            }else {
                if(null != this.userAttributes){
                    this.userAttributes.remove(key);
                }
            }
        }
    
        @Override
        public Object getUserAttribute(String key) {
            return (this.userAttributes != null ? this.userAttributes.get(key):null);
        }
    
    }
    

设计AOP基础实现

  • Advice作为所有回调通知的顶层接口设计,暂时只是作为规范,没实现任何功能。

    package com.czh.spring.framework.aop.aspect;
    
    /**
     * 回调通知顶层接口
     */
    public interface MyAdvice {
    }
    
  • AbstractAspectJAdvice:使用模版设计MyAbstractAspectJAdvice类,封装拦截器回调的通用逻辑,主要封装反射动态调用方法,其子类只需要控制调用顺序即可。

    package com.czh.spring.framework.aop.aspect;
    
    import java.lang.reflect.Method;
    
    /**
     * 封装拦截器回调的通用逻辑,主要封装了反射动态调用方法
     */
    public class MyAbstractAspectJAdvice implements MyAdvice{
        private Method aspectMethod;
        private Object aspectTarget;
    
        public MyAbstractAspectJAdvice(Method aspectMethod, Object aspectTarget) {
            this.aspectMethod = aspectMethod;
            this.aspectTarget = aspectTarget;
        }
        
        //反射动态调用方法
        protected Object invokeAdviceMethod(MyJoinPoint joinPoint,Object returnValue,Throwable ex)throws Throwable{
            Class<?>[] paramsTypes = this.aspectMethod.getParameterTypes();
            if(null == paramsTypes || paramsTypes.length == 0){
                return this.aspectMethod.invoke(aspectTarget);
            } else {
                Object[] args = new Object[paramsTypes.length];
                for (int i = 0; i < paramsTypes.length; i++) {
                    if(paramsTypes[i] == MyJoinPoint.class){
                        args[i] = joinPoint;
                    }else if(paramsTypes[i] == Throwable.class){
                        args[i] = ex;
                    }else if(paramsTypes[i] == Object.class){
                        args[i] = returnValue;
                    }
                }
                return this.aspectMethod.invoke(aspectTarget,args);
            }
        }
    }
    
  • MethodBeforeAdvice:继承自AbstractAspectJAdivce,实现Advice和MethodInterceptor接口,在invoke()中控制前置通知的调用顺序

    package com.czh.spring.framework.aop.aspect;
    
    import com.czh.spring.framework.aop.intercept.MyMethodInterceptor;
    import com.czh.spring.framework.aop.intercept.MyMethodInvocation;
    
    import java.lang.reflect.Method;
    
    /**
     * 前置通知具体实现
     */
    public class MyMethodBeforeAdvice extends MyAbstractAspectJAdvice implements MyAdvice, MyMethodInterceptor {
    
        private MyJoinPoint joinPoint;
    
        public MyMethodBeforeAdvice(Method aspectMethod, Object aspectTarget) {
            super(aspectMethod, aspectTarget);
        }
    
        public void before(Method method,Object[] args,Object target)throws Throwable{
            invokeAdviceMethod(this.joinPoint,null,null);
        }
    
        @Override
        public Object invoke(MyMethodInvocation mi) throws Throwable {
            this.joinPoint = mi;
            this.before(mi.getMethod(), mi.getArguments(), mi.getThis());
            return mi.proceed();
        }
    }
    
    
  • AfterReturningAdvice:继承自AbstractAspectJAdivce,实现Advice和MethodInterceptor接口,在invoke()中控制后置通知的调用顺序

    package com.czh.spring.framework.aop.aspect;
    
    import com.czh.spring.framework.aop.intercept.MyMethodInterceptor;
    import com.czh.spring.framework.aop.intercept.MyMethodInvocation;
    
    import java.lang.reflect.Method;
    
    public class MyAfterReturningAdvice extends MyAbstractAspectJAdvice implements MyAdvice, MyMethodInterceptor{
        
        private MyJoinPoint joinPoint;
        
        public MyAfterReturningAdvice(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 {
            invokeAdviceMethod(joinPoint,retVal,null);
        }
    }
    
    
  • AfterThrowingAdvice:继承自AbstractAspectJAdivce,实现Advice和MethodInterceptor接口,在invoke()中控制异常通知的调用顺序

    package com.czh.spring.framework.aop.aspect;
    
    import com.czh.spring.framework.aop.intercept.MyMethodInterceptor;
    import com.czh.spring.framework.aop.intercept.MyMethodInvocation;
    
    import java.lang.reflect.Method;
    
    /**
     * 异常通知的具体实现
     */
    public class MyAfterThrowingAdvice extends MyAbstractAspectJAdvice implements MyAdvice, MyMethodInterceptor {
        
        private String throwingName;
        private MyMethodInvocation mi;
        
        public MyAfterThrowingAdvice(Method aspectMethod, Object aspectTarget) {
            super(aspectMethod, aspectTarget);
        }
        
        public void setThrowingName(String aspectAfterThrowingName) {
            this.throwingName = aspectAfterThrowingName;
        }
    
        @Override
        public Object invoke(MyMethodInvocation mi) throws Throwable {
            try {
                return mi.proceed();
            }catch (Throwable ex){
                invokeAdviceMethod(mi,null,ex.getCause());
                throw ex;
            }
        }
    
    }
    

接入getBean方法

  • 如何集成到IoC容器中去?在MyApplicationContext中的instantiateBean()方法,在初始化时就可以确定是否返回原生Bean或者ProxyBean。

        private Object instantiateBean(MyBeanDefinition beanDefinition) {
            Object instance = null;
            String beanClassName = beanDefinition.getBeanClassName();
    
            try {
                //因为根据Class才能确定一个类是否有实例
                if(this.factoryBeanObjectCache.containsKey(beanClassName)){
                    instance = this.factoryBeanObjectCache.get(beanClassName);
                }else {
                    Class<?> clazz = Class.forName(beanClassName);
                    instance = clazz.newInstance();
                    
                    MyAdvisedSupport config = instantionAopConfig(beanDefinition);
                    config.setTargetClass(clazz);
                    config.setTarget(instance);
                    
                    if(config.pointCutMatch()){
                        instance = createProxy(config).getProxy();
                    }
                    this.factoryBeanObjectCache.put(beanClassName,instance);
                    this.factoryBeanObjectCache.put(beanDefinition.getFactoryBeanName(),instance);
                }
                return instance;
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        private MyAdvisedSupport instantionAopConfig(MyBeanDefinition beanDefinition) {
            MyAopConfig config = new MyAopConfig();
            config.setPointCut(reader.getConfig().getProperty("pointCut"));
            config.setAspectClass(reader.getConfig().getProperty("aspectClass"));
            config.setAspectBefore(reader.getConfig().getProperty("aspectBefore"));
            config.setAspectAfter(reader.getConfig().getProperty("aspectAfter"));
            config.setAspectAfterThrow(reader.getConfig().getProperty("aspectAfterThrow"));
            config.setAspectAfterThrowingName(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类,实现对业务方法的监控。主要是记录目标方法的调用日志,获取目标方法名、实参列表、每次调用所消耗的时间

    package com.czh.spring.demo.aspect;
    
    import com.czh.spring.framework.aop.aspect.MyJoinPoint;
    import lombok.extern.slf4j.Slf4j;
    
    import java.util.Arrays;
    
    /**
     * 定义一个织入的切面逻辑,也就是要针对目标代理对象增强的逻辑
     * 本类主要完成对方法调用的监控,监听目标方法每次执行所消耗的时间
     */
    @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());
        }
    }
    
    
  • 修改下ModifyService,在里面故意抛出异常测试

    package com.czh.spring.demo.service.impl;
    
    import com.czh.spring.demo.service.ModifyService;
    
    public class ModifyServiceImpl implements ModifyService {
        /**
         * 增加
         */
        @Override
        public String add(String name, String address) throws Exception {
            throw new Exception("故意抛出异常,测试前面通知是否生效!!!");
    //        return "modifyService add,name=" + name + ",address=" + address;
        }
    
        /**
         * 修改
         */
        @Override
        public String edit(Integer id, String name) {
            return "modifyService edit,id=" + id + ",name=" + name;
        }
    
        /**
         * 删除
         */
        @Override
        public String remove(Integer id) {
            return "modifyService id=" + id;
        }
    
    }
    

测试

  • http://localhost:8080/luelueking_Spring_Source_war/web/add.json以及控制台输出

    请添加图片描述

  • 控制台输出

    请添加图片描述

  • http://localhost:8080/luelueking_Spring_Source_war/web/query.json?name=luelueking

    请添加图片描述

  • 控制台输出

    请添加图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

luelueking

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

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

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

打赏作者

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

抵扣说明:

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

余额充值