前言
代理模式可以在訪問目標對象的基礎上,增強額外的功能。
代理模式分爲靜態代理和動態代理,但是靜態代理的代理對象需要與目標對象實現一樣的接口,所以會有很多代理類,類太多.同時,一旦接口增加方法,目標對象與代理對象都要維護。動態代理便解決了此問題。
動態代理在Spring,mybatis等框架中有廣泛的應用。所以在瞭解框架源碼前需要先了解動態代理。
1.java反射
動態代理以java反射爲基礎。在瞭解動態代理前,需要先了解java反射機制。
Java 反射機制在程序運行時,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性。這種 動態的獲取信息 以及 動態調用對象的方法 的功能稱爲 java 的反射機制。
如上看着着實懵逼,看下代碼:
例如有一個UserInfo對象
public class UserInfo {
private Long id;
private String name;
public void init() throws Exception {
System.out.println("這裏是 UserInfo init");
}
public void init(String a) throws Exception {
System.out.println("這裏是 UserInfo init a:"+a);
}
public void init(String a,Integer b) throws Exception {
System.out.println("這裏是 UserInfo init a:"+a+" b:"+b);
}
}
那麼創建此對象有以下幾種方式:
public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
//普通方式進行創建
UserInfo userInfo = new UserInfo();
//通過反射機制進行創建
Class userInfoClass = UserInfo.class;
UserInfo userInfo1 = (UserInfo) userInfoClass.newInstance();
//反射進行創建
Class userInfoClass2= Class.forName("com.example.model.UserInfo");
UserInfo userInfo2 = (UserInfo) userInfoClass2.newInstance();
}
通過反射執行對象中的方法
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
Class userInfoClass = UserInfo.class;
Object userinfo = userInfoClass.newInstance();
// 執行init(String a) 方法
Method init = userInfoClass.getMethod("init", new Class[]{String.class});
init.invoke(userinfo,new Object[]{"123"});
// 執行init(String a,Integer b)方法
Method init2 = userInfoClass.getMethod("init", new Class[]{String.class,Integer.class});
init2.invoke(userinfo,new Object[]{"123",123});
}
輸出:
這裏是 UserInfo init a:123
這裏是 UserInfo init a:123 b:123
詳細可參考 java反射機制詳解
java反射學習
2.java動態代理
java實現動態代理主要有兩種,jdk動態代理和CGLIB動態代理。
jdk動態代理是jdk原生,不需要引用其他jar包,但是隻能代理接口,如果需要對類進行動態代理則需要引用CLIGB動態代理。
2.1 jdk動態代理
Proxy
提供了創建動態代理類和實例的靜態方法
InvocationHandler
是由代理實例的調用處理程序實現的接口。
代理者需要實現InvocationHandler
接口
示例:
IUserInfoService.java
public interface IUserInfoService {
void addUserInfo(UserInfo userInfo);
}
接口實現類UserInfoService.java
// 這裏加上事務註解
@Transactional
public class UserInfoService implements IUserInfoService {
@Override
public void addUserInfo(UserInfo userInfo) {
System.out.println("保存UserInfo");
}
}
代理類ProxyHandler
public class ProxyHandler implements InvocationHandler {
private Object object;
public ProxyHandler(Object object){
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法調用之前");
//是否有開啓事務註解
boolean annotationPresent = object.getClass().isAnnotationPresent(Transactional.class);
if(annotationPresent){
System.out.println("開啓事務");
}
Object invoke = method.invoke(object, args);
if(annotationPresent){
System.out.println("提交事務");
}
return invoke;
}
}
Test.java
public class Test {
public static void main(String[] args) {
IUserInfoService userInfoService = new UserInfoService();
//獲取代理實現類
// 由下面代理類可見,這裏其實等於 IUserInfoService o = new UserServiceProxy()
//然後UserServiceProxy這個對UserInfoService進行了代理。
IUserInfoService o = (IUserInfoService)Proxy.newProxyInstance(userInfoService.getClass().getClassLoader(),userInfoService.getClass().getInterfaces(),new ProxyHandler(userInfoService));
// 通過代理類實現方法,
o.addUserInfo(new UserInfo());
//保存代理類.class文件
createClassFile(o.getClass(),"UserServiceProxy");
}
public static void createClassFile(Class clazz, String proxyName) {
// 根據類信息和提供的代理類名稱,生成字節碼
byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, clazz.getInterfaces());
String paths = clazz.getResource("/").getPath();
System.out.println(paths);
FileOutputStream out = null;
try {
//保留到硬盤中
out = new FileOutputStream(paths + proxyName + ".class");
out.write(classFile);
out.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
代理類
public final class UserServiceProxy extends Proxy implements IUserInfoService {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m4;
private static Method m0;
public UserServiceProxy(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final UserInfo getUserInfoById(Long var1) throws {
try {
return (UserInfo)super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void addUserInfo(UserInfo var1) throws {
try {
super.h.invoke(this, m4, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final int hashCode() throws {
try {
return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("com.example.proxy.IUserInfoService").getMethod("getUserInfoById", new Class[]{Class.forName("java.lang.Long")});
m4 = Class.forName("com.example.proxy.IUserInfoService").getMethod("addUserInfo", new Class[]{Class.forName("com.example.model.UserInfo")});
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
通過代理,java動態生成了UserServiceProxy
類來對目標對象進行代理。
2.2 CLIGB動態代理
引入jar包
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
被代理的類UserInfoService
public class UserInfoService implements IUserInfoService {
@Override
public void addUserInfo(UserInfo userInfo) {
System.out.println("保存UserInfo");
}
}
代理類,這裏要實現MethodInterceptor
接口,在方法執行的時候會調用intercept
方法,這一點和jdk代理類似
public class CglibHandler implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("執行方法");
Object o1 = methodProxy.invokeSuper(o, objects);
System.out.println("執行方法之後");
return o1;
}
}
接下來進行測試
public class Test {
public static void main(String[] args) {
//它是一個字節碼增強器,可以用來爲無接口的類創建代理 ,與JDK的Proxy類類似
Enhancer enhancer = new Enhancer();
// 設置被代理類
enhancer.setSuperclass(UserInfoService.class);
// 設置代理類
enhancer.setCallback(new CglibHandler());
//獲取代理類
UserInfoService hello = (UserInfoService)enhancer.create();
// 執行方法,此會調用 代理類的intercept方法
hello.addUserInfo(new UserInfo());
}
}
輸出:
執行方法
保存UserInfo
執行方法之後