代理机制

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{@Overridepublic void add() {System.out.println("添加书籍");}@Overridepublic void update() {System.out.println("更新书籍");}@Overridepublic void delete() {System.out.println("删除书籍");}@Overridepublic 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 目标对象@Overridepublic 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//返回值  returnValuepublic 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接口实现aoppublic 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接口实现aoppublic 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的

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

  1. 【Spring】Spring中AOP解析 上 两种动态代理方式与ProxyFactory代理工厂

    1.两种动态代理方式演示 1.1 JDK动态代理 JDK的动态代理,就是在程序运行的过程中,根据被代理的接口来动态生成代理类的class文件,并加载运行的过程.要求被代理类必须实现一个接口 . 如下案 ...

  2. AOP的两种动态代理机制

    从多处拷贝粘贴而来,原文无处可考了,感谢几位原创者的付出 JDK动态代理和Cglib动态代理 写了个小demo,GitHub:https://github.com/ConanDennis/dynami ...

  3. Java两种动态代理JDK动态代理和CGLIB动态代理

    目录 代理模式 JDK动态代理 cglib动态代理 测试 代理模式 代理模式是23种设计模式的一种,他是指一个对象A通过持有另一个对象B,可以具有B同样的行为的模式.为了对外开放协议,B往往实现了一个 ...

  4. Java中三种代理方式—— 静态代理与两种动态代理的实现机制

    个人博客请访问 http://www.x0100.top 1.代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现 ...

  5. Spring的两种动态代理:Jdk和Cglib 的区别和实现

    这是有意义的一天!自己研究一路畅通的感觉真爽 原理是参考大神的,代码手敲 一.原理区别: java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处 ...

  6. spring aop 会根据实际情况(有无接口)自动选择 两种 动态代理(jdk和cglib)之一...

    资料: (1)Cglib的简单使用: https://blog.csdn.net/zhanghongjie0302/article/details/45648947 (2)关于java字节码框架ASM ...

  7. Spring AOP的作用,动态代理模式

    AOP即面向切面编程.AOP是基于代理模式的. 代理模式: 当我们需要修改一个类,在类中加入代码时,为了不破坏这个类的封装性.可以使用代理模式,建立一个代理类. 比如:修改需求,在调用UserCont ...

  8. spring aop的两种写法aspect和advisor

    本文转自:https://www.cnblogs.com/leiOOlei/p/3709607.html 首先看个例子,如下 接口代码: package com.lei.demo.aop.schema ...

  9. sqlmap的两种挂代理方式

    文章目录 前言 一.第一种方式直接挂代理 二.第二种方式间接挂代理 前言 为什么要挂代理呢: sqlmap原理和暴力破解类似如果速度过快则会很容易被封ip,所以挂代理是避免这种情况的发生,即使发生也可 ...

最新文章

  1. TColorToHex 与 HexToTColor
  2. python基础学习教程:Python基础语法
  3. 生成式对抗网络Generative Adversarial Networks(GANs)
  4. python最简分数_1062 最简分数 Python实现
  5. 小学认识计算机硬件ppt,认识计算机硬件课件.ppt
  6. html5显示字母的值,使用HTML5 Canvas API控制字体的显示与渲染的方法
  7. Tricks(三十九)—— 使用 list comprehension 构造笛卡尔积
  8. 10a 16a 插座区别_10A和16A的插座能混用吗?
  9. java 线程池 hash_java线程池实例 - Hashsound的个人空间 - OSCHINA - 中文开源技术交流社区...
  10. 【python】批量替换文本中的某部分内容
  11. 幼儿抽象逻辑思维举例_幼教科目二 | 幼儿认知的发展(思维)
  12. 联想电脑重装win7系统详细图文教程
  13. 百度人脸识别 人脸识别模型_人脸识别的现代君主制
  14. 《C++ Primer 第5版》-13.3交换操作-康奈尔笔记
  15. 润乾报表Api导出word只读
  16. Qt QImage scaled方法缩放中的问题
  17. 常见Web安全漏洞深入解析
  18. 关于HMC5883L驱动书写及调试的总结
  19. Kubernetes高可用性监控:Thanos的部署
  20. java需要学哪些知识习

热门文章

  1. redis jredis jedis 使用
  2. 我的世界java版刷雪球机,我的世界自动刷雪球机制作方法,自动刷雪球机怎么做?...
  3. 直播平台搭建中音视频通信——直播协议和视频推流
  4. 一步一图带你深入剖析 JDK NIO ByteBuffer 在不同字节序下的设计与实现
  5. 按时间(年月日、当前时间以及秒数)保存图片
  6. 大学计算机改革PPT,合肥工业大学计算机与信息学院毕业设计改革与实践_..ppt_点石文库dswenku.com...
  7. 360服务器维护,大皇帝,360大皇帝官方网站-大皇帝选区|大皇帝攻略|大皇帝开服表-360游戏中心...
  8. 申宝公司-道指、标普创收盘新高
  9. trnd matlab什么意思,MATLAB隨機數生成器
  10. JS用switch写如下程序 我们来做个周计划,周一、二学习理念知识, 周三、四到企业实践,周五总结经验,周六、日休息和娱乐