AOP初识

AOP是什么?

AOP(Aspect Oriented Programming),即为面向切面编程。其作为一种新的编程思想,主要是将多数代码中共用的部分抽象出来,采用动态代理、静态代理等方式,自动添加到对应代码的首部或尾部。从而简化业务代码重复逻辑,提升开发效率。

AOP常见概念

  1. 增强/通知(advice),在特定连接点需要执行的动作。Spring下主要包括五种通知类型:
    • 前置通知(Before)
    • 后置通知(After)
    • 返回通知(After-returning)
    • 异常通知(After-throwing)
    • 环绕通知(Around)
  2. 切点(pointcut),指在特定连接点应该调用的时机。
  3. 连接点(Joint Point),指的是可以应用通知进行增强的方法。
  4. 切面(Aspect),切入点和通知的结合
  5. 织入(weaving),通过代理对目标对象方法进行增强的过程。

AOP原理

AOP实现的原理,主要基于代理模式。其又主要分为两种:1、静态代理;2、动态代理。

静态代理

静态代理来说相对比较简单,主要逻辑如下所示:

主要关注几个点:

1、被代理类与代理类需要共同实现一个代理接口的方法。

2、被代理类需要通过getter、setter方法注入到代理类中。

3、外部调用代理类的方法,从而实现增强逻辑。

具体代码实现如下:

@Data
@Slf4j
public class NormalAction implements MyFunction { // 被代理类
    @Override
    public void display() {
        LOGGER.info("我是被代理的用户,我很苦逼!");
    }
}
@Slf4j
@Data
public class StaticProxyAction implements MyFunction { // 代理类
    NormalAction normalAction;//被注入的代理类,由getter、setter方法注入

    @Override
    public void display() {
        LOGGER.info("我是代理人>>>>>>>>");
        LOGGER.info("开始代理>>>>>>>>");
        normalAction.display();
        LOGGER.info("结束代理>>>>>>>>");
    }
}
@ApiModel(value = "需实现的代理接口")
public interface MyFunction {

    public void display();
}
@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        //Spring首先注入被代理人到容器中
        NormalAction normalAction = new NormalAction();

        //然后Spring将被代理人注入到代理类中
        StaticProxyAction staticProxyAction = new StaticProxyAction();
        staticProxyAction.setNormalAction(normalAction);

        //调用MyFunction方法的时候,即可实现增强!
        staticProxyAction.display();
    }
}

输出结果:

可以看到,整个过程中,被代理类的方法并没有进行修改,但是前后逻辑确实发生了变化,这就是AOP所谓的无侵入性。

动态代理

JDK动态代理

代码实践:

采用JDK的动态代理,需要自定义一个对应的Handler类,其继承了InvocationHandler类,并需要对相应的invoke方法进行重载。通过传入的方式对当前的invokeResult进行调用。

@Data
@Slf4j
public class DynamicProxyHandler implements InvocationHandler {
    //被代理的对象
    private Object object;

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        LOGGER.info("你被增强了!快上!");
        Object invokeResult = method.invoke(object, args);
        LOGGER.info("增强结束!");
        return invokeResult;
    }
}

主函数中,通过Proxy对象新建对应的类加载器,并调用对应的方法实现。

public static void main(String[] args) {
  NormalAction normalAction = new NormalAction();
  DynamicProxyHandler dynamicProxyHandler = new DynamicProxyHandler();
  dynamicProxyHandler.setObject(normalAction);
  //获取对应的代理接口
  MyFunction proxyInstance = (MyFunction) Proxy.newProxyInstance(NormalAction.class.getClassLoader(), // 类加载器
                                                                 NormalAction.class.getInterfaces(), // 需要增强的接口
                                                                 dynamicProxyHandler);// 实际代理类
  //调用对应的接口信息
  proxyInstance.display();
}
底层原理:
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class[] interfaces,
                                          InvocationHandler h)  throws IllegalArgumentException
    {
        Objects.requireNonNull(h);
        // 拷贝出接口数组 
        final Class[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }
                //依据类加载器及接口,查找并创建对应的代理类
        //将相应的字节码文件,注入到被代理类中。同时,调用defineClass0来解析字节码,最终生成了Proxy的Class对象。
        Class cl = getProxyClass0(loader, intfs);
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
            //获取到InvocationHandler类的构建器
            final Constructor cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            // 根据构造器创建对应的新实例 
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

CGLIB动态代理

代码实践:
@Slf4j
public class CglibProxy implements MethodInterceptor { // 继承相应的方法拦截器
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        LOGGER.info("你被增强了!");
        Object object = methodProxy.invokeSuper(o, objects);
        LOGGER.info("增强结束!");
        return object;
    }
}
public class ProxyApplication {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer(); // 创建增强器
        enhancer.setSuperclass(NormalAction.class); // 设置被增强的类
        enhancer.setCallback(new CglibProxy()); // 设置代理类
        NormalAction proxyInstance = (NormalAction) enhancer.create(); // 创建代理实例
        proxyInstance.display(); // 调用被代理方法
    }
}
底层原理:
    private Object createHelper() {
    // 预先的校验逻辑
        preValidate(); 
    // 根据对应的superclassName 生成对应的类 
        Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
                ReflectUtils.getNames(interfaces),
                filter == ALL_ZERO ? null : new WeakCacheKey(filter),
                callbackTypes,
                useFactory,
                interceptDuringConstruction,
                serialVersionUID);
        this.currentKey = key;
    // 根据这个key再生成相应的代理类对象 
        Object result = super.create(key);
        return result;
    }
