在Spring中,AOP(面向切面编程)通过代理模式来实现横切关注点的织入。代理模式是AOP的核心机制之一,它允许在目标对象的方法执行前、执行后或异常抛出时插入额外的逻辑,而无需修改目标对象的源代码。

所谓代理模式其实就是二十三种设计模式中的一种,属于结构型模式。它的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中剥离出来——解耦。调用目标方法时先调用代理对象的方法,减少对目标方法的调用和打扰,同时让附加功能能够集中在一起也有利于统一维护。

使用代理模式之前

Spring系列学习-AOP之代理模式在AOP中的运用_spring


使用代理模式之后

Spring系列学习-AOP之代理模式在AOP中的运用_代理模式_02


代理在我们现实生活中的运用:

  1. 广告公司找大明星拍广告需要经过经纪人。
  2. 合作伙伴找老板谈合作约见面时间一般需要经过秘书。
  3. 房产中介是双方的代理
  4. 还有入厂中介,外包公司都是中介的一种

相关术语:
代理: 将非核心逻辑剥离出来以后,封装这些非核心逻辑的类,对象,方法。
目标:被代理“套用”了非核心逻辑代码的类、对象、方法。

在AOP中,代理模式主要有两种实现方式:静态代理动态代理。

静态代理:静态代理是通过手动编写代理类来实现的。代理类和目标类实现相同的接口,代理类中包含目标对象的引用,并在方法调用前后插入额外的逻辑。在Spring中,可以使用ProxyFactoryBean来创建静态代理。

动态代理:动态代理是在运行时动态生成代理类的方式。Spring使用JDK动态代理和CGLIB动态代理两种方式来实现动态代理。

  • JDK动态代理:基于接口的代理,通过实现InvocationHandler接口来实现代理逻辑。在运行时,通过Proxy类的静态方法newProxyInstance来创建代理对象。
  • CGLIB动态代理:基于类的代理,通过继承目标类来实现代理逻辑。在运行时,通过Enhancer类来创建代理对象。
静态代理的实现

静态代理是通过手动编写代理类来实现的。我们先通过一个简单的静态代理示例来说明什么具体什么是静态代理:
首先我们定义一个接口(Subject) 和一个实现类(RealSubject),代表目标对象和实际业务逻辑

public interface Subject {
    void doSomething();
}

public class RealSubject implements Subject {
    @Override
    public void doSomething() {
        System.out.println("RealSubject is doing something.");
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

然后,创建一个代理类(ProxySubject),实现与目标对象相同的接口,并在方法调用前后插入额外的逻辑:

public class ProxySubject implements Subject {
    private Subject realSubject;

    public ProxySubject(Subject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void doSomething() {
        System.out.println("Before doSomething");
        realSubject.doSomething();
        System.out.println("After doSomething");
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

在上述示例中,ProxySubject是代理类,它持有一个实际的目标对象(RealSubject)。在doSomething方法中,代理类在调用目标对象的方法之前和之后分别输出额外的日志信息。

最后,可以通过创建代理对象来使用静态代理:

public class Main {
    public static void main(String[] args) {
        Subject realSubject = new RealSubject();
        Subject proxySubject = new ProxySubject(realSubject);

        proxySubject.doSomething();
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

在上述示例中,创建了一个RealSubject对象作为目标对象,然后创建了一个ProxySubject对象作为代理对象。通过调用代理对象的doSomething方法,实际上会触发代理类中的逻辑,包括在调用目标对象的方法之前和之后输出日志信息。

需要注意的是,静态代理的缺点是每个目标对象都需要对应一个代理类,当目标对象较多时,会导致代理类的数量增加,代码复杂度提高。此外,静态代理也无法动态地切换和修改代理逻辑。

实际案例

public class CalculatorStaticProxy implements Calculator{
   //将被代理的目标对象声明为成员变量
   private Calculator target;
   /**
    * 静态代理确实实现了解耦,但是由于代码都写死了,完全不具备任何的灵活性。就拿日志功能来说,将来其他地方也需要附加日志,那还得再声明更多个静态代理类,那就产生了大量重复的代码,日志功能还是分散的,没有统一管理。
    */
   public CalculatorStaticProxy(Calculator target) {
       this.target = target;
   }


   @Override
   public int add(int i, int j) {
       System.out.println("[日志] add 方法开始了,参数是:" + i + "," + j);
       //int result =  i + j;
       int result = target.add(i,j); //由上述代码变换成这种方式
       System.out.println("方法内部 result = " + result);
       System.out.println("[日志] add 方法结束了,结果是:" + result);
       return result;
   }
   @Override
   public int sub(int i, int j) {
       System.out.println("[日志] sub 方法开始了,参数是:" + i + "," + j);
       int result = i - j;
       System.out.println("方法内部 result = " + result);
       System.out.println("[日志] sub 方法结束了,结果是:" + result);
       return result;
   }
   @Override
   public int mul(int i, int j) {
       System.out.println("[日志] mul 方法开始了,参数是:" + i + "," + j);
       int result = i * j;
       System.out.println("方法内部 result = " + result);
       System.out.println("[日志] mul 方法结束了,结果是:" + result);
       return result;
   }
   @Override
   public int div(int i, int j) {
       System.out.println("[日志] div 方法开始了,参数是:" + i + "," + j);
       int result = i / j;
       System.out.println("方法内部 result = " + result);
       System.out.println("[日志] div 方法结束了,结果是:" + result);
       return result;
   }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.

上述代码,静态代理确实实现了解耦,但是由于代码都写死了,完全不具备任何的灵活性。就拿日志功能来说,将来其他地方也需要附加日志,那还得再声明更多个静态代理类,那就产生了大量重复的代码,日志功能还是分散的,没有统一管理。

提出进一步的需求:将日志功能集中到一个代理类中,将来有任何日志需求,都通过这一个代理类来实现。这就需要使用动态代理技术了。

动态代理

说到动态代理,上面说了两种动态代理一种是JDK动态代理,一种是CGLIB动态代理。

JDK动态代理的相关知识

首先,我们定义一个接口(Subject)和一个实现类(RealSubject),代表我们的目标对象和实际业务逻辑,与静态代理示例中的基本上无差别。
然后创建一个实现InvocationHandler的接口和代理处理器类(ProxyHandler),实现invoke方法,在方法调用前后插入额外的逻辑:

public interface Subject {
    void doSomething();
}

public class RealSubject implements Subject {
    @Override
    public void doSomething() {
        System.out.println("RealSubject is doing something.");
    }
}

public class ProxyHandler implements InvocationHandler {
    private Object target;

    public ProxyHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before " + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("After " + method.getName());
        return result;
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.

ProxyHandler是代理处理器类,它持有一个实际的目标对象(target)。在invoke方法中,代理处理器在调用目标对象的方法之前和之后分别输出额外的日志信息。

public class Main {
    public static void main(String[] args) {
        Subject realSubject = new RealSubject();
        InvocationHandler handler = new ProxyHandler(realSubject);

        Subject proxySubject = (Subject) Proxy.newProxyInstance(
                realSubject.getClass().getClassLoader(),
                realSubject.getClass().getInterfaces(),
                handler);

        proxySubject.doSomething();
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
CGLIB实现动态代理

CGLIB动态代理是基于类的代理,通过继承目标类来实现代理逻辑。相比JDK动态代理,CGLIB动态代理不要求目标对象实现接口。

public interface Subject {
    void doSomething();
}

public class RealSubject implements Subject {
    @Override
    public void doSomething() {
        System.out.println("RealSubject is doing something.");
    }
}
public class ProxyHandler implements MethodInterceptor {
    private Object target;

    public ProxyHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Before " + method.getName());
        Object result = proxy.invoke(target, args);
        System.out.println("After " + method.getName());
        return result;
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.

ProxyHandler是代理处理器类,它持有一个实际的目标对象(target)。在intercept方法中,代理处理器在调用目标对象的方法之前和之后分别输出额外的日志信息。
使用Enhancer类来创建代理对象:

public class Main {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        ProxyHandler handler = new ProxyHandler(realSubject);

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(RealSubject.class);
        enhancer.setCallback(handler);

        RealSubject proxySubject = (RealSubject) enhancer.create();

        proxySubject.doSomething();
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

在上述示例中,创建了一个RealSubject对象作为目标对象,然后创建了一个ProxyHandler对象作为代理处理器。通过使用Enhancer类,设置目标类和代理处理器,即可动态生成代理对象。

通过调用代理对象的doSomething方法,实际上会触发代理处理器中的intercept方法,包括在调用目标对象的方法之前和之后输出日志信息。

需要注意的是,使用CGLIB动态代理时,目标类不能是final类,且目标方法不能是final或private方法。

案例实现

Spring系列学习-AOP之代理模式在AOP中的运用_动态代理_03

public interface Calculator {
    int add(int i, int j);
    int sub(int i, int j);
    int mul(int i, int j);
    int div(int i, int j);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
@Component
public class CalculatorPureImpl implements Calculator{
    @Override
    public int add(int i, int j) {
        System.out.println("[日志] add 方法开始了,参数是:" + i + "," + j);
        int result = i + j;
        System.out.println("方法内部 result = " + result);
        System.out.println("[日志] add 方法结束了,结果是:" + result);
        return result;
    }
    @Override
    public int sub(int i, int j) {
        System.out.println("[日志] sub 方法开始了,参数是:" + i + "," + j);
        int result = i - j;
        System.out.println("方法内部 result = " + result);
        System.out.println("[日志] sub 方法结束了,结果是:" + result);
        return result;
    }
    @Override
    public int mul(int i, int j) {
        System.out.println("[日志] mul 方法开始了,参数是:" + i + "," + j);
        int result = i * j;
        System.out.println("方法内部 result = " + result);
        System.out.println("[日志] mul 方法结束了,结果是:" + result);
        return result;
    }
    @Override
    public int div(int i, int j) {
        System.out.println("[日志] div 方法开始了,参数是:" + i + "," + j);
        int result = i / j;
        System.out.println("方法内部 result = " + result);
        System.out.println("[日志] div 方法结束了,结果是:" + result);
        return result;
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.

动态代理实现类:

public class ProxyFactory {

    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    public Object getProxyInstance() {
        /**
         * newProxyInstance() :创建一个代理实例
         * 其中有三个参数:
         * classLoader : 类加载器,加载动态生成的代理类的类加载器
         *  interfaces:目标对象实现的所有接口的class对象所组成的数组
         *   invocationHandler:设置代理对象实现目标对象方法的过程,即代理类中如何重写接口中的抽象方法
         */
        ClassLoader classLoader = target.getClass().getClassLoader();
        Class<?>[] interfaces = target.getClass().getInterfaces();
        InvocationHandler invocationHandler = new InvocationHandler(){
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 在这里可以做一些事情,比如日志记录,权限验证等
                /**
                 * proxy : 代理对象
                 *  method : 目标对象的方法,也就我们需要重写的方法
                 *   args : 目标对象方法的参数
                 */
                Object result  = null;
                try {
                    System.out.println("[动态代理][日志] "+method.getName()+",参数:"+ Arrays.toString(args));
                    result = method.invoke(target, args);
                    System.out.println("[动态代理][日志] "+method.getName()+",结 果:"+ result);
                } catch (Exception e) {
                    e.printStackTrace();
                    System.out.println("[动态代理][日志] "+method.getName()+",异常:"+e.getMessage());
                } finally {
                    System.out.println("[动态代理][日志] "+method.getName()+",方法执行完毕");
                }
                // 在这里可以做一些事情,比如日志记录,权限验证等
                return result;
            }

        };
        return java.lang.reflect.Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.

测试案例

@Test
    public void test(){
        ProxyFactory proxyFactory = new ProxyFactory( new CalculatorPureImpl());
        com.miaow.test.Calculator calculator = (com.miaow.test.Calculator) proxyFactory.getProxyInstance();

        calculator.add(1,2);
        calculator.sub(1,2);
        calculator.mul(1,2);
        calculator.div(1,2);
    }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
[动态代理][日志] add,参数:[1, 2]
[日志] add 方法开始了,参数是:1,2
方法内部 result = 3
[日志] add 方法结束了,结果是:3
[动态代理][日志] add,结 果:3
[动态代理][日志] add,方法执行完毕
[动态代理][日志] sub,参数:[1, 2]
[日志] sub 方法开始了,参数是:1,2
方法内部 result = -1
[日志] sub 方法结束了,结果是:-1
[动态代理][日志] sub,结 果:-1
[动态代理][日志] sub,方法执行完毕
[动态代理][日志] mul,参数:[1, 2]
[日志] mul 方法开始了,参数是:1,2
方法内部 result = 2
[日志] mul 方法结束了,结果是:2
[动态代理][日志] mul,结 果:2
[动态代理][日志] mul,方法执行完毕
[动态代理][日志] div,参数:[1, 2]
[日志] div 方法开始了,参数是:1,2
方法内部 result = 0
[日志] div 方法结束了,结果是:0
[动态代理][日志] div,结 果:0
[动态代理][日志] div,方法执行完毕

Process finished with exit code 0
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.