一、什么是代理模式?
我们在生活中可能会听见一些xxx代理之类的概念,比如水果商代理、饮品商代理等等。那么我们可以这么理解,本来厂家A生产的东西是自己要卖,但是由于厂家不精通这种销售业务,所以就交给销售商B去卖。B理解为A的代理商。
通过这种方式,可以产家生产出的东西价值达到了最大化。可能这么说不容易理解,下面用代码举例子的方式进行描述。
二、静态代理
1、生产对象
Apple是产家生产的,是要被出售的。我们暂时称为被代理的对象。
public class Apple {
private String name ;
public Apple(String name) {
this.name = name;
}
@Override
public String toString() {
return "Apple{" +
"name='" + name + '\'' +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2、厂家出售接口
厂家生产的Apple要 出售,但是自己出售的价格比较低。所以就需要代理商去出售这些苹果。产家只按统一价格给代理商,至于代理商怎么卖,就不关厂家的事情了。
public interface Producter {
String sell( Apple apple );
}
3、厂家自己出售
public class ProducterSell implements Producter{
@Override
public String sell(Apple apple) {
System.out.println("我是厂家,我要出售的是" + apple );
return null;
}
}
4、代理商出售
public class Agent implements Producter{
private ProducterSell producter;
public Agent(ProducterSell producter) {
this.producter = producter;
}
//代理的出售 加入了自己的输出
@Override
public String sell(Apple apple) {
System.out.println("我是代理商A,我要出售的东西是" + apple );
producter.sell( apple );
return null ;
}
}
5、用户接触
public class Customer {
public static void main(String[] args) {
Apple apple = new Apple("红富士");
Agent agent = new Agent( new ProducterSell() );
agent.sell( apple );
}
}
输出结果:
我是代理商A,我要出售的东西是Apple{name=‘红富士’}
我是厂家,我要出售的是Apple{name=‘红富士’}
通俗的讲,有两种方式的销售。一种是厂家自己销售,一种是代理商销售。那么厂家销售结果不行,卖不出去的时候,是不是需要一个牌子比较响亮的代理商去出售自家东西。
这里原本是厂家要出售的是Apple,但是在main方法中,用户选择了代理商出售的Apple。归根结底,出售还是厂家的出售,只不过多了一个环节。
这种模式就是我们所说的代理,也就是静态代理。
静态代理就是在编译的时候就把接口、实现类、代理类变成class文件。
三、动态代理机制
在静态代理中,代理商要怎么出售,得自己去实现。如果厂家的出售模式增加,代理就得跟着增加。这种模式就非常的不灵活而且麻烦。所以就产生了一种动态代理机制。
相比静态代理,动态代理更加灵活。不需要去针对每个厂商去创建代理。动态代理是在运行时动态产生类字节码,并加载JVM中。
3.1、JDK的动态代理
3.1.1 创建动态代理
还是以上面的例子,我们定义一个动态的代理商。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class JDKAgentHandler implements InvocationHandler {
/**
* 代理类中的真实对象
*/
private final Object target ;
public JDKAgentHandler( Object target ){
this.target = target ;
}
//invoke() 方法: 当我们的动态代理对象调用原生方法的时候,最终实际上调用到的是 invoke() 方法
//然后invoke() 方法代替我们去调用了被代理对象的原生方法。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法执行前 :" + method.getName() );
Object result = method.invoke( target , args );
System.out.println("方法执行后 :" + method.getName() );
return result;
}
}
在这个类中的invoke方法中有三个参数
- Object proxy 动态生成的代理类
- Method method 与代理类对象调用的方法对应
- Object[] args 当前method方法的参数
其他文章已经说过反射,所以这里不做过多的深究。
3.1.2 造一个用来获取代理对象的工厂类
public class JdkProxyFactory {
//getProxy() :主要通过类方法方法获取某个类的代理对象
public static Object getProxy( Object target ){
ClassLoader loader = target.getClass().getClassLoader();
Class<?>[] interfaces = target.getClass().getInterfaces();
JDKAgentHandler handler = new JDKAgentHandler( target );
Object obj = Proxy.newProxyInstance(loader, interfaces, handler) ;
return obj ;
}
}
3.1.2 测试动态代理
我们还是用这个用户类进行测试,从而对比其中的区别
public class Customer {
public static void main(String[] args) {
Apple apple = new Apple("红富士");
Agent agent = new Agent( new ProducterSell() );
agent.sell( apple );
Producter proxy = (Producter)JdkProxyFactory.getProxy( new ProducterSell() );
proxy.sell( apple );
}
}
输出结果:
我是代理商A,我要出售的东西是Apple{name=‘红富士’}
我是厂家,我要出售的是Apple{name=‘红富士’}
方法执行前 :sell
我是厂家,我要出售的是Apple{name=‘红富士’}
方法执行后 :sell
这个例子我们能直观感受到,不用再去手动自己挨个创建代理类。这个只是JDK的动态代理,还有一种CGLIB动态代理,由于要引入jar包,所以暂时不做介绍。
四、对比
1、 动态代理比静态代理更加灵活
动态代理不需要去实现接口,就能直接代理实现类。而且也不需要针对每个目标类都创建一个代理类。
2、JVM
静态代理是在编译的时候,将这些类编译成为字节码文件。而动态代理是在运行时生成类字节码文件,通过类加载器加载到JVM中。