说在前头: 笔者本人为大三在读学生,书写文章的目的是为了对自己掌握的知识和技术进行一定的记录,同时乐于与大家一起分享,因本人资历尚浅,发布的文章难免存在一些错漏之处,还请阅读此文章的大牛们见谅与斧正。若在阅读时有任何的问题,也可通过评论提出,本人将根据自身能力对问题进行一定的解答。

手撸Spring系列是笔者本人首次尝试的、较为规范的系列博客,将会围绕Spring框架分为 IOC/DI 思想Spring MVCAOP 思想Spring JDBC 四个模块,并且每个模块都会分为 理论篇源码篇实战篇 三个篇章进行讲解(大约12篇文章左右的篇幅)。从原理出发,深入浅出,一步步接触Spring源码并手把手带领大家一起写一个 迷你版的Spring框架 ,促进大家进一步了解Spring的本质!

由于源码篇涉及到源码的阅读,可能有小伙伴没有成功构建好Spring源码的阅读环境,笔者强烈建议:想要真正了解Spring,一定要构建好源码的阅读环境再进行研究,具体构建过程可查看笔者此前的博客:《如何构建Spring5源码阅读环境》

前言

经过前面AOP理论篇源码篇,我们又再次进入到了实战篇,此次的实战篇也是建立在之前IOC/DI实战篇的代码基础之上的,因此在进入到AOP实战篇之前,请确保自己已经成功实现了IOCDI功能~!!

还是老规矩,在正式开始编写实战篇的代码前,还是先让各位读者朋友们瞅瞅整个程序的架构。(AOP功能相对于之前我们实现的IOCDIMVC都要复杂很多,也是我们手撸Spring系列最难的模块,因此需要手写的类会比前面章节的实战篇多一些)

代码仓库地址:https://gitee.com/bosen-once/mini-spring


一、注解类的编写

还是一样的,我们实战篇的代码从最简单的注解类开始着手编写,除了之前IOC需要的注解外,我们还需要再编写与AOP相关的注解:

  • After:后置通知注解类
  • AfterReturning:后置返回通知注解类
  • AfterThrowing:异常通知注解类
  • Aspect:切面注解类
  • Before:前置通知注解类
  • Pointcut:切点注解类

①After:后置通知注解类

package org.springframework.annotation.aop;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** <p>后置通知注解类</p>* @author Bosen* @date 2021/9/19 15:52*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface After {String value() default "";
}

②AfterReturning:后置返回通知注解类

package org.springframework.annotation.aop;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** <p>后置返回通知注解类</p>* @author Bosen* @date 2021/9/19 15:53*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AfterReturning {String value() default "";
}

③AfterThrowing:异常通知注解类

package org.springframework.annotation.aop;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** <p>异常通知注解类</p>* @author Bosen* @date 2021/9/19 15:54*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AfterThrowing {String value() default "";
}

④Aspect:切面注解类

package org.springframework.annotation.aop;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** <p>切面注解类</p>* @author Bosen* @date 2021/9/19 15:50*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Aspect {String value() default "";
}

⑤Before:前置通知注解类

package org.springframework.annotation.aop;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** <p>前置通知注解类</p>* @author Bosen* @date 2021/9/19 15:52*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Before {String value() default "";
}

⑥Pointcut:切点注解类

package org.springframework.annotation.aop;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** <p>切点注解类</p>* @author Bosen* @date 2021/9/19 15:51*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Pointcut {String value() default "";
}

二、接口类的编写

主要编写回调通知接口、连接点接口、回调方法拦截器接口、代理工厂接口:

  1. Advice:回调通知定义接口
  2. JoinPoint:连接点接口
  3. AopProxy:代理工厂接口
  4. MethodInterceptor:方法拦截器接口

1.Advice:回调通知定义接口

package org.springframework.aop.aspect;/*** <p>回调通知接口</p>* @author Bosen* @date 2021/9/19 21:40*/
public interface Advice {}

2.JoinPoint:连接点接口

package org.springframework.aop.aspect;import java.lang.reflect.Method;/*** <p>连接点接口,定义切点的抽象</p>* @author Bosen* @date 2021/9/19 16:10*/
public interface JoinPoint {/*** <p>业务方法本身</p>*/Method getMethod();/*** <p>该方法的参数列表</p>*/Object[] getArguments();/*** <p>该方法对应的对象</p>*/Object getThis();/*** <p>在joinPoint中添加自定义属性</p>*/void setUserAttribute(String key, Object value);/*** <p>获取自定义属性</p>*/Object getUserAttribute(String key);
}

3.AopProxy:代理工厂接口

package org.springframework.aop;/*** <p>代理工厂接口</p>* @author Bosen* @date 2021/9/19 22:51*/
public interface AopProxy {Object getProxy();Object getProxy(ClassLoader classLoader);
}

4.MethodInterceptor:方法拦截器接口

package org.springframework.aop.intercept;/*** <p>方法拦截器接口</p>* @author Bosen* @date 2021/9/19 16:38*/
public interface MethodInterceptor {Object invoke(MethodInvocation mi) throws Exception;
}

三、配置类的编写

笔者要实现的AOP的功能是基于注解作为配置的,因此,我们在IOC容器初始化时就应该对有AOP注解标记类进行扫描,将与切面相关的信息存储起来,

  1. AopConfig:存储切面信息(一个切面对应一个AopConfig)
  2. AdvisedSupport:完成对切面类的扫描

1.AopConfig

package org.springframework.aop;/*** <p>AOP配置的封装对象</p>* @author Bosen* @date 2021/9/19 16:08*/
public class AopConfig {/*** <p>切点</p>*/private String pointCut;/*** <p>前置通知</p>*/private String before;/*** <p>后置通知</p>*/private String afterReturn;/*** <p>异常通知</p>*/private String afterThrow;/*** <p>异常类型</p>*/private String afterThrowClass;/*** <p>切面类</p>*/private String aspectClass;public String getPointCut() {return pointCut;}public void setPointCut(String pointCut) {this.pointCut = pointCut;}public String getBefore() {return before;}public void setBefore(String before) {this.before = before;}public String getAfterReturn() {return afterReturn;}public void setAfterReturn(String afterReturn) {this.afterReturn = afterReturn;}public String getAfterThrow() {return afterThrow;}public void setAfterThrow(String afterThrow) {this.afterThrow = afterThrow;}public String getAfterThrowClass() {return afterThrowClass;}public void setAfterThrowClass(String afterThrowClass) {this.afterThrowClass = afterThrowClass;}public String getAspectClass() {return aspectClass;}public void setAspectClass(String aspectClass) {this.aspectClass = aspectClass;}
}

2.AdvisedSupport

