Spring AOP的两种动态代理方式的原理和实现(JDK和CGLIB)

1 篇文章 0 订阅
1 篇文章 0 订阅

代理机制

Spring 的AOP 部分使用使用JDK动态代理,部分使用CGLIB来为目标对象创建代理。如果被代理的目标对象实现了至少一个接口,则会使用JDK动态代理;如果目标对象没有实现任何接口,则会创建CGLIB动态代理。CGLIB是第三方包,从Spring4.3开始就无需再次导入包了。
在这里插入图片描述
注意
1)客户端直接调用与目标类相对应的代理类的功能,
2)为了保证代理类与目标类具有相同功能,代理类与目标类必须实现相同的接口

代理是实现AOP的核心和关键技术。只要是AOP,就一定会涉及代理技术

. 代理技术的分类

按照是否是在程序运行期间产生代理类可以将代理分为静态代理和动态代理

1.静态代理:就是手动为每一个目标类的每一个方法都增加交叉业务,也就是手动为每一个目标类增加代理类
缺点:如果目标类数量非常多或者目标类中的功能非常多,直接使用静态代理的方式来为目标类增加交叉业务会非常的繁琐。

2.动态代理:通过特定的设置,在程序运行期间指示JVM动态地生成类的字节码。这种动态生成的类往往被用作代理类,即动态代理类。也就是运行时做 编译的事情并且把生成的字节码加载成这个类的Class对象

首先是静态代理

创建一个抽象角色,比如咋们平时做的用户业务,抽象起来就是增删改查!

编写接口方法

package com.ice.spring.service;
//抽象角色:增删改查业务
public interface UserService {
    void add();
    void delete();
    void update();
    void query();
}

我们需要一个真实对象来完成这些增删改查操作,也就是接口的实现类

package com.ice.spring.service;
//真实对象,完成增删改查操作的人
public class UserServiceImpl implements UserService {
    public void add() {
        System.out.println("增加了一个用户");
    }
    public void delete() {
        System.out.println("删除了一个用户");
    }
    public void update() {
        System.out.println("更新了一个用户");
    }
    public void query() {
        System.out.println("查询了一个用户");
    }
}

现在我们需要增加一个日志功能,怎么实现!

思路1 :在实现类上增加代码 繁琐,影响业务

思路2:使用静态代理来做,能够不改变原来的业务情况下,可以实现此功能

设置一个代理类来处理日志! 代理角色

package com.ice.spring.service;
//代理角色,在这里面增加日志的实现
public class UserServiceProxy implements UserService {
    private UserServiceImpl userService;
    public void setUserService(UserServiceImpl userService) {
        this.userService = userService;
    }
    public void add() {
        log("add");
        userService.add();
    }
    public void delete() {
        log("delete");
        userService.delete();
    }
    public void update() {
        log("update");
        userService.update();
    }
    public void query() {
        log("query");
        userService.query();
    }
    public void log(String msg){
        System.out.println("执行了"+msg+"方法");
    }
}

测试访问类:

package com.ice.spring;

import com.ice.spring.service.UserServiceImpl;
import com.ice.spring.service.UserServiceProxy;

public class StaticTest {


        public static void main(String[] args) {
            //真实业务
            UserServiceImpl userService = new UserServiceImpl();
            //代理类
            UserServiceProxy proxy = new UserServiceProxy();
            //使用代理类实现日志功能!
            proxy.setUserService(userService);
            proxy.add();
        }

}

运行结果
在这里插入图片描述
总节
缺点:
1)目标类增多,代理类会成倍增多
2)接口和功能增加了修改了,会影响较多的实现类,厂家类,代理类都需要修改,工作量巨大

动态代理

1.动态代理的角色和静态代理的一样 .

2.动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的

3.动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理

          1)  基于接口的动态代理——JDK动态代理
          2)  基于类的动态代理—cglib动态代理

JDK的动态代理

核心 : InvocationHandler 接口和 Proxy类

1.前提:要求目标类必须实现某些接口

2.JVM生成的动态代理类只能作用于具有相同接口目标类的代理类

3.要求目标类实现特定的接口才能生成和目标类对应的动态代理类

代码的实现

接口

package com.ice.spring.service;

public interface booksService {
    public void add();
    public void delete();
    public void update();
    public void select();
}


接口的实现类

//真实对象,完成增删改查操作的人
package com.ice.spring.service;

import org.mybatis.spring.support.SqlSessionDaoSupport;

public class  booksServiceImpl  implements booksService{
    @Override
    public void add() {
        System.out.println("添加书籍");
    }

    @Override
    public void update() {
        System.out.println("更新书籍");
    }

    @Override
    public void delete() {
        System.out.println("删除书籍");
    }

    @Override
    public void select() {
        System.out.println("查询书籍");
    }
}

现在我们需要增加新的功能,写我们的增强类 , 我们编写两个 , 一个前置增强 一个后置增强
在这里插入图片描述

前置增强

package com.ice.spring.Log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class beforeLog implements MethodBeforeAdvice {
    //Method 要执行的目标对象的方法
    //args 参数
    //target 目标对象
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("开始日志");
        System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
    }
}

