Spring基础之AOP和代理模式

文章目录

理解AOP

OOP - - Object Oriented Programming 面向对象编程

AOP - - Aspect Oriented Programming 面向切面编程

AOP是Spring提供的关键特性之一。AOP即面向切面编程,是OOP编程的有效补充。使用AOP技术,可以将一些系统性相关的编程工作,独立提取出来,独立实现,然后通过切面切入进系统。从而避免了在业务逻辑的代码中混入了很多的系统性相关的逻辑--比如:事务管理、日志记录、权限管理等,从而达到了将不同的关注点分离出来松耦合的效果。

AOP的实现原理

AOP的实现原理就是代理模式----其关键点是AOP框架自动创建的AOP代理

AOP代理分为静态AOP代理和动态AOP代理:

  1. 静态AOP代理 ---- 是指AspectJ实现的AOP代理,它是将切面代码直接编译到Java字节码文件中,发生在编译时。
  2. 动态AOP代理 ---- 是指将切面代码进行动态织入实现的AOP代理,发生在运行时。Spring的AOP即为动态AOP实现技术为:JDK提供的动态代理技术和CGLIB(Code Generation Library动态字节码增强技术)。尽管实现技术不一样,但都是基于代理模式,都是生成一个代理对象。

AOP代理模式

代理模式的核心作用就是通过代理,控制对被代理的目标对象的访问。它的设计思路是:定义一个抽象角色{接口},让代理角色和真实角色分别去实现它。

真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。它只关注真正的业务逻辑。

代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并在前后可以附加自己的操作。

代理模式实现歌星演出场景

静态代理

要求被代理类和代理类同时实现相应的一套接口,通过代理类调用重写接口的方法,实际上调用的是原始对象的同样的方法。如下图

java 复制代码
/**
 * 明星接口
 */
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
     */
    public ProxyStar(Star star) {
        this.star = star;
    }

    @Override
    public void sing() {
        System.out.println("明星代理先进行商务谈判...");
        //真实明星唱歌
        star.sing();
        System.out.println("明星演出完,代理收款...");
    }
}

这样的话,逻辑就非常清晰了。在代理类中,可以看到,维护了一个Star对象,通过构造方法传进来一个真实的Star对象给其赋值,

然后在唱歌这个方法里,使用真实对象来唱歌。所以说面谈、收钱都是由代理对象来实现的,唱歌是代理对象让真实对象来做。

java 复制代码
/**
 * 测试客户端
 */
public class TestClient {
    /**
     * 测试静态代理结果
     * @param args
     */
    public static void main(String[] args) {
        Star realStar = new RealStar(); //创建真实对象
        Star proxy = new ProxyStar(realStar); //创建代理对象
        proxy.sing();

    }
}

静态代理的优缺点:
优点:

1.被代理的业务类只需要做好自己的业务,实现了责任分离,保证了业务类的重用性

2.将业务类隐藏起来,起到了保护作用
缺点:

1.代理对象的某个接口只服务于某一个业务对象,每个真实对象都得创建一个代理对象

2.如果接口增加一个方法,除了所有实现类需要实现这个方法外,代理类也需要实现,增加了代码的复杂度和成本

动态代理

在程序运行时由JVM通过反射机制动态的创建出代理对象的字节码。代理对象和真实对象的关系是在程序运行时才确定

1-JDK动态代理

JDK动态代理是使用java.lang.reflect包中的Proxy类与InvocationHandler接口来完成的,要使用JDK动态代理,必须要定义接口

text 复制代码
API分析:
(1) java.lang.reflect.Proxy 类
  |-Java动态代理机制生成的所有动态代理类的父类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。
   public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler hanlder)
方法职责:为指定类加载器、一组接口及调用处理器  生成动态代理类实例 
参数:
   loader        :类加载器
   interfaces        :目标(真实)对象实现的接口
   hanlder        :代理执行处理器
返回:动态生成的代理对象
(2) java.lang.reflect.InvocationHandler接口:
public Object invoke(Object proxy, Method method, Object[] args)
方法职责:负责集中处理动态代理类上的所有方法调用
参数: 
    proxy :  生成的代理对象
    method: 当前调用的真实方法对象
    args:    当前调用方法的实参
返回: 真实方法的返回结果
jdk动态代理操作步骤 
 实现InvocationHandler接口,创建自己增强代码的处理器。
② 给Proxy类提供ClassLoader对象和代理接口类型数组,创建动态代理对象。
③ 在处理器中实现增强操作。

示例:

java 复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * JDK动态代理处理类
 */
public class JdkProxyHandler implements InvocationHandler {
    /**
     * 用来接收真实明星对象
     */
    private Object realStar;

    /**
     * 传入真实明星对象,动态创建一个代理
     *
     * @param realStar
     * @return
     */
    public Object bind(Object realStar) {
        this.realStar = realStar;
        return Proxy.newProxyInstance(realStar.getClass().getClassLoader(),
                realStar.getClass().getInterfaces(), this);
    }


