优秀文章
博客:
ykzhen2015 > MyBATIS插件原理第一篇——技术基础(反射和JDK动态代理)
ITEYE:
layznet > java静态代理和动态代理
zheng12tian > java 动态代理学习(Proxy,InvocationHandler)——自己的理解
说明
- 代理是对真实对象的代表;
- 代理通常使用接口来规范(JDK动态代理必须满足该条件);
PS:下文中RealSubject为实际类,RealProxy为代理类,首字母小写为各自实例对象。
代理模式
为对象提供一个专用于访该对象的代理对象,外部通过访问代理对象实现对该对象的访问。
静态代理
- 设计接口:
设计接口的目的是为RealSubject与RealProxy规范代理方法,并为两者提供共同的引用;
public interface Subject {
public void func1();
public void func2(String str);
}
- 编写RealSubject类:
类需实现Subject接口;
public class RealSubject implements Subject {
public void func1() {
System.out.println("RealSubject类的func1方法被调用了。");
}
public void func2(String str) {
System.out.println("RealSubject类的func2方法被调用了,参数是"+str+"。");
}
}
- 编写Proxy类:
实现Subject接口并设置被代理对象RealSubject的引用;
public class RealProxy implements Subject {
// 在代理对象内声明被代理对象的引用,此处使用接口则可以接收任何实现该接口的代理对象;
private Subject subject;
// 设置被代理对象实例;
public void setSubject(Subject subject){
this.subject = subject;
}
// 对代理对象的方法代理;
public void func1() {
System.out.println("RealProxy类的func1方法被调用了。");
subject.func1();
}
public void func2(String str) {
System.out.println("RealProxy类的func2方法被调用了,参数是"+str+"。");
subject.func2(str);
}
}
- 实现:
利用Proxy完成对RealSubject的代理;
public static void main(String[] args) {
RealProxy realProxy = new RealProxy();
realProxy.setSubject(new RealSubject());
realProxy.func1();
realProxy.func2("demo");
}
结果
RealProxy类的func1方法被调用了。
RealSubject类的func1方法被调用了。
RealProxy类的func2方法被调用了,参数是demo。
RealSubject类的func2方法被调用了,参数是demo。
动态代理
动态代理与静态代理不同点在于静态代理需要在RealProxy类中手动对RealSubject类的代理方法进行设置,而动态代理则是通过Java的反射(此处链接我的另一篇文章:反射)机制来实现对RealSubject类的全部代理方法进行设置,这里所指的全部代理方法是通过RealSubject类实现的Subject接口来判定的,因此,要实现动态代理,就必须使用接口规范代理方法,且RealProxy类在动态代理中不需要实现Subject接口而必须实现InvocationHandler接口;
- 关键接口:
InvocationHandler
- 所有实现动态代理的代理对象都必须实现这个接口;
- 实现该接口后,所有代理方法都是在invoke()方法中实现,故可以在此方法内设置额外的代理内容;
- 关键方法:
Proxy.newProxyInstance(realSubject.getClass().getClassLoader(),realSubject.getClass().getInterfaces(), realProxy);
- 获取具备RealSubject信息的RealProxy对象;
- 修改RealProxy类:
public class RealProxy implements InvocationHandler {
// 在代理对象内声明被代理对象的引用,此处使用接口则可以接收任何实现该接口的代理对象;
private Subject subject;
// 设置被代理对象实例;
public void setSubject(Subject subject){
this.subject = subject;
}
// 对代理对象的方法代理;
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
// 获取参数;
String str = "";
if(args!=null && args.length>0){
for(Object obj : args){
String s = (String) obj;
str = str + ",参数是" + s;
}
}
System.out.println("RealProxy类的"+method.getName()+"方法被调用了"+str+"。");
result = method.invoke(subject, args);
return result;
}
}
- 实现
public static void main(String[] args) {
RealProxy realProxy = new RealProxy();
RealSubject realSubject = new RealSubject();
realProxy.setSubject(realSubject);
// 获取动态代理对象;
Subject subject = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(),realSubject.getClass().getInterfaces(), realProxy);
subject.func1();
subject.func2("demo");
}
- 结果
RealProxy类的func1方法被调用了。
RealSubject类的func1方法被调用了。
RealProxy类的func2方法被调用了,参数是demo。
RealSubject类的func2方法被调用了,参数是demo。
CGLib
CGLib(Code Generation Library)是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。——CGLib百度百科
使用CGLib,可以对未实现接口的代理对象进行代理,其原理是根据代理对象生成新的子类对象,通过拦截子类对象的所有方法来实现动态的代理,使用者所得到的代理对象实质是原代理对象的子类对象。
- CGLib的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。
- 由于CGLib由于是采用动态创建子类的方法,对于final方法,无法进行代理。
最后
- 动态代理是很多框架的基础实现技术,常见的就有Spring的AOP和MaBatis的Mapper;
网友评论