代理模式分为类
1、静态代理
2、动态代理
静态代理:
概述:
为目标对象提供一种代理用来控制对该对象的访问
JDK中的动态代理是通过反射类Proxy和InvocationHandler回调接口实现的。但是,JDK中所要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中具有一定的局限性,而且使用反射的效率也并不是很高。
使用CGLib实现动态代理,完全不受代理类必须实现接口的限制,而且CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final和static的方法进行代理,因为CGLib原理是动态生成被代理类的子类。
拓展:ASM 是一个 Java 字节码操纵框架。它可以直接以二进制形式动态地生成 stub 类或其他代理类,或者在装载时动态地修改类。ASM 提供类似于 BCEL 和 SERP 之类的工具包的功能,但是被设计得更小巧、更快速,这使它适用于实时代码插装。
静态代理模式类图如下:
相关代码示例:
首先定义一个抽象主题接口Subject
public interface Subject {
void request();
}
编写真正主题RealSubject
//真正的实现类
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("real subject execute request");
}
}
编写静态代理主题实现类ProxySubject
//代理主题实现类,方法调用还是委托给了真正的实现类,自己只是进行了一些边缘的操作
public class ProxySubject implements Subject {
private RealSubject realSubject;
public ProxySubject(RealSubject realSubject) {
this.realSubject=realSubject;
}
@Override
public void request() {
System.out.println("before");
try {
realSubject.request();
}catch(Exception e) {
System.out.println("ex"+e);
}finally{
System.out.println("after");
}
}
}
客户端调用代码如下:
//客户端
public class Client {
public static void main(String[] args) {
Subject subject= new ProxySubject(new RealSubject());
subject.hello();
}
}
静态代理缺点:当需要代理的方法越来越多时,重复的代理类就会越多------产生了动态代理技术
动态代理:
动态代理有两种实现方式:基于接口代理(JDK代理)和基于继承代理(Cglib代理)
动态代理:在程序运行时,通过反射机制动态创建一个代理类
动态代理简易关系图:
相关代码示例:
首先定义一个抽象主题接口Subject
public interface Subject {
void request();
}
编写真正主题RealSubject
//真正的实现类
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("real subject execute request");
}
}
编写动态代理主题实现类JdkProxySubject
/*
* 相当于AOP的aspect
*/
public class JdkProxySubject implements InvocationHandler{
private RealSubject realSubject;
public JdkProxySubject(RealSubject realSubject) {
this.realSubject = realSubject;
}
//基于反射,动态的去执行方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
Object result = null;
try {
result=method.invoke(realSubject,args);
}catch(Exception e) {
System.out.println("ex"+e);
throw e;
}finally{
System.out.println("after");
}
return result;
}
}
客户端调用代码如下:
public class Client {
public static void main(String[] args) {
Subject subject = (Subject)Proxy.newProxyInstance(Client.class.getClassLoader(), new Class[] {Subject.class},
new JdkProxySubject(new RealSubject()));
subject.hello();
}
}
Cglib动态代理
原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final和static的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。
底层:使用字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。
缺点:对于final和static方法,无法进行代理。
cglib与JDK代理区别
1:JDK只能针对有接口的类的接口方法进行动态代理
2:Cglib基于继承来实现代理,无法对static、final类进行代理
3:Cglib基于继承来实现代理,无法对private、static方法进行代理
相关代码展示(不要忘了导入cglib的相关jar包)
需要被代理的类:
public class RealWorkPerson {
public void hello() {
System.out.println("我就是需要被代理的人");
}
}
cglib实现的动态代理
public class DemoMethodInterceptor implements MethodInterceptor{
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("before in cglib");
Object result = null;
try {
result = proxy.invokeSuper(obj, args);
}catch(Exception e) {
System.out.println("get ex:"+e.getMessage());
throw e;
}finally {
System.out.println("after in cglib");
}
//返回执行方法的结果
return result;
}
}
调用的客户端代码如下:
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
//告诉他superclass是谁,即实际的业务对象
enhancer.setSuperclass(RealWorkPerson.class);
//要织入的代码(织入代理)
enhancer.setCallback(new DemoMethodInterceptor());
//接收生成的代理类
RealWorkPerson realWorkPerson = (RealWorkPerson)enhancer.create();
realWorkPerson.hello();
}