package org.springframework.aop.support;import org.springframework.aop.AopConfig;
import org.springframework.aop.aspect.AfterReturningAdvice;
import org.springframework.aop.aspect.MethodBeforeAdvice;
import org.springframework.aop.aspect.AfterThrowingAdvice;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;/*** <p>解析AOP配置</p>* @author Bosen* @date 2021/9/19 16:56*/
public class AdvisedSupport {private Class targetClass;private Object target;private Pattern pointCutClassPattern;private transient Map<Method, List<Object>> methodCache = new HashMap<>();private AopConfig config;public AdvisedSupport(AopConfig config) {this.config = config;}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 = this.methodCache.get(method);// 缓存未命中if (cached == null) {Method m = targetClass.getMethod(method.getName(), method.getParameterTypes());cached = this.methodCache.get(m);this.methodCache.put(m, cached);}return cached;}public boolean pointCutMatch() {return this.pointCutClassPattern.matcher(this.targetClass.toString()).matches();}private void parse() {String pointcut = config.getPointCut().replaceAll("\\.", "\\\\.").replaceAll("\\*", "\\.\\*").replaceAll("\\(", "\\\\(").replaceAll("\\)", "\\\\)");String pointCutForClass = pointcut.substring(0, pointcut.lastIndexOf("\\(") - 4);pointCutClassPattern = Pattern.compile("class " + pointCutForClass.substring(pointCutForClass.lastIndexOf(" ") + 1));Pattern pattern = Pattern.compile(pointCutForClass);try {// 获取切面类Class aspectClass = Class.forName(config.getAspectClass());Map<String, Method> aspectMethods = new HashMap<>();// 获取切面类的通知方法for (Method method : aspectClass.getMethods()) {aspectMethods.put(method.getName(), method);}// 获取当前对象的方法for (Method method : targetClass.getMethods()) {String methodString = method.toString();// 去掉方法头中throws及其往后的字符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<>();// 前置通知if (!(config.getBefore() == null || "".equals(config.getBefore()))) {advices.add(new MethodBeforeAdvice(aspectMethods.get(config.getBefore()), aspectClass.newInstance()));}// 后置返回通知if (!(config.getAfterReturn() == null || "".equals(config.getAfterReturn()))) {advices.add(new AfterReturningAdvice(aspectMethods.get(config.getAfterReturn()), aspectClass.newInstance()));}// 异常通知if (!(config.getAfterThrow() == null || "".equals(config.getAfterThrow()))) {AfterThrowingAdvice afterThrowingAdvice =new AfterThrowingAdvice(aspectMethods.get(config.getAfterThrow()), aspectClass.newInstance());afterThrowingAdvice.setThrowingName(config.getAfterThrowClass());advices.add(afterThrowingAdvice);}this.methodCache.put(method, advices);}}} catch (Exception e) {e.printStackTrace();}}
}

四、拦截器的编写

通知拦截器有前置、后置和异常通知拦截器,除了这三个拦截器外,他们共同还拥有一个父类,用于处理三个拦截器通用的逻辑任务。

  1. AbstractAspectJAdvice:通知拦截器父类(用于处理子类的通用逻辑任务)
  2. MethodBeforeAdvice:前置通知拦截器
  3. AfterReturningAdvice:后置通知拦截器
  4. AfterThrowingAdvice:异常通知拦截器
  5. MethodInvocation:通知拦截器执行链

1.AbstractAspectJAdvice

