30个类手写Spring核心原理之AOP代码织入(5)

private Map<Method,Map<String, Method>> methodAdvices = new HashMap<Method, Map<String, Method>>();

下面我完整的写出一个简易的ApplicationContex,小伙伴可以参考 一下:

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

import java.io.IOException;

import java.io.InputStream;

import java.lang.reflect.Method;

import java.util.HashMap;

import java.util.Map;

import java.util.Properties;

import java.util.regex.Matcher;

import java.util.regex.Pattern;

public class GPApplicationContext {

private Properties contextConfig = new Properties();

private Map<String,Object> ioc = new HashMap<String,Object>();

//用来保存配置文件中对应的Method和Advice的对应关系

private Map<Method,Map<String, Method>> methodAdvices = new HashMap<Method, Map<String, Method>>();

public GPApplicationContext(){

//为了演示,手动初始化一个Bean

ioc.put(“memberService”, new MemberService());

doLoadConfig(“application.properties”);

doInitAopConfig();

}

public Object getBean(String name){

return createProxy(ioc.get(name));

}

private Object createProxy(Object instance){

return new GPJdkDynamicAopProxy(instance).getProxy();

}

//加载配置文件

private void doLoadConfig(String contextConfigLocation) {

//直接从类路径下找到Spring主配置文件所在的路径

//并且将其读取出来放到Properties对象中

//相对于scanPackage=com.gupaoedu.demo 从文件中保存到了内存中

InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);

try {

contextConfig.load(is);

} catch (IOException e) {

e.printStackTrace();

}finally {

if(null != is){

try {

is.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

private void doInitAopConfig() {

try {

Class apectClass = Class.forName(contextConfig.getProperty(“aspectClass”));

Map<String,Method> aspectMethods = new HashMap<String,Method>();

for (Method method : apectClass.getMethods()) {

aspectMethods.put(method.getName(),method);

}

//PonintCut 表达式解析为正则表达式

String pointCut = contextConfig.getProperty(“pointCut”)

.replaceAll(“\.”,“\\.”)

.replaceAll(“\\.\“,”.”)

.replaceAll(“\(”,“\\(”)

.replaceAll(“\)”,“\\)”);

Pattern pointCutPattern = Pattern.compile(pointCut);

for (Map.Entry<String,Object> entry : ioc.entrySet()) {

Class<?> clazz = entry.getValue().getClass();

//循环找到所有的方法

for (Method method : clazz.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,Method> advices = new HashMap<String,Method>();

if(!(null == contextConfig.getProperty(“aspectBefore”) || “”.equals( contextConfig.getProperty(“aspectBefore”)))){

advices.put(“before”,aspectMethods.get(contextConfig.getProperty(“aspectBefore”)));

}

if(!(null == contextConfig.getProperty(“aspectAfter”) || “”.equals( contextConfig.getProperty(“aspectAfter”)))){

advices.put(“after”,aspectMethods.get(contextConfig.getProperty(“aspectAfter”)));

}

if(!(null == contextConfig.getProperty(“aspectAfterThrow”) || “”.equals( contextConfig.getProperty(“aspectAfterThrow”)))){

advices.put(“afterThrow”,aspectMethods.get(contextConfig.getProperty(“aspectAfterThrow”)));

}

methodAdvices.put(method,advices);

}

}

}

}catch (Exception e){

e.printStackTrace();

}

}

class GPJdkDynamicAopProxy implements GPInvocationHandler {

private Object instance;

public GPJdkDynamicAopProxy(Object instance) {

this.instance = instance;

}

public Object getProxy() {

return Proxy.newProxyInstance(instance.getClass().getClassLoader(),instance.getClass().getInterfaces(),this);

}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

Object aspectObject = Class.forName(contextConfig.getProperty(“aspectClass”)).newInstance();

Map<String,Method> advices = methodAdvices.get(instance.getClass().getMethod(method.getName(),method.getParameterTypes()));

Object returnValue = null;

advices.get(“before”).invoke(aspectObject);

try {

returnValue = method.invoke(instance, args);

}catch (Exception e){

advices.get(“afterThrow”).invoke(aspectObject);

e.printStackTrace();

throw e;

}

advices.get(“after”).invoke(aspectObject);

return returnValue;

}

}

}

测试代码:

public class MemberServiceTest {

public static void main(String[] args) {

GPApplicationContext applicationContext = new GPApplicationContext();

IMemberService memberService = (IMemberService)applicationContext.getBean(“memberService”);

try {

memberService.get(“1”);

memberService.save(new Member());

} catch (Exception e) {

e.printStackTrace();

}

}

}

我们通过简单几百行代码,就可以完整地演示Spring AOP的核心原理,是不是很简单呢?当然,小伙伴们还是要自己动手哈亲自体验一下,这样才会印象深刻。下面,我们继续完善,将Spring AOP 1.0升级到2.0,那么2.0版本我是完全仿真Spring的原始设计来写的,希望能够给大家带来不一样的手写体验,从而更加深刻地理解Spring AOP的原理。

3 完成AOP顶层设计


3.1 GPJoinPoint

定义一个切点的抽象,这是AOP的基础组成单元。我们可以理解为这是某一个业务方法的附加信息。可想而知,切点应该包含业务方法本身、实参列表和方法所属的实例对象,还可以在GPJoinPoint中添加自定义属性,看下面的代码:

package com.tom.spring.formework.aop.aspect;

import java.lang.reflect.Method;

/**

  • 回调连接点,通过它可以获得被代理的业务方法的所有信息

*/

public interface GPJoinPoint {

Method getMethod(); //业务方法本身

Object[] getArguments(); //该方法的实参列表

Object getThis(); //该方法所属的实例对象

//在JoinPoint中添加自定义属性

void setUserAttribute(String key, Object value);

//从已添加的自定义属性中获取一个属性值

Object getUserAttribute(String key);

}

3.2 GPMethodInterceptor

方法拦截器是AOP代码增强的基本组成单元,其子类主要有GPMethodBeforeAdvice、GPAfterReturningAdvice和GPAfterThrowingAdvice。

package com.tom.spring.formework.aop.intercept;

/**

  • 方法拦截器顶层接口

*/

public interface GPMethodInterceptor{

Object invoke(GPMethodInvocation mi) throws Throwable;

}

3.3 GPAopConfig

定义AOP的配置信息的封装对象,以方便在之后的代码中相互传递。

package com.tom.spring.formework.aop;

import lombok.Data;

/**

  • AOP配置封装

*/

@Data

public class GPAopConfig {

//以下配置与properties文件中的属性一一对应

private String pointCut; //切面表达式

private String aspectBefore; //前置通知方法名

private String aspectAfter; //后置通知方法名

private String aspectClass; //要织入的切面类

private String aspectAfterThrow; //异常通知方法名

private String aspectAfterThrowingName; //需要通知的异常类型

}

3.4 GPAdvisedSupport

GPAdvisedSupport主要完成对AOP配置的解析。其中pointCutMatch()方法用来判断目标类是否符合切面规则,从而决定是否需要生成代理类,对目标方法进行增强。而getInterceptorsAndDynamic- InterceptionAdvice()方法主要根据AOP配置,将需要回调的方法封装成一个拦截器链并返回提供给外部获取。

package com.tom.spring.formework.aop.support;

import com.tom.spring.formework.aop.GPAopConfig;

import com.tom.spring.formework.aop.aspect.GPAfterReturningAdvice;

import com.tom.spring.formework.aop.aspect.GPAfterThrowingAdvice;

import com.tom.spring.formework.aop.aspect.GPMethodBeforeAdvice;

import java.lang.reflect.Method;

import java.util.*;

import java.util.regex.Matcher;

import java.util.regex.Pattern;

/**

  • 主要用来解析和封装AOP配置

*/

public class GPAdvisedSupport {

private Class targetClass;

private Object target;

private Pattern pointCutClassPattern;

private transient Map<Method, List> methodCache;

private GPAopConfig config;

public GPAdvisedSupport(GPAopConfig 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 getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) throws Exception {

List cached = methodCache.get(method);

//缓存未命中,则进行下一步处理

if (cached == null) {

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 = config.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>();

Pattern pattern = Pattern.compile(pointCut);

try {

Class aspectClass = Class.forName(config.getAspectClass());

Map<String,Method> aspectMethods = new HashMap<String,Method>();

for (Method m : aspectClass.getMethods()){

aspectMethods.put(m.getName(),m);

}

//在这里得到的方法都是原生方法

for (Method m : targetClass.getMethods()){

String methodString = m.toString();

if(methodString.contains(“throws”)){

methodString = methodString.substring(0,methodString.lastIndexOf(“throws”)).trim();

}

Matcher matcher = pattern.matcher(methodString);

if(matcher.matches()){

//能满足切面规则的类,添加到AOP配置中

List advices = new LinkedList();

//前置通知

if(!(null == config.getAspectBefore() || “”.equals(config.getAspectBefore().trim()))) {

advices.add(new GPMethodBeforeAdvice(aspectMethods.get (config.getAspectBefore()), aspectClass.newInstance()));

}

//后置通知

if(!(null == config.getAspectAfter() || “”.equals(config.getAspectAfter(). trim()))) {

advices.add(new GPAfterReturningAdvice(aspectMethods.get (config.getAspectAfter()), aspectClass.newInstance()));

}

//异常通知

if(!(null == config.getAspectAfterThrow() || “”.equals(config.getAspectAfterThrow().trim()))) {

GPAfterThrowingAdvice afterThrowingAdvice = new GPAfterThrowingAdvice (aspectMethods.get(config.getAspectAfterThrow()), aspectClass.newInstance());

afterThrowingAdvice.setThrowingName(config.getAspectAfterThrowingName());

advices.add(afterThrowingAdvice);

}

methodCache.put(m,advices);

}

}

} catch (Exception e) {

e.printStackTrace();

}

}

}

3.5 GPAopProxy

GPAopProxy是代理工厂的顶层接口,其子类主要有两个:GPCglibAopProxy和GPJdkDynamicAopProxy,分别实现CGlib代理和JDK Proxy代理。

package com.tom.spring.formework.aop;

/**

  • 代理工厂的顶层接口,提供获取代理对象的顶层入口

*/

//默认就用JDK动态代理

public interface GPAopProxy {

//获得一个代理对象

Object getProxy();

//通过自定义类加载器获得一个代理对象

Object getProxy(ClassLoader classLoader);

}

3.6 GPCglibAopProxy

本文未实现CglibAopProxy,感兴趣的“小伙伴”可以自行尝试。

package com.tom.spring.formework.aop;

import com.tom.spring.formework.aop.support.GPAdvisedSupport;

/**

  • 使用CGlib API生成代理类,在此不举例

  • 感兴趣的“小伙伴”可以自行实现

*/

public class GPCglibAopProxy implements GPAopProxy {

private GPAdvisedSupport config;

public GPCglibAopProxy(GPAdvisedSupport config){

this.config = config;

}

@Override

public Object getProxy() {

return null;

}

@Override

public Object getProxy(ClassLoader classLoader) {

return null;

}

}

3.7 GPJdkDynamicAopProxy

下面来看GPJdkDynamicAopProxy的实现,主要功能在invoke()方法中。从代码量来看其实不多,主要是调用了GPAdvisedSupport的getInterceptorsAndDynamicInterceptionAdvice()方法获得拦截器链。在目标类中,每一个被增强的目标方法都对应一个拦截器链。

package com.tom.spring.formework.aop;

import com.tom.spring.formework.aop.intercept.GPMethodInvocation;

import com.tom.spring.formework.aop.support.GPAdvisedSupport;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

import java.util.List;

/**

  • 使用JDK Proxy API生成代理类

*/

public class GPJdkDynamicAopProxy implements GPAopProxy,InvocationHandler {

private GPAdvisedSupport config;

public GPJdkDynamicAopProxy(GPAdvisedSupport config){

this.config = config;

}

//把原生的对象传进来

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 interceptorsAndDynamicMethodMatchers = config.getInterceptorsAndDynamicInterceptionAdvice(method,this.config.getTargetClass());

//交给拦截器链MethodInvocation的proceed()方法执行

GPMethodInvocation invocation = new GPMethodInvocation(proxy,this.config.getTarget(), method,args,this.config.getTargetClass(),interceptorsAndDynamicMethodMatchers);

return invocation.proceed();

}

}

从代码中可以看出,从GPAdvisedSupport中获得的拦截器链又被当作参数传入GPMethodInvocation的构造方法中。那么GPMethodInvocation中到底又对方法链做了什么呢?

3.8 GPMethodInvocation

GPMethodInvocation的代码如下:

package com.tom.spring.formework.aop.intercept;

import com.tom.spring.formework.aop.aspect.GPJoinPoint;

import java.lang.reflect.Method;

import java.util.List;

/**

  • 执行拦截器链,相当于Spring中ReflectiveMethodInvocation的功能

*/

public class GPMethodInvocation implements GPJoinPoint {

private Object proxy; //代理对象

private Method method; //代理的目标方法

private Object target; //代理的目标对象

private Class<?> targetClass; //代理的目标类

private Object[] arguments; //代理的方法的实参列表

private List interceptorsAndDynamicMethodMatchers; //回调方法链

//保存自定义属性

private Map<String, Object> userAttributes;

private int currentInterceptorIndex = -1;

public GPMethodInvocation(Object proxy, Object target, Method method, Object[] arguments,

Class<?> targetClass, List interceptorsAndDynamicMethodMatchers) {

this.proxy = proxy;

this.target = target;

this.targetClass = targetClass;

this.method = method;

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 GPMethodInterceptor) {

GPMethodInterceptor mi = (GPMethodInterceptor) interceptorOrInterceptionAdvice;

return mi.invoke(this);

} else {

//执行当前Intercetpor

return proceed();

}

}

@Override

public Method getMethod() {

return this.method;

}

@Override

public Object[] getArguments() {

return this.arguments;

}

@Override

public Object getThis() {

return this.target;

}

public void setUserAttribute(String key, Object value) {

if (value != null) {

if (this.userAttributes == null) {

this.userAttributes = new HashMap<String,Object>();

}

this.userAttributes.put(key, value);

}

else {

if (this.userAttributes != null) {

this.userAttributes.remove(key);

}

}

}

public Object getUserAttribute(String key) {

return (this.userAttributes != null ? this.userAttributes.get(key) : null);

}

}

