代理到底是什么?

大家好,我是本周的值班编辑 江南一点雨 ,本周将由我为大家排版并送出技术干货,大家可以在公众号后台回复“springboot”,获取最新版 Spring Boot2.1.6 视频教程试看。



之前星球的球友面试,问了我一些问题,说让我写一下这个代理,和代理到底是根据什么来进行区分,又该在什么地方使用。这篇文章我细致的讲解一下关于代理的一些问题。

代理分类

  1. 静态代理

  2. 动态搭理

静态代理

我们先说静态代理的实现方式,为什么不推荐使用静态代理?

1.继承方式实现代理(静态代理中的继承代理)

 
 
  1. //目标对象

  2. public class UserImpl {

  3. public void system(){

  4. System.out.println("输出测试");

  5. }

  6. }

  7. //代理对象

  8. public class Proxy extends UserImpl {

  9. @Override

  10. public void system() {

  11. super.system();

  12. System.out.println("增强之后的输出");

  13. }

  14. }

  15. //测试类

  16. public class TestMain {

  17. public static void main(String[] args) {

  18. UserImpl user = new Proxy();

  19. user.system();

  20. }

  21. }

静态代理可以看出来一点问题吧?

每次代理都要实现一个类,导致项目中代码很多;你每次想要代理,都要去实现一个类,代码就会成堆的增加,然后你就会发现项目的类就会越来越多,就会导致你们的项目显得很臃肿。而且代码的复用性太低了,并且耦合度非常高,这个我们所说的高内聚低耦合是相悖的。

动态代理

我们在看一下这个动态代理:

 
 
  1. //接口类

  2. public interface Italk {

  3. public void talk(String msg);

  4. }

  5. //实现类

  6. public class People implements Italk {

  7. public String username;

  8. public String age;

  9. public String getName() {

  10. return username;

  11. }

  12. public void setName(String name) {

  13. this.username= name;

  14. }

  15. public String getAge() {

  16. return age;

  17. }

  18. public void setAge(String age) {

  19. this.age = age;

  20. }

  21. public People(String name1, String age1) {

  22. this.username= name1;

  23. this.age = age1;

  24. }

  25. public void talk(String msg) {

  26. System.out.println(msg+"!你好,我是"+username+",我年龄是"+age);

  27. }

  28. }


  29. //代理类

  30. public class TalkProxy implements Italk {

  31. Italk talker;

  32. public TalkProxy (Italk talker) {

  33. //super();

  34. this.talker=talker;

  35. }

  36. public void talk(String msg) {

  37. talker.talk(msg);

  38. }

  39. public void talk(String msg,String singname) {

  40. talker.talk(msg);

  41. sing(singname);

  42. }

  43. private void sing(String singname){

  44. System.out.println("唱歌:"+singname);

  45. }

  46. }


  47. //测试


  48. public class MyProxyTest {

  49. //代理模式

  50. public static void main(String[] args) {


  51. //不需要执行额外方法的

  52. Italk people1=new People("湖海散人","18");

  53. people1.talk("No ProXY Test");

  54. System.out.println("-----------------------------");


  55. //需要执行额外方法的

  56. TalkProxy talker=new TalkProxy(people1);

  57. talker.talk("ProXY Test","七里香");

  58. }

  59. }

上面代码解析

一个 Italk 接口,有空的方法 talk()(说话),所有的 people 对象都实现(implements)这个接口,实现 talk() 方法,前端有很多地方都将 people 实例化,执行 talk 方法,后来发现这些前端里有一些除了要说话以外还要唱歌(sing),那么我们既不能在 Italk 接口里增加 sing() 方法,又不能在每个前端都增加 sing 方法,我们只有增加一个代理类 talkProxy ,这个代理类里实现 talksing 方法,然后在需要 sing 方法的客户端调用代理类即可,

这也是实现动态代理的方式,是通过实现(implements)的方式来实现的,这种方法的优点,在编码时,代理逻辑与业务逻辑互相独立,各不影响,没有侵入,没有耦合。

cgLib代理

还有一种是cgLib的代理,这种代理则是适合那些没有接口抽象的类代理,而Java 动态代理适合于那些有接口抽象的类代理。

