JDK设计模式之—动态代理
代理模式的特点
代理模式是常用的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.Proxy的newProxyInstance方法创建代理类的实列
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管理的事物,原理也是动态代理
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设计模式之—动态代理的更多相关文章
- Java设计模式之代理模式(静态代理和JDK、CGLib动态代理)以及应用场景
我做了个例子 ,需要可以下载源码:代理模式 1.前言: Spring 的AOP 面向切面编程,是通过动态代理实现的, 由两部分组成:(a) 如果有接口的话 通过 JDK 接口级别的代理 (b) 如果没 ...
- JDK、CGlib动态代理详解
Java动态代理之JDK实现和CGlib实现(简单易懂) 一 JDK和CGLIB动态代理原理 1.JDK动态代理 利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生 ...
- 动态代理的两种方式,以及区别(静态代理、JDK与CGLIB动态代理、AOP+IoC)
Spring学习总结(二)——静态代理.JDK与CGLIB动态代理.AOP+IoC 目录 一.为什么需要代理模式 二.静态代理 三.动态代理,使用JDK内置的Proxy实现 四.动态代理,使用cg ...
- Spring AOP中的JDK和CGLib动态代理哪个效率更高?
一.背景 今天有小伙伴面试的时候被问到:Spring AOP中JDK 和 CGLib动态代理哪个效率更高? 二.基本概念 首先,我们知道Spring AOP的底层实现有两种方式:一种是JDK动态代理, ...
- 【java高级编程】JDK和CGLIB动态代理区别
转载:https://blog.csdn.net/yhl_jxy/article/details/80635012 前言 JDK动态代理实现原理(jdk8):https://blog.csdn.net ...
- JDK和CGLIB动态代理区别
背景:虽然自己了解这两种代理的区别,但是面试时候还是答的很模糊,需要好好总结. 前言JDK动态代理实现原理(jdk8):https://blog.csdn.net/yhl_jxy/article/de ...
- JDK和CGLIB动态代理原理
1.JDK动态代理利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类, 在调用具体方法前调用InvokeHandler来处理. 2.CGLiB动态代 ...
- JDK和CGLIB动态代理原理区别
JDK和CGLIB动态代理原理区别 https://blog.csdn.net/yhl_jxy/article/details/80635012 2018年06月09日 18:34:17 阅读数:65 ...
- Spring框架中的JDK与CGLib动态代理
JDK和CGLib动态代理区别 JDK动态代理:利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类, 在调用具体方法前调用InvokeHandler ...
随机推荐
- SQL语句完整的执行顺序(01)
一.sql语句的执行步骤: 1)语法分析,分析语句的语法是否符合规范,衡量语句中各表达式的意义. 2) 语义分析,检查语句中涉及的所有数据库对象是否存在,且用户有相应的权限. 3)视图转换,将涉 ...
- php 常用的知识点归集(下)
24.静态属性与静态方法在类中的使用 需求:在玩CS的时候不停有伙伴加入,那么现在想知道共有多少人在玩,这个时候就可能用静态变量的方法来处理 利用原有的全局变量的方法来解决以上的问题 <?php ...
- iOS开发之获取当前展示的VC
/** 递归查找当前显示的VC*/ + (UIViewController *)recursiveFindCurrentShowViewControllerFromViewController:(UI ...
- 考勤输入导入OA平台与考勤统计报表导出功能源代码
注:以某某公司为例,每日签到时间为8点整 每日签退时间为17点30分 规则:公司签到签退时间在OA平台中可以视实际情况调整,当天有请假并通过工作流审批通过为有效,当天因公外出并通过工作流审批通过为 ...
- requireJS简单应用
项目结构目录: Html页面 <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> < ...
- 浅析布隆过滤器及实现demo
布隆过滤器 布隆过滤器(Bloom Filter)是一种概率空间高效的数据结构.它与hashmap非常相似,用于检索一个元素是否在一个集合中.它在检索元素是否存在时,能很好地取舍空间使用率与误报比例. ...
- 1到n的最小步数
1到n的最小步数 Time Limit: 1 Sec Memory Limit: 128 MB 给你一个数n,让你求从1到n的最小步数是多少. 对于当前的数x有三种操作: 1: x+1 2: x ...
- JS数字转中文
function number2Chinese(n) { if (!/^(0|[1-9]\d*)(\.\d+)?$/.test(n)) return '数据非法'; let unit = '京亿万仟佰 ...
- Navicat Premium 12.1.12.0安装与激活
转载自:Navicat Premium 12.1.12.0安装与激活 作者:丿记忆灬永殇丨 链接:https://www.jianshu.com/p/5f693b4c9468 navicat12112 ...
- vue笔记-条件渲染
条件渲染 1:指令v-if单独使用和结合v-else //单独使用 <h1 v-if="ok">Yes</h1> //组合使用 <h1 v-if=&q ...