[02][02][03] 用30个类手写Spring V2.0版本之AOP实现

[TOC]

在完全掌握 Spring 系统结构,实现原理,在理解设计模式的基础上,自己动手写一个高仿真版本的 Spring 框架,以达到透彻理解 Spring 的目的,感受作者创作意图

1. 基础配置

在 appliction.properties 中增力加如下自定义配置∶

#类扫描包路径
scanPackage=com.gupaoedu.vip.demo

templateRoot=layouts

#切面表达式 expression
pointCut=public .* com.gupaoedu.vip.demo.service..*Service..*(.*)
#切面类
aspectClass=com.gupaoedu.vip.demo.aspect.LogAspect
#切面前置通知
aspectBefore=before
#切面后置通知
aspectAfter=after
#切面异常通知
aspectAfterThrow=afterThrowing
#切面异常类型
aspectAfterThrowingName=java.lang.Exception

下面是 Spring AOP 的原生配置,为了方便操作,用 properties 文件来代替 xml,以简化操作∶





    
    
        
        
        
        
        
        
    

2. 完成 AOP 顶层设计

2.1 GPJdkDynamicAopProxy 基于 JDK 动态代理实现

public class GPJdkDynamicAopProxy implements InvocationHandler {
    private GPAdvisedSupport config;

    public GPJdkDynamicAopProxy(GPAdvisedSupport config) {
        this.config = config;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Map advices = config.getAdvices(method,null);

        Object returnValue;
        try {
            invokeAdivce(advices.get("before"));

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

            invokeAdivce(advices.get("after"));
        }catch (Exception e){
            invokeAdivce(advices.get("afterThrow"));
            throw e;
        }

        return returnValue;
    }

