代理模式的特点

代理模式是常用的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. 2018年最新JAVA面试题总结之基础(1)

    转自于:https://zhuanlan.zhihu.com/p/39322967 1.JAVA中能创建volatile数组吗?volatile能使得一个非原子操作变成原子操作吗? 回答: 能,Jav ...

  2. python第二十二天(面向对象)

    1.面向过程编程: 核心就是过程两个字,过程是指解决问题的步骤,即先干什么后做什么. 基于该思想编写程序就好比在编写一条流水线,是一种机械式的思维方式 优点:复杂的问题流程化.进而简单化 缺点:可扩展 ...

  3. python之socketserver实现并发

    python之socketserver实现并发 服务端 import socketserver #socketserver模块是用来实现并发 # 我们自己的类里一定要继承socketserver.Ba ...

  4. django framawork

    中文文档: https://q1mi.github.io/Django-REST-framework-documentation/ 神奇的generics from snippets.models i ...

  5. 【Vue-Cli3.0】【1】创建一个Vue-Cli3.0的项目

    最近在做爬虫,然后要爬好多数据,代码写完了,就让它在爬了.不想闲着就复习一下Vue吧! 开始开始! ***正式讲解之前 先下载一个node.js吧! 一.首先检查一下 版本 PS D:\徐孟林\D D ...

  6. jquery只获取自身文本节点,不获取子元素的

    jQuery.text()方法时候,会把子元素的文本也获取到,以下方法可获取自身文本节点,不包括子元素 <div id="demo">只获取我<a href=&q ...

  7. meta标签整合

    <title>名称</title>(便于搜索) <meta name="keywords" content="内容"> ke ...

  8. MyBatis3系列__04CRUD以及参数处理

    本文将会简单介绍一下MyBatis的CRUD以及结合源码讲解一下MyBatis对参数的处理. 作为一个ORM框架,最基本的使用也就是CRUD了,MyBatis提供了两种方法:xml配置文件和动态注解. ...

  9. 用python写一个非常简单的QQ轰炸机

    闲的没事,就想写一个QQ轰炸机,按照我最初的想法,这程序要根据我输入的QQ号进行轰炸,网上搜了一下,发现网上的案列略复杂,就想着自己写一个算了.. 思路:所谓轰炸机,就是给某个人发很多信息,一直刷屏, ...

  10. JQuery实现旋转轮播图

    css部分 <style> *{ margin:; padding:; } .container{ width:100%; height:353px; margin-top: 20px; ...