代理模式
代理模式就是设置一个中间代理来控制访问原目标对象,以达到增强原对象的功能和简化访问。简而言之,代理对象=增强代码+目标对象

RealSubject(目标对象):真实的业务逻辑类。比如房东,它的action是租房Subject:定义代理类和RealSubject的公共对外方法。比如租房Proxy:代理类,帮助RealSubject去完成action,同时还能增强action
静态代理类
首先编写Subject接口,定义rent方法
public interface Subject {public void rent();}
接着编写一个房东类,房东需要去租房,是具体的逻辑实现
public class LandLord implements Subject {@Overridepublic void rent() {System.out.println("房东要租房");}}
编写一个中介类,负责帮助房东租房
public class Proxy implements Subject {public LandLord landLord;public Proxy(LandLord landLord){this.landLord = landLord;}@Overridepublic void rent() {System.out.println("带你去看房");landLord.rent();System.out.println("带你签合同");}}
最后就是真正的房客去租房了
public class Client {public static void main(String[] args) {LandLord landLord = new LandLord();Proxy proxy = new Proxy(landLord);proxy.rent();}}
缺陷:程序员要手动为每一个目标类编写对应的代理类。如果当前系统已经有成百上千个类,为这些目标类添加代理类,工作量太大了,因此增强目标类(比如打日志)变得很难处理。
动态代理
动态代理利用JDK API,动态的在内存中构建代理对象,从而实现对目标对象的代理功能。
静态代理和动态代理的区别:
- 静态代理在编译时就已经实现,编译完成后代理类是一个实际的class文件
- 动态代理是在运行时动态生成,即编译完成后没有实际的class文件,而是在运行时动态生成类字节码,并加载到JVM中
以上面的租房为例,现在房东除了租房还要卖房
public interface Subject {public void rent();public void sale();}public class LandLord implements Subject {@Overridepublic void rent() {System.out.println("房东要租房");}@Overridepublic void sale() {System.out.println("房东要卖房子");}}
编写中介类ProxyHandler
public class ProxyHandler {public Object newProxyInstance(Object targetObject){InvocationHandler h = new InvocationHandler() {/**** @param proxy:代理对象,给jdk使用,任何时候都不要动这个对象* @param method:当前要执行的目标对象的方法* @param args:方法调用时的参数* @return* @throws Throwable*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("带你去看房");Object result = method.invoke(targetObject, args);System.out.println("带你去签合同");return result;}};ClassLoader loader = targetObject.getClass().getClassLoader();Class<?>[] interfaces = targetObject.getClass().getInterfaces();return Proxy.newProxyInstance(loader, interfaces, h);}}
测试
public class Client {public static void main(String[] args) {ProxyHandler proxyHandler=new ProxyHandler();Subject subject=(Subject) proxyHandler.newProxyInstance(new LandLord());subject.rent();System.out.println("==================");subject.sale();}}
在生成代理内对象的时候,最重要的方法是Proxy.newProxyInstance,方法的参数要求传入接口,这样子返回的对象就继承了这些接口,具体的实现原理看下面的介绍。
Java Proxy的原理
代理类是动态生成的,该代理类肯定继承了Subject接口,要不然怎么能调用目标对象的方法。其次为了达到增强目标对象的功能,代理类的方法中不仅要调用目标方法,还要有增强功能。由于字节码是在内存中生成的,为了方便学习,我们将字节码保存到本地。
package com.zstu.proxy;import sun.misc.ProxyGenerator;import java.io.FileOutputStream;public class GenerateClass {public static void generateClassFile(Class clazz, String proxyName) throws Exception{byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, new Class[]{clazz});FileOutputStream fileOutputStream = new FileOutputStream(proxyName + ".class");fileOutputStream.write(classFile);fileOutputStream.close();}public static void main(String[] args) throws Exception{generateClassFile(Subject.class, "proxy");}}
生成的proxy.class我们用反编译工具查看
//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//import com.zstu.proxy.Subject;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.lang.reflect.UndeclaredThrowableException;public final class proxy extends Proxy implements Subject {private static Method m1;private static Method m4;private static Method m2;private static Method m3;private static Method m0;public proxy(InvocationHandler var1) throws {super(var1);}public final boolean equals(Object var1) throws {try {return (Boolean)super.h.invoke(this, m1, new Object[]{var1});} catch (RuntimeException | Error var3) {throw var3;} catch (Throwable var4) {throw new UndeclaredThrowableException(var4);}}public final void rent() throws {try {super.h.invoke(this, m4, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}public final String toString() throws {try {return (String)super.h.invoke(this, m2, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}public final void sale() throws {try {super.h.invoke(this, m3, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}public final int hashCode() throws {try {return (Integer)super.h.invoke(this, m0, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}static {try {m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));m4 = Class.forName("com.zstu.proxy.Subject").getMethod("rent");m2 = Class.forName("java.lang.Object").getMethod("toString");m3 = Class.forName("com.zstu.proxy.Subject").getMethod("sale");m0 = Class.forName("java.lang.Object").getMethod("hashCode");} catch (NoSuchMethodException var2) {throw new NoSuchMethodError(var2.getMessage());} catch (ClassNotFoundException var3) {throw new NoClassDefFoundError(var3.getMessage());}}}
可见,当代理对象调用sale方法的时候,首先会调用InvocationHandler的invoke方法,这个方法里添加了增强功能,并会调用目标对象的sale方法
mybatis的模拟
我们知道,在mybatis中只要写一个接口,那么mybatis就会帮我们实现这个接口,里面的原理就是使用了动态代理。
新建UserMapper.java,其中的Select注解使我们自己定义的
package com.zstu.xyz.center.core.dao;import java.util.List;public interface UserMapper {@Select("select * from user")public List<String> query();}
创建代理对象并测试
package com.zstu.xyz.center.core.dao;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class ProxyHandler {public static Object newProxyInstance(Class aclass){InvocationHandler h = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("connection...");Select declaredAnnotation = method.getDeclaredAnnotation(Select.class);String value = declaredAnnotation.value();System.out.println(value);return null;}};ClassLoader loader = aclass.getClassLoader();Class<?>[] interfaces = new Class[]{aclass};return Proxy.newProxyInstance(loader, interfaces, h);}public static void main(String[] args) {UserMapper proxy = (UserMapper)newProxyInstance(UserMapper.class);proxy.query();}}
运行结果:
connection...select * from user
cglib
使用java自带的API实现代理类,被代理的类必须实现接口;而cglib不需要这个要求。代理对象是继承了目标对象,通过重写目标对象的方法起到增强作用。
Spring提供的cglib代理:
package com.zstu.springdemo.aop;import org.springframework.cglib.proxy.Enhancer;import org.springframework.cglib.proxy.MethodInterceptor;import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class ProxyTest {public static void main(String[] args) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(LandLord.class);enhancer.setCallback(new MethodInterceptor() {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("带你去看房");methodProxy.invokeSuper(o, null);System.out.println("带你去签合同");return null;}});LandLord landLordProxy = (LandLord) enhancer.create();landLordProxy.rent();}}
