java 代理模式详解
前言:
在某些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个称之为“代理”的第三者来实现间接引用。代理对象可以在客户端和目标对象之间起到 中介的作用,并且可以通过代理对象去掉客户不能看到 的内容和服务或者添加客户需要的额外服务。
简单来说代理模式就是通过一个代理对象去访问一个实际对象,并且可以像装饰模式一样给对象添加一些功能。
静态代理
所谓静态代理即在程序运行前代理类就已经存在,也就是说我们编写代码的时候就已经把代理类的代码写好了,而动态代理则是在程序运行时自动生成代理类。
描述起来太过抽象,看一下代码就明白是怎么回事了
main
1
2
3
4
5
6
7
8
9
|
public class Main {
public static void main(String[] args) {
Water water = new Water();
WaterProxy waterProxy = new WaterProxy(water);
waterProxy.drink();
}
}
|
接口
1
2
3
4
|
//代理类与被代理类共同实现的接口
public interface Drink {
void drink();
}
|
被代理类
1
2
3
4
5
6
7
8
9
|
//被代理的类
public class Water implements Drink {
@Override
public void drink() {
System.out.println( "drink water" );
}
}
|
代理类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
//代理类
//与被代理类实现同一个接口
public class DrinkProxy implements Drink {
private Drink drinkImpl;
//通过构造函数传入Water对象
public DrinkProxy(Drink drinkImpl) {
this .drinkImpl = drinkImpl;
}
@Override
public void drink() {
//在执行被代理对象的方法前做一些事情
System.out.println( "before drink" );
//执行被代理对象的方法
drinkImpl.drink();
//在执行被代理对象的方法后做一些事
System.out.println( "after drink" );
}
}
|
执行结果
1
2
3
|
before drink
drink water
after drink
|
动态代理
有时候我们只想改变代理类所代理的类,但是代理对象执行实际对象的方法前后所做的事情是一样的,正所谓铁打的代理类,流水的被代理类。而采用静态代理就只能代理实现了同一接口的类,如果要代理任意类则必须写很多重复的代理类。此时我们可以采用动态代理,Java已经为实现动态代理提供了一套比较方便的工具。
java.lang.reflect.Proxy类中可以动态生成代理对象的方法
1
2
3
4
5
6
7
8
9
10
|
/**
*返回实现了指定接口的对象,调用代理对象的方法会调用
*InvocationHandler的invoke方法
*
* @param loader 获取代理类所使用的类加载器
* @param interfaces 代理类所要实现的接口
* @param h 实现了InvocationHandler接口的对象
* @return 代理对象
*/
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)
|
InvocationHandler接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
/**
*每个代理类都有一个关联的InvocationHandler
*当代理对象执行一个方法的时候会直接执行invoke方法
*/
public interface InvocationHandler {
/**
* @param 调用该方法的代理对象
* @param method 代理对象所调用的方法
* @param args 调用的方法的参数
* @return 调用的方法的返回值
*/
public Object invoke(Object proxy, Method method, Object[] args)
}
|
描述总是比较抽象,还是看实际例子比较好理解
例子
InvocationHandler接口的实现类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public class CommonInvocationHandler implements InvocationHandler {
//被代理的对象
private Object proxied;
public CommonInvocationHandler(Object proxied) {
this .proxied = proxied;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//在调用被代理对象的方法前做一些事情
System.out.println( "before doing something" );
//调用被代理对象的方法
Object result = method.invoke(proxied, args);
//在调用被代理对象的方法后做一些事情
System.out.println( "after doing something" );;
return result;
}
}
|
Main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public class Main {
public static void main(String[] args) {
//被代理的对象
Water water = new Water();
//动态获取代理对象
Drink waterProxy =
(Drink) Proxy.newProxyInstance(water.getClass().getClassLoader(),
water.getClass().getInterfaces(),
new CommonInvocationHandler(water));
//通过代理对象调用方法
waterProxy.drink();
}
}
|
输出结果
1
2
3
|
before doing something
drink water
after doing something
|
也可以不要具体的被代理对象,但是必须有相应的接口(没有实现接口的类可以使用cglib实现动态代理)才可以动态获取代理对象。像最近比较火的Retrofit就直接通过声明好的接口使用动态代理进行网络请求。
例子
简单的模拟一下retrofit
POST注解
1
2
3
4
5
6
|
//Post请求注解
@Target (ElementType.METHOD)
@Retention (RetentionPolicy.RUNTIME)
public @interface POST {
String value() default "" ;
}
|
Query注解
1
2
3
4
5
6
|
//Post请求注解
@Target (ElementType.METHOD)
@Retention (RetentionPolicy.RUNTIME)
public @interface POST {
String value() default "" ;
}
|
Service接口
1
2
3
4
5
6
7
|
public interface Service {
//用POST注解声明请求的方式和相对路径
@POST ( "/login" )
//@Query注解声明请求的参数名
void login( @Query ( "username" )String username,
@Query ( "password" )String password);
}
|
Main
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
26
27
28
29
30
31
|
public class Main {
public static void main(String[] args) {
// 动态获取Service接口的代理
Service service = (Service) Proxy.newProxyInstance(Service. class .getClassLoader(),
new Class[] { Service. class }, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 通过注解获取请求的相对路径
String retativePath = ((POST) method.getAnnotations()[ 0 ]).value();
System.out.println( "relative path: " + retativePath);
// 获取参数的注解
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
// 通过参数的注解获取请求参数
for ( int i = 0 ; i < parameterAnnotations.length; i++) {
if (parameterAnnotations[i].length != 0 ) {
for ( int j = 0 ; j < parameterAnnotations[i].length; j++) {
Query query = (Query) parameterAnnotations[i][j];
System.out.println(query.value() + ": " + args[i].toString());
}
}
}
return null ;
}
});
// 调用代理对象的方法
service.login( "hello" , "world" );
}
}
|
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
相关文章
- ASP.NET本地开发时常见的配置错误及解决方法? 2025-06-10
- ASP.NET自助建站系统的数据库备份与恢复操作指南 2025-06-10
- 个人网站服务器域名解析设置指南:从购买到绑定全流程 2025-06-10
- 个人网站搭建:如何挑选具有弹性扩展能力的服务器? 2025-06-10
- 个人服务器网站搭建:如何选择适合自己的建站程序或框架? 2025-06-10
- 2025-07-10 怎样使用阿里云的安全工具进行服务器漏洞扫描和修复?
- 2025-07-10 怎样使用命令行工具优化Linux云服务器的Ping性能?
- 2025-07-10 怎样使用Xshell连接华为云服务器,实现高效远程管理?
- 2025-07-10 怎样利用云服务器D盘搭建稳定、高效的网站托管环境?
- 2025-07-10 怎样使用阿里云的安全组功能来增强服务器防火墙的安全性?
快网idc优惠网
QQ交流群
-
2025-05-27 96
-
2025-06-04 46
-
详解Java中Comparable和Comparator接口的区别
2025-05-29 40 -
2025-06-04 97
-
H5官网建站服务器的云服务优势:为什么越来越多的企业选择云服务器?
2025-06-05 95