一、代理的優勢
代理通過繼承或一些其它接口方法來實現原實現類額外的功能,而不修改實現類,可以避免額外功能代碼對原代碼的侵入。
假設有一個接口
public interface userservice {
boolean login(String name,String passwd);
boolean logout();
}
對接口的實現類為
public class userserviceimpl1 implements userservice {
public boolean login(String name, String passwd) {
System.out.println("----login-----");
return false;
}
public boolean logout() {
System.out.println("----logout-----");
return false;
}
}
如何在不修改實現類的情況下實現功能的增加。
二、代理的實現方式
(1)靜態代理
public class staticproxy implements userservice{
userserviceimpl1 l;
staticproxy(userserviceimpl1 u)
{l=u;}
@Override
public boolean login(String name, String passwd) {
System.out.println("----before-----");
boolean res=l.login(name,passwd);
System.out.println("----after-----");
return res;
}
@Override
public boolean logout() {
System.out.println("----before-----");
boolean res=l.logout();
System.out.println("----after-----");
return false;
}
}
通過新建一個類繼承接口,同時設置一個實現類的對象屬性,來完成方法的調用和額外功能的實現。如果有多個類需要代理,則需要創建多個代理類,代碼冗余且容易出錯。
(2)動態代理
通過JDK或CGlib兩種工具來實現java動態字節碼的加載。
-
JDK方式
通過
Proxy.newProxyInstance(ClassLoader,Interfaces,InvocationHandler)
來返回一個代理對象,可以強制類型轉換為接口類型,之后使用接口方法,調用的是Handler中的方法,即自定義方法。前面參數傳入的是實現類的類加載器和接口,之后自定義實InvocationHandler接口類。
public class userviceproxy implements InvocationHandler {
Object object;
userviceproxy(Object target)
{
object=target;
}
userviceproxy(){}
public Object bind(Object target)
{
object=target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {
System.out.println("----before-----");
Object t=method.invoke(object,args);
System.out.println("----after-----");
return t;
}
}
類加載器(ClassLoader)的作用:
- 將.java文件編譯后得到的.class文件字節碼加載進JVM方法區中。
- 通過類加載器創建類的Class對象,進而創建這個類的對象。
類加載器和這個類本身組成了類在JVM中的唯一性,即只有經過相同的類加載器加載的class文件類才是同一個類,否則即使來源于同一個class文件,在同一個JVM中使用,但由不同的classloader加載,那么也是不同的類。相同是指包括代表類的Class對象的equals()方法、 isAssignableFrom() 方法、 isInstance() 方法的返回結果。在加載class文件時,jvm會給類分配一個獨特的classloader。動態代理中,不存在java文件和class文件,因此不需要classloader加載,但jvm中不存在代理類的class對象,需要一個classloader來創建class對象,此時借用被代理類的classloader來創建。
-
CGlib方式實現動態代理
UserService userService = new UserService();
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(TestCglib.class.getClassLoader());
enhancer.setSuperclass(userService.getClass());
MethodInterceptor interceptor = new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
System.out.println("--- cglib log ----");
Object ret = method.invoke(userService, args);
// 執行原始方法
return ret;
}
};
enhancer.setCallback(interceptor);
UserService userServiceProxy = (UserService) enhancer.create();
userServiceProxy.login("zhenyu", "123456");
userServiceProxy.register(new User());
三、總結
|
JDK
|
Cglib
|
|
classloader
|
enhancer.setClassLoader()
|
|
Interfaces
|
setSuperClass
|
|
InvocationHandler
|
methodIntercepter
|
|
Proxy.newProxyInstance() cast to interface
|
enhancer.create() cast to superclass
|
- JDK通過實現接口來創建動態代理,要求原始類實現接口,只代理接口上的方法,方法返回也只能轉換為接口類型proxy.newProxyInstance(classloader,interfaces,invocationhandler)。
- CGlib通過繼承原始類來創建動態代理,不需要原始類實現接口,方法返回轉換為父類類型。