在學習動態代理之前,最好先去理解靜態代理,如果未曾了解過靜態代理,建議先理解靜態代理。
在靜態代理中,你需要自己去寫一個代理類和被代理類實現相同的接口,在代理類中寫上代理邏輯,確定要代理哪些方法,如果有新的變動需要修改源代碼,重新編譯部署,非常不方便,可以看看下面的例子:
我們已經通過靜態代理的方實現了再登陸之前打印日志的能,但是現在有新的需求,A1類除了登陸,還要實現吃飯的功能,吃飯前后也要打印日志,
為了實現功能,於是做了一些修改,下圖中紅色的部分就是改動的部分:
至此,靜態代理的缺點十分明顯了,因為代理類是在編譯之前就寫好的,而且代理類和被代理類關系太緊,只要被代理類的邏輯變了,或者實現的接口變了,代理也得跟着變,直接修程序原意味着要重新測試,部署,難以維護;問題之二:代理中的重復代碼(打印日志部分)是無法通過縱向抽取(繼承)來消除的,也就有了橫向抽取(橫切)來解決,於是就AOP(Aspect Oriented Progranming),那又是另外一個故事了。
試想,如果在編譯之前不能用確定代理類是哪一種類型,只有在編譯的時候根據傳入接口,代理邏輯,需要代理的類來生成代理類,不用我們自己寫代理類,也就沒有代碼重復和大面積修改的行為,這就是jdk的動態代理機制,整體架構如下.
tip : 1)依賴關系:A類的某個方法的入參是B類對象,這是一種臨時的關系 2)關聯關系:A類的成員變量中包含B類對象
通過上面這種組織關系,調用者只需要面對JDK自動生成的代理類,代理類會去完成業具體的業務邏輯和代理邏輯,不用我們自己編寫代理類,自然也就不會為接口改變而犯難了。想要使用jdk的動態代理,首先被代理類你的寫出來,然后你要解決代理邏輯寫在那個類的問題。
為了解決第一個問題:需要下面兩個類:
//JDK動態代理只能代理有接口的類,所以和靜態代理一樣,需要有接口,和對應的實現類
public interface UserService { void login(String userName,String password); void register(); }
/**
* 被代理的對象 也是接口的實現類
*/
public class UserServiceImpl implements UserService,UserLife{
@Override
public void login(String userName, String password) {
System.out.println(userName + " login");
}
@Override
public void register() {
System.out.println("register().....");
}
}
第二步 :代理邏輯寫在什么地方,按照jdk動態代理的實現規則,代理邏輯要寫在一個實現 了 java.lang.reflect.InvocationHandler接口的類中,於是就有了下面的代碼
public class UserServiceInvocationHandler implements InvocationHandler { /** * 要被代理的目標對象 */ private UserService userService; public UserServiceInvocationHandler(UserService userService){ this.userService = userService; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("do something before 打印日志...."); //調用被代理對象的業務處理方法 Object result = method.invoke(userService,args); System.out.println("do something before 打印日志...."); return result; } }
至此,我們要寫的代碼已經寫完了,動態代理類是在運行的時候動態生成的,該如何去調用者動態生成的對象去完成特定功能呢?具體代碼如下
public class Test{ public static void main(String[] args) { System.out.println("---------動態代理類-----------");
//讓jvm動態生成代理類 這就是JDK的動態代理 UserService service = (UserService)Proxy.newProxyInstance(Test.class.getClassLoader(), new Class[]{UserService.class, UserLife.class},//被代理類實現的接口 new UserServiceInvocationHandler(new UserServiceImpl()));//代理邏輯 service.login("jack","12345"); System.out.println("-----------------------"); service.register(); System.out.println("動態代理類的名字" + service.getClass().getName()); } }
------------------------------運行結果如下--------------------------
---------動態代理類-----------
do something before 打印日志....
jack login
do something before 打印日志....
-----------------------
do something before 打印日志....
register().....
do something before 打印日志....
動態代理類的名字com.sun.proxy.$Proxy0
總結和思考:通過JDK的動態代理機制,我們可以對增強類的功能,在那些需要的方法執行前后加入相應的代理邏輯,但是本文演示的只是最簡單的動態代理方式,也就是默認代理類中的所有方法的都需要被代理,所以被代理的類的每一個方法調用前后都會執行額外操作,如果想要靈活的使用動態代理還需要借助java中的反射機制和注解,就能隨心所欲,只為代理類中特定方法進行代理。