Spring aop

2018, Dec 10    

Spring AOP

1.代理模式

*代理模式:代理模式的核心作用就是通过代理,控制对对象的访问。它的设计思路是:定义一个抽象角色,让代理角色和真实角色分别去实现它。(代理模式由三部分组成:抽象角色、代理类、实现类)
真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
代理角色:代理角色:实现抽象角色,在真实角色的实现方法前后,加上自己的逻辑

代理模式可以分为三种:

静态代理:我们自己创建一个代理类

动态代理:程序自动帮我们生成一个代理类,动态代理又分为jdk代理和cglib代理

  • JDK代理:JDK动态代理是利用反射机制生成一个实现抽象角色的匿名代理类,在调用具体方法前调用InvokeHandler 来处理

    缺点:JDK 实现动态代理需要实现类通过接口定义业务方法
    (JDK 实现动态代理需要实现类通过接口定义业务方法,那对于没有接口的类,如何实现动态代理呢,这就需要 CGLIB 了)

  • CGLIB代理:GLIB 采用了非常底层的字节码技术,通过字节码技术为一个类创建子类(代理类),并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。

    缺点:因为采用的是继承,所以不能对final修饰的类或有final修饰的方法的类进行代理

// 静态代理

/**
 * 明星接口类(抽象角色)
 */
public interface Star {

    /**
     * 唱歌方法
     */
    void sing();

}
----------------------------------------------------
/**
 * 真实明星类(真实角色)
 */
public class RealStar implements Star {

    @Override
    public void sing() {
        System.out.println("明星本人开始唱歌……");
    }
}
----------------------------------------------------
/**
 * 明星的静态代理类(代理角色)
 */
public class ProxyStar implements Star {

    /**
     * 接收真实的明星对象
     */
    private Star star;

    /**
     * 通过构造方法传进来真实的明星对象
     * @param star star
     */
    public ProxyStar(Star star) {
        this.star = star;
    }

    @Override
    public void sing() {
        System.out.println("代理先进行谈判……");
        // 唱歌只能明星自己唱
        this.star.sing();
        System.out.println("演出完代理去收钱……");
    }

}
----------------------------------------------------
/**
 * 测试客户端
 */
public class Client {

    /**
     * 测试静态代理结果
     * @param args args
     */
    public static void main(String[] args) {
        Star realStar = new RealStar();
        Star proxy = new ProxyStar(realStar);

        proxy.sing();
    }
}
// JDK 动态代理

/**
 * 明星接口类(抽象角色)
 */
public interface Star {

    /**
     * 唱歌方法
     */
    void sing();

}
----------------------------------------------------
/**
 * 真实明星类(真实角色)
 */
public class RealStar implements Star {

    @Override
    public void sing() {
        System.out.println("明星本人开始唱歌……");
    }
}
----------------------------------------------------
/**
 * 动态代理[处理类](这个处理类,是一个公用方法,不能看作代理类)
 */
public class JdkProxyHandler {

    /**
     * 用来接收真实明星对象
     */
    private Object realStar;

    /**
     * 通过构造方法传进来真实的明星对象
     *
     * @param star star
     */
    public JdkProxyHandler(Star star) {
        super();
        this.realStar = star;
    }

    /**
     * 给真实对象生成一个代理对象实例
     *
     * @return Object
     */
    public Object getProxyInstance() {
        return Proxy.newProxyInstance(realStar.getClass().getClassLoader(),
                realStar.getClass().getInterfaces(), (proxy, method, args) -> { // 第二个参数是,代理类要实现的接口列表(也就是说代理类一定要实现抽象接口)
                    System.out.println("代理先进行谈判……");
                    // 唱歌需要明星自己来唱
                    Object object = method.invoke(realStar, args);
                    System.out.println("演出完代理去收钱……");
                    return object;
                });
    }
}
----------------------------------------------------
/**
 * 测试客户端
 */
public class Client {

