Java动态代理

Java动态代理

动态代理在Java中有着广泛的应用,比如Spring AOP、Hibernate数据查询、测试框架的后端mock、RPC远程调用、Java注解对象获取、日志、用户鉴权、全局性异常处理、性能监控,甚至事务处理等

代理模式

创建一个代理对对象进行包装,用该代理对象取代原对象。后续操作中,任何对原对象的调用都要先通过代理对象。代理对象决定是否将方法调用转到原对象上。

代理模式是一种结构型设计模式,代理模式角色分为 3 种:

Subject(抽象主题角色):定义代理类和真实主题的公共对外方法,也是代理类代理真实主题的方法;

RealSubject(真实主题角色):真正实现业务逻辑的类;

Proxy(代理主题角色):用来代理和封装真实主题;

代理模式的结构比较简单,其核心是代理类,为了让客户端能够一致性地对待真实对象和代理对象,在代理模式中引入了抽象层.

代理模式按照职责(使用场景)来分类,至少可以分为以下几类:

1、远程代理

2、虚拟代理

3、Copy-on-Write 代理

4、保护(Protect or Access)代理

5、Cache代理

6、防火墙(Firewall)代理

7、同步化(Synchronization)代理

8、智能引用(Smart Reference)代理等等。

如果根据字节码的创建时机来分类,可以分为静态代理和动态代理:

  • 所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和真实主题角色的关系在运行前就确定了。
  • 而动态代理的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以在运行前并不存在代理类的字节码文件。

代理模式优势

代理模式能够赋予我们控制访问某个类的能力,在某些情况下,代理可以允许我们屏蔽或控制某个对象的逻辑。在此基础上,引申出来的作用,也是目前开发中最常使用的作用:在不修改原对象代码的基础上,对原对象的功能进行修改或者增强

解耦

降低耦合度甚至解除耦合

耦合

当项目中的各个模块或者各个组件,当一个发生变动或者调用的时候,其他一些模块或组件也会产生变化或者调用,类似于齿轮的运转。

耦合的问题

当模块或组件耦合度过高时,会带来难以扩展、维护性差、纠错困难等问题,所以在设计时,要尽可能避免模块或组件之间的耦合度过高。

当我们想要给某个对象添加有些额外逻辑时,就可以使用代理模式,在不修改原代码的前提下,只针对额外功能进行编码。在整个过程中,原对象的逻辑和额外逻辑完全解耦,互不干扰。

高扩展

由于模块之间的解耦性,我们可以随时添加任意的功能或者修改之前的功能而不影响原模块的正常执行。

JAVA代理模式

Java中有三种方式来创建代理对象:

  • 静态代理
  • 基于JDK(接口)的动态代理
  • 基于CGLIB(Jar包)的动态代理

静态代理


手写代理类的代码,工程中有代理类的源码,代理类会编译执行。实现目标类的接口或者直接继承目标类,完成逻辑的修改和增强。

接口实现方式

目标类: 程序猿(Developer)

代理类: Java程序猿(JavaDeveloper) FullStack程序猿(FullStackDeveloper)

程序猿(Develpoer.java)

定义一个程序员的接口,只干两件事情(程序员太忙,别的做不了)

1
2
3
4
public interface Developer {
void coded();//会写代码
void debug();//会调试程序
}

java程序猿会开发和调试java代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class JavaDeveloper implements Developer {
private String name;

public JavaDeveloper(String name)
{
this.name = name;
}

pubic void code()
{
System.out.println(this.name+"is codding JavaDeveloper");
}
public void debug()
{
System.out.println(this.name+"is debugging JavaDeveloper");
}
}

然后有个FullStack程序猿,在开发之前都会祈祷一下,这样他的代码就不会有bug,也不需要调试。

全栈程序猿(FullStackDeveloper.java)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class FullStackDeveloper implements Developer {
private Developer developer;
public FullStackDeveloper(Developer developer)
{
this.developer = developer;
}
public void code()
{
System.out.println("FullStackDeveloper is praying for the code!");
this.developer.code();
}
public void debug()
{
System.out.println("FullStackDeveloper's code is bug-free and does not require dedbugging");
}
}

主函数

1
2
3
4
5
6
7
8
9
public class App {
public static void main(String[] args)
{
Developer str = new JavaDeveloper("YZT");
Developer developer = new FullStackDeveloper(str);
developer.code();
developer.debug();
}
}

运行结果:

如果YZT只是一个普通的Java程序猿,则开发结果是:

1
2
3
YZT is  codding JavaDeveloper!

YZT is debugging JavaDeveloper!

但YZT是个全栈程序猿是这样的:

1
2
3
YZT is  praying for the code!
YZT is codding JavaDeveloper!
YZT'code is bug-free and does not require dedbugging!

继承方式

示例

目标类: 程序猿(Developer)

代理类: Java程序猿(JavaDeveloper)

程序猿类(Developer)

1
2
3
4
5
6
7
8
9
10
public class Developer {
public String code(String str)
{
return str+" is coding Developer!";
}
public String debug(String str)
{
return str+" is a debugging Developer!";
}
}

Java程序猿(javaDeveloper)

1
2
3
4
5
6
7
8
9
public class JavaDeveloper extends Developer {
public String code(String str)
{
String str1 = super.code(str);
String str2 = super.debug(str);
System.out.println("Wish you have a good Luck!");
return str1 +str2+ " He will become a good Developer!";
}
}

主程序

1
2
3
4
5
6
7
public class App {
public static void main(String[] args)
{
Developer developer = new JavaDeveloper();
System.out.println(developer.code("YZT"));
}
}

运行结果:

存在问题

静态代理虽然能实现代理模式,完成解耦,但是静态代理类的代码维护依然非常复杂,一旦接口或者父类发生了变动,则代理类的代码就得随之修改,代理类多的时候维护十分麻烦。

