代理模式的特点

代理模式是常用的java设计模式,它的特征是代理类与委托类有同样的接口。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类。

代理类的对象并不是真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。

按照代理类的创建时期,代理类可分为两种

  静态代理类:由程序员创建源代码,在对其编译。在程序运行之前,代理类的.class文件就已经存在了。

  动态代理类:在程序运行时,通过反射机制创建而成。

静态代理

1.首先我们写一个被代理类

  1. package javaee.net.cn.proxy;
  2. /**
  3. * 需要动态代理的接口
  4. */
  5. public interface Subject{
  6. public void save();
  7. }

2.在写一个实现类(实际被代理的对象)

  1. package javaee.net.cn.proxy;
  2. /**
  3. * 实际对象
  4. */
  5. public class RealSubject implements Subject{
  6. public void save(){
  7. System.out.println("insert into ......");
  8. }
  9.  
  10. }

3 手动编写代理类

  1. public class StaticProxy implements Subject{
  2. private Subject subject;
  3. public StaticProxy(Subject subject){
  4. this.subject=subject;
  5. }
  6. @Override
  7. public String save() {
  8. System.out.println("trancation start");
  9. String result = subject.save();
  10. System.out.println("trancation commit");
  11. return result;
  12. }
  13. }

动态代理

与静态代理对照的是动态代理,动态代理的字节码在程序运行时由java反射机制动态生成,无需程序员手工编写它的源代码。

动态代理不仅简化了编程工作,而且提高了软件系统的可扩展性,因为java反射机制可以生成任意类型的动态代理类。

java.lang.reflect包中的Proxy类和InvocationHandler接口提供了生成动态代理类的能力。

Proxy提供了创建动态代理类及其实例的静态方法。

1)getProxyClass(ClassLoader loader,Class<?>... interfaces)

  1.   静态方法负责创建动态代理类,参数loader指定动态代理类的类加载器,参数interfaces指定动态代理类需要实现的所有接口。

2)newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

静态方法负责创建代理类的实列,参数loader指定动态代理类的类加载器,参数interfaces指定动态代理类需要实现的所有接口。

以下两种方式都创建了实现Subject接口的动态代理类的实列。

  1. /**方式一*/
  2. //创建InvocationHandler对象
  3. InvocationHandler invocationHandler = new InvocationHandler(...);
  4. //创建动态代理类
  5. Class proxyClass = Proxy.getProxyClass(Subject.class.getClassLoader(), new Class[] {Subject.class});
  6. //创建动态代理类的实列
  7. Subject subject = (Subject) proxyClass.getConstructor(new Class[] {InvocationHandler.class}).newInstance(new Object[] {invocationHandler});
  8.  
  9. /**方式二*/
  10. //创建InvocationHandler对象
  11. InvocationHandler invocationHandler = new InvocationHandler(...);
  12. //直接创建动态代理类的实列
  13. Subject subject = Proxy.newProxyInstance(Subject.class.getClassLoader(), new Class[] {Subject.class},invocationHandler);

由Proxy方法创建的动态代理类具有以下特点:

1) 动态代理类是public 、final 和非抽象类型的。

2)动态代理类继承了 java.lang.reflect.Proxy类;

3)动态代理类的名字以"$Proxy"开头

4) 动态代理类实现getProxyClass()和newProxyInstance()方法中的参数interface指定的所有接口

5)Proxy类的isProxy(Class<?> cl)静态方法用来判断指定的类是否为动态代理类。

6)动态代理类都具有一个Public类型的构造方法,该构造方法有一个InvocationHandler类型的参数

由Proxy静态方法创建动态代理类的实列有以下特点。

1)每一个动态代理类的实列都和一个InvocationHandler实列关联。Proxy类的getInvocationHandler(Object proxy)静态方法返回与参数proxy指定的代理类实列所关联的InvocationHandler对象。

2) 假定Subject有一个 save()方法,那么程序调用动态代理类实列subject的save()方法时,该方法会调用与他关联的InvocationHandler对象的invoke()方法。

InvocationHandler 接口为方法调用接口,它声明了负责调用任意一个方法的invoke()方法:

invoke(Object proxy, Method method, Object[] args)

参数Proxy指定动态代理类的实列,参数method指定被调用的方法,参数args指定向被调用方法传递的参数,invoke()方法的返回值表示被调用方法的返回值。

