专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

Spring AOP里面的代理知识

什么是代理

为某⼀个对象创建⼀个代理对象,程序不直接⽤原本的对象,⽽是由创建的代理对象来控制对原对象,通过代理类这中间⼀层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间

静态代理和动态代理

  • 静态代理

由程序创建或特定⼯具⾃动⽣成源代码,在程序运⾏前,代理类的.class⽂件就已经存在

  • 动态代理

在程序运⾏时,运⽤反射机制动态创建⽽成,⽆需⼿动编写代码,分为

  • JDK动态代理
  • CGLIB动态代理

静态代理讲解

  • 由程序创建或特定⼯具⾃动⽣成源代码,在程序运⾏前,代理类的.class⽂件就已经存在
  • 通过将⽬标类与代理类实现同⼀个接⼝,让代理类持有真实类对象,然后在代理类⽅法中调
  • ⽤真实类⽅法,在调⽤真实类⽅法的前后添加我们所需要的功能扩展代码来达到增强的⽬的

优点

  • 代理使客户端不需要知道实现类是什么,怎么做的,⽽客户端只需知道代理即可
  • ⽅便增加功能,拓展业务逻辑

缺点

  • 代理类中出现⼤量冗余的代码,⾮常不利于扩展和维护
  • 如果接⼝增加⼀个⽅法,除了所有实现类需要实现这个⽅法外,所有代理类也需要实现此⽅法。增加了代码维护的复杂度

案例

public interface payService {

    public String callBack(String ouTradeNo);

    int save(int userId,int productId);
}
//实现类
public class payServiceImpl implements payService {
    public String callBack(String ouTradeNo) {
        System.out.println("payService callBack 回调");
        return ouTradeNo;
    }
    public int save(int userId, int productId) {
        System.out.println("payService save 回调");
        return productId;
    }
}

//代理类
public class proxyPayServiceImpl  implements payService{
    private payService payService;

    public proxyPayServiceImpl(payService payService) {
        this.payService = payService;
    }
    public String callBack(String ouTradeNo) {
        System.out.println("proxyPayServiceImpl callback begin");

        String result = payService.callBack(ouTradeNo);
        System.out.println("proxyPayServiceImpl callback end");
        return result;
    }
    public int save(int userId, int productId) {
        System.out.println("proxyPayServiceImpl save begin");

        int result = payService.save(userId,productId);
        System.out.println("proxyPayServiceImpl save end");
        return result;
    }
}

这样就可以做到,在不更改目标类的逻辑,增强这个功能

public class ProxyTest {
    public static void main(String[] args) {
        payService payService = new payServiceImpl();
        String s = payService.callBack("777");
        System.out.println(s);
        System.out.println(payService.price(800));

        //静态代理后
        payService payService1=new proxyPayServiceImpl(new payServiceImpl());
        payService1.callBack("999");
        int price = payService1.price(800);
        System.out.println(price);
    }
}

81_1.png

动态代理

什么是动态代理

  • 在程序运⾏时,运⽤反射机制动态创建⽽成,⽆需⼿动编写代码
  • JDK动态代理与静态代理⼀样,⽬标类需要实现⼀个代理接⼝,再通过代理对象调⽤⽬标⽅法

JDK动态代理

定义⼀个java.lang.reflect.InvocationHandler接⼝的实现类,重写invoke⽅法
//Object proxy:被代理的对象
//Method method:要调⽤的⽅法
//Object[] args:⽅法调⽤时所需要参数
public interface InvocationHandler {
 public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable;
} 

//动态代理类
public class JDKProxy implements InvocationHandler {
    //目标类
    private Object targetObject;
    //获取代理对象
    public Object newProxyInstance(Object targetObject) {
        this.targetObject = targetObject;
        //绑定关系,也就是和具体的哪个实现类
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        try {
            System.out.println("JDK动态代理"+method.getName());
            int price =(Integer) args[0];
            if ("price".equals(method.getName())){
                result=method.invoke(targetObject,price-100);
            }else {
                result = method.invoke(targetObject,args);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
}

//调用测试
 JDKProxy jdkProxy = new JDKProxy();
        payService payServiceProxy = (cn.junko.proxy.payService) jdkProxy.newProxyInstance(new payServiceImpl());
        int price = payServiceProxy.price(800);
        System.out.println(price);

81_2.png

CgLib动态代理

CgLib动态代理的原理是对指定的业务类⽣成⼀个⼦类,并覆盖其中的业务⽅法来实现代理

public class CglibProxy implements MethodInterceptor {
    //目标类
    private  Object targetObject;
    //绑定关系
    public Object newProxyInstance(Object targetObject) {
        this.targetObject = targetObject;
        //设置代理的父类(目标类)
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.targetObject.getClass());
        //设置回调函数
        enhancer.setCallback(this);
        //创建子类
        return enhancer.create();
    }

    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Object result = null;
        try {
            System.out.println("Cglib动态代理"+method.getName());
            result = methodProxy.invokeSuper(o,objects);
        }catch (Exception e){
            e.printStackTrace();
        }
        return result;
    }
}

实现类

CglibProxy cglibProxy = new CglibProxy();
        payService payService = (cn.junko.proxy.payService) cglibProxy.newProxyInstance(new payServiceImpl());
        payService.callBack("777");

81_3.png

代理总结

总结CGlib和JDk动态代理

动态代理与静态代理相⽐较,最⼤的好处是接⼝中声明的所有⽅法都被转移到调⽤处理器⼀个集中的⽅法中处理,解耦和易维护

CGlib和JDk代理的区别

