首先咱們來看看代理模式: java
所謂代理模式,是指客戶端(Client)並不直接調用實際的對象(下圖右下角的RealSubject),而是經過調用代理(ProxySubject),來間接的調用實際的對象。代理模式的使用場合,通常是因爲客戶端不想直接訪問實際對象,或者訪問實際的對象存在技術上的障礙,於是經過代理對象做爲橋樑,來完成間接訪問。spring
首先有個UserService接口,接口裏有一個添加用戶的方法ide
public interface UserService {
void addUser();
}
複製代碼
這是它的實現類函數
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("添加一個用戶");
}
}
複製代碼
如今須要在添加用戶的時候記錄一下日誌。固然,你能夠直接在addUser裏面直接寫添加日誌的代碼,工具
public void addUser() {
System.out.println("添加一個用戶");
System.out.println("拿個小本本記一下");
}
複製代碼
可是Java推崇單一職責原則,若是這樣寫就違背了這個原則,咱們須要將添加日誌的代碼解耦出來,讓addUser()方法專一寫本身的業務邏輯。測試
根據類圖,建立一個靜態代理類this
public class UserStaticProxy implements UserService{
private UserService userService;
public UserStaticProxy(UserService userService) {
this.userService = userService;
}
@Override
public void addUser() {
userService.addUser();
System.out.println("拿個小本本記錄一下");
}
}
複製代碼
咱們創建一個測試類來測試靜態代理:編碼
public class Test {
public static void main(String[] args) {
UserStaticProxy userStaticProxy = new UserStaticProxy(new UserServiceImpl());
userStaticProxy.addUser();
}
}
複製代碼
運行結果: spa
如此,一個靜態代理類就建立好了,咱們能夠專一在Service寫業務邏輯,添加日誌等非業務邏輯交給這個靜態代理類來完成。隨着業務擴大,UserService類裏不知有addUser方法,還有updateUser、deleteUser、batchUpdateUser、batchDeleteUser等方法,這些方法都須要記錄日誌。3d
UserServiceImpl類以下:
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("添加一個用戶");
}
@Override
public void updateUser() {
System.out.println("更新一個用戶");
}
@Override
public void deleteUser() {
System.out.println("刪除一個用戶");
}
@Override
public void batchUpdateUser() {
System.out.println("批量更新用戶");
}
@Override
public void batchDeleteUser() {
System.out.println("批量刪除用戶");
}
}
複製代碼
那麼對應的靜態代理類以下:
public class UserStaticProxy implements UserService{
private UserService userService;
public UserStaticProxy(UserService userService) {
this.userService = userService;
}
@Override
public void addUser() {
userService.addUser();
System.out.println("拿個小本本記錄一下");
}
@Override
public void updateUser() {
userService.updateUser();
System.out.println("拿個小本本記錄一下");
}
@Override
public void deleteUser() {
userService.deleteUser();
System.out.println("拿個小本本記錄一下");
}
@Override
public void batchUpdateUser() {
userService.batchUpdateUser();
System.out.println("拿個小本本記錄一下");
}
@Override
public void batchDeleteUser() {
userService.batchDeleteUser();
System.out.println("拿個小本本記錄一下");
}
}
複製代碼
從上面咱們能夠看到,代理類裏有不少重複的日誌代碼。由於代理類和目標對象實現同一個接口,一旦接口增長方法,代理類也得同步增長方法而且得同步增長重複的額外功能代碼,增大了代碼量
若是須要增長業務類,如StudentService,TeacherService等等,這些類裏的方法也都須要實現增長日誌的方法,那麼就須要同步建立對應的代理類。此外靜態代理類不是自動生成的,須要在編譯以前就編寫好的,若是業務愈來愈龐大,那麼建立的代理類愈來愈多,這樣又增大了代碼量
如何解決這些缺點呢?這時候就須要動態代理方法了
其實動態代理和靜態代理的本質是同樣的,最終程序運行時都須要生成一個代理對象實例,經過它來完成相關加強以及業務邏輯,只不過靜態代理須要硬編碼的方式指定,而動態代理支持運行時動態生成這種實現方式。
JDK自己幫咱們實現了動態代理,只須要使用newProxyInstance方法:
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 複製代碼
注意該方法是在Proxy類中是靜態方法,且接收的三個參數依次爲:
代碼示例:
public class DynamicProxy implements InvocationHandler {
private Object target; // 目標對象
public Object bind(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(target, args);
System.out.println("拿個小本本記錄一下");
return result;
}
}
複製代碼
上文的invoke方法,負責加強目標對象的方法,接口類的全部方法都會走這個invoke方法。另外bind方法簡單封裝了JDK的代理方法newProxyInstance,負責返回接口類。
測試類:
public static void main(String[] args) {
DynamicProxy dynamicProxy = new DynamicProxy();
UserService userService = (UserService)dynamicProxy.bind(new UserServiceImpl());
userService.addUser();
userService.updateUser();
}
複製代碼
運行結果以下:
如圖UserService接口裏的全部方法都已經加上了日誌邏輯了,此外,咱們看一下UserDynamicProxy這個類裏的target屬性是Object類型的。因此,這個動態代理的方法一樣能夠給其餘Service複用。能夠這樣調用:
DynamicProxy dynamicProxy = new DynamicProxy();
TeacherService teacherService = (TeacherService)dynamicProxy.bind(new TeacherServiceImpl());
複製代碼
綜上,動態代理解決了靜態代理的缺點
動態代理是運行時候動態生成代理類的,這個類放在內存中,咱們要怎麼才能看到這個類呢?
artias是阿里開源的一個牛逼閃閃的Java診斷工具,不懂的能夠看看這篇文章http://www.dblearn.cn/article/5,用它就能夠線上反編譯代碼。
這裏咱們添加一個斷點:
public static void main(String[] args) throws IOException {
DynamicProxy dynamicProxy = new DynamicProxy();
UserService userService = (UserService)dynamicProxy.bind(new UserServiceImpl());
userService.addUser();
userService.updateUser();
System.in.read();
}
複製代碼
運行 arthas
用jad命令反編譯,java生成的代理類都在com.sun.proxy目錄下。所以反編譯命令以下
jad com.sun.proxy.$Proxy0
package com.sun.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.UserService;
public final class $Proxy0 extends Proxy implements UserService {
private static Method m1;
private static Method m6;
private static Method m2;
private static Method m7;
private static Method m0;
private static Method m3;
private static Method m4;
private static Method m5;
public final void addUser() {
try {
this.h.invoke(this, m3, null);
return;
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final void updateUser() {
try {
this.h.invoke(this, m4, null);
return;
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final void deleteUser() {
try {
this.h.invoke(this, m5, null);
return;
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final void batchUpdateUser() {
try {
this.h.invoke(this, m6, null);
return;
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final void batchDeleteUser() {
try {
this.h.invoke(this, m7, null);
return;
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public $Proxy0(InvocationHandler invocationHandler) {
super(invocationHandler);
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m6 = Class.forName("proxy.UserService").getMethod("batchUpdateUser", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m7 = Class.forName("proxy.UserService").getMethod("batchDeleteUser", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m3 = Class.forName("proxy.UserService").getMethod("addUser", new Class[0]);
m4 = Class.forName("proxy.UserService").getMethod("updateUser", new Class[0]);
m5 = Class.forName("proxy.UserService").getMethod("deleteUser", new Class[0]);
return;
}
catch (NoSuchMethodException noSuchMethodException) {
throw new NoSuchMethodError(noSuchMethodException.getMessage());
}
catch (ClassNotFoundException classNotFoundException) {
throw new NoClassDefFoundError(classNotFoundException.getMessage());
}
}
public final boolean equals(Object object) {
try {
return (Boolean)this.h.invoke(this, m1, new Object[]{object});
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString() {
try {
return (String)this.h.invoke(this, m2, null);
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode() {
try {
return (Integer)this.h.invoke(this, m0, null);
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}
複製代碼
由上面的代碼能夠看到咱們的代理類已經生成好了,沒當咱們調用方法如 addUser(),實際分派到h變量的invoke方法上執行:
this.h.invoke(this, m3, null);
h變量是什麼呢?其實就是咱們實現了InvocationHandler的DynamicProxy類。
經過觀察上面的靜態代理和JDK動態代理模式,發現要求目標對象實現一個接口,可是有時候目標對象只是一個單獨的對象,並無實現任何的接口。這時候要怎麼處理呢?下面引出大名鼎鼎的CGlib動態代理
cglib代理,也叫做子類代理,它是在內存中構建一個子類對象從而實現對目標對象功能的擴展。
要用cglib須要引入它的jar包,由於spring已經集成了它,所以引入spring包便可
編寫代理類:
public class CGLibProxy implements MethodInterceptor {
private Object target; // 目標對象
public Object bind(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
//設置父類
enhancer.setSuperclass(this.target.getClass());
//設置回調函數
enhancer.setCallback(this);
//建立子類(代理對象)
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object result = methodProxy.invokeSuper(obj, args);
System.out.println("拿個小本本記錄一下");
return result;
}
}
複製代碼
其中,Enhancer須要設置目標對象爲父類(由於生成的代理類須要繼承目標對象)
測試類:
public static void main(String[] args) throws IOException {
CGLibProxy cgLibProxy = new CGLibProxy();
UserServiceImpl userService = (UserServiceImpl)cgLibProxy.bind(new UserServiceImpl());
userService.addUser();
userService.updateUser();
System.in.read();
}
複製代碼
運行結果:
咱們看到已經成功代理了。可是結果有亂碼出現,此處設置一個// TODO,我猜想是Spring對CGlib再封裝致使的,也請知道的大大回答一下。
步驟和JDK代理類雷同,只不過cglib的代理類生成在和測試類同一個包下,因爲代碼太多,只上部分代碼
package com.example.demo.proxy;
import com.example.demo.proxy.UserServiceImpl;
import java.lang.reflect.Method;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.cglib.core.Signature;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Factory;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class UserServiceImpl$$EnhancerByCGLIB$$3ca8cfc3 extends UserServiceImpl implements Factory {
private boolean CGLIB$BOUND;
public static Object CGLIB$FACTORY_DATA;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;
private static Object CGLIB$CALLBACK_FILTER;
private static final Method CGLIB$deleteUser$0$Method;
private static final MethodProxy CGLIB$deleteUser$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$addUser$1$Method;
private static final MethodProxy CGLIB$addUser$1$Proxy;
private static final Method CGLIB$updateUser$2$Method;
private static final MethodProxy CGLIB$updateUser$2$Proxy;
private static final Method CGLIB$batchUpdateUser$3$Method;
private static final MethodProxy CGLIB$batchUpdateUser$3$Proxy;
private static final Method CGLIB$batchDeleteUser$4$Method;
private static final MethodProxy CGLIB$batchDeleteUser$4$Proxy;
private static final Method CGLIB$equals$5$Method;
private static final MethodProxy CGLIB$equals$5$Proxy;
private static final Method CGLIB$toString$6$Method;
private static final MethodProxy CGLIB$toString$6$Proxy;
private static final Method CGLIB$hashCode$7$Method;
private static final MethodProxy CGLIB$hashCode$7$Proxy;
private static final Method CGLIB$clone$8$Method;
private static final MethodProxy CGLIB$clone$8$Proxy;
public final void deleteUser() {
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
if (methodInterceptor == null) {
UserServiceImpl$$EnhancerByCGLIB$$3ca8cfc3.CGLIB$BIND_CALLBACKS(this);
methodInterceptor = this.CGLIB$CALLBACK_0;
}
if (methodInterceptor != null) {
Object object = methodInterceptor.intercept(this, CGLIB$deleteUser$0$Method, CGLIB$emptyArgs, CGLIB$deleteUser$0$Proxy);
return;
}
super.deleteUser();
}
public final void addUser() {
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
if (methodInterceptor == null) {
UserServiceImpl$$EnhancerByCGLIB$$3ca8cfc3.CGLIB$BIND_CALLBACKS(this);
methodInterceptor = this.CGLIB$CALLBACK_0;
}
if (methodInterceptor != null) {
Object object = methodInterceptor.intercept(this, CGLIB$addUser$1$Method, CGLIB$emptyArgs, CGLIB$addUser$1$Proxy);
return;
}
super.addUser();
}
public final void updateUser() {
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
if (methodInterceptor == null) {
UserServiceImpl$$EnhancerByCGLIB$$3ca8cfc3.CGLIB$BIND_CALLBACKS(this);
methodInterceptor = this.CGLIB$CALLBACK_0;
}
if (methodInterceptor != null) {
Object object = methodInterceptor.intercept(this, CGLIB$updateUser$2$Method, CGLIB$emptyArgs, CGLIB$updateUser$2$Proxy);
return;
}
super.updateUser();
}
複製代碼
其中
public class UserServiceImpl$$EnhancerByCGLIB$$3ca8cfc3 extends UserServiceImpl
能夠看到生成的代理類繼承了目標對象,所以有兩個注意點: