定義:爲其他對象提供一種代理,以控制對這個對象的訪問,代理對象在客戶端和目標對象之間起到中介的作用;
類型:結構型;
適用場景:
- 保護代理對象;
- 增強代理對象;
優點:
- 將代理對象與真實被調用的目標對象分離;
- 一定程度上降低了系統耦合度,擴展性好;
- 保護目標對象;
- 增強目標對象;
缺點:
- 代理模式會造成系統設計中目標類增加;
- 在客戶端和目標對象之間增加一個代理對象,相對會造成請求處理速度變慢;
jdk靜態代理:
jdk靜態代理實現比較簡單,一般是代理對象直接包裝了被代理對象。只能爲一個被代理類服務,如果需要代理的類比較多,那麼會產生過多的代理類。jdk靜態代理在編譯時產生class文件,運行時無需產生,可直接使用。
public class Tank implements Movable{
public void move() throws InterruptedException {
System.out.println("tank move---");
Thread.sleep(new Random().nextInt(10000));
}
public static void main(String[] args) throws InterruptedException {
new TankProxy(new Tank()).move();
}
}
class TankProxy implements Movable{
private Movable movable;
public TankProxy(Movable movable) {
this.movable = movable;
}
public void move() throws InterruptedException {
System.out.println("tank start---");
movable.move();
System.out.println("tank end---");
}
}
interface Movable{
void move() throws InterruptedException;
}
// 輸出:
tank start---
tank move---
tank end---
jdk動態代理
jdk動態代理會根據被代理對象生成一個繼承了Proxy類,並實現了該業務接口的jdk代理類,該類的字節碼會被傳進去的ClassLoader加載,創建了jdk代理對象實例。jdk動態代理必須實現接口,通過反射來動態代理方法,消耗系統性能。但是無需產生過多的代理類,避免了重複代碼的產生,系統更加靈活。
class TankHandler implements InvocationHandler{
Tank tank;
public TankHandler(Tank tank) {
this.tank = tank;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("tank start---");
Object invoke = method.invoke(tank, args);
System.out.println("tank end---");
return invoke;
}
}
public static void main(String[] args) throws InterruptedException {
Tank tank = new Tank();
Movable movable = (Movable) Proxy.newProxyInstance(Tank.class.getClassLoader()
, new Class[]{Movable.class}, new TankHandler(tank));
movable.move();
}
// 輸出:
tank start---
tank move---
tank end---
CGLib動態代理
CGLib動態代理是繼承代理,通過ASM字節碼框架修改字節碼生成新的子類,重寫並增強方法的功能。CGLib動態代理無需實現接口,通過生成子類字節碼來實現,jdk8以後比反射快一點。但是由於cglib會繼承被代理類,需要重寫被代理方法,所以被代理類不能是final類,被代理方法不能是final。
class ProxyMethodInterceptor implements MethodInterceptor{
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("tank start---");
Object invoke = methodProxy.invokeSuper(o, objects);
System.out.println("tank end---");
return invoke;
}
}
public static void main(String[] args) throws InterruptedException {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Tank.class);
enhancer.setCallback(new ProxyMethodInterceptor());
Tank tank = (Tank) enhancer.create();
tank.move();
}
// 輸出:
tank start---
tank move---
tank end---
Spring AOP:
AOP(Aspect-OrientedProgramming,面向切面編程),可以說是OOP(Object-Oriented Programing,面向對象編程)的補充和完善。它利用一種稱爲“橫切”的技術,剖解開封裝的對象內部,並將那些影響了多個類的公共行爲封裝到一個可重用模塊,並將其名爲“Aspect”,即方面。所謂“方面”,簡單地說,就是將那些與業務無關,卻爲業務模塊所共同調用的邏輯或責任封裝起來,便於減少系統的重複代碼,降低模塊間的耦合度,並有利於未來的可操作性和可維護性。
- Aspect(切面):通常是一個類,裏面可以定義切入點和通知;
- JointPoint(連接點):程序執行過程中明確的點,一般是方法的調用;
- Advice(通知):AOP在特定的切入點上執行的增強處理,有before, after, afterReturning, afterThrowing, around;
- Pointcut(切入點):就是帶有通知的連接點,在程序中主要體現爲書寫切入點表達式;
- AOP代理:AOP框架創建的對象,代理就是目標對象的加強。Spring中的AOP代理可以使JDK動態代理,也可以是CGLIB代理,前者基於接口,後者基於子類;
@Aspect
public class AopProxy {
@Before("execution (void designmodel.structral.proxy.aop.Tank.move())")
public void before(){
System.out.println("tank start---");
}
@After("execution (void designmodel.structral.proxy.aop.Tank.move())")
public void after(){
System.out.println("tank end---");
}
}
// 輸出:
tank start---
tank move---
tank end---