文献阅读提示:并不是他们的demo在你的本地都能跑通,同一个地方综合大家的说法理解成自己的,这样效果更好。
正文
首先介绍一下代理模式的种类,分为静态代理、jdk动态代理、cglib动态代理。
什么是代理模式?
代理模式的定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。举例说明,就是一个人或者一个机构,代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之前起到中介的作用。
用绘图方式举例
房屋中介这个角色就是代理模式的核心,中介可以做很多事,房屋加价,改造房屋等等,在代码中就是对被代理对象或方法的处理等等。
下面集合代码来讲解,由于本人能力不足,只能给出代理模式的大概思想,没有结合实际开发生产的场景讲解。
静态代理
静态代理是最好理解的,涉及到一个接口、一个接口实现类、还有一个修饰这个接口实现类的代理类,分别对应着租房的动作,房东,中介三者的关系。
由于很简单,三个类的代码都贴到类一起,大家一看便知。
public interface IRenting { //租房 Integer letOut(); } public class Landlady implements IRenting { //目标类,也可以说房东 public Integer letOut() { System.out.println("房东同意租房,租金100元"); return 100; } } public class Proxy implements IRenting { //代理类,可以看作中介 public Integer letOut() { Landlady landlady = new Landlady(); Integer integer = landlady.letOut(); //现实中,代理类要处理很复杂的业务,甚至是调用其他系统操作一些事。 System.out.println("中介把房东的房子出租给你并从中渔利100元"); return integer + 100; } }
下面来模拟调用者,也就是租客是怎么找中介租房的。
/*tianqiweiqi.com*/ public class StaticProxyTest { public static void main(String[] args) { Proxy proxy = new Proxy(); Integer integer = proxy.letOut(); System.out.println("你一共话了" + integer + "元成功租房,全程没有见过房东,中介把你们隔离开"); } }
调用方只需要把代理类注入到本类中就可以使用了。对于调用者来说,你只知道中介可以租给房子,至于他怎么操作的对你是隔离的。
JDK动态代理
jdk代理模式虽然是动态代理,但是也算比较好理解,只要你的电脑有jdk就可以玩。
其实动态代理和静态代理的思想是不变的,动态代理和静态代理的区别就是,动态代理不用我们去手编写代理类,在运行时,动态的在内存中生产代理类。
JDK动态代理的API:
在java.lang.reflect包中有一个代理类。
java.lang.reflect.Proxy
我们分别使用三个包就可以完成JDK动态代理。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
方便大家看到动态代理的输出过程,我讲被代理对象多写一个方法,思想还是一样的,接口、实现类、代理工厂三者。
public interface TargetInterface { //被代理的两个方法 void method1(); int method2(Integer i); }public class Target implements TargetInterface { public void method1() { System.out.println("被代理的method1 running ...相当于做了一些列的业务"); } public int method2(Integer i) { System.out.println("被代理的method2 running ...并对参数做了处理"); return i + 10; } }
与静态代理唯一有区别的地方就是这里,代理类。
代理类的作用是,无论被代理的类是什么返回值类型,什么参数类型,都可以通过我去调用到,对客户端实现隔离。
实际情况代理类需要做的事情远比我demo要复杂的多。
public class ProxyFactory { public staticObject getProxy(final T t) { //返回一个代理对象 Object object = Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), new InvocationHandler() { //invoke()方法是因为new InvocationHandler()而重写的。 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // proxy就是目标对象,method就是调用目标对象中方法,args就是调用目标对象中方法的参数。 //比如说:代理对象.method1(),这时proxy就是目标类,method1就是method,args就是method1方法参数。 System.out.println("执行方法前..."); //执行了invoke方法就相当于把Target(目标类)的所有方法都交给代理类去调用,这里都t是类,args是参数,可以有很多参数。 Object invoke = method.invoke(t, args); System.out.println("执行方法后..."); return invoke; } }); return object; } }
使用的时候,返回类型是一个接口,然后使用工厂类调用即可。
public class JDKProxyTest { public static void main(String[] args) { Target target = new Target(); TargetInterface proxy = (TargetInterface)ProxyFactory.getProxy(target); proxy.method1(); System.out.println("-------------------------"); int i = proxy.method2(100); System.out.println(i); } }
输出结果
注意:JDK的Proxy方式实现的动态代理 目标对象必须有接口 没有接口不能实现jdk版动态代理!
下面来说一下这个方法Proxy.newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h);
返回值:Object就是代理对象
参数:
loader:代表与目标对象相同的类加载器——-目标对象.getClass().getClassLoader()
interfaces:代表与目标对象实现的所有的接口字节码对象数组—-数组因为目标类可以有多个接口
h:具体的代理的操作,InvocationHandler接口
Cglib动态代理
第三方代理技术–Cglib代理。
可以对任何类生成代理,代理的原理是可以对目标对象接口实现代理,也可以进行继承代理。
需要引入cglib的jar文件,但是Spring的核心包中已经包括了Cglib功能,所以直接引入spring-core包,由于我依赖了SpringBoot所以这点不用担心。
跟jdk动态代理区别,第一点是可以省略目标接口。 第二点代理工厂使用的是 org.springframework.cglib.proxy.Enhancer; 来帮助我们生成代理对象的,千万不用用net 的包,可能导致错误。
代码实现:
需要依赖springboot
‹dependencies› ‹dependency› ‹groupId›org.springframework.boot‹/groupId› ‹artifactId›spring-boot-starter‹/artifactId› ‹version›2.0.0.RELEASE‹/version› ‹/dependency›‹/dependencies›
目标类,被代理对象
public class Target { public void method1() { System.out.println("被代理的method1 running ...相当于做了一些列的业务"); } public int method2(Integer i) { System.out.println("被代理的method2 running ...并对参数做了处理"); return i + 10; } }
代理类
public class CglibFactory implements MethodInterceptor { public Object getProxy(Class clazz) { Enhancer enhancer = new Enhancer(); //帮我们生成代理对象 //设置需要创建子类的类 enhancer.setSuperclass(clazz); //设置要代理的目标类,就是当前类,所以this enhancer.setCallback(this); //通过字节码技术动态创建子类实例 return enhancer.create(); } public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("执行方法前dosomething。。。"); Object invoke = methodProxy.invokeSuper(o, objects); System.out.println("执行方法后dosomething。。。"); return invoke; } }
让我们测试一下吧
public class CglibProxyTest { public static void main(String[] args) { CglibFactory cglibFactory=new CglibFactory(); Target proxy= (Target) cglibFactory.getProxy(Target.class); proxy.method1(); System.out.println(proxy.method2(100)); } }
输出结果
以上三种代理模式已经讲完,代码结构如下:
总结:
静态代理需要自己手动编写代理类和目标方法。
JDK动态代理就不需要自己手动实现代理类和目标方法,但动态代理的目标类要必须实现接口!
Cglib 代理的目标类可以实现接口也可以不实现,因为可以使用继承子类的方式代理。