    /**
     * 当调用代理对象的方法时,会调用此方法
     *
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("明星代理先进行谈判...");
        Object result = method.invoke(realStar, args); //真实对象明星唱歌
        System.out.println("演出完,代理收款...");
        return result;
    }
}
/**
 * 测试Jdk动态代理客户端
 */
public class TestJdkProxyClient {
    public static void main(String[] args) {
        Star realStar = new RealStar(); //真实对象
        //创建一个代理对象实例
        Star proxy = (Star)new JdkProxyHandler().bind(realStar);
        //代理对象调用方法
        proxy.sing();
    }
}

相对于静态代理,JDK 动态代理大大减少了我们的开发任务,同时减少了对业务接口的依赖,降低了耦合度

2-CGLIB动态代理

由上面的分析可知,JDK 实现动态代理需要实现类通过接口定义业务方法,那对于没有接口的类,如何实现动态代理呢,这就需要 CGLIB 了。

  • CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类.
  • CGLIB动态代理是针对没有接口的类进行的代理,和JDK动态代理的区别是获取代理对象的方法不一样
java 复制代码
/**
 * 真实明星类
 */
public class RealStar{
    public void sing() {
        System.out.println("明星本人开始唱歌...");
    }
}

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * Cglib代理处理类
 */
public class CglibProxyHandler implements MethodInterceptor {
    /**
     * 维护的目标对象
     */
    private Object realStar;
    /**
     *  Enhancer类是CGLIB中的一个字节码增强器,它可以方便的对你想要处理的类进行扩展
     */
    private Enhancer enhancer = new Enhancer();

    /**
     * 根据被代理对象的类型 ,动态创建一个代理对象
     * @param realStar
     * @return
     */
    public Object bind(Object realStar){
this.realStar = realStar;
        enhancer.setSuperclass(realStar.getClass()); //使用增强器,设置被代理对象为父类
        enhancer.setCallback(this);  //设置回调方法,拦截器
        //动态创建一个代理类对象,并返回
        return enhancer.create();
    }

    /**
     *  当调用代理对象的方法时,会调用此方法进行拦截
     * @param proxy 代理对象
     * @param method 需要增强的方法
     * @param objects 需要增强方法的参数
     * @param methodProxy  需要增强的方法的代理
     * @return 方法执行后的返回值
     * @throws Throwable
     */
    @Override
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("代理明星先进行演出谈判...");
        //invokeSuper方法调用的对象已经是增强了
        Object result = methodProxy.invokeSuper(object,args); //明星唱歌
        System.out.println("演出完成,代理去收款...");
        return result;
    }
}

/**
 * 测试Cglib动态代理客户端
 */
public class TestCglibProxyClient {
    public static void main(String[] args) {
        RealStar realStar = new RealStar(); //真实对象
        //创建一个代理对象实例
        RealStar proxy = (RealStar) new CglibProxyHandler().bind(realStar);
        //代理对象调用方法
        proxy.sing();
    }
}

关于CGLIB 动态代理总结:

  • CGLib所创建的动态代理对象在实际运行时候的性能要比JDK动态代理高不少,但是CGLIB 创建代理对象时所花费的时间却比JDK多得多,所以对于单例singleton的对象,因为无需频繁创建对象,用 CGLIB 合适,反之使用JDK方式要更为合适一些。
  • CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的,诸如private的方法也是不可以作为切面的。

小结

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

总结

  • AOP面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
  • 使用AOP技术,可以将一些系统性相关的编程工作,独立提取出来,独立实现,然后通过切面切入进系统。
  • Spring的AOP为动态AOP,实现的技术为: JDK提供的动态代理技术 和 CGLIB(动态字节码增强技术)
  • 代理模式--官方的定义为"为其他对象提供一种代理以控制对这个对象的访问"
  • java中有静态代理、JDK动态代理、CGLib动态代理的方式。静态代理指的是代理类是在编译期就存在的,相反动态代理则是在程序运行期动态生成的
相关推荐
xmh-sxh-13143 分钟前
jdk各个版本介绍
java
天天扭码22 分钟前
五天SpringCloud计划——DAY2之单体架构和微服务架构的选择和转换原则
java·spring cloud·微服务·架构
程序猿进阶22 分钟前
堆外内存泄露排查经历
java·jvm·后端·面试·性能优化·oom·内存泄露
FIN技术铺27 分钟前
Spring Boot框架Starter组件整理
java·spring boot·后端
小曲程序34 分钟前
vue3 封装request请求
java·前端·typescript·vue
陈王卜1 小时前
django+boostrap实现发布博客权限控制
java·前端·django
小码的头发丝、1 小时前
Spring Boot 注解
java·spring boot
java亮小白19971 小时前
Spring循环依赖如何解决的?
java·后端·spring
飞滕人生TYF1 小时前
java Queue 详解
java·队列
武子康1 小时前
大数据-230 离线数仓 - ODS层的构建 Hive处理 UDF 与 SerDe 处理 与 当前总结
java·大数据·数据仓库·hive·hadoop·sql·hdfs