设计模式-代理模式
当年不做笔记,现在不会了,要重学框架,先把代理模式敲一遍。
1.概念
为其他对象提供一种代理以控制对这个对象的访问。代理对象起到中介作用,可去掉功能服务或增加额外的服务。有动态代理和静态代理两种实现方式,动态代理是Spring框架AOP思想的核心。
2.涉及到的角色
抽象角色:提供 一个接口或者抽象类 让真实/代理角色 实现或者继承
真实/目标角色:实现抽象类,实现需要,被代理
代理角色:在自己的方法里调用真实角色的方法,还可以补充扩展,充当中介作用。
3.静态代理
程序运行前就存在的代理类,即手写的
public class StaticProxyTest { public static void main(String[] args) {//静态代理 Producer producer=new Store(); ProducerProxy pp=new ProducerProxy(producer); producer.sell(); pp.sell(); } } interface Producer { public void sell(); } class Store implements Producer{ public void sell() { System.out.println("商店在卖货!"); } } class ProducerProxy implements Producer{ private Producer producer; public ProducerProxy(Producer producer) { this.producer=producer; } public void sell() { System.out.print("代理");//代理类对被代理类的方法增添的功能 producer.sell();//在代理类里调用 被代理类的方法 } }
4.JDK动态代理
代理对象不需要实现接口,但是真实对象需要实现所有接口,
import java.lang.reflect.Proxy; public class JDKProxyTest { public static void main(String[] args) {//JDK动态代理 Producer producer=new Store(); Producer pp=(Producer)Proxy.newProxyInstance(producer.getClass().getClassLoader(), producer.getClass().getInterfaces(), (proxy,method,args1)->{ Object invoke=method.invoke(producer, args1); return invoke; }); pp.sell(); } } interface Producer { public void sell(); } class Store implements Producer{ public void sell() { System.out.println("商店在卖货!"); } }
static Object newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h);
//java.lang.reflect.Proxy的方法,API原话是“返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序”。说白了就是创建一个代理类的对象,可以使用真实类的方法。
对参数涉及到的几个方法了解一下
(1)ClassLoader getClassLoader();
获取类加载器。
(2)类<?>[] getInterfaces();
Class的方法,获取 该对象表示的类 实现的接口。
(3)InvocationHandler的实例化需要实现invoke()方法,这个方法很陌生,通过下面的代码来理解。
Object invoke(Object object,Object... args);//Method类的方法,在具有指定参数的 方法对象上调用此 方法对象表示的底层方法。
import java.lang.reflect.Method; public class Demo { public static void main(String[] args) throws Exception { Class c=Class.forName("demo.Test");//获取类 Test test=(Test)c.newInstance();//创建Test类的实例对象test Method method = c.getMethod("run",String.class,Integer.class);//获取名叫"run"并且参数类型分别是String和Integer的方法 method.invoke(test, null,336);//对象test调用run方法,参数可以0-无限多个 //getMethod方法的名字、参数个数要正确,并且调用的invoke方法参数也要正确 } } class Test { public void run(String s,Integer i) { System.out.println("s:" + s + " i:" + i); } } /*输出 s:null i:336 */
较详细一些的JDK动态代理
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class JDKProxyTest { public static void main(String[] args) {//JDK动态代理 Producer producer=new Store(); ClassLoader classLoader=producer.getClass().getClassLoader(); Class<?>[] c=producer.getClass().getInterfaces(); InvocationHandler h=new InvocationHandler(){ public Object invoke(Object proxy,Method method,Object[] args) throws Exception { System.out.println("发财1"); Object res=method.invoke(producer,args);//args是把原来的若干个参数都弄进数组 System.out.println("发财2"); return res; } }; Producer pp=(Producer)Proxy.newProxyInstance(classLoader, c,h); System.out.println("发财3"); pp.sell();//代理对象pp通过invoke方法调用真实类的方法 System.out.println("发财4"); } } interface Producer { public void sell(); } class Store implements Producer{ public void sell() { System.out.println("商店在卖货!"); } } /*输出: 发财3 发财1 商店在卖货! 发财2 发财4 */
5.Cglib动态代理
真实对象可以没有抽象类,是单独一个类
需要导入Spring-core包,使用某些类
import java.lang.reflect.Method; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; public class CglibProxyTest { public static void main(String[] args){ Store store=new Store(); Store p=new CgLibProxy(store).proxy(); p.sell(); } } class Store{ public void sell() { System.out.println("商店在卖货!"); } } class CgLibProxy implements MethodInterceptor { private Store store; public CgLibProxy(Store store){ this.store=store; } public Store proxy() { Enhancer enhancer = new Enhancer();//Enhancer是一个字节码增强器,可以用来为无接口的类创建代理。 enhancer.setSuperclass(Store.class);//设置代理的目标类 enhancer.setCallback(this);//照抄,全网都没有解释 return (Store)enhancer.create(); } @Override //接口要实现的方法 public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("发财1"); Object res=method.invoke(store, args);//和JDK代理一样,通过invoke调用方法 System.out.println("发财2"); return res; } } /*输出: 发财1 商店在卖货! 发财2 */
CglibProxy类就像是一个代理工厂,写一个proxy()方法来生产真实类的代理对象。
6.小结
- 静态代理:可以做到在不修改目标对象的功能前提下,对目标功能扩展;代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护。
- JDK动态代理:代理对象不需要实现接口;但目标类一定要实现接口。
- Cglib动态代理:目标类不需要实现接口,可以是单独的类。
7.JDK代理 vs Cglib代理
JDK动态代理使用Java的反射技术生成代理类,只能代理实现了接口的类,没有实现接口的类不能实现动态代理,CGLib会在运行时动态的生成一个被代理类的子类,子类重写了被代理类中所有非final的方法,在子类中采用方法拦截的技术拦截所有父类方法的调用,不需要被代理类对象实现接口,从而CGLIB动态代理效率比Jdk动态代理反射技术效率要高
参考&引用
https://www.jianshu.com/p/bacaafb5d02d