面試的時候,java開發必問的知識點是spring,spring中核心的是IOC和AOP。問到AOP的實現原理的時候,咱們都知道是代理模式,卻對代理模式只知其一;不知其二,今天就來記記筆記。
下面借鑑一張代理模式的導圖:java
首先假設一種業務場景,須要實現對用戶進行CRUD的操做,因此咱們建立了一個UserService接口和UserServiceImpl的實現類。代碼以下:面試
public interface UserService {
void addUser();
void updateUser();
}
public class UserServiceImpl implements UserService{
@Override
public void addUser() {
System.out.println("添加一個用戶");
}
@Override
public void updateUser() {
System.out.println("更新一個用戶");
}
}
複製代碼
如今咱們須要在每次對用戶信息進行增長、刪除和更新操做的時候記錄一下日誌,用靜態代理的方式,咱們須要建立一個靜態代理類,將被代理對象(目標對象)傳入,而後建立須要加強的方法,如addUser和updateUser,實現記錄日誌的功能spring
public class UserStaticProxy {
private UserService userService;
public UserStaticProxy(UserService userService) {
this.userService=userService;
}
public void addUser() {
userService.addUser();
System.out.println("打印一條日誌");
}
}
複製代碼
測試bash
public class StaticProxyTest {
public static void main(String [] args) {
UserService userService = new UserServiceImpl();
UserStaticProxy staticProxy = new UserStaticProxy(userService);
staticProxy.addUser();
}
}
複製代碼
運行結果:ide
一、接口增長方法,代理類須要同步維護。
二、接口越多,須要建立的代理類就越多。好比之後咱們有TeacherService,StudentService,就須要建立TeacherStaticProxy,StudentStaticProxy,這樣就增長了代碼量。函數
其實動態代理和靜態代理的本質是同樣的,最終程序運行時都須要生成一個代理對象實例。只不過靜態代理是採用硬編碼的方式在程序運行以前就建立好代理類,而動態代理是運行時動態生成的方式。
JDk幫咱們實現了動態代理,使用的是newProxyInstance方法
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
該方法中接收三個參數:
ClassLoader loader,:指定當前目標對象使用類加載器
Class<?>[] interfaces,:代理類須要實現的接口列表
InvocationHandler h:調用處理程序,將目標對象的方法分派到該調用處理程序
代碼示例:學習
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 Exception{
//執行目標對象的方法
Object result = method.invoke(target,args);
//實現擴展功能
System.out.println("打印一下日誌");
return result;
}
}
複製代碼
bind方法簡單封裝jdk的newProxyInstance(),並返回目標接口對象。invoke方法負責加強目標對象的方法,實現擴展功能。
測試類:測試
public class DynamicProxyTest {
public static void main(String [] args) {
DynamicProxy dynamicProxy = new DynamicProxy();
UserService userService = (UserService) dynamicProxy.bind(new UserServiceImpl());
userService.addUser();
userService.updateUser();
}
}
複製代碼
運行結果: ui
上面兩種代理方式,目標對象UserServiceimpl都實現了一個接口,若是隻是一個普通的類,沒有實現任何接口,該如何進行代理呢?這就引出了CGLib動態代理。CGLib動態代理也叫子類代理,它是在內存中構建一個子類對象從而實現對目標對象功能的擴展。
要用cglib代理,須要導入相應的包,好在spring已經集成了它,引入spring便可。
代理類:this
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 o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable{
Object object = methodProxy.invokeSuper(o,objects);
System.out.println("打印一下日誌");
return object;
}
}
複製代碼
測試類:
public class CGLibProxyTest {
public static void main(String [] args) {
CGLibProxy cgLibProxy = new CGLibProxy();
UserService userService = (UserService) cgLibProxy.bind(new UserServiceImpl());
userService.addUser();
userService.updateUser();
}
}
複製代碼
運行結果:
今天的筆記就記到這裏,感謝前來閱讀的大家,咱們一塊兒學習,一塊兒進步。