JDK代理和Cglib代理實現和區別
阿新 • • 發佈:2019-01-08
一.JDK代理實現
介面類
public interface UserManager {
void addUser(@NotBlank String username, @NotNull(message = "javax.validation.constraints.NotNull.message") String password);
void delUser(int userId);
String findUserById(int userId);
}
實現類
public class UserManagerImpl implements UserManager { @Override public void addUser(String username, String password) { System.out.println("addUser:" + username + "," + password); } @Override public void delUser(int userId) { System.out.println("delUser:" + userId); } @Override public String findUserById(int userId) { System.out.println("findUserById:" + userId); return String.valueOf(userId); } }
代理類
public class SecurityHandler implements InvocationHandler { private Object targetObject; public Object createProxyInstanceObject(Object targetObject) { this.targetObject = targetObject; return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { checkSecurity(); return method.invoke(targetObject,args); } private void checkSecurity(){ System.out.println("-----------checkSecurity----------"); } }
二.CgLib代理實現
實現類
public class UserService { public void addUser(String username, String password) { System.out.println("addUser:" + username + "," + password); } public void delUser(int userId) { System.out.println("delUser:" + userId); } public String findUserById(int userId) { System.out.println("findUserById:" + userId); return String.valueOf(userId); } }
代理類
public class CglibProxy implements MethodInterceptor {
private Object targetObject;
public Object createProxyInstanceObject(Object targetObject) {
this.targetObject = targetObject;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetObject.getClass());
enhancer.setCallback(this);
return enhancer.create();
//return targetObject;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object obj = null;
//obj = methodProxy.invokeSuper(o, objects);
obj = method.invoke(targetObject, objects);
after();
return obj;
}
private void before() {
System.out.println("-----------before----------");
}
private void after() {
System.out.println("-----------after----------");
}
}
三.測試
public class App {
public static void proxyManager() {
SecurityHandler securityHandler = new SecurityHandler();
UserManager userManager = (UserManager) securityHandler.createProxyInstanceObject(new UserManagerImpl());
userManager.addUser("張飛", null);
}
public static void proxyService() {
SecurityHandler securityHandler = new SecurityHandler();
UserService userService = (UserService) securityHandler.createProxyInstanceObject(new UserService());
userService.addUser("李四", null);
}
public static void main(String[] args) {
cglibProxyManager();
}
public static void cglibProxyService() {
CglibProxy proxy = new CglibProxy();
UserService userService = (UserService) proxy.createProxyInstanceObject(new UserService());
userService.findUserById(1234);
}
public static void cglibProxyManager() {
CglibProxy proxy = new CglibProxy();
UserManager userManager = (UserManager) proxy.createProxyInstanceObject(new UserManagerImpl());
userManager.findUserById(1234);
}
}
執行結果會發現方法proxyService報錯。
提出問題:
1.Enhancer類的作用
生成代理類,用來繼承被代理物件
2.obj.invoke 和 obj.invokeSuper兩種寫法的區別
一般使用invokeSuper(obj,args)方法。執行原始類的方法。方法.invoke,這是執行生成子類的方法。如果傳入的obj就是子類的話,會發生記憶體溢位,因為子類的方法不停的進入intercept方法,而這個方法又去呼叫子類的方法,兩個方法直接迴圈呼叫了。
我們來看看MethodProxy,原始類裡每一個方法都會在動態的子類裡有一個對應的MethodProxy,而一個MethodProxy又對應了兩個動態生成的FastClass類,一個是對應原始方法,一個對應新生成的子類,MethodProxy.invokeSuper就是交給對應原始方法那個FastClass,MethodProxy.invoke交給另一個。
3.proxyService方法為什麼會報錯
核心在於ProxyGenerator.generateProxyClass
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
final byte[] var4 = var3.generateClassFile();
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
try {
int var1 = var0.lastIndexOf(46);
Path var2;
if (var1 > 0) {
Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
Files.createDirectories(var3);
var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
} else {
var2 = Paths.get(var0 + ".class");
}
Files.write(var2, var4, new OpenOption[0]);
return null;
} catch (IOException var4x) {
throw new InternalError("I/O exception saving generated file: " + var4x);
}
}
});
}
return var4;
}
private byte[] generateClassFile() {
this.addProxyMethod(hashCodeMethod, Object.class);
this.addProxyMethod(equalsMethod, Object.class);
this.addProxyMethod(toStringMethod, Object.class);
Class[] var1 = this.interfaces;
int var2 = var1.length;
int var3;
Class var4;
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
Method[] var5 = var4.getMethods();
int var6 = var5.length;
for(int var7 = 0; var7 < var6; ++var7) {
Method var8 = var5[var7];
this.addProxyMethod(var8, var4);
}
}
Iterator var11 = this.proxyMethods.values().iterator();
List var12;
while(var11.hasNext()) {
var12 = (List)var11.next();
checkReturnTypes(var12);
}
Iterator var15;
try {
this.methods.add(this.generateConstructor());
var11 = this.proxyMethods.values().iterator();
while(var11.hasNext()) {
var12 = (List)var11.next();
var15 = var12.iterator();
while(var15.hasNext()) {
ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
this.methods.add(var16.generateMethod());
}
}
this.methods.add(this.generateStaticInitializer());
} catch (IOException var10) {
throw new InternalError("unexpected I/O Exception", var10);
}
if (this.methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
} else if (this.fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
} else {
this.cp.getClass(dotToSlash(this.className));
this.cp.getClass("java/lang/reflect/Proxy");
var1 = this.interfaces;
var2 = var1.length;
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
this.cp.getClass(dotToSlash(var4.getName()));
}
this.cp.setReadOnly();
ByteArrayOutputStream var13 = new ByteArrayOutputStream();
DataOutputStream var14 = new DataOutputStream(var13);
try {
var14.writeInt(-889275714);
var14.writeShort(0);
var14.writeShort(49);
this.cp.write(var14);
var14.writeShort(this.accessFlags);
var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
var14.writeShort(this.interfaces.length);
Class[] var17 = this.interfaces;
int var18 = var17.length;
for(int var19 = 0; var19 < var18; ++var19) {
Class var22 = var17[var19];
var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
}
var14.writeShort(this.fields.size());
var15 = this.fields.iterator();
while(var15.hasNext()) {
ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
var20.write(var14);
}
var14.writeShort(this.methods.size());
var15 = this.methods.iterator();
while(var15.hasNext()) {
ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
var21.write(var14);
}
var14.writeShort(0);
return var13.toByteArray();
} catch (IOException var9) {
throw new InternalError("unexpected I/O Exception", var9);
}
}
}
1.在需要繼承proxy類獲得有關方法和InvocationHandler構造方法傳參的同時,java不能同時繼承兩個類,我們需要和想要代理的類建立聯絡,只能實現一個介面2.需要反射獲得代理類的有關引數,必須要通過某個類,反射獲取有關方法,如本次測試用的 :printSomeThing
3.成功返回的是object型別,要獲取原類,只能繼承/實現,或者就是那個代理類
4.對具體實現的方法內部並不關心,這個交給InvocationHandler.invoke那個方法裡去處理就好了,我只想根據你給我的介面反射出對我有用的東西。
5.考慮到設計模式,以及proxy編者編寫程式碼的邏輯使然
結論:
JDK代理和Cglib代理的區別
JDK代理 | Cglib代理 | |
實現 | InvocationHandle 底層使用反射機制進行方法的呼叫r | MethodInterceptor 底層將方法全部存入一個數組中,通過陣列索引直接進行方法呼叫 |
優點 | 不需要硬編碼介面,程式碼複用率高 | 可以在執行時對類或者是介面進行增強操作,且委託類無需實現介面 |
缺點 | 只能夠代理實現了介面的委託類 | 不能對final類以及final方法進行代理 |