Java 动态代理以及自定义注解

关于Java中的动态代理,是一种常用的设计模式,即代理模式,根据创建代理的方式,又可以分为静态代理和动态代理

代理模式

代理模式是常用的java设计模式,他的特征是代理类和委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等,代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联。代理类的对象本身并不会去实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务,在程序的编译过程中,我们访问实际的对象,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。在这里我们主要总结一下动态代理的实现。

为何使用代理模式?

如何去理解代理模式

这里我们举个栗子来描述一下简单的代理模式

这里有个打印机类

1
2
3
4
5
public class Printer {
public void print(){
System.out.println("打印!")
}
}

如果说为了验证打印的资料是否正确,我却要在打印机打印之前加上一段记录日志

1
2
3
4
5
6
public class Printer {
public void print(){
System,out,prinbln("记录日志:balabalala")
System.out.println("打印!")
}
}

感觉没有问题,但是注意我们修改了打印机的源代码,这里便破坏了面向对象的开闭原则,有可能会影响到其他功能,那应该去如何解决呢?

PS 开闭原则:软件中的对象(类, 模块, 函数等等)应该对于扩展是开放的,但是对于修改是封闭的

我可以新建一个类

1
2
3
4
5
6
7
public class LogPrinter extends Printer {
@Override
public void print() {
System,out,prinbln("记录日志:balabalala")
System.out.println("打印!")
}
}

我这个类继承了打印机的类,重写了打印机print的方法,提供了记录日志的功能

我们还可以抽象出一个接口:

1
2
3
public interface IPrinter {
void print();
}

通过打印机去实现这个接口

1
2
3
4
5
public class Printer implements IPrinter {
public void print(){
System.out.println("打印!");
}
}

创建打印机代理类也实现该接口,在构造函数中将打印机对象传进去,实现接口的打印方法时调用打印机对象的打印方法并在前面加上记录日志的功能:

1
2
3
4
5
6
7
8
9
10
11
public class PrinterProxy implements IPrinter {
private IPrinter printer;
public PrinterProxy(){
this.printer = new printer();
}
@Override
public void print() {
System.out.println("记录日志");
printer.print();
}
}

以后我们就可以直接实例化PrinterProxy对象调用它的打印方法了,这就是静态代理模式,通过抽象出接口让程序的扩展性和灵活性更高了。

但是在一个服务中,类似日志记录,监控,安全处理的代码涉及到的业务和类很多,让他们都实现这样的静态代理显然是不现实的,于是我们的动态代理类便闪亮登场了。

要想不重复写记录日志的功能,针对每一个接口实现一个代理类的做法肯定不可行了,可不可以让这些代理类的对象自动生成呢?

Jdk提供了invocationHandler接口和Proxy类,借助这两个工具可以达到我们想要的效果。

1
2
3
4
5
6
//Object proxy:被代理的对象 
//Method method:要调用的方法
//Object[] args:方法调用时所需要参数
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

之类我们新创建一个动态代理对象

Proxy.newProxyInstance,非常重要的方法,我们来看一下如何实现的呢

1
2
3
4
//CLassLoader loader:被代理对象的类加载器 
//Class<?> interfaces:被代理类全部的接口
//InvocationHandler h:实现InvocationHandler接口的对象
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

接口IPrinter 和 该接口的实现类 Printer的代码同前。

实现一个类,该类用来创建代理对象,它实现了InvocationHandler接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class ProxyHandler implements InvocationHandler {
private Object targetObject;//被代理的对象
//将被代理的对象传入获得它的类加载器和实现接口作为Proxy.newProxyInstance方法的参数。
public Object newProxyInstance(Object targetObject){
this.targetObject = targetObject;
//targetObject.getClass().getClassLoader():被代理对象的类加载器
//targetObject.getClass().getInterfaces():被代理对象的实现接口
//this 当前对象,该对象实现了InvocationHandler接口所以有invoke方法,通过invoke方法可以调用被代理对象的方法
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(),this);
}
//该方法在代理对象调用方法时调用
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("记录日志");
return method.invoke(targetObject,args);
}
}

被代理的对象targetObject可以通过方法参数传进来:

1
2
public Object newProxyInstance(Object targetObject){
this.targetObject=targetObject;
1
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this);

示例示范

show the code

在这里我们先创建一个自定义注解, @Target(ElementType.METHOD) 表明注解是使用在方法上的,@Retention(RetentionPolicy.RUNTIME) 代表是在运行时注解,这里就可以通过反射取到,如果使用@Documented标注了,在生成javadoc的时候就会把@Documented注解给显示出来。

1
2
3
4
5
6
7
8
9
10
11
12
package Example.ProxyExample;

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CallMethod {

String value();

}

当然再创建一个在类上面使用的

1
2
3
4
5
6
7
8
9
10
11
12
13
package Example.ProxyExample;
import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CallType {
String name();

String Gender();

String Phone();
}

我们自定义个方法拦截器,invoke()里面便是对方法的增强,在这里通过反射来取到注解中的属性

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
32
package Example.ProxyExample;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class CustomerInvocationHandle<T> implements InvocationHandler {

private final Class<?> clazz;
private T target;

public CustomerInvocationHandle(Class<?> clazz,T target){
//传入对象和class
this.clazz = clazz;
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("现在进入代理对象进行对象增强了");
String lalal = (String) method.invoke(target, args);
System.out.println(lalal);
//在这里得到方法的自定义注解
CallMethod callMethod = method.getAnnotation(CallMethod.class);
//得到类的自定义注解
CallType callType = clazz.getDeclaredAnnotation(CallType.class);
System.out.println("我的名字叫:" + callType.name() + "我是" + callType.Gender() + "我的电话是" + callType.Phone());
System.out.println(callMethod.value());
System.out.println("代理对象方法增强结束");
return null;
}
}

创建一个动态代理的工厂类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package Example.ProxyExample;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class ProxyFactory {

public static Object getProxyObject(Object target, InvocationHandler invocationHandler){
//这里就是我们创建动态代理的主要方法,注意invocationHandler需要传入我们自定义的
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
invocationHandler);
}

}

接下来就是我们主要的main方法了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package Example.ProxyExample;


public class TestMain {
public static void main(String[] args) {
//lambda表达式创建新的对象
XiaoMing student = () -> "Hi i am XiaoMing";

//通过工厂类的方法传入参数
XiaoMing XiaoMing = (TestMain.XiaoMing) ProxyFactory.getProxyObject(student,new CustomerInvocationHandle<XiaoMing>(XiaoMing.class, student));
XiaoMing.hi();

}


//新建接口,用自定义注解修饰
@CallType(name = "小明", Gender = "man", Phone = "135658642589")
public interface XiaoMing{
@CallMethod(value = "12344556")
String hi();
}
}

运行实例

1
2
3
4
5
现在进入代理对象进行对象增强了
Hi i am XiaoMing
我的名字叫:小明我是man我的电话是135658642589
12344556
代理对象方法增强结束

参考应用资料:

java动态代理实现与原理详细分析

Java 动态代理作用是什么?