我们来通过代码了解一下他到底是怎么玩的。

 
 
  1. //我们实现一个业务类,不实现任何的接口

  2. /**

  3. * 业务类,

  4. */

  5. public class TestService {

  6. public TestService() {

  7. System.out.println("TestService的构造");

  8. }


  9. /**

  10. * 该方法不能被子类覆盖,Cglib是无法代理final修饰的方法的

  11. */

  12. final public String sayOthers(String name) {

  13. System.out.println("TestService:sayOthers>>"+name);

  14. return null;

  15. }


  16. public void sayHello() {

  17. System.out.println("TestService:sayHello");

  18. }


  19. }



  20. /**

  21. * 自定义MethodInterceptor

  22. */

  23. public class MethodInterceptorTest implements MethodInterceptor {

  24. @Override

  25. public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

  26. System.out.println("======插入前置通知======");

  27. Object object = methodProxy.invokeSuper(o, objects);

  28. System.out.println("======插入后者通知======");

  29. return object;

  30. }


  31. }


  32. /**

  33. * 测试

  34. */

  35. public class Client {

  36. public static void main(String[] args) {

  37. // 代理类class文件存入本地磁盘方便我们反编译查看源码

  38. System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");

  39. // 通过CGLIB动态代理获取代理对象的过程

  40. Enhancer enhancer = new Enhancer();

  41. // 设置enhancer对象的父类

  42. enhancer.setSuperclass(TestService.class);

  43. // 设置enhancer的回调对象

  44. MethodInterceptorTest t = new MethodInterceptorTest();

  45. enhancer.setCallback(t);

  46. // 创建代理对象

  47. TestService proxy= (TestService)enhancer.create();

  48. // 通过代理对象调用目标方法

  49. proxy.sayHello();

  50. }

  51. }

  52. 上面代码想要运行,需要cglibjarasmjar不要忘记找

运行结果

 
 
  1. CGLIB debugging enabled, writing to 'D:\code'

  2. TestService的构造

  3. ======插入前置通知======

  4. TestService:sayHello

  5. ======插入后者通知======

实现CGLIB动态代理必须实现MethodInterceptor(方法拦截器)接口,

这个接口只有一个intercept()方法,这个方法有4个参数:

  • obj表示增强的对象,即实现这个接口类的一个对象;

  • method表示要被拦截的方法;

  • args表示要被拦截方法的参数;

  • proxy表示要触发父类的方法对象;

代理的使用

那么什么时候使用静态态代理,什么时候使用动态代理和cgLib代理呢?

一般情况静态代理是很少是用的,因为他对代码的复用性或者说是耦合度都非常不友好,不推荐使用。

如果目标对象至少实现了一个接口,那么就用JDK动态代理,所有由目标对象实现的接口将全部都被代理。

如果目标对象没有实现任何接口,就是个类,那么就用CGLIB代理。

我是懿,一个正在被打击还在努力前进的码农。欢迎大家关注我们的公众号,加入我们的知识星球,我们在知识星球中等着你的加入。



往期精彩回顾:

浅谈Java中字符串的初始化及字符串操作类

MyBatis 核心配置综述之 ParameterHandlers

spring 注解编程之注解属性别名与覆盖

分布式下必备神器之分布式锁


640?wx_fmt=jpeg

于加入知识星球的同学提供基本的福利:

文章有疑问的地方可以提问,其他工作问题都可以提问出来,作者免费作答。

 https://t.zsxq.com/Y3fYny7


每周都有大牛分享一些面试题,和面试注意的知识点!

 https://t.zsxq.com/2bufE2v


每周由Java极客技术独家编制的设计模式与大家分享!

 https://t.zsxq.com/3bUNbEI


每两周还会分享一个话题,和大家一起成长!

 https://t.zsxq.com/BI6Unm2


还有Java极客技术团队亲自录制了一套 Spring Boot 视频,这套视频加密,加密后放到云盘上,下载链接加密之后,一机一码,每个星球的用户一个播放授权码。

 

我们做知识星球的目的和其他星主一样,就是为了帮助大家一起更好的成长,与高手拉近距离,减少差距,其实你也是高手!

640?wx_fmt=png 640?wx_fmt=jpeg 640?wx_fmt=jpeg 640?wx_fmt=jpeg 640?wx_fmt=jpeg

1000人,50元/每年,现在大约还剩不到300个名额。

长按二维码

640?wx_fmt=png


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值