从代码中可以看出,proceed()方法才是MethodInvocation的关键所在。在proceed()中,先进行判断,如果拦截器链为空,则说明目标方法无须增强,直接调用目标方法并返回。如果拦截器链不为空,则将拦截器链中的方法按顺序执行,直到拦截器链中所有方法全部执行完毕。

4 设计AOP基础实现


4.1 GPAdvice

GPAdvice作为所有回调通知的顶层接口设计,在Mini版本中为了尽量和原生Spring保持一致,只是被设计成了一种规范,并没有实现任何功能。

/**

  • 回调通知顶层接口

*/

public interface GPAdvice {

}

4.2 GPAbstractAspectJAdvice

使用模板模式设计GPAbstractAspectJAdvice类,封装拦截器回调的通用逻辑,主要封装反射动态调用方法,其子类只需要控制调用顺序即可。

package com.tom.spring.formework.aop.aspect;

import java.lang.reflect.Method;

/**

  • 封装拦截器回调的通用逻辑,在Mini版本中主要封装了反射动态调用方法

*/

public abstract class GPAbstractAspectJAdvice implements GPAdvice {

private Method aspectMethod;

private Object aspectTarget;

public GPAbstractAspectJAdvice(

Method aspectMethod, Object aspectTarget) {

this.aspectMethod = aspectMethod;

this.aspectTarget = aspectTarget;

}

//反射动态调用方法

protected Object invokeAdviceMethod(GPJoinPoint 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++) {
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

面试准备+复习分享:

为了应付面试也刷了很多的面试题与资料,现在就分享给有需要的读者朋友,资料我只截取出来一部分哦

秋招|美团java一面二面HR面面经,分享攒攒人品

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!

//反射动态调用方法

protected Object invokeAdviceMethod(GPJoinPoint 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++) {
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。[外链图片转存中…(img-Z4tRTpDR-1712087058425)]

[外链图片转存中…(img-DvWjVLGI-1712087058426)]

[外链图片转存中…(img-9W2O3RiX-1712087058426)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

面试准备+复习分享:

为了应付面试也刷了很多的面试题与资料,现在就分享给有需要的读者朋友,资料我只截取出来一部分哦

[外链图片转存中…(img-37yn3rsg-1712087058427)]

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值