纯手写springaop,深入了解springaop核心思想

上节利用反射及注解技术我们是现在springmvc的核心功能,本节我们继续完成springaop的代理过程。

1.首先我们定义aop需要用到的注解

Aspect:aop的起始点,注解在类上
PointCut:注解包名,aop扫描该包
Before:controller方法执行前
AfterReturning:controller方法执行结束后

2.扫描包,找到aspect类,并找到pointcut注解,获取包名

private ConcurrentHashMap<String, Object> iocMap;
public AopLoader(ConcurrentHashMap<String, Object> iocMap){
    this.iocMap=iocMap;
}
private ClassLoader classLoader = this.getClass().getClassLoader();

/**
 * 扫描注解aspect
 *
 * @param packageName :需要扫描的基础包
 */
public void scanAspect(String packageName) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException {
    URL url = classLoader.getResource("/" + packageName.replaceAll("\\.", "/"));
    assert url != null;
    File classDir = new File(url.getFile());
    List<File> dirList = Arrays.asList(Objects.requireNonNull(classDir.listFiles()));
    for (File dir : dirList) {
        if (dir.isDirectory()) {
            scanAspect(packageName + "." + dir.getName());
            continue;
        }
        System.out.println("aop==" + packageName + "." + dir.getName());
        String realPackageName = (packageName + "." + dir.getName()).replaceAll(".class", "");
        Class<?> cls = Class.forName(realPackageName);
        if (!cls.isAnnotationPresent(Aspect.class))
            continue;
        Method[] methods = cls.getMethods();
        for (Method method : methods) {
            if(method.isAnnotationPresent(PointCut.class)){
                PointCut pointCut=method.getAnnotation(PointCut.class);
                //扫描到了切入点包,下一步进行动态代理
                addProxy(pointCut.value(),cls.newInstance());
            }
        }
        //包含Aspect标签,需要扫描切入包


    }
}

3.扫描到包,对切入点的包下的controller进行动态代理

本文用到的iocmap,请参照上文手写springmvc
public void addProxy(String proxyPackage,Object aspectClass) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException {
    URL url = classLoader.getResource("/" + proxyPackage.replaceAll("\\.", "/"));
    assert url != null;
    File classDir = new File(url.getFile());
    List<File> dirList = Arrays.asList(Objects.requireNonNull(classDir.listFiles()));
    for (File dir : dirList) {
        if (dir.isDirectory()) {
            scanAspect(proxyPackage + "." + dir.getName());
            continue;
        }
        String realPackageName = (proxyPackage + "." + dir.getName()).replaceAll(".class", "");
        Class<?> cls = Class.forName(realPackageName);
        if(!iocMap.containsKey(StringUtil.lowerFirstCapse(cls.getSimpleName())))
            continue;
       Object iocBean= iocMap.get(StringUtil.lowerFirstCapse(cls.getSimpleName()));
        AdviceProxy adviceProxy=new AdviceProxy();
        iocBean= adviceProxy.createProxyObject(iocBean);
        adviceProxy.setAspect(aspectClass);
       iocMap.put(StringUtil.lowerFirstCapse(cls.getSimpleName()),iocBean);
    }
}

下面贴出动态代理的方法

package com.gfh.mvc.framework.aop.proxy;

import com.gfh.mvc.framework.aop.annotation.AfterReturning;
import com.gfh.mvc.framework.aop.annotation.Before;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class AdviceProxy implements MethodInterceptor {

    private Object aspect;
    /**
     * 要被代理的对象
     */
    private Object targetObject;


    public Object createProxyObject(Object target) {
        this.targetObject = target;
        //该类用于生成代理对象
        Enhancer enhancer = new Enhancer();
        //设置目标类为代理对象的父类
        enhancer.setSuperclass(this.targetObject.getClass());
        //设置回调用对象为本身
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        Object result;
        //执行前
        before();
        //执行拦截的方法
        result = methodProxy.invoke(targetObject, args);
        after();

        return result;
    }

    /**
     * 执行方法前
     */
    private void before() {
        Method[] methods = aspect.getClass().getMethods();
        for (Method method : methods) {
            //查询出带有Before注解的方法
            if (!method.isAnnotationPresent(Before.class))
                continue;
            try {
                method.invoke(aspect);
            } catch (IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 执行方法后
     */
    private void after() {
        Method[] methods = aspect.getClass().getMethods();
        for (Method method : methods) {
            //查询出带有Before注解的方法
            if (!method.isAnnotationPresent(AfterReturning.class))
                continue;
            try {
                method.invoke(aspect);
            } catch (IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }


    public Object getAspect() {
        return aspect;
    }

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

4.我们只需要在ioc容器注入完成以后初始化aop即可

AopLoader aopLoader=new AopLoader(iocMap);
try {
    aopLoader.scanAspect("com.gfh.mvc.framework");

} catch (IOException | ClassNotFoundException | IllegalAccessException | InstantiationException e) {
    e.printStackTrace();
}

5.我们来写一下aop的切入方法

package com.gfh.mvc.framework.aop;

import com.gfh.mvc.framework.aop.annotation.AfterReturning;
import com.gfh.mvc.framework.aop.annotation.Aspect;
import com.gfh.mvc.framework.aop.annotation.Before;
import com.gfh.mvc.framework.aop.annotation.PointCut;


@Aspect
public class AopAdvice {
    private Long time;
    @PointCut("com.gfh.mvc.framework.controller")
    public void cut(){
    }

    @Before
    public void before(){
    System.out.println("我来了before");
    time=System.currentTimeMillis();
    }

    @AfterReturning
    public void AfterReturning(){
        System.out.println("我来了AfterReturning:"+(System.currentTimeMillis()-time));
    }
}

6.执行结果

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值