代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。
UML:
如果按照代理创建的时期来进行分类的话, 可以分为两种:静态代理、动态代理。静态代理是由程序员创建或特定工具自动生成源代码,在对其编译。在程序员运行之前,代理类.class文件就已经被创建了。动态代理是在程序运行时通过反射机制动态创建的。
1.1 创建服务类接口:
public interface Flyable { void fly(); }
public interface Runnable { void run(); }
1.2 创建服务类实现类:
public class Animal implements Flyable, Runnable { @Override public void fly() { System.out.println("animal flying"); } @Override public void run() { System.out.println("animal running"); } }
1.3 创建代理类:
public class AnimalProxy implements Flyable, Runnable { private Animal animal; public AnimalProxy(Animal animal) { this.animal = animal; } @Override public void fly() { System.out.println("ready: fly"); animal.fly(); } @Override public void run() { System.out.println("ready: run"); animal.run(); } }
1.4 测试:
Animal animal1 = new Animal(); AnimalProxy animalProxy = new AnimalProxy(animal1); animalProxy.fly(); animalProxy.run();
测试结果:
ready: fly
animal flying
ready: run
animal running
静态代理总结:
1.可以做到在不修改目标对象的功能前提下,对目标功能扩展。
2.缺点: 因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护。
动态代理有以下特点:
1.代理对象,不需要实现接口
2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
3.动态代理也叫做:JDK代理,接口代理
JDK中生成代理对象的API
代理类所在包:java.lang.reflect.Proxy
JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:
ClassLoader loader,
:指定当前目标对象使用类加载器,获取加载器的方法是固定的Class<?>[] interfaces,
:目标对象实现的接口的类型,使用泛型方式确认类型InvocationHandler h
:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入2.1 编写动态处理器:
public class ProxyHandler implements InvocationHandler { private Animal animal; public ProxyHandler(Animal animal) { this.animal = animal; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("invoke before:" + method.getName()); Object object = method.invoke(animal, args); System.out.println("invoke after:" + method.getName()); return object; } }
2.2 测试:
Animal animal = new Animal(); ClassLoader classLoader = animal.getClass().getClassLoader(); Class[] interfaces = animal.getClass().getInterfaces(); ProxyHandler proxyHandler = new ProxyHandler(animal); Object newProxyInstance = Proxy.newProxyInstance(classLoader, interfaces, proxyHandler); Flyable fly = (Flyable) newProxyInstance; fly.fly(); Runnable run = (Runnable) newProxyInstance; run.run();
测试结果:
invoke before:fly
animal flying
invoke after:fly
invoke before:run
animal running
invoke after:run
总结:
代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理
JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了。CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。
3.1 创建CGLIB代理类:
public class CglibProxy implements MethodInterceptor { private Object target; public Object getInstance(final Object target) { this.target = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.target.getClass()); enhancer.setCallback(this); return enhancer.create(); } public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("before : " + method.getName()); Object result = methodProxy.invoke(object, args); System.out.println("after : " + method.getName()); return result; } }
3.2 测试:
Animal animal = new Animal(); CglibProxy cglibProxy = new CglibProxy(); Animal animalCglibProxy = (Animal) cglibProxy.getInstance(animal); animalCglibProxy.buyHosue();
CGLIB代理总结: CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象,因为无需频繁创建对象,用CGLIB合适,反之使用JDK方式要更为合适一些。同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。
原文:https://www.cnblogs.com/sqzr-001/p/11380395.html