1.ProxynewProxyInstance方法创建代理类的实列

  Proxy 提供用于创建动态代理类和实例的静态方法

  1. package javaee.net.cn.proxy;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Proxy;
  4.  
  5. /**
  6. * 动态代理演示
  7. * 通过分析代码可以看出Java 动态代理,具体有如下四步骤:
  8. 通过实现 InvocationHandler 接口创建自己的调用处理器;
  9. 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
  10. 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
  11. 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
  12. */
  13. public class Test{
  14. public static void main(String[] args) {
  15. //代理的真实对象
  16. Subject realSubject = new RealSubject();
  17. InvocationHandler handler = new LogInterceptor(realSubject);
  18. ClassLoader loader = realSubject.getClass().getClassLoader();
  19. Class<?>[] interfaces = realSubject.getClass().getInterfaces();
  20. /**
  21. * 该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
  22. */
  23. Subject subjectProxy = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);
  24. subjectProxy.save();
  25. }
  26. }

2.实现InvocationHandler接口 用来创建一个InvocationHandler对象。

  1. package javaee.net.cn.proxy;
  2.  
  3. import java.lang.reflect.InvocationHandler;
  4. import java.lang.reflect.Method;
  5.  
  6. /**
  7. * 调用处理器实现类
  8. * 每次生成动态代理类对象时都需要指定一个实现了该接口的调用处理器对象
  9. */
  10. public class LogInterceptor implements InvocationHandler{
  11. /**
  12. * 这个就是我们要代理的真实对象
  13. */
  14. private Object target;
  15. /**
  16. * 构造方法,给我们要代理的真实对象赋初值
  17. * @param subject
  18. */
  19. public LogInterceptor(Object target){
  20. this.target = target;
  21. }
  22.  
  23. /**
  24. * 该方法负责集中处理动态代理类上的所有方法调用。
  25. * 调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行
  26. * @param proxy 代理类实例
  27. * @param method 被调用的方法对象
  28. * @param args 调用参数
  29. * @throws Throwable
  30. */
  31. @Override
  32. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
  33. //在代理真实对象前我们可以添加一些自己的操作
  34. System.out.println("trancation start");
  35.  
  36. //当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
  37. Object returnValue = method.invoke(target, args);
  38.  
  39. //在代理真实对象后我们也可以添加一些自己的操作
  40. System.out.println("trancation commit");
  41. return returnValue;
  42. }
  43. }

调用顺序:当我们调用subjectProxy 的save()方法 会进入 InvocationHandler实现类的invoke()方法,invoke()方法里面会执行realSubject的save()方法

下面是方法运行的结果

trancation start
insert into ......
trancation commit

像是Spring的事物吧。Spring AOP管理的事物,原理也是动态代理

这是对反射和classLoader的一些补充解释

Spring事物不生效的原因

对于JDK而言,它是要求被代理的目标对象必须拥有接口,而对于CGLIB则不做要求。默认情况下,Spring会安装一条这样的规则处理

当你需要使用AOP的类拥有接口时,它会以JDK动态代理运行,否则以CGLIB运行。

思考:Spring中如何强制使用CGLIB实现AOP?
 (1)添加CGLIB库
 (2)在spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>

@Transactional自调用失效的问题

  1. class UserService{
  2. @Transactional
  3. void A(){
  4. B();
  5. }
  6. @Transactional
  7. void B(){
  8. }
  9. }

我们在一个类userService的接口A,调用userService的另一个接口B。虽然接口A和接口B都加上了@Transactional注解。但是对于B接口而言 事物不生效(出现错误不会回滚)。

原因:Spring数据库事物的约定实现的原理是AOP,而AOP的原理是动态代理,在自调用的过程中是类自身的调用,而不是代理对象去调用,那么久不会产生AOP,

这样Spring就不能把你的代码织入到约定的流程中。

解决方法: 想办法把类调用其内部的方法编程代理类之间的调用,那就是 从IOC容器中再去获得一次类userService,此时获取的对象是Spring IOC容器中的代理对象

最后用新获取的对象执行接口B,此时接口A和接口B的@Transaction都会生效。

CGLIB实现动态代理

JDK设计模式之—动态代理的更多相关文章

  1. Java设计模式之代理模式(静态代理和JDK、CGLib动态代理)以及应用场景

    我做了个例子 ,需要可以下载源码:代理模式 1.前言: Spring 的AOP 面向切面编程,是通过动态代理实现的, 由两部分组成:(a) 如果有接口的话 通过 JDK 接口级别的代理 (b) 如果没 ...

  2. JDK、CGlib动态代理详解

    Java动态代理之JDK实现和CGlib实现(简单易懂)      一 JDK和CGLIB动态代理原理 1.JDK动态代理 利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生 ...

  3. 动态代理的两种方式,以及区别(静态代理、JDK与CGLIB动态代理、AOP+IoC)

    Spring学习总结(二)——静态代理.JDK与CGLIB动态代理.AOP+IoC   目录 一.为什么需要代理模式 二.静态代理 三.动态代理,使用JDK内置的Proxy实现 四.动态代理,使用cg ...

  4. Spring AOP中的JDK和CGLib动态代理哪个效率更高?

    一.背景 今天有小伙伴面试的时候被问到:Spring AOP中JDK 和 CGLib动态代理哪个效率更高? 二.基本概念 首先,我们知道Spring AOP的底层实现有两种方式:一种是JDK动态代理, ...

  5. 【java高级编程】JDK和CGLIB动态代理区别

    转载:https://blog.csdn.net/yhl_jxy/article/details/80635012 前言 JDK动态代理实现原理(jdk8):https://blog.csdn.net ...

  6. JDK和CGLIB动态代理区别

    背景:虽然自己了解这两种代理的区别,但是面试时候还是答的很模糊,需要好好总结. 前言JDK动态代理实现原理(jdk8):https://blog.csdn.net/yhl_jxy/article/de ...

  7. JDK和CGLIB动态代理原理

    1.JDK动态代理利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类, 在调用具体方法前调用InvokeHandler来处理. 2.CGLiB动态代 ...

  8. JDK和CGLIB动态代理原理区别

    JDK和CGLIB动态代理原理区别 https://blog.csdn.net/yhl_jxy/article/details/80635012 2018年06月09日 18:34:17 阅读数:65 ...

  9. Spring框架中的JDK与CGLib动态代理

    JDK和CGLib动态代理区别 JDK动态代理:利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类, 在调用具体方法前调用InvokeHandler ...

随机推荐

  1. docker启动,重启,停止容器

    docker 启动已经停止的容器 docker start 容器ID或容器名 docker 停止容器 docker stop 容器ID或容器名 docker 启动一个容器 -d:后台运行 -p:端口映 ...

  2. angular6、7 兼容ie9、10、11

    1. 找到 polyfill.ts 并打开注释 /** * This file includes polyfills needed by Angular and is loaded before th ...

  3. Django学习(一)

    Django 是python开发的一个免费开源的Web框架,可以用于快速搭建高性能,优雅的网站.(emmmm,百度百科).本学期需要做python数据的可视化,之前搞过两次SMM和SSH,但是之后ja ...

  4. js获取当前时间并实时刷新

    效果如图: 代码如下: <html> <head> <title>js获取当前时间并实时刷新</title> <script> //页面加载 ...

  5. Nginx 文件下载 apk 文件下载不了

    通过nginx 做下载服务器 下载 apk 安装包, 出现错误502和 499. 解决办法在 nginx的  mime.types 中 来自为知笔记(Wiz)

  6. IIS处理并发请求设置

    一个ASP.NET项目在部署到生产环境时,当用户并发量达到200左右时,IIS出现了明显的请求排队现象,发送的请求都进入等待,无法及时响应,系统基本处于不可用状态.   当发现请求明显延迟,没有被即时 ...

  7. 平滑升级nginx到新版本

    这里测试一下nginx的平滑升级,以备不时之需 查看nginx版本号: [root@zklf-server01 ~]# /application/nginx/sbin/nginx -V nginx v ...

  8. java编程(1)——servlet和Ajax异步请求的接口编程(没有调用数据库的数据)

    编程应用背景: 使用HttpServlet接口来编写一个动态登录的接口(需要在Tomcat容器发布) 登录的 LoginSample 类代码: package com.zhang.java; publ ...

  9. Love Live!-01字典树启发式合并

    链接:https://ac.nowcoder.com/acm/contest/201/D?&headNav=www 思路:题目要求的是每个等级下的最大 简单路径中的最大异或值,那么我们为了保证 ...

  10. python 爬虫与数据可视化--matplotlib模块应用

    一.数据分析的目的(利用大数据量数据分析,帮助人们做出战略决策) 二.什么是matplotlib? matplotlib: 最流行的Python底层绘图库,主要做数据可视化图表,名字取材于MATLAB ...