代理模式从业务上讲其实还是比较好理解的,比如过年我想买一张火车票,总也买不到,怎么办?
比如可以让男朋友去火车站排队去买!这叫静态代理
一、静态代理
首先我们定义一个接口,买火车票
public interface BuyTicket { public void buy(); }
然有个美女,想要回家,需要买一张火车票
public class BuyTicketGirl implements BuyTicket { @Override public void buy() { System.out.println("I need a ticket"); } }
很遗憾,过年的时候火车票不好买,美女只好求男朋友去帮忙买
public class BuyTicketBoy implements BuyTicket { private BuyTicket buyTicket; public BuyTicketBoy(BuyTicket buyTicket) { this.buyTicket = buyTicket; } @Override public void buy() { System.out.println("I am boy friend"); buyTicket.buy(); } }
public class Main { public static void main(String[] args) { BuyTicket buyTicket = new BuyTicketGirl(); BuyTicket proxy = new BuyTicketBoy(buyTicket); proxy.buy(); } }
买到了,输出:
I am boy friend
I need a ticket
很好,但这有一个问题,如果让男朋友帮忙买票,前提是每个人都有男朋友,显然这并不现实。
这也是静态代理的缺点:代理只能为一个类服务,如果需要为很多类服务,需要写很多代理类,这并不是我们想看到的。
那怎么办?
如果你去过火车站,会发现火车站就会有一些到处晃悠的大哥悄没声问:买票不?
对于买票这个行为来说,他的身份就是代理,他能帮我完成买票这个行为,具体他是谁,怎么做到,我不关心。
而且在买票这个事上,他能帮很多人实现梦想。这就是动态代理。
二、动态代理
首先我们先实现一个买票大哥,他持有一个object属性,意味他可以为所有人帮忙买票,如果你需要,说不定还可以帮忙买房买车。
public class ProxyHandler implements InvocationHandler { private Object object; public ProxyHandler(Object object) { this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("I am Dynamic Proxy"); method.invoke(object,args); return null; } }
开始买票
public class Main { public static void main(String[] args) { BuyTicket buyTicket = new BuyTicketGirl(); ProxyHandler proxyHandler = new ProxyHandler(buyTicket); BuyTicket proxyInstance = (BuyTicket) Proxy.newProxyInstance( buyTicket.getClass().getClassLoader(), buyTicket.getClass().getInterfaces(), proxyHandler); proxyInstance.buy();
}
}
输出:
I am Dynamic Proxy
I need a ticket
也买到票了,这就是动态代理
三、动态代理底层原理
实际上JDK动态代理是采用字节重组,重新生成对象来代替原始对象以达到动态代理的目的。JDK Proxy生成对象的步骤如下。
1.拿到被代理对象的引用BuyTicket ,并且获取到它所有的接口反射获取。(这也是为什么JDK动态代理需要代理类和被代理类都要事先同一接口的原因)
2.JDK Proxy类重新生成一个新的类,这个新的类需要实现被代理类(BuyTicket)的所有实现的所有接口(buy)
3.动态生成java代码,把新加的业务逻辑方法由一定的逻辑代码去调用(在代码中体现)
4.编译新生成的java代码.class
5.再重新加载到JVM中运行
四、Spring AOP中的代理机制
springboot没有默认使用的代理 : (SpringBoot中默认开启了proxyTargetClass,默认都是使用CGLIB代理)
- 如果代理的是实现接口的类,则使用JDK动态代理. 只要代理的类实现了至少一个接口,那么目标类型实现的接口都将会使用JDK动态代理
- 如果代理的是子类,没有实现任何接口,则使用CGLIB代理
参考:
https://www.jianshu.com/p/8aee43cbc373