protected Object create(Object key) { // 根据被代理类的加载器加载对应的类书籍
  try {
    // 获取类加载器
    ClassLoader loader = this.getClassLoader(); 
    // 获取缓存
    Map cache = CACHE;
    AbstractClassGenerator.ClassLoaderData data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
    //经典的双重校验锁,保证线程安全
    if (data == null) {
      Class var5 = AbstractClassGenerator.class;
      synchronized(AbstractClassGenerator.class) {
        cache = CACHE;
        data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
        if (data == null) {
          // 创建弱引用的哈希表,并保存到缓存中
          Map newCache = new WeakHashMap(cache);
          data = new AbstractClassGenerator.ClassLoaderData(loader);
          newCache.put(loader, data);
          CACHE = newCache;
        }
      }
    }
        //保存对应的key值
    this.key = key;
    Object obj = data.get(this, this.getUseCache()); //=====>深入进入
    return obj instanceof Class ? this.firstInstance((Class)obj) : this.nextInstance(obj);
  } catch (Error | RuntimeException var9) {
    throw var9;
  } catch (Exception var10) {
    throw new CodeGenerationException(var10);
  }
}
//  获取对应的类数据信息
public Object get(AbstractClassGenerator gen, boolean useCache) {
  if (!useCache) {
    // 不使用缓存的话,直接利用数据生成新的抽象类
    return gen.generate(this);
  } else {
    // 从缓存中获取
    Object cachedValue = generatedClasses.get(gen);
    return gen.unwrapCachedValue(cachedValue);
  }
}

AOP最佳实践

RPC日志组件

首先引入对应的包依赖:

        
            org.aspectj
            aspectjrt
            1.9.4
        
        
            org.aspectj
            aspectjweaver
            1.9.4
        

定义切点有两种方式:

1、采用execution表达式,指定对应的函数执行的时刻。

2、采用注解,在对应需要增强的地方进行织入。

这里我们采用方法二,首先定义一个自己的注解。需要注意的是,注解的作用只是给你的代码打下一个标记,需要对应采用反射或者特殊的处理类对标记的代码进行处理。

@Target({ElementType.TYPE,ElementType.METHOD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation {

    String SERVER_NAME() default "";

    String action() default "";
}

紧接着,定义我们的切面类,对相应的切面方法进行增强。

@Aspect
@Component
@Slf4j
public class MyAspect {
    //采用execution表达式的方式定义切点
    @Pointcut("execution(* com.example.demo.controller..*.*(..)) ")
    private void PointCutofAnno(){}

    //或是直接 采用注解的方法定义,需要注意的是,如果需要使用注解内的值,需要保持变量名称都是“myAnnotation”
    @Around(value = "@annotation(myAnnotation)") 
    public  T test(ProceedingJoinPoint point, MyAnnotation myAnnotation) throws Throwable {
        // 编写相应的增强代码
        ResultDTO res;
        String serverName = myAnnotation.SERVER_NAME();
        String action = myAnnotation.action();
        LOGGER.info("AOP执行前,{}", JSONObject.toJSONString(point.getArgs()));
        try{
            res = (ResultDTO) point.proceed();
        }catch (Exception e){
            throw new NrsBusinessException(500, String.format("请求%s%s时失败,错误信息为:%s",serverName,action,e.getMessage()),e);
        }
        if (res == null) {
            throw new NrsBusinessException(500, String.format("请求%s%s时返回值为空", serverName,action));
        }
        if (res.isSuccess()) {
            return res.getData();
        } else {
            throw new NrsBusinessException(500,String.format("请求%s%s时, %s异常,code为:%d,错误信息为:%s",serverName, action, serverName,res.getCode(), res.getMessage()));
        }
    }
}

最后,在我们调用下游的函数处添加上对应的注解,从而实现对相应RPC异常的处理。

@Component
public class MyRpc {
    @MyAnnotation(SERVER_NAME = "下游系统",action = "调用下游系统时")
    public ResultDTO testFunction(){
        ResultDTO resultDTO = new ResultDTO<>();
        resultDTO.success(true);
        return resultDTO;
    }
}
@Service("testService")
public class TestService {
    @Resource
    MyRpc myRpc;
    public Boolean test(){
        return myRpc.testFunction().getData();
    }
}
@RestController
@RequestMapping(value = "")
public class TestController {
    @Resource
    TestService testService;
    @GetMapping("/test")
    public ResultDTO myTest() {
        return new ResultDTO<>().success(testService.test());
    }
}

PS:ResultDTO为自定义的结果类,可以根据自己的业务自定义,一般包括返回的错误码、返回的数据内容以及调用的错误信息。

参考文献

AOP实战:一个面向切面的实战项目,方法级别的简单监控

java-AOP彻底解析

JAVA动态代理

cglib动态代理机制

你可能感兴趣的:(AOP初识)