后置增强

package com.ice.spring.Log;
import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

public class afterLog implements AfterReturningAdvice {
    @Override
    //返回值  returnValue
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {

        System.out.println(target.getClass().getName()+"的"+method.getName()+"方法被执行了"+"结果返回为"+returnValue);
        System.out.println("结束日志");
    }
}

最后去spring的文件中注册 , 并实现aop切入实现 , 注意导入约束 .

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--
    xmlns 即 xml namespace xml 使用的命名空间
    xmlns:xsi 即 xml schema instance xml 遵守的具体规范
    xsi:schemaLocation 本文档 xml 遵守的规范 官方指定
    -->
   <!--注册bean-->
     <!--使用spring APi 接口-->
    <bean id="booksService" class="com.ice.spring.service.booksServiceImpl" />
    <bean id="beforeLog" class="com.ice.spring.Log.beforeLog"/>
    <bean id="afterLog" class="com.ice.spring.Log.afterLog"/>
    <!--配置AOP 需要配置AOP的约束-->
    <aop:config>
        <!--切入点 -->
        <aop:pointcut id="pointcut" expression="execution (* com.ice.spring.service.booksServiceImpl.*())"/>
        <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog"  pointcut-ref="pointcut"/>

    </aop:config>
   <!-- <aop:aspectj-autoproxy proxy-target-class="true"/>-->
 
</beans>

动态代理测试

package com.ice.spring;


import com.ice.spring.service.booksService;
import com.ice.spring.service.booksServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
//方式一  使用spring的api接口实现aop

public class AOPtest {
    public static void main(String[] args) {
        //创建容器
        ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
        //动态代理代理的是接口
        booksService booksService=(booksService) context.getBean("booksService");
        System.out.println(booksService.getClass().getName());
        booksService.select();
    }

}

运行结果(执行JDK动态代理 com.sun.proxy.$Proxy5)
在这里插入图片描述

CGLIB的动态代理

1.目标类没有实现相应的接口,又需要为这个类动态生成代理类。此时第三方类库CGLIB是最好的选择。

2.CGLIB可以为目标类动态生成目标类的子类,并把这个动态生成的子类作为这个类的代理类。

代码实现一(有接口的情况)

只需要将实现JDK动态代理的代码稍作修改即可
1.在spring配置文件添加如下代码

  <aop:aspectj-autoproxy proxy-target-class="true"/>
 

运行结果(执行了CGLIB动态代理(com.ice.spring.service.booksServiceImpl E n h a n c e r B y S p r i n g C G L I B EnhancerBySpringCGLIB EnhancerBySpringCGLIBb439268e)

在这里插入图片描述

代码实现二(没有接口的情况)

1.编写没有实现任何接口的目标类

package com.ice.spring.service;
//真实对象,完成增删改查操作的人
public class UserServiceCglib  {
    public void add() {
        System.out.println("增加了一个用户");
    }
    public void delete() {
        System.out.println("删除了一个用户");
    }
    public void update() {
        System.out.println("更新了一个用户");
    }
    public void query() {
        System.out.println("查询了一个用户");
    }
}

2.在spring配置文件中注册bean

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--
    xmlns 即 xml namespace xml 使用的命名空间
    xmlns:xsi 即 xml schema instance xml 遵守的具体规范
    xsi:schemaLocation 本文档 xml 遵守的规范 官方指定
    -->
   <!--注册bean-->
     <!--使用spring APi 接口-->
    <bean id="booksService" class="com.ice.spring.service.booksServiceCglib" />
    <bean id="beforeLog" class="com.ice.spring.Log.beforeLog"/>
    <bean id="afterLog" class="com.ice.spring.Log.afterLog"/>
    <!--配置AOP 需要配置AOP的约束-->
    <aop:config>
        <!--切入点 -->
        <aop:pointcut id="pointcut" expression="execution (* com.ice.spring.service.booksServiceCglib.*())"/>
        <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog"  pointcut-ref="pointcut"/>

    </aop:config>
</beans>

3.注意:无需写代理类,由spring自动生成

4.编写测试类代码

package com.ice.spring;
import com.ice.spring.service.booksService;
import com.ice.spring.service.booksServiceCglib;
import com.ice.spring.service.booksServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
//方式一  使用spring的api接口实现aop

public class AOPtest {
    public static void main(String[] args) {
        //创建容器
        ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
        //动态代理代理的是接口
        booksServiceCglib booksServiceCglib=(booksServiceCglib) context.getBean("booksService");
        System.out.println(booksServiceCglib.getClass().getName());
        booksServiceCglib.select();
    }

}

运行结果(在没有实现接口的情况下,spring默认开启了CGLIB,无需手动开启)

在这里插入图片描述

总结:

一、有接口的情况
1)默认使用JDK动态代理

2)可以开启CGLIB动态代理,该方式执行效率远远高于JDK的
开启方法如下

<aop:aspectj-autoproxy proxy-target-class="true"/>
 

二、没有接口的情况
1)默认开启CGLIB动态代理,由spring自动生成代理类,该方式执行效率远远高于JDK的

  • 13
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

紫风魅影

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值