Java Spring-AOP动态代理-JDK和CGlib实现

  • Post author:
  • Post category:java




Java Spring-AOP CGlib和JDK动态代理实现

编码是为了解决生活中的问题,譬如现在我想用筷子吃饭的时候看手机,那么首先会有两个对象。

在这里插入图片描述

现在如果是

面向对象编程

思想,我们会new一个筷子对象,new一个手机对象,然后先调用 chopsticks.pickUp() ,再调用 phone.user() 。

那有没有一种做法,在不改动现有两个对象类内容的情况下,我直接把手机的使用方法

增强到

筷子的拿起方法里,这样我

直接调用这个增强后的操作

就能完成吃饭的时候玩手机这个功能,这个就是AOP的思想,即

面向切面编程

在这里插入图片描述


AOP

:Aspect Oriented Programming,面向切面编程,将创建Bean的权利给第三方,第三方创建的Bean是

代理Bean的代理类

,代理增强的方法和Bean本身的方法当作一个切面,是对面向对象编程OOP的升华。OOP是纵向对一个事物的抽象,一个对象包括静态的属性信息,包括动态的方法信息等。而AOP是横向的对不同事物的抽象,属性与属性、方法与方法、对象与对象都可以组成一个切面,而用这种思维去设计编程的方式叫做

面向切面编程

根据这个例子,再介绍几个AOP的

术语

属性 属性名 对应 说明
Target 目标对象 Chopsticks 被增强的方法所在的对象
Proxy 代理对象 ChopsticksProxy 对目标对象进行增强后的对象,客户端实际调用的对象
Joinpoint 连接点 pickUp()、putDown() 目标对象中可以被增强的方法
Pointcut 切入点 pickUp() 目标对象中实际被增强的方法
Advice 通知\增强 use() 增强部分的代码逻辑
Aspect 切面 pickUp()+use() 增强和切入点的组合
Weaving 织入 组合pickUp()+use()的动作 将通知和切入点组合动态组合的过程

此时use()方法的代码逻辑叫做

通知

,通知有

5种类型

类型 类型名 说明
before 前置通知 在切点方法执行前执行
afterReturning 后置通知 在切点方法执行后执行,切点执行抛异常时不执行
around 环绕通知 在切点方法执行前执行前环绕通知,切点方法执行后执行后环绕通知,切点执行抛异常时不执行后环绕通知
afterThrowing 异常通知 在切点方法执行抛异常时执行
after 最终通知 不管切点方法执行有无异常都在最后执行

那么实现此操作,有两种实现方式,一种是

JdkDynamicAopProxy

实现,一种是

CglibAopProxy

实现。


JdkDynamicAopProxy源码获取代理对象:

在这里插入图片描述


CglibAopProxy源码获取代理对象:

在这里插入图片描述

从源码中我们可以看出来区别:

  • JDK动态代理:

    基于接口

    动态生成实现类的代理对象,目标类有接口
  • Cglib动态代理:

    基于被代理对象动态生成子对象

    为代理对象,目标类无接口且不能使用final修饰

一般如果目标有接口的话,Spring

默认

使用JDK去动态代理实现,也可以

配置指定

使用Cglib实现。

下面简单的Demo介绍一下两种实现的基本原理:


JDK动态代理Demo:

//实现BeanPosrProcessor接口,实现Bean处理器的after方法将代理类封装成BeanDefinition对象注册到BeanDefinitionMap中
//实现ApplicationContextAware接口,实现aware回调方法,获取Spring容器中的增强方法类
public class AopBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware {

    private ApplicationContext applicationContext;

    //目的:对Chopsticks中的pickUp()进行增强,增强的方法存在于Phone
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

            //生成当前Bean的Proxy对象
            Object beanProxy =  Proxy.newProxyInstance(bean.getClass().getClassLoader(),
                    bean.getClass().getInterfaces(), new InvocationHandler() {
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            //Phone的获取(从Spring容器中获取)
                            Phone phone= applicationContext.getBean(Phone.class);
                            //执行增强对象的方法,此处举例前置通知类型
                            phone.use();
                            //执行目标对象的目标方法
                            Object result = method.invoke(bean, args);
                            return result;
                        }
                    });
            return beanProxy;
        
        return bean;
    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        //获取Spring容器
        this.applicationContext = applicationContext;
    }
}


Cglib实现Demo:

public class CGlibTest {

    public static void main(String[] args) {

        //CGlib基于父类(目标类)生成Proxy

        //目标对象
        Chopsticks chopsticks= new Chopsticks();
        //通知对象
        Phone phone= new Phone();

        //编写CGlib代码
        Enhancer enhancer = new Enhancer();
        //设置父类
        enhancer.setSuperclass(Chopsticks .class);//生成的代理对象就是Chopsticks的子类
        //设置回调
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            //intercept方法相当于JDK的Proxy的invoke方法
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                //执行增强对象的方法,此处举例前置通知类型
                phone.use();
                Object invoke = method.invoke(target, objects);//执行目标方法
                return invoke;
            }
        });

        //生成代理对象
        Chopsticks o = (Chopsticks ) enhancer.create();

        //测试
        o.show();

    }

}



版权声明:本文为qq_39004632原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。