AOP与动态代理

求知探索 1年前 ⋅ 794 阅读

AOP

AOP:全称是 Aspect Oriented Programming 即:面向切面编程。

简单的说它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的

基础上,对我们的已有方法进行增强。(例如事务处理、日志管理、权限控制等)

作用:

在程序运行期间,不修改源码对已有方法进行增强。

优势:

减少重复代码

提高开发效率

维护方便

动态代理

字节码随用随创建,随用随加载。

它与静态代理的区别也在于此。因为静态代理是字节码一上来就创建好,并完成加载。

装饰者模式就是静态代理的一种体现。

动态代理的两种方式

基于接口的动态代理

提供者:JDK 官方的 Proxy 类。

要求:被代理类最少实现一个接口。

基于子类的动态代理

提供者:第三方的 CGLib。

要求:被代理类不能用 final 修饰的类(最终类)。

使用 JDK 官方的 Proxy 类创建代理对象

package com.liuermeng.proxy;

/**
 * 一个经纪公司的要求:
 * 能做基本的表演和危险的表演
 */
public interface IActor {
    /**
     * 基本演出
     * @param money
     */
    public void basicAct(float money);
    /**
     * 危险演出
     * @param money
     */
    public void dangerAct(float money);
}
package com.liuermeng.proxy;
/**
 * 一个演员
 */
//实现了接口,就表示具有接口中的方法实现。即:符合经纪公司的要求
public class Actor implements IActor{
    @Override
    public void basicAct(float money) {
        System.out.println("拿到钱,开始基本的表演:"+money);
    }

    @Override
    public void dangerAct(float money) {
        System.out.println("拿到钱,开始危险的表演:"+money);
    }
}
package com.liuermeng.proxy;

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

public class Client {
    public static void main(String[] args) {
        Actor actor = new Actor();
        /*
         * 代理:
         * 间接。
         * 获取代理对象:
         * 要求:
         * 被代理类最少实现一个接口
         * 创建的方式
         * Proxy.newProxyInstance(三个参数)
         * 参数含义:
         * ClassLoader:和被代理对象使用相同的类加载器。
         * Interfaces:和被代理对象具有相同的行为。实现相同的接口。
         * InvocationHandler:如何代理。
         * 策略模式:使用场景是:
         * 数据有了,目的明确。
         * 如何达成目标,就是策略。
         *
         */
        IActor proxyActor = (IActor) Proxy.newProxyInstance(actor.getClass().getClassLoader(), actor.getClass().getInterfaces(), new InvocationHandler() {
            /**
             * 执行被代理对象的任何方法,都会经过该方法。
             * 此方法有拦截的功能。
             *
             * 参数:
             * proxy:代理对象的引用。不一定每次都用得到
             * method:当前执行的方法对象
             * args:执行方法所需的参数
             * 返回值:
             * 当前执行方法的返回值
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                String name = method.getName();
                float money = (float) args[0];
                Object rtValue = null;
                if (name.equals("basicAct")) {
                    //基本演出,没有 2000 不演
                    if (money > 2000) {
                        //看上去剧组是给了 8000,实际到演员手里只有 4000
                        //这就是我们没有修改原来 basicAct 方法源码,对方法进行了增强
                        rtValue = method.invoke(actor, money / 2);
                    }
                }
                if(name.equals("dangerAct")){
                    if(money>4000){
                        rtValue=method.invoke(actor,money/2);
                    }
                }

                return rtValue;
            }
        });
        //没有经纪公司的时候,直接找演员。
        // actor.basicAct(1000f);
        // actor.dangerAct(5000f);
        //剧组无法直接联系演员,而是由经纪公司找的演员
        proxyActor.basicAct(8000f);// 拿到钱,开始基本的表演:4000.0
        proxyActor.dangerAct(50000f);// 拿到钱,开始危险的表演:25000.0
    }
}

使用 CGLib 的 Enhancer 类创建代理对象

package com.liuermeng.proxy.cglib;

public class Actor {

    public void basicAct(float money) {
        System.out.println("拿到钱,开始基本的表演:"+money);
    }
    public void dangerAct(float money) {
        System.out.println("拿到钱,开始危险的表演:"+money);
    }
}
package com.liuermeng.proxy.cglib;

public class Client {
    /**
     * 基于子类的动态代理
     * 要求:
     * 被代理对象不能是最终类
     * 用到的类:
     * Enhancer
     * 用到的方法:
     * create(Class, Callback)
     * 方法的参数:
     * Class:被代理对象的字节码
     * Callback:如何代理
     *
     * @param args
     */
    public static void main(String[] args) {
        final Actor actor = new Actor();
        Actor cglibActor = (Actor) Enhancer.create(actor.getClass(),
                new MethodInterceptor() {
                    /**
                     * 执行被代理对象的任何方法,都会经过该方法。在此方法内部就可以对被代理对象的任何
                     方法进行增强。
                     *
                     * 参数:
                     * 前三个和基于接口的动态代理是一样的。
                     * MethodProxy:当前执行方法的代理对象。
                     * 返回值:
                     * 当前执行方法的返回值
                     */
                    @Override
                    public Object intercept(Object proxy, Method method, Object[] args,
                                            MethodProxy methodProxy) throws Throwable {
                        String name = method.getName();
                        Float money = (Float) args[0];
                        Object rtValue = null;
                        if ("basicAct".equals(name)) {
//基本演出
                            if (money > 2000) {
                                rtValue = method.invoke(actor, money / 2);
                            }
                        }
                        if ("dangerAct".equals(name)) {
//危险演出
                            if (money > 5000) {
                                rtValue = method.invoke(actor, money / 2);
                            }
                        }
                        return rtValue;
                    }
                });
        cglibActor.basicAct(10000);
        cglibActor.dangerAct(100000);
    }
}

AspectJ AOP

当然你也可以使用 AspectJ ,Spring AOP 已经集成了AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。

使用 AOP 之后我们可以把一些通用功能抽象出来,在需要用到的地方直接使用即可,这样大大简化了代码量。我们需要增加新功能时也方便,这样也提高了系统扩展性。日志功能、事务管理等等场景都用到了 AOP 。

Spring AOP 和 AspectJ AOP 有什么区别?

Spring AOP 属于运行时增强,而 AspectJ 是编译时增强。 Spring AOP 基于代理(Proxying),而 AspectJ 基于字节码操作(Bytecode Manipulation)。

Spring AOP 已经集成了 AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。AspectJ 相比于 Spring AOP 功能更加强大,但是 Spring AOP 相对来说更简单,

如果我们的切面比较少,那么两者性能差异不大。但是,当切面太多的话,最好选择 AspectJ ,它比Spring AOP 快很多。


全部评论: 0

    我有话说: