代理模式的特点

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

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

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

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

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

静态代理

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

package javaee.net.cn.proxy;
/**
* 需要动态代理的接口
*/
public interface Subject{
public void save();
}

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

package javaee.net.cn.proxy;
/**
* 实际对象
*/
public class RealSubject implements Subject{
public void save(){
System.out.println("insert into ......");
} }

3 手动编写代理类

public class StaticProxy implements Subject{
private Subject subject;
public StaticProxy(Subject subject){
this.subject=subject;
}
@Override
public String save() {
System.out.println("trancation start");
String result = subject.save();
System.out.println("trancation commit");
return result;
}
}

动态代理

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

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

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

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

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

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

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

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

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

    /**方式一*/
//创建InvocationHandler对象
InvocationHandler invocationHandler = new InvocationHandler(...);
//创建动态代理类
Class proxyClass = Proxy.getProxyClass(Subject.class.getClassLoader(), new Class[] {Subject.class});
//创建动态代理类的实列
Subject subject = (Subject) proxyClass.getConstructor(new Class[] {InvocationHandler.class}).newInstance(new Object[] {invocationHandler}); /**方式二*/
//创建InvocationHandler对象
InvocationHandler invocationHandler = new InvocationHandler(...);
//直接创建动态代理类的实列
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 提供用于创建动态代理类和实例的静态方法

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

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

package javaee.net.cn.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; /**
* 调用处理器实现类
* 每次生成动态代理类对象时都需要指定一个实现了该接口的调用处理器对象
*/
public class LogInterceptor implements InvocationHandler{
/**
* 这个就是我们要代理的真实对象
*/
private Object target;
/**
* 构造方法,给我们要代理的真实对象赋初值
* @param subject
*/
public LogInterceptor(Object target){
this.target = target;
} /**
* 该方法负责集中处理动态代理类上的所有方法调用。
* 调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行
* @param proxy 代理类实例
* @param method 被调用的方法对象
* @param args 调用参数
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
//在代理真实对象前我们可以添加一些自己的操作
System.out.println("trancation start"); //当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
Object returnValue = method.invoke(target, args); //在代理真实对象后我们也可以添加一些自己的操作
System.out.println("trancation commit");
return returnValue;
}
}

调用顺序:当我们调用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自调用失效的问题

    class UserService{
@Transactional
void A(){
B();
}
@Transactional
void B(){
}
}

我们在一个类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. 关于父窗口获取跨域iframe子窗口中的元素

    这几天在项目中遇到,一个难点, 就是需要异步加载一个pdf插件, 同时又需要获取这个插件中的点击事件来生成用户的下载记录. 刚开始也是想了很多方法,网上搜的 格式1:$("#iframe的I ...

  2. 微信小程序上的map组件bindregionchange地图视野变化函数成功回调会产生2次值的问题?

    bindregionchange确实是会触发两次,第一次是视野变化开始,第二次是视野变化结束. 你可以尝试把e.type给打印出来,值为begin表示开始,值为end表示结束. wxml: js:

  3. Linux基础-命令(续)

    touch  命令: 如果文件不存在,创建文件,  如果文件存在,则修改文件最后修改时间. mkdir  命令: -p  递归创建目录,如,mkdir  -p  a/b/c/d Linux 中同一目录 ...

  4. webpack分片chunk加载原理

    首先,使用create-react-app快速创建一个demo npx create-react-app react-demo # npx命令需要npm5.2+ cd react-demo npm s ...

  5. Outlook IMAP 修改PST文件存储路径

    IMAP类型的账户修改PST文件位置方法: 对于IMAP类型账户的PST文件,既没有“修改文件夹”的选项,也无法按OFFICE官方操作指南中的操作.因为每次Outlook只要检测到默认路径下的PST文 ...

  6. c/c++再学习:查找算法了解

    1.顺序查找 说明:顺序查找适合于存储结构为顺序存储或链接存储的线性表. 基本思想:顺序查找也称为线形查找,属于无序查找算法.从数据结构线形表的一端开始,顺序扫描,依次将扫描到的结点关键字与给定值k相 ...

  7. Motrix for Mac(百度网盘加速/全能下载软件) v1.3.7最新版!

    Motrix for Mac最新版第一时间在本站上线!Mac上最强大实用百度网盘加速器Motrix for Mac分享给您!Motrix for Mac是一款非常优秀的下载工具,采用Aria 2作为核 ...

  8. 2018-2019-2 20165235 《网络对抗技术》 Exp6 信息搜集与漏洞扫描

    2018-2019-2 20165235 <网络对抗技术> Exp6 信息搜集与漏洞扫描 1.实践目标 掌握信息搜集的最基础技能与常用工具的使用方法. 2.实践内容 (1)各种搜索技巧的应 ...

  9. mysql查询出近一周,三个月,一年的数据

    SELECT * FROM 表名 WHERE 时间字段>DATE_SUB(CURDATE(), INTERVAL YEAR) 一年 SELECT * FROM 表名 WHERE 时间字段> ...

  10. dos choice 命令

    dos choice 命令 choice /c YNC /m "Are you sure?" if %errorlevel%==1 goto Y if %errorlevel%== ...