Java代理机制
Java代理
代理模式:提高真实对象的封装性、拓展性,采用代理对象来代替对真实对象的访问,在不修改原目标对象的基础上,进行额外的功能操作。
Java代理分为静态代理和动态代理
重点关注一下基于反射的动态代理,此部分为Java安全中的常见机制
静态代理
直接上代码比较好说明
首先我有一个Person的接口类, 定义苏醒和睡眠两种函数声明
// 接口提供函数声明,但不提供具体定义
public interface Person
{
void wakeup();
void sleep();
}
Student以Person为接口进行具体类实现
// 从Person接口进行二次开发
public class Student implements Person{
public String name;
// 构造函数
public Student(){
}
public Student(String name){
this.name = name;
}
@Override
public void wakeup() {
System.out.printf("%s wake up!\n" ,name);
}
@Override
public void sleep() {
System.out.printf("%s sleep!\n", name);
}
}
再为Person设置代理类
public class PersonProxy implements Person {
private Person person;
public PersonProxy(Person person) {
this.person = person;
}
@Override
public void wakeup() {
System.out.println("Good Morning!");
person.wakeup();
}
@Override
public void sleep() {
System.out.println("Good Night!");
person.sleep();
}
}
main函数实现
public class Main {
public static void main(String[] args) throws Exception{
Person student = new Student("icfh");
PersonProxy studentProxy = new PersonProxy(student);
studentProxy.wakeup();
studentProxy.sleep();
}
}
运行结果:
上述一顿代码操作可能有点懵逼
但是对于同一个接口类而言, 需要为其实现一个特定的代理类, 此为静态代理的一个缺点
动态代理
1. JDK动态代理机制(反射代理)
- 定义一个接口及其实现类;
- 自定义
InvocationHandler
并重写invoke
方法,在invoke
方法中我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑;- 通过
Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
方法创建代理对象
对于Person接口类的具体实现如上
JDK代理类
import java.lang.reflect.Method;
public class JdkProxy implements InvocationHandler {
private Object bean;
public JdkProxy(Object bean){
this.bean = bean;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 获取运行时的函数
String methodName = method.getName();
// Hook指定的方法,完成代理工作逻辑
if(methodName.equals("wakeup")){
System.out.println("GoodMorning!");
}else if(methodName.equals("sleep")){
System.out.println("GoodNight!");
}
// 再调用被代理的类的相应方法
return method.invoke(bean, args);
}
}
main函数
public class Main {
public static void main(String[] args) throws Throwable{
// 代理类绑定需要代理的类实例
JdkProxy proxy = new JdkProxy(new Student("icfh"));
// 生成绑定对象实例
Person student = (Person) Proxy.newProxyInstance(proxy.getClass().getClassLoader(), new Class[]{Person.class}, proxy);
// 经过代理之后,调用原生方法时会先经过代理方法调用。然后在代理方法中再调用原生方法。
student.wakeup();
student.sleep();
}
}
2. CGLIB动态代理机制(继承代理)
可以发现,使用JDK进行动态代理时,只能代理那些实现了接口的类。
CGLIBopen in new window(Code Generation Library)是一个基于ASM字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。
使用方法:
- 定义一个类
- 自定义实现MethodIntercepter接口类实现并重写intercept方法(原生方法调用时,intercept会拦截,有点像反射代理中的invoke)
- 通过Enhancer类的create()进行创建对象
- 在调用原生方法时会自动进入代理方法
原生类:
public class AliSmsService {
public String send(String message){
System.out.println("send message:"+ message);
return message;
}
}
实现MethodInterceptor接口类,然后重写intercept函数,完成代理
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class DebugMethodInterceptor implements MethodInterceptor{
@Override
public Object intercept(Object o, Method method, Object[] args,MethodProxy methodProxy) throws Throwable{
System.out.println("before method "+method.getName());
// 原生方法调用
Object object = methodProxy.invokeSuper(o, args);
System.out.println("after method "+method.getName());
return object;
}
}
Main函数
import net.sf.cglib.proxy.Enhancer;
public class Main{
public static void main(String[] args) throws Throwable{
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(AliSmsService.class.getClassLoader());
enhancer.setSuperclass(AliSmsService.class);
enhancer.setCallback(new DebugMethodInterceptor());
AliSmsService a = (AliSmsService) enhancer.create();
a.send("java");
}
}
静态代理和动态代理的比较
- 静态代理:
- 需要实现接口类
- 代理类中需要实现接口类中要进行代理的每个方法
- 基于反射的动态代理
- 仍旧需要实现接口类
- 需要重写invoke方法,所有需要代理的原生方法都会在invoke方法中被拓展调用
- 基于继承的动态代理
- 可以代理未实现任何接口的类
- 代理类需要实现MethodInterceptor接口,重写拦截器intercept