代理模式
在Java中代理模式是一種設計模式,是通過代理類來代替原始的對象,可以在不改變原始對象的基礎上,對它進行擴展(新增一些新功能)。在目標方法的執行的執行前后添加一些自定義的方法。
靜態代理
步驟:
1.創建一個接口以及它的實現類
2.創建它的代理類實現該接口(重寫接口中的方法,自定義一些新功能)
3.在代理類中注入目標類,并且調用代理類中的相關方法。
具體實現如下:
(1.)接口和其實現類
public interface SsSercive {void say(String message);
}
實現類
public class ImpService implements SsSercive {public void say(String message){System.out.println("信息:"+message);}
}
(2)代理類[也要實現該接口,并重寫接口中的新方法,自定義新功能]
public class ProxyImpl implements SsSercive {private final SsSercive service;//構造器public ProxyImpl(SsSercive service){this.service=service;}@Overridepublic void say(String message) {System.out.println("操作前可以添加一些方法");service.say(message);System.out.println("操作后面也可以添加一些操作");}
}
(3)真正實現
public class Main {public static void main(String[] args) {SsSercive service=new ImpService();//在代理類中調用目標類ProxyImpl proxy=new ProxyImpl(service);//調用代理類中的相關方法proxy.say("hello world");}
}
運行結果:
總結:
在靜態代理中,對目標類的每個方法的增強都是手動的(因為代理類實現接口要重寫各個方法),一個目標類對應一個代理類,因此十分不靈活,擴展性差。
從JVM角度來說,靜態編譯在接口中就將接口類、實現類、代理類轉化成為class文件。
動態代理
動態代理不需要為每一個目標類創建一個代理類,也可以不實現接口(CGLIB動態代理不需要實現接口)。
從JVM角度來看,動態代理是在運行時生成類字節碼,并且加載到JVM中。
JDK動態代理
JDK動態代理還是需要實現接口類Invocation,核心是Proxy代理類
(1.)接口類
public interface SsSercive {void say(String message);void send(String name);
}
實現類
public class ImpService implements SsSercive {@Overridepublic void say(String message){System.out.println("信息:"+message);}@Overridepublic void send(String name) {System.out.println("發送:"+name);}
}
(2)代理類
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
//代理類實現InvocationHandler接口
public class DebugInvocationHandler implements InvocationHandler {private Object target;public DebugInvocationHandler(Object target){this.target=target;}@Override/*proxy:是動態代理生成的代理類method:調用的相關方法args:調用方法的相關參數*/public Object invoke(Object proxy, Method method,Object[] args) throws InvocationTargetException, IllegalAccessException {System.out.println("方法之前可以添加一些功能:"+method.getName());//動態代理調用的是invoke方法,invoke方法代替我們去調用原方法Object result=method.invoke(target,args);System.out.println("方法之后可以添加一些功能:"+method.getName());return result;}
}
(3)代理對象的工廠類
import java.lang.reflect.Proxy;
public class JdkProxyFactory {
//getProxy方法的本質是調用Proxy.newProxyInstance( ),獲取某個類的代理對象public static Object getProxy(Object target){return Proxy.newProxyInstance(target.getClass().getClassLoader(),//類加載器target.getClass().getInterfaces(),//代理對象實現的接口new DebugInvocationHandler(target)//自定義實現的Invocation接口);}
}
(4)實際使用
public class Main {public static void main(String[] args) {SsSercive service=(SsSercive) JdkProxyFactory.getProxy(new ImpService());service.send("hello World");service.say("我是張三");}
}
(5)運行結果
CGLIB動態代理
(1)引入依賴
說明:Spring Boot 的 parent POM(如 spring-boot-starter-parent)已經為所有常用 Spring 模塊定義好了兼容版本。顯式指定版本可能導致找不到依賴或版本沖突,所以可以不用顯示指定版本。
<dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId></dependency>
(2)目標類
public class ImpService {public void send(String name) {System.out.println("發送:"+name);}
}
(3)自定義方法攔截器
mport org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class DebugMethodInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {//調用方法之前,我們可以添加自己的操作System.out.println("before method " + method.getName());Object object = methodProxy.invokeSuper(o, args);//調用方法之后,我們同樣可以添加自己的操作System.out.println("after method " + method.getName());return object;}}
(4)代理類
import org.springframework.cglib.proxy.Enhancer;public class CglibProxyFactory {public static Object getProxy(Class<?> clazz) {// 創建動態代理增強類Enhancer enhancer = new Enhancer();// 設置類加載器enhancer.setClassLoader(clazz.getClassLoader());// 設置被代理類enhancer.setSuperclass(clazz);// 設置方法攔截器enhancer.setCallback(new DebugMethodInterceptor());// 創建代理類return enhancer.create();}
}
(5)使用
public static void main(String[] args) {ImpService SmsService = (ImpService) CglibProxyFactory.getProxy(ImpService.class);SmsService.send("java");
(6)運行結果
JDK動態代理與CGLIB動態代理的區別
(1)JDK動態代理只能直接代理接口類或是代理實現接口的類,
CGLIB動態代理可以代理不實現任何接口的類。
(2)CGLIB在運行時生成代理類的子類,不需要通過實現接口,因此不能代理聲明為final的類。
動態代理與靜態代理的區別
(1)JVM角度:靜態代理在編譯時就將接口、實現類、代理類生成class文件,
而動態代理在運行時動態生成類字節碼,并加載到jvm中。
(2)動態代理要比靜態代理更加靈活,不用必須實現接口,可直接代理。不用針對每個目標類實現一個代理類。對于目標類中方法的改動,靜態代理需要對代理類、目標對象進行修改,十分麻煩。