动态代理

在内存中生成代理对象的技术。整个代理过程在内存中进行,不需要手动写代理类的代码,也不存在代理类的编译过程,而是直接在Java运行期,凭空在JVM中生成一个代理类对象,供我们使用。

基于JDK的动态代理

JDK自带的动态代理技术,需要使用静态方法来创建代理对象,要求目标类必须实现接口。

Developer.java

1
2
3
4
public interface Developer {
void code();//会写代码
void debug();//会调试程序
}

GoodDeveloper.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class GoodDeveloper implements Developer {
private String name;
public GoodDeveloper(String name)
{
this.name = name;
}
@Override
public void code() {
System.out.println(this.name+" is coding Gooddeveloper!!!!");
}
public void debug() {
System.out.println(this.name+" is debugging Gooddeveloper!!!");
}
}

ProxyTest.java

动态代理的例子采用了lambda表达式,主要代码是对InvocationHandler的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyTest {
public static void main(String[] args) throws Throwable {
Developer developer = new GoodDeveloper("YZT");
//新建Developer类型,GoodDeveloper对象
Developer developerproxy = (Developer) Proxy.newProxyInstance(developer.getClass().getClassLoader(),developer.getClass().getInterfaces(),(proxy, method, args1) ->{
if(method.getName().equals("code"))
{
System.out.println("YZT is praying for the code!!!");
return method.invoke(developer,args1);

} else if (method.getName().equals("debug")) {
System.out.println("YZT's code is bug-free and does not require debugging!!!");
return method.invoke(developer,args1);
}
return null;
});
//labmba匿名函数,新建Developer类型接口,定义方法体输入结果对code()和debug()函数进行动态设置
developerproxy.code();
developerproxy.debug();
}
}

运行结果:

代码分析

developerproxy的类型是Developer接口,而不是一个实现类。developer是基于Developer接口和GoodDeveloper的实现类加载代理出来的对象,并不属于任何一个实现类。

我们来看下ProxyTest.java中的newProxyInstance()接口:

1
2
3
java.lang.reflect.Proxy @NotNull 
public static Object newProxyInstance(ClassLoader loader,@NotNull Class<?>[] interfaces, @NotNull reflect.InvocationHandler h)
throws IllegalArgumentException

接口中包括三个参数:

1
2
3
4
5
ClassLoader loader:固定写法,执行目标类对象的类加载器即可,用于加载目标类及其接口的代码

Class<?>[] interfaces:固定写法,指定目标类对象的所有接口的Class对象的数组,通常使用目标类的Class对象调用getInterfaces()即可得到

InvocationHandler h:这个参数是一个接口,主要关注它里面唯一一个方法,invoke方法。它会在代理对象调用方法时执行。也就是说,在代理类对象中调用任何方法,都会执行到invoke()方法。所以在该方法中完成对增强或扩展代码逻辑

loader和interfaces决定这个类到底是个怎么样的类。而h是InvocationHandler,决定这个代理类到底是多了什么功能。所以动态代理的内容重点就是这个InvocationHandler。

基于CGLIB(父类)的动态代理

该方式是通过一个静态方法来创建代理对象,不要求目标类实现接口,但是对于目标类不能使用final进行修饰变量,基于目标类生成一个子类作为代理类,所以必须被继承。

首先在pom.xml文件中添加CGLIB依赖并下载 相关依赖文件资源。

1
2
3
4
5
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>

Cglibtest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
ublic class Cglibtest {
public static void main(String[] args) {
// 创建一个目标类对象,也就是顾客对象
Developer developer = new Developer("YZT");
// 使用CGLIB创建代理类对象
Developer proxy = (Developer) Enhancer.create(developer.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
if("code".equals(method.getName())){
System.out.println("YZT is praying for the code!!!");
return method.invoke(developer, args);
}else if ("debug".equals(method.getName())){
System.out.println("YZT's code is bug-free and does not require debugging!!!");
return method.invoke(developer, args);
}
return null;
}
});
proxy.code();
proxy.debug();

}
}
1
Enhancer.create(Class type, Callback callback)

参数列表

Class type:指定目标类Class对象,也就是目标类类型

Callback callback:Callback是一个接口,该接口只是一个名称定义接口,并不包含方法的声明,所以使用时通常使用它的一个子接口MethodInterceptorMethodInterceptor接口中只有一个方法:intercept

1
Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy)

proxy:代理类对象

method:对应的是出发intercept执行的方法的Method对象

args:代理对象调用方法时,传递的实际参数

前三个参数与基于JDK的参数一致

methodProxy:方法的代理对象,一般也不作处理,可以暂时忽略

总结

Proxy角色在执行代理业务的时候,无非是在调用真正业务之前或者之后做一些“额外”业务。

代理类处理的逻辑很简单,在调用某个方法前及方法后做一些额外的业务。换一种思路就是,在触发(invoke)真实角色的方法之前或者之后做一些额外的业务。为了构造出具有通用、简单的代理类,可以将所有的触发真实角色动作交给一个触发的管理器。这种管理器就是InvocationHandler。

在这种模式之中,代理Proxy和RealSubject需要实现相同的功能(函数方法)。

面向对象的编程之中,想要约定Proxy和RealSubject实现相同的功能(函数方法)有两种方式

a、定义一个功能接口,Proxy 和RealSubject都实现这个接口。

b、通过继承,Proxy继承自RealSubject,这样Proxy则拥有了RealSubject的功能,

JDK中提供的创建动态代理的机制采用a思路;而cglib采用b思路(spring两者都使用了)。

打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2021-2024 John Doe
  • 访问人数: | 浏览次数:

让我给大家分享喜悦吧!

微信