SpringBoot与AOP

一、引言

    Springboot的两大核心IOC和AOP:即控制翻转和面向切面编程,今天学习探讨AOP的作用、原理和使用。

二、什么是AOP

    1.在不修改源码前提下,扩展功能。从而和业务代码剥离,做到解耦。

    2.AOP在Spring框架中应用广泛,包括缓存,事务,定时任务,重试等这一些业务类的注解。

三、AOP的通知类型

    1.Before前置通知:在方法执行前执行。

    2.AfterReturning返回通知:方法正常结束后执行。

    3.After后置通知:方法执行后执行,不管是(异常或者非异常都会执行)。

    4.AfterThrowing异常通知:方法执行中有异常抛出。

    5.Around环绕通知:在方法执行前和执行后都执行。

      @Aspect 表明是一个切面类
      @Component 将当前类注入到Spring容器内
      @Pointcut 切入点,其中execution用于使用切面的连接点。使用方法:execution(方法修饰符(可选) 返回类型 方法名 参数 异常  模式(可选)) ,可以使用通配符匹配字符,*可以匹配任意字符。
      @Before 在方法前执行
      @After 在方法后执行
      @AfterReturning 在方法执行后返回一个结果后执行
      @AfterThrowing 在方法执行过程中抛出异常的时候执行
      @Around 环绕通知,就是可以在执行前后都使用,这个方法参数必须为ProceedingJoinPoint,proceed()方法就是被切面的方法,上面四个方法可以使用JoinPoint,JoinPoint包含了类名,被切面的方法名,参数等信息。

package com.zte.zyl.aop;


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;
import org.aspectj.lang.annotation.*;

@Aspect
@Component
public class LogAspect {
    @Pointcut("execution(public * com.zte.zyl.*.*(..))")
    public void LogAspect(){}

    @Before("LogAspect()")
    public void doBefore(JoinPoint joinPoint){
        System.out.println("doBefore");
    }

    @After("LogAspect()")
    public void doAfter(JoinPoint joinPoint){
        System.out.println("doAfter");
    }

    @AfterReturning("LogAspect()")
    public void doAfterReturning(JoinPoint joinPoint){
        System.out.println("doAfterReturning");
    }

    @AfterThrowing("LogAspect()")
    public void deAfterThrowing(JoinPoint joinPoint){
        System.out.println("deAfterThrowing");
    }

    @Around("LogAspect()")
    public Object deAround(ProceedingJoinPoint joinPoint) throws Throwable{
        System.out.println("deAround");
        return joinPoint.proceed();
    }

}


四、AOP如何实现代理

    1.基于接口和基于继承的方式;基于接口就是基于JDK动态代理的方式,使用的是反射机制;基于继承就是使用Cglib来创建目标类的子类,覆盖父类中的方法。

    2.基于接口的动态代理,即JDK动态代理方式

    1)声明一个共用的接口

//接口
public interface Play {
    void play(String e);
}

    2)真正的对象

package com.zte.zyl.proxy;

public class RealPlayer implements Play {
    @Override
    public void play(String e) {
        System.out.println("play:"+e);
    }
}

    3)代理类,要实现同样的接口;以及调用范例。

package com.zte.zyl.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JdkProxyPlayerSubject implements InvocationHandler {


    private RealPlayer realPlayer;

    public JdkProxyPlayerSubject(RealPlayer realPlayer) {
        this.realPlayer = realPlayer;
    }

    /*
     *invoke方法方法参数解析
     *Object proxy:指被代理的对象。
     *Method method:要调用的方法
     *Object[] args:方法调用时所需要的参数
     *InvocationHandler接口的子类可以看成代理的最终操作类。
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before");
        Object result = null;
        try{
            //利用反射动态的来反射方法,这就是动态代理和静态代理的区别
            result = method.invoke(realPlayer,args);
        }catch (Exception e){
            System.out.println("ex:"+e.getMessage());
            throw e;
        }finally {
            System.out.println("after");
        }
        return result;
    }

    public static void main(String[] args) {

        /*
         *newProxyInstance方法参数解析
         *ClassLoader loader:类加载器
         *Class<?>[] interfaces:得到全部的接口
         *InvocationHandler h:得到InvocationHandler接口的子类实例
         */
            Play play = (Play) Proxy.newProxyInstance(JdkProxyPlayerSubject.class.getClassLoader(),new Class[]{Play.class},new JdkProxyPlayerSubject(new RealPlayer()));
            play.play("hello");
        }

}



2. CGLIB动态代理;主要是基于cglib底层的字节码技术来生成代理类。原理是生成目标类的子类作为代理类。

1)一个真实对象

public class Subject {

    public void show(String x){

        System.out.println(x);
    }
}

 

2.代理类,要实现MethodInterceptor接口

public class SubjectCglibProxy  implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

        Object o1 = methodProxy.invokeSuper(o, objects);
        return o1;
    }
}

3)使用

public class CglibProxy {

    public static void main(String[] args) {

        Subject subject = (Subject)Enhancer.create(Subject.class,new SubjectCglibProxy());
        subject.show("hello");
    }
}

三、JDK 和 CGLib动态代理区别
1、JDK动态代理具体实现原理:

通过实现InvocationHandlet接口创建自己的调用处理器;

通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理;

通过反射机制获取动态代理类的构造函数,其唯一参数类型就是调用处理器接口类型;

通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数参入;

JDK动态代理是面向接口的代理模式,如果被代理目标没有接口那么Spring也无能为力,Spring通过Java的反射机制生产被代理接口的新的匿名实现类,重写了其中AOP的增强方法。

2、CGLib动态代理:

CGLib是一个强大、高性能的Code生产类库,可以实现运行期动态扩展java类,Spring在运行期间通过 CGlib继承要被动态代理的类,重写父类的方法,实现AOP面向切面编程呢。

3、两者对比:

JDK动态代理是面向接口的。

CGLib动态代理是通过字节码底层继承要代理类来实现(如果被代理类被final关键字所修饰,那么抱歉会失败)。

4、使用注意:

如果要被代理的对象是个实现类,那么Spring会使用JDK动态代理来完成操作(Spirng默认采用JDK动态代理实现机制);

如果要被代理的对象不是个实现类那么,Spring会强制使用CGLib来实现动态代理。
————————————————
参考:https://blog.csdn.net/xlgen157387/article/details/82497594

有兴趣有时间的话可以参考下面链接,做源码AOP的详细阅读。

Spring AOP相关的源码解析:https://blog.csdn.net/woshilijiuyi/article/details/83448407

五、AOP应用场景

    1.日志记录

    2.性能统计

    1)统计方法执行时间

 @Around("LogAspect()")
    public Object doAround(ProceedingJoinPoint pjp) {
        long startTime = System.currentTimeMillis();

        Object obj = null;
        try {
            obj = pjp.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();

        MethodSignature signature = (MethodSignature) pjp.getSignature();
        String methodName = signature.getDeclaringTypeName() + "." + signature.getName();
        System.out.println(methodName + "方法执行了" + (endTime - startTime) + "ms");
        return obj;

    }

    3.安全控制

    4.事务管理

    5.异常处理

六、AOP未来使用趋势

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值