    /**
     * 测试JDK动态代理结果
     * @param args args
     */
    public static void main(String[] args) {
        Star realStar = new RealStar();
        // 创建一个代理对象实例
        Star proxy = (Star) new JdkProxyHandler(realStar).getProxyInstance();

        proxy.sing();
    }
}
// CGLib 动态代理
/**
 * cglib代理处理类(这个处理类,是一个公用方法,不能看作代理类)
 */
public class CglibProxyHandler implements MethodInterceptor {

    /**
     * 维护目标对象
     */
    private Object target;

    public Object getProxyInstance(final Object target) {
        this.target = target;
        // Enhancer类是CGLIB中的一个字节码增强器,它可以方便的对你想要处理的类进行扩展
        Enhancer enhancer = new Enhancer();
        // 将被代理的对象设置成父类
        enhancer.setSuperclass(this.target.getClass());
        // 回调方法,设置拦截器
        enhancer.setCallback(this);
        // 动态创建一个代理类
        return enhancer.create();
    }

    @Override
    public Object intercept(Object object, Method method, Object[] args,
            MethodProxy methodProxy) throws Throwable {

        System.out.println("代理先进行谈判……");
        // 唱歌需要明星自己来唱
        Object result = methodProxy.invokeSuper(object, args);
        System.out.println("演出完代理去收钱……");
        return result;
    }
}
使用 CGLIB 需要实现 MethodInterceptor 接口并重写intercept 方法在该方法中对原始要执行的方法前后做增强处理该类的代理对象可以使用代码中的字节码增强器来获取接下来写个客户端测试程序

/**
 * 测试客户端
 */
public class Client {

    /**
     * 测试Cglib动态代理结果
     * @param args args
     */
    public static void main(String[] args) {
        Star realStar = new RealStar();
        Star proxy = (Star) new CglibProxyHandler().getProxyInstance(realStar);

        proxy.sing();
    }
}
2.AOP

Spring的AOP就是采用代理模式这种思想,为目标代码的前后添加逻辑处理

JDK 动态代理和 CGLIB 动态代理均是实现 Spring AOP 的基础

Spring AOP 中的代理使用逻辑了:如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理实现 AOP;如果目标对象没有实现了接口,则采用 CGLIB 库,Spring 会自动在 JDK 动态代理和 CGLIB 动态代理之间转换。

Spring的aop怎么实现的?

每个Bean都会被JDK或者Cglib 代理,如果一个bean没有实现任何接口,就用JDK代理。
每个 Bean 会有多个“方法拦截器”。注意:拦截器分为两层,外层由 Spring 内核控制流程,内层拦截器是用户设置,也就是 AOP。
当代理方法被调用时,先经过外层拦截器,外层拦截器根据方法的各种信息判断该方法应该执行哪些“内层拦截器”。内层拦截器的设计就是责任链的设计。

AOP 分成 2 个部分来:
(1)代理的创建(按步骤):
首先,需要创建代理工厂,代理工厂需要 3 个重要的信息:拦截器数组,目标对象接口数组,目标对象。
创建代理工厂时,默认会在拦截器数组尾部再增加一个默认拦截器 —— 用于最终的调用目标方法。
当调用 getProxy 方法的时候,会根据接口数量大余 0 条件返回一个代理对象(JDK or Cglib)。
注意:创建代理对象时,同时会创建一个外层拦截器,这个拦截器就是 Spring 内核的拦截器。用于控制整个 AOP 的流程。
(2)代理的调用
当对代理对象进行调用时,就会触发外层拦截器。
外层拦截器根据代理配置信息,创建内层拦截器链。创建的过程中,会根据表达式判断当前拦截是否匹配这个拦截器。而这个拦截器链设计模式就是职责链模式。
当整个链条执行到最后时,就会触发创建代理时那个尾部的默认拦截器,从而调用目标方法。最后返回。

什么场景下用AOP
AOP是对OOP开发模式的补足,像日志记录、事务管理等场景(系统中的每个业务对象都要加入的功能需求就被称为横切关注点)