package org.springframework.aop.aspect;import java.lang.reflect.Method;/*** <p>定义回调通知的通用逻辑</p>* @author Bosen* @date 2021/9/19 21:41*/
public abstract class AbstractAspectJAdvice {private Method aspectMethod;private Object aspectTarget;public AbstractAspectJAdvice(Method aspectMethod, Object aspectTarget) {this.aspectMethod = aspectMethod;this.aspectTarget = aspectTarget;}protected Object invokeAdviceMethod(JoinPoint joinPoint, Object returnValue, Throwable ex) throws Exception {Class<?>[] paramsTypes = this.aspectMethod.getParameterTypes();if (paramsTypes.length == 0) {return this.aspectMethod.invoke(aspectTarget);}Object[] args = new Object[paramsTypes.length];for (int i = 0; i < paramsTypes.length; i++) {if (paramsTypes[i] == JoinPoint.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);}public Method getAspectMethod() {return aspectMethod;}public void setAspectMethod(Method aspectMethod) {this.aspectMethod = aspectMethod;}public Object getAspectTarget() {return aspectTarget;}public void setAspectTarget(Object aspectTarget) {this.aspectTarget = aspectTarget;}
}

2.MethodBeforeAdvice

package org.springframework.aop.aspect;import org.springframework.aop.intercept.MethodInterceptor;
import org.springframework.aop.intercept.MethodInvocation;import java.lang.reflect.Method;/*** <p>前置通知</p>* @author Bosen* @date 2021/9/19 21:38*/
public class MethodBeforeAdvice extends AbstractAspectJAdvice implements Advice, MethodInterceptor {public MethodBeforeAdvice(Method aspectMethod, Object aspectTarget) {super(aspectMethod, aspectTarget);}@Overridepublic Object invoke(MethodInvocation mi) throws Exception {super.invokeAdviceMethod(mi, null, null);return mi.proceed();}
}

3.AfterReturningAdvice

package org.springframework.aop.aspect;import org.springframework.aop.intercept.MethodInterceptor;
import org.springframework.aop.intercept.MethodInvocation;import java.lang.reflect.Method;/*** <p>后置通知</p>* @author Bosen* @date 2021/9/19 22:18*/
public class AfterReturningAdvice extends AbstractAspectJAdvice implements Advice, MethodInterceptor {public AfterReturningAdvice(Method aspectMethod, Object aspectTarget) {super(aspectMethod, aspectTarget);}@Overridepublic Object invoke(MethodInvocation mi) throws Exception {Object returnValue = mi.proceed();invokeAdviceMethod(mi, returnValue, null);return returnValue;}
}

4.AfterThrowingAdvice

package org.springframework.aop.aspect;import org.springframework.aop.intercept.MethodInterceptor;
import org.springframework.aop.intercept.MethodInvocation;import java.lang.reflect.Method;/*** <p>异常通知</p>* @author Bosen* @date 2021/9/19 22:17*/
public class AfterThrowingAdvice extends AbstractAspectJAdvice implements Advice, MethodInterceptor {private String throwingName;private MethodInvocation mi;public AfterThrowingAdvice(Method aspectMethod, Object aspectTarget) {super(aspectMethod, aspectTarget);}public void setThrowingName(String name) {this.throwingName = name;}@Overridepublic Object invoke(MethodInvocation mi) throws Exception {try {return mi.proceed();} catch (Throwable ex) {super.invokeAdviceMethod(mi, null, ex.getCause());throw ex;}}
}

5.MethodInvocation

package org.springframework.aop.intercept;import org.springframework.aop.aspect.JoinPoint;import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** <p>执行拦截器链</p>* @author Bosen* @date 2021/9/19 16:15*/
public class MethodInvocation implements JoinPoint {/*** <p>代理对象</p>*/private Object proxy;/*** <p>代理的目标方法</p>*/private Method method;/*** <p>代理的目标对象</p>*/private Object target;/*** <p>代理的目标类</p>*/private Class<?> targetClass;/*** <p>代理的方法的参数列表</p>*/private Object[] arguments;/*** <p>回调方法链</p>*/private List<Object> interceptorsAndDynamicMethodMatchers;/*** <p>保存自定义属性</p>*/private Map<String, Object> userAttributes;private int currentInterceptor = -1;public MethodInvocation(Object proxy, Method method, Object target, Class<?> targetClass, Object[] arguments,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 Exception {// 如果执行链执行完后,执行joinPoint自己的业务逻辑方法if (this.currentInterceptor == this.interceptorsAndDynamicMethodMatchers.size() -1) {return this.method.invoke(this.target, this.arguments);}Object interceptorOrInterceptionAdvice =this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptor);// 如果该对象属于拦截器if (interceptorOrInterceptionAdvice instanceof MethodInterceptor) {MethodInterceptor mi = (MethodInterceptor) interceptorOrInterceptionAdvice;return mi.invoke(this);}return proceed();}@Overridepublic Method getMethod() {return this.method;}@Overridepublic Object[] getArguments() {return this.arguments;}@Overridepublic Object getThis() {return this.target;}@Overridepublic void setUserAttribute(String key, Object value) {if (value != null) {if (this.userAttributes == null) {this.userAttributes = new HashMap<>();}this.userAttributes.put(key, value);} else {if (this.userAttributes != null) {this.userAttributes.remove(key);}}}@Overridepublic Object getUserAttribute(String key) {return this.userAttributes != null ? this.userAttributes.get(key) : null;}
}

五、CGLib和JDK动态代理

如果需要代理的类实现了接口,默认使用JDK动态代理,否则使用CGLib代理

1.JdkDynamicAopProxy

package org.springframework.aop;import org.springframework.aop.intercept.MethodInvocation;
import org.springframework.aop.support.AdvisedSupport;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;/*** <p>JDK动态代理</p>* @author Bosen* @date 2021/9/19 22:58*/
public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {private Class targetClass;private Object target;private AdvisedSupport config;public JdkDynamicAopProxy(AdvisedSupport config) {this.config = config;}@Overridepublic Object getProxy() {return getProxy(this.config.getTargetClass().getClassLoader());}@Overridepublic Object getProxy(ClassLoader classLoader) {this.targetClass = this.config.getTargetClass();this.target = this.config.getTarget();return Proxy.newProxyInstance(classLoader, this.config.getTargetClass().getInterfaces(), this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Exception {List<Object> interceptorsAndDynamicMethodMatchers =this.config.getInterceptorAndDynamicInterceptionAdvice(method, this.targetClass);MethodInvocation invocation =new MethodInvocation(proxy, method, this.target,this.targetClass, args, interceptorsAndDynamicMethodMatchers);return invocation.proceed();}
}

2.CglibAopProxy

package org.springframework.aop;import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.springframework.aop.intercept.MethodInvocation;
import org.springframework.aop.support.AdvisedSupport;import java.lang.reflect.Method;
import java.util.List;/*** <p>CGLIG代理</p>* @author Bosen* @date 2021/9/19 22:53*/
public class CglibAopProxy implements AopProxy, MethodInterceptor {private Class targetClass;private Object target;private AdvisedSupport config;public CglibAopProxy(AdvisedSupport config) {this.config = config;}@Overridepublic Object getProxy() {return getProxy(this.config.getTargetClass().getClassLoader());}@Overridepublic Object getProxy(ClassLoader classLoader) {this.targetClass = this.config.getTargetClass();this.target = this.config.getTarget();Enhancer enhancer = new Enhancer();enhancer.setSuperclass(this.targetClass);enhancer.setCallback(this);return enhancer.create();}@Overridepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Exception {List<Object> interceptorsAndDynamicMethodMatchers =this.config.getInterceptorAndDynamicInterceptionAdvice(method, this.targetClass);MethodInvocation invocation =new MethodInvocation(proxy, method, this.target,this.targetClass, args, interceptorsAndDynamicMethodMatchers);return invocation.proceed();}
}

六、工具类的编写

工具类AopUtils主要完成的任务如下:

  1. 存储切面配置信息: 使用集合存储扫描出来的切面配置
  2. 初始化AOP配置类: 启动扫描有AOP注解标记的类
  3. 创建代理类: 为被代理的对象创建对应的代理类
  4. 判断是否是代理类: 判断传入来的对象是否是代理类
  5. 获取被代理的对象: 由代理对象获取被代理的对象

AopUtils

package org.springframework.aop.util;import org.springframework.annotation.aop.*;
import org.springframework.aop.AopConfig;
import org.springframework.aop.AopProxy;
import org.springframework.aop.CglibAopProxy;
import org.springframework.aop.JdkDynamicAopProxy;
import org.springframework.aop.support.AdvisedSupport;
import org.springframework.beans.factory.config.BeanDefinition;import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;/*** <p>Aop工具类</p>* @author Bosen* @date 2021/9/19 23:49*/
public class AopUtils {/*** <p>存储切面配置信息</p>*/public static final List<AdvisedSupport> CONFIGS = new ArrayList<>();/*** <p>初始化AOP配置类</p>*/public static void instantiationAopConfig(List<BeanDefinition> beanDefinitions) throws Exception {for (BeanDefinition beanDefinition : beanDefinitions) {Class<?> clazz = Class.forName(beanDefinition.getBeanClassName());// 如果该类不是切面Aspect则跳过if (!clazz.isAnnotationPresent(Aspect.class)) {continue;}AopConfig config = new AopConfig();Method[] methods = clazz.getMethods();// 设置切点和回调方法for (Method method : methods) {if (method.isAnnotationPresent(Pointcut.class)) {// 设置切点config.setPointCut(method.getAnnotation(Pointcut.class).value());}else if (method.isAnnotationPresent(Before.class)) {// 前后方法config.setBefore(method.getName());}else if (method.isAnnotationPresent(AfterReturning.class)) {// 后置方法config.setAfterReturn(method.getName());}else if (method.isAnnotationPresent(AfterThrowing.class)) {// 异常方法config.setAfterThrow(method.getName());config.setAfterThrowClass("java.lang.Exception");}}// 没有设置切点,跳过if (config.getPointCut() == null) {continue;}config.setAspectClass(beanDefinition.getBeanClassName());CONFIGS.add(new AdvisedSupport(config));}}/*** <p>创建代理类</p>*/public static AopProxy createProxy(AdvisedSupport config) {Class targetClass = config.getTargetClass();// 如果实现了接口则使用jdk动态代理,否则使用Cglib代理if (targetClass.getInterfaces().length > 0) {return new JdkDynamicAopProxy(config);}// 使用CGLIB代理return new CglibAopProxy(config);}/*** <p>判断是否是代理类</p>*/public static boolean isAopProxy(Object object) {return object.getClass().getSimpleName().contains("$");}/*** <p>获取被代理的对象</p>*/public static Object getTarget(Object proxy) throws Exception {if(proxy.getClass().getSuperclass() == Proxy.class) {return getJdkDynamicProxyTargetObject(proxy);} else {return getCglibProxyTargetObject(proxy);}}/*** <p>获取CGLIB被代理的对象</p>*/private static Object getCglibProxyTargetObject(Object proxy) throws Exception {Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");h.setAccessible(true);Object dynamicAdvisedInterceptor = h.get(proxy);Field config = dynamicAdvisedInterceptor.getClass().getDeclaredField("config");config.setAccessible(true);return ((AdvisedSupport) config.get(dynamicAdvisedInterceptor)).getTarget();}/*** <p>获取jdk代理被代理对象</p>*/private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception {Field h = proxy.getClass().getSuperclass().getDeclaredField("h");h.setAccessible(true);Object aopProxy = h.get(proxy);Field config = aopProxy.getClass().getDeclaredField("config");config.setAccessible(true);return ((AdvisedSupport) config.get(aopProxy)).getTarget();}
}

七、IOC容器接入AOP功能

为了让我们写好的AOP功能接入IOC容器中,我们需要对IOC的代码进行一定的修改:

  1. IOC容器初始化时,初始化AOP配置信息。
  2. 实例化Bean时,判断该Bean是否是需要被代理的类,如果是,则使用代理类代替其存入IOC容器中
  3. 依赖注入

1.初始化AOP配置

修改refresh方法如下:

@Override
public void refresh() throws Exception {// 扫描需要扫描的包,并把相关的类转化为beanDefinitionList<BeanDefinition> beanDefinitions = reader.loadBeanDefinitions();// 注册,将beanDefinition放入IOC容器存储doRegisterBeanDefinition(beanDefinitions);// 初始化AOP配置类AopUtils.instantiationAopConfig(beanDefinitions);// 将非懒加载的类初始化doAutowired();
}

2.将代理类存入IOC容器

修改instantiateBean方法如下:

private Object instantiateBean(BeanDefinition beanDefinition) {Object instance = null;String className = beanDefinition.getBeanClassName();try {// 先判断单例池中是否存在该类的实例if (this.factoryBeanObjectCache.containsKey(className)) {instance = this.factoryBeanObjectCache.get(className);} else {Class<?> clazz = Class.forName(className);instance = clazz.newInstance();// 接入AOP功能for (AdvisedSupport aspect : AopUtils.CONFIGS) {aspect.setTargetClass(clazz);aspect.setTarget(instance);if (aspect.pointCutMatch()) {instance = AopUtils.createProxy(aspect).getProxy();}}this.factoryBeanObjectCache.put(className, instance);this.factoryBeanObjectCache.put(beanDefinition.getFactoryBeanName(), instance);}} catch (Exception e) {e.printStackTrace();}return instance;
}

3.依赖注入

修改populateBean方法如下:

public void populateBean(Object instance) throws Exception {// 判断是否是代理类if (AopUtils.isAopProxy(instance)) {instance = AopUtils.getTarget(instance);}Class clazz = instance.getClass();// 判断是否有Controller、ITestService、Component、Repository等注解标记if (!(clazz.isAnnotationPresent(Component.class) ||clazz.isAnnotationPresent(Controller.class) ||clazz.isAnnotationPresent(Service.class) ||clazz.isAnnotationPresent(Repository.class))) {return;}Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {// 如果属性没有被Autowired标记,则跳过if (!field.isAnnotationPresent(Autowired.class)) {continue;}String autowiredBeanName = field.getType().getName();field.setAccessible(true);try {field.set(instance, this.factoryBeanInstanceCache.get(autowiredBeanName).getWrappedInstance());} catch (IllegalAccessException e) {e.printStackTrace();}}
}

八、AOP功能测试

1.LogAspect切面类

定义切点,前置、后置、异常通知

package org.springframework.test.aspect;import org.springframework.annotation.aop.*;@Aspect
public class LogAspect {/*** <p>配置切面</p>*/@Pointcut("public * org.springframework.test.service.*.*(..)")public void logPointcut(){}/*** <p>前置通知</p>*/@Beforepublic void logBefore() {System.out.println("This is LogAspect before");}/*** <p>后置返回通知</p>*/@AfterReturningpublic void logAfter() {System.out.println("This is LogAspect After");}/*** <p>异常通知</p>*/@AfterThrowingpublic void logAfterThrowing() {System.out.println("This is LogAspect AfterThrowing");}
}

2.ApplicationConfig容器配置类

IOC模块的配置类(基于注解配置)

package org.springframework.test.config;import org.springframework.annotation.core.ComponentScan;/*** <p>配置类</p>* @author Bosen* @date 2021/9/11 14:11*/
@ComponentScan("org.springframework.test")
public class ApplicationConfig {}

3.TestDAO层类

用于模拟访问数据库的操作

package org.springframework.test.dao;import org.springframework.annotation.core.Repository;/*** @author Bosen* @date 2021/9/11 22:29*/
@Repository
public class TestDAO {public String echo() {return "This is TestDAO#echo!!!";}
}

4.IService层接口

用于测试jdk动态代理,其中定义了echo方法

package org.springframework.test.service;/*** <p>用于测试AOP JDK动态代理的接口</p>* @author Bosen* @date 2021/9/21 21:05*/
public interface IService {void echo();
}

5.TestService实现类

package org.springframework.test.service;import org.springframework.annotation.beans.Autowired;
import org.springframework.annotation.core.Service;
import org.springframework.test.dao.TestDAO;/*** @author Bosen* @date 2021/9/11 22:30*/
@Service
public class TestService implements IService {@AutowiredTestDAO testDAO;@Overridepublic void echo() {System.out.println(testDAO.echo());}
}

6.编写测试类

package org.springframework.test;import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.test.config.ApplicationConfig;
import org.springframework.test.service.IService;/*** <p>测试启动类</p>* @author Bosen* @date 2021/9/12 0:25*/
public class ApplicationTest {public static void main(String[] args) throws Exception {ApplicationContext applicationContext =new AnnotationConfigApplicationContext(ApplicationConfig.class);IService service = (IService) applicationContext.getBean("testService");service.echo();}
}

执行main方法,运行结果如下

此时,在运行echo的前后会分别执行前置通知和后置通知。接下来我们尝试在echo方法中故意抛出一个异常,再次执行main方法,运行结果如下

此时,在echo方法前仍会执行前置通知,但是并不会执行后置通知,被取而代之的运行了异常通知。

至此,迷你版Spirng AOP功能我们已经亲手成功实现~!!

手撸Spring系列10:Spring AOP(实战篇)相关推荐

  1. QCC304x系列开发教程(实战篇) 之10.2 QCC3040之教你调试入仓和出仓情景下的程序运行

    查看全部教程开发请点击:高通蓝牙耳机QCC304x开发详解汇总(持续更新中) 查看本文全部文章请点击:QCC304x系列开发教程(实战篇) 之10.2 QCC3040之教你调试入仓和出仓情景下的程序运 ...

  2. QCC304x系列开发教程(实战篇) 之 QCC3040之RF测试

    查看全部教程开发请点击:高通蓝牙耳机QCC304x开发详解汇总(持续更新中) 查看本文全部文章请点击:QCC304x系列开发教程(实战篇) 之 QCC3040之RF测试 更新记录链接:QCC514x- ...

  3. QCC304x系列开发教程(实战篇) 之 QCC304x之DFU(固件升级)

    高通蓝牙耳机QCC304x开发详解汇总(持续更新中) 查看全部文章地址QCC304x系列开发教程(实战篇) 之 QCC304x之DFU(固件升级)_心跳包的博客-CSDN博客 版权归作者所有,未经允许 ...

  4. QCC304x系列开发教程(实战篇) 之5.3 QCC3040之QACT用户指南

    高通蓝牙耳机QCC304x开发详解汇总(持续更新中) 查看全部文章地址QCC304x系列开发教程(实战篇)  之5.3 QCC3040之QACT用户指南_心跳包的博客-CSDN博客 版权归作者所有,未 ...

  5. QCC304x系列开发教程(实战篇)之4.2QCC3040之MDE按键导入配置

    查看全部教程开发请点击:高通蓝牙耳机QCC304x开发详解汇总(持续更新中) 查看本文全部文章请点击:QCC304x系列开发教程(实战篇)之4.2QCC3040之MDE按键导入配置 ========= ...

  6. QCC304x系列开发教程(实战篇)之5.2 QCC3040之提示音

    查看全部教程开发请点击:高通蓝牙耳机QCC304x开发详解汇总(持续更新中) 查看本文全部文章请点击:QCC304x系列开发教程(实战篇)之5.2 QCC3040之提示音 更新记录链接:QCC514x ...

  7. [Spring手撸专栏学习笔记]——把AOP动态代理,融入到Bean的生命周期

    本文是学习<Spring 手撸专栏>第 10 章笔记,主要记录我的一些debug调试过程,方便后期复习.具体学习,大家可以去看一下这个专栏,强烈推荐. 方案 其实在有了AOP的核心功能实现 ...

  8. 【Spring 系列】Spring知识地图

    文章目录 Spring IOC 知道 会用 熟练 掌握 专家 Spring AOP 知道 会用 熟练 掌握 专家 Spring MVC 知道 会用 熟练 掌握 专家 Spring WebFlux 知道 ...

  9. Spring系列之Spring框架和SpringAOP集成过程分析(十)

    转载请注明出处:https://blog.csdn.net/zknxx/article/details/80724180 在开始这个系列之前大家先想一下我们是怎么在项目中使用SpringAOP的(这里 ...

最新文章

  1. 全自动驾驶“生死时速”,特斯拉收购计算机视觉创企DeepScale
  2. TensorFlow 笔记6--迁移学习
  3. PHP Hashtable实现源码分析
  4. String s1==s2面试题
  5. 51nod1245 Binomial Coefficients Revenge
  6. Maven—Eclipse中配置Maven
  7. Vue2.0安装教程
  8. git-remote-https.exe-无法找到入口
  9. (CVPR-2022)AdaViT:用于高效图像识别的自适应视觉变换器
  10. 塞班S60V5上的画图工具
  11. 人工智能中的顶级期刊
  12. CHIL-SQL-NULL 函数
  13. javaeye恢复正常了
  14. 科视Christie 举行中国分销商授证仪式
  15. matlab中wav转txt6,WAV转TXT专家下载
  16. Word分页符和分节符区别
  17. JS_自己写的JQ插件
  18. x38和x48是服务器芯片吗,guoshuo X58是什么主板
  19. 我的知识星球为什么要设置为6000元/年?
  20. UMP系统功能 容灾

热门文章

  1. CUDA + Visual Studio 环境搭建
  2. SQL中NVL()用法
  3. MySql项目查询优化经验总结
  4. char *c和char c[]区别
  5. element el-table 表格行列合并[{class1:‘1101‘,arr1:[1,2,3,5],class2:‘1102‘,arr2:[4,5,6],name:‘h‘}]
  6. 2018网易校招移动安全工程师笔试题
  7. Redis如何应对并发访问
  8. 整除java符号_Java运算符
  9. C++实现字符串的部分复制
  10. 通过sql给表添加字段