    private void invokeAdivce(GPAdvice advice) {
        try {
            advice.getAdviceMethod().invoke(advice.getAspect());
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

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

2.2 GPAdvisedSupport 解析配置

public class GPAdvisedSupport {
    private GPAopConfig config;
    private Object target;
    private Class targetClass;
    private Pattern pointCutClassPattern;

    private Map> methodCache;

    public GPAdvisedSupport(GPAopConfig config) {
        this.config = config;
    }

    //解析配置文件的方法
    private void parse() {

        //把 Spring 的 Excpress 变成 Java 能够识别的正则表达式
        String pointCut = config.getPointCut().replaceAll("\\.", "\\\\.").replaceAll("\\\\.\\*", ".*")
                .replaceAll("\\(", "\\\\(").replaceAll("\\)", "\\\\)");

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


        //享元的共享池
        methodCache = new HashMap>();
        //保存专门匹配方法的正则
        Pattern pointCutPattern = Pattern.compile(pointCut);
        try {
            Class aspectClass = Class.forName(this.config.getAspectClass());
            Map aspectMethods = new HashMap();
            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 advices = new HashMap();

                    if (!(null == config.getAspectBefore() || "".equals(config.getAspectBefore()))) {
                        advices.put("before", new GPAdvice(aspectClass.newInstance(), aspectMethods.get(config.getAspectBefore())));
                    }
                    if (!(null == config.getAspectAfter() || "".equals(config.getAspectAfter()))) {
                        advices.put("after", new GPAdvice(aspectClass.newInstance(), aspectMethods.get(config.getAspectAfter())));
                    }
                    if (!(null == config.getAspectAfterThrow() || "".equals(config.getAspectAfterThrow()))) {
                        GPAdvice advice = new GPAdvice(aspectClass.newInstance(), aspectMethods.get(config.getAspectAfterThrow()));
                        advice.setThrowName(config.getAspectAfterThrowingName());
                        advices.put("afterThrow", advice);
                    }

                    //跟目标代理类的业务方法和 Advices 建立一对多个关联关系,以便在 Porxy 类中获得
                    methodCache.put(method, advices);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //根据一个目标代理类的方法,获得其对应的通知
    public Map getAdvices(Method method, Object o) throws Exception {
        //享元设计模式的应用
        Map 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 pointCutMath() {
        return pointCutClassPattern.matcher(this.targetClass.toString()).matches();
    }

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

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

    public Class getTargetClass() {
        return targetClass;
    }

    public Object getTarget() {
        return target;
    }
}

2.3 GPAdvice 通知接口定义

@Data
@NoArgsConstructor
@AllArgsConstructor
public class GPAdvice {
    private Object aspect;
    private Method adviceMethod;
    private String throwName;

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

2.4 GPAopConfig 封装配置

@Data
@NoArgsConstructor
@AllArgsConstructor
public class GPAopConfig {

    private String pointCut;
    private String aspectClass;
    private String aspectBefore;
    private String aspectAfter;
    private String aspectAfterThrow;
    private String aspectAfterThrowingName;
}

3. 设计 AOP 基础实现

3.1 接入 getBean() 方法与 IOC 容器衔接

找到 GPApplicationContext 的 getBean() 方法,我们知道 getBean() 中负责 Bean 初始化的方法其实就是 instantiateBean(),我们在初始化时就可以确定是否返回原生 Bean 还是 Proxy Bean.代码实现如下

public class GPApplicationContext {

    private GPBeanDefinitionReader reader;

    private Map beanDefinitionMap = new HashMap();

    private Map factoryBeanInstanceCache = new HashMap();
    private Map factoryBeanObjectCache = new HashMap();

    public GPApplicationContext(String... configLocations) {

        //1、加载配置文件
        reader = new GPBeanDefinitionReader(configLocations);

        try {
            //2、解析配置文件,封装成 BeanDefinition
            List beanDefinitions = reader.loadBeanDefinitions();

            //3、把 BeanDefintion 缓存起来
            doRegistBeanDefinition(beanDefinitions);

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

    private void doAutowrited() {
        //调用 getBean()
        //这一步,所有的 Bean 并没有真正的实例化,还只是配置阶段
        for (Map.Entry beanDefinitionEntry : this.beanDefinitionMap.entrySet()) {
            String beanName = beanDefinitionEntry.getKey();
            getBean(beanName);
        }
    }

    private void doRegistBeanDefinition(List beanDefinitions) throws Exception {
        for (GPBeanDefinition beanDefinition : beanDefinitions) {
            if (this.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())) {
                throw new Exception("The " + beanDefinition.getFactoryBeanName() + "is exists");
            }
            beanDefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition);
            beanDefinitionMap.put(beanDefinition.getBeanClassName(), beanDefinition);
        }
    }

    //Bean 的实例化,DI 是从而这个方法开始的
    public Object getBean(String beanName) {
        //1、先拿到 BeanDefinition 配置信息
        GPBeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
        //2、反射实例化 newInstance();
        Object instance = instantiateBean(beanName, beanDefinition);
        //3、封装成一个叫做 BeanWrapper
        GPBeanWrapper beanWrapper = new GPBeanWrapper(instance);
        //4、保存到 IoC 容器
        factoryBeanInstanceCache.put(beanName, beanWrapper);
        //5、执行依赖注入
        populateBean(beanName, beanDefinition, beanWrapper);

        return beanWrapper.getWrapperInstance();
    }

    private void populateBean(String beanName, GPBeanDefinition beanDefinition, GPBeanWrapper beanWrapper) {
        //可能涉及到循环依赖?
        //A{ B b}
        //B{ A b}
        //用两个缓存,循环两次
        //1、把第一次读取结果为空的 BeanDefinition 存到第一个缓存
        //2、等第一次循环之后,第二次循环再检查第一次的缓存,再进行赋值

        Object instance = beanWrapper.getWrapperInstance();

        Class clazz = beanWrapper.getWrappedClass();

        //在 Spring 中@Component
        if (!(clazz.isAnnotationPresent(GPController.class) || clazz.isAnnotationPresent(GPService.class))) {
            return;
        }

        //把所有的包括 private/protected/default/public 修饰字段都取出来
        for (Field field : clazz.getDeclaredFields()) {
            if (!field.isAnnotationPresent(GPAutowired.class)) {
                continue;
            }

            GPAutowired autowired = field.getAnnotation(GPAutowired.class);

            //如果用户没有自定义的 beanName,就默认根据类型注入
            String autowiredBeanName = autowired.value().trim();
            if ("".equals(autowiredBeanName)) {
                //field.getType().getName() 获取字段的类型
                autowiredBeanName = field.getType().getName();
            }

            //暴力访问
            field.setAccessible(true);

            try {
                if (this.factoryBeanInstanceCache.get(autowiredBeanName) == null) {
                    continue;
                }
                //ioc.get(beanName) 相当于通过接口的全名拿到接口的实现的实例
                field.set(instance, this.factoryBeanInstanceCache.get(autowiredBeanName).getWrapperInstance());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                continue;
            }
        }

    }

    //创建真正的实例对象
    private Object instantiateBean(String beanName, GPBeanDefinition beanDefinition) {
        String className = beanDefinition.getBeanClassName();
        Object instance = null;
        try {
            if (this.factoryBeanObjectCache.containsKey(beanName)) {
                instance = this.factoryBeanObjectCache.get(beanName);
            } else {
                Class clazz = Class.forName(className);
                //2、默认的类名首字母小写
                instance = clazz.newInstance();

                //==================AOP 开始=========================
                //如果满足条件,就直接返回 Proxy 对象
                //1、加载 AOP 的配置文件
                GPAdvisedSupport config = instantionAopConfig(beanDefinition);
                config.setTargetClass(clazz);
                config.setTarget(instance);

                //判断规则,要不要生成代理类,如果要就覆盖原生对象
                //如果不要就不做任何处理,返回原生对象
                if (config.pointCutMath()) {
                    instance = new GPJdkDynamicAopProxy(config).getProxy();
                }

                //===================AOP 结束========================
                this.factoryBeanObjectCache.put(beanName, instance);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }

    private GPAdvisedSupport instantionAopConfig(GPBeanDefinition beanDefinition) {
        GPAopConfig config = new GPAopConfig();
        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 GPAdvisedSupport(config);
    }

    public Object getBean(Class beanClass) {
        return getBean(beanClass.getName());
    }

    public int getBeanDefinitionCount() {
        return this.beanDefinitionMap.size();
    }

    public String[] getBeanDefinitionNames() {
        return this.beanDefinitionMap.keySet().toArray(new String[this.beanDefinitionMap.size()]);
    }

    public Properties getConfig() {
        return this.reader.getConfig();
    }
}

4. 织入业务代码

4.1 LogAspect 自定义切面配置

@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("出现异常");
    }
}

你可能感兴趣的:([02][02][03] 用30个类手写Spring V2.0版本之AOP实现)