  • JDK动态代理:要求⽬标对象实现⼀个接⼝,但是有时候⽬标对象只是⼀个单独的对象,并没有实现任何的接⼝,这个时候就可以⽤CGLib动态代理
  • CGLib动态代理,它是在内存中构建⼀个⼦类对象从⽽实现对⽬标对象功能的扩展
  • JDK动态代理是⾃带的,CGlib需要引⼊第三⽅包
  • CGLib动态代理基于继承来实现代理,所以⽆法对final类、private⽅法和static⽅法实现代理

SpringAOP中代理的策略

  • 如果⽬标对象实现了接⼝,则默认采⽤JDK动态代理
  • 如果⽬标对象没有实现接⼝,则采⽤CgLib进⾏动态代理
  • 如果⽬标对象实现了接扣,程序⾥⾯依旧可以指定使⽤CGlib动态代理

CGlib和JDk的另一种使用场景

动态代理

特点:字节码随用随建,随用随加载
作用:不修改源码的基础上方法增强
分类:
1、基于接口的动态代理
2、基于子类的动态代理


  • 创建一个生产者的类
package cn.junko.cglib;

import cn.junko.proxy.IProducer;

/**
 * 一个生产者
 */
public class Producer implements IProducer {
    /**
     * 销售
     * @param money
     */
    public void saleProduct(float money){
        System.out.println("卖出产品并拿到钱了"+money);
    }

    /**
     * 售后
     * @param money
     */
    public void afterService(float money){
        System.out.println("提供售后服务并拿到钱了"+money);
    }
}

  • 创建一个接口
package cn.junko.proxy;

/**
 * 对生产者要求的接口
 */
public interface IProducer {
    /**
     * 销售
     * @param money
     */
    public void saleProduct(float money);
    /**
     * 售后
     * @param money
     */
    public void afterService(float money);
}

创建一个Client类

基于接口的动态代理

     *  基于接口的动态代理
     *      涉及的类:Proxy
     *      提供者:官方
     *  如何创建代理对象:
     *      使用Proxy的newProxyInstance方法
     *  创建代理对象的要求:
     *      被代理类最少实现一个接口,如果没有则不能使用
     *  newProxyInstance方法的参数:
     *      ClassLoader:类加载器
     *          用于加载代理对象字节码的,和被代理对象使用相同的类加载器,固定写法
     *      Class[]:字节码数组
     *          用于让代理对象和被代理对象有相同的方法,固定写法
     *      InvocationHandler:用于提供增强的代码
     *          写如何代理,一般是写一个接口的实现类,通常情况下都是匿名内部类,但不是必须的,此接口的实现类都是谁用谁写

public class Client {
    public static void main(String[] args) {
        Producer producer=new Producer();
        IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
                                    producer.getClass().getInterfaces(),new InvocationHandler() {
                    /**
                     * 作用:执行被代理对象的任何接口方法都会经过该方法
                     * 方法参数含义
                     * @param o         代理对象的引用
                     * @param method    当前执行的方法
                     * @param args      当前执行方法所需的参数
                     * @return          和被代理对象方法有相同的返回值
                     * @throws Throwable
                     */
                    @Override
                    public Object invoke(Object o, Method method, Object[] args) throws Throwable {
                        //提供增强的代码
                        Object returnValue = null;
                        //1.获取方法执行的参数
                        Float money= (Float) args[0];
                        //2.判断当前方法是不是销售
                        if ("saleProduct".equals(method.getName())){
                            returnValue=method.invoke(producer,money*0.8f);
                        }
                        return returnValue;
                    }
                });
        //实现消费者拿出10000,经销商2k,生产8k
        proxyProducer.saleProduct(10000f);
    }
}

基于子类的动态代理

     *  基于子类的动态代理
     *      涉及的类:Enhancer
     *      提供者:第三方cglib库
     *  如何创建代理对象:
     *      使用Enhancer的create方法
     *  创建代理对象的要求:
     *      被代理类不能是最终类
     *  create方法的参数:
     *      Class:字节码
     *          用于指定被代理对象的字节码
     *
     *      Callback:用于提供增强的代码
     *          写如何代理,一般是写一个接口的实现类,通常情况下都是匿名内部类,但不是必须的,此接口的实现类都是谁用谁写,
     *          一般写的都是该接口的子接口实现类:MethodInterceptor(方法拦截)
     */  

因此我们需要导入一个cglib的jar包,或者直接在maven配置中加入坐标,而且生产者不再需要继承接口

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.1_3</version>
        </dependency>

Client

package cn.junko.cglib;

import cn.junko.proxy.IProducer;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

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

/**
 * 一个消费者
 */
public class Client {
    public static void main(String[] args) {
        //当匿名内部类访问外部成员变量,要求final修饰
        final Producer producer=new Producer();
        Producer cglibProducer = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
            /**
             * 执行被代理对象的任何方法都会经过该方法
             * @param proxy
             * @param method
             * @param args
             *      以上三个参数和基于接口动态代理中invoke方法的参数是一样的
             * @param methodProxy   当前执行方法的代理对象
             * @return
             * @throws Throwable
             */
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                 Object returnValue=null;
                 Float money= (Float) args[0];
                 if ("saleProduct".equals(method.getName())){
                     returnValue = method.invoke(producer,money*0.8f);
                 }
                return returnValue;
            }
        });
        cglibProducer.saleProduct(10000f);
    }
}

文章永久链接:https://tech.souyunku.com/27248

未经允许不得转载:搜云库技术团队 » Spring AOP里面的代理知识

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们