代理模式
静态代理
静态代理模式的角色:
- 抽象角色:一般使用接口或实现类类解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理角色会增加一些附属操作
- 客户:访问代理对象的人
静态代理的实现:真实角色、代理角色继承(实现)抽象角色;代理角色中拥有真实角色;真实角色的业务方法被代理角色调用(代理角色可以扩展功能);客户通过访问代理角色来间接访问真实角色;
代理模式的好处:
- 使真实角色更加纯粹,专注自身的业务,而不同考虑一些公共的业务
- 公共业务交给代理对象!实现了业务的分工!
- 对于扩展业务使用扩展代理类中的业务即可!方便几种管理!
静态代理的缺点:
- 一个真实对象就会产生一个代理对象;增加了代码量!
动态代理可以解决代码量增加的问题,因为动态代理使用了反射机制!静态代理一个真实类就要写一个代理类对应;使用动态代理可以不写代理类,使用调用处理程序生成任意真实类的代理类!
- 动态代理的代理类是动态生成的,不是我们直接写的。
这样使用代理完成横向开发,可以实现不修改原有代码增加新的业务!
例如:淘宝代理商家卖东西
public interface Sell {
public void putaway();
}
public class Merchant implements Sell {
private int n;
public void putaway(){
System.out.println("商家上架商品");
}
}
public class TaoBao implements Sell {
private Sell sell;
public TaoBao(Sell sell){
this.sell = sell;
}
public void setSell(Sell sell) {
this.sell = sell;
}
@Override
public void putaway() {
System.out.println("淘宝进行代理");
sell.putaway();
}
}
public class Client {
public static void main(String[] args) {
Merchant merchant = new Merchant();
TaoBao taoBao = new TaoBao(merchant);
taoBao.putaway();
}
}
动态代理
从上述代码可以看出每次写一个真实类就必须再写一个代理类,如果真实类特别多的话代码量就非常大!那么有没有一个方法可以自动的根据真实类(真实接口)创建一个代理类呢?动态代理可以解决这个问题!
静态代理一个真实类就要写一个代理类对应;使用动态代理可以不写代理类,使用调用处理程序生成任意真实类的代理类!
动态代理使用反射机制实现,所以不需要增加代码!
-
动态代理和静态代理的角色相同
-
动态代理的代理类是动态生成的,不是我们直接写好的
-
动态代理分为两类:基于接口的动态代理;基于类的动态代理。
- 基于接口:JDK的动态代理
- 基于类: cglib
- Java字节码实现:javasist(第三种)
实现动态代理需要了解两个类:Proxy(代理)、InvocationHandler(调用处理程序)
注意!处理程序并不是代理类,而是用处理程序生成一个代理类!(InvocationHandler决定了代理类是的行为)。而Proxy可以生成代理对象!(代理对象是根据代理类生成的,而代理类是通过调用处理程序生成的)
动态代理的好处:
- 使真实角色更加纯粹,专注自身的业务,而不同考虑一些公共的业务
- 公共业务交给代理对象!实现了业务的分工!
- 对于扩展业务使用扩展代理类中的业务即可!方便几种管理!
- 一个动态代理类代理的是一个接口,一般就是对应的一类业务
- 一个动态代理类可以代理多个类,只要是实现类同一个接口即可!
例如:我需要一个咸鱼代理卖牛肉的商家,又要一个咸鱼代理买牛肉的买家。(如果使用静态代理我们需要分别写两个代理类,因为两个真实类的实现接口不同)
public interface Buy {
void buy();
void wobuy();
}
public class BuyImpl implements Buy{
@Override
public void buy() {
System.out.println("我是买牛肉的");
}
@Override
public void wobuy() {
System.out.println("hahahaha");
}
}
public interface Sell {
void sell();
}
public class SellImpl implements Sell{
@Override
public void sell() {
System.out.println("我是卖牛肉的");
}
}
public class ProxyInvocationHandler implements java.lang.reflect.InvocationHandler {
private Object target;
public void setTarget(Object target) {
this.target = target;
}
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(target, args);
this.dosome(method.getName());
return invoke;
}
/*
InvocationHandler是一个调用处理程序,实现了这个就实现了代理类的模板
其中invoke方法是调用方法,其作用是根据真实类的每一个方法生成代理类,如果代理类有些额外业务的话可以放到invoke方法中。method.getName()可以获得方法的名称,可以根据方法的名字做对应的业务。
Proxy.newProxyInstance是根据调用处理程序以及真实类的接口生成代理对象的。
1.真实对象传入调用处理类
2.invoke方法根据真实对象,构造对应的代理类的功能
3.proxy通过调用处理类与真实对象生成代理对象
*/
public void dosome(String s){
if ("sell".equals(s)){
System.out.println("咸鱼代理的卖家");
}else if ("buy".equals(s)){
System.out.println("咸鱼代理的买家");
}
}
}
public class Test {
public static void main(String[] args) {
Buy buy = new BuyImpl();
Sell sell = new SellImpl();
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setTarget(buy);
Buy proxy1 = (Buy) pih.getProxy();
proxy1.buy();
proxy1.wobuy();
pih.setTarget(sell);
Sell proxy = (Sell) pih.getProxy();
proxy.sell();
}
}
结果:
我是买牛肉的
咸鱼代理的买家
hahahaha
我是卖牛肉的
咸鱼代理的卖家
- InvocationHandler是一个调用处理程序,实现了这个就实现了代理类的模板
其中invoke方法是调用方法,其作用是根据真实类的每一个方法生成代理类,如果代理类有些额外业务的话可以放到invoke方法中。method.getName()可以获得方法的名称,可以根据方法的名字做对应的业务。Proxy.newProxyInstance是根据调用处理程序以及真实类的接口生成代理对象的。
-
真实对象传入调用处理类
-
invoke方法根据真实对象,构造对应的代理类的功能
-
proxy通过调用处理类与真实对象生成代理对象
再理解
public class ProxyInvocationHandler implements java.lang.reflect.InvocationHandler {
private Object target;
public void setTarget(Object target) {
this.target = target;
}
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(target, args);
this.dosome(method.getName());
return invoke;
}
public void dosome(String s){
if ("sell".equals(s)){
System.out.println("咸鱼代理的卖家");
}else if ("buy".equals(s)){
System.out.println("咸鱼代理的买家");
}
}
}
- 首先创建ProxyInvocationHandler对象,然后调用setTarget将真实对象放入调用处理类中。这样调用处理程序才知道真实对象是谁
- 调用getProxy后,会根据代理类的静态方法生成一个代理对象。因为是静态方法所以并不知道哪个是代理调用处理类,哪个是真实类,又使用的哪个类加载器。所以需要传入这些参数。
- 在newProxyInstance方法中获取调用对象(传进入的哪个this)的类对象的所有方法(getDeclaredMethods)调用调用对象的invoke方法将每个method遍历调用一遍。 所以你可以看到加上dosome方法其代理类的每个方法都会调用一遍,因为每个Method都被同一个invoke方法使用,invoke方法相同所以每次都会调用dosome,可以根据方法名分别做每个方法相应的扩展业务!