有不夠細緻的地方或者想討論的可以在評論區留言
生活中常見的示例
明星的經紀人,第三方無法直接與某明星接觸,只能聯繫經紀人,經紀人可以代爲某明星進行一些事情的處理
Java中代理常分以下三種
-
靜態代理: 核心理解->持有target,編碼在target的方法前後進行處理
-
動態代理:核心理解->代理對象proxy和被代理對象target要實現共同的接口
-
Cglib代理:核心理解->爲被代理對象target生成子類,子類即爲proxy
靜態代理
創建一個測試IUserDao
public interface IUserDao {
public void save();
}
創建一個測試UserDao
public class UserDao implements IUserDao {
public void save(){
System.out.println("測試保存數據=======");
}
}
創建靜態代理對象
public class StaticProxyUserDao implements IUserDao {
//持有被代理對象
private UserDao userDao;
public StaticProxyUserDao(UserDao userDao){
this.userDao = userDao;
}
@Override
public void save() {
System.out.println("目標方法執行前的處理。。。。。。。。。。");
//執行被代理對象的方法
userDao.save();
System.out.println("目標方法執行後的處理。。。。。。。。。。。");
}
}
總結:
靜態代理,target和proxy都需要實現共同的接口,且proxy需要持有target,可以以構造器的方式。
動態代理
動態代理爲何代理對象proxy和被代理對象target要實現共同的接口?
先從源碼分析中查看JDK對動態代理的支持類java.lang.reflect.Proxy, 該類下有newProxyInstance()方法來創建代理對象,注意該方法中的第二個入參Class<?>[] interfaces 表示被代理類target的接口類型,因此需要target要實現某個接口
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
示例
在上一步靜態代理中已經創建了IUserDao和UserDao,可以複用
在次創建JDK動態代理
public class JdkProxyFactory {
private Object target;
//構造方法注入target
public JdkProxyFactory(Object target){
this.target = target;
}
/**
* 創建代理對象
* @return
*/
public Object getProxyInstance(){
//使用Proxy的newProxyInstance方法
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),//target的類加載器
target.getClass().getInterfaces(),//target的接口類型
new InvocationHandler() { //InvocationHandler 很好理解,即在代理對象proxy中對target方法的額外處理
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("目標方法執行前的處理。。。。。。。。。。。。。。");
//執行目標對象的方法
Object returnValue = method.invoke(target, args);
System.out.println("目標方法執行後的處理。。。。。。。。。。");
return returnValue;
}
});
}
}
JDK動態代理測試
public class JdkProxyTest {
public static void main(String[] args) {
IUserDao target = new UserDao();
System.out.println("userDao.getClass() = " + target.getClass());
IUserDao proxy = (IUserDao) new JdkProxyFactory(target).getProxyInstance();
System.out.println("proxy.getClass() = " + proxy.getClass());
//調用代理對象的方法
proxy.save();
}
}
結果
userDao.getClass() = class com.coolcoding.boot.proxy.UserDao
proxy.getClass() = class com.sun.proxy.$Proxy0
目標方法執行前的處理。。。。。。。。。。。。。。
測試保存數據=======
目標方法執行後的處理。。。。。。。。。。
Cglib代理
核心理解:爲被代理對象target生成子類,底層使用字節碼處理框架ASM,有興趣可以自己深入研究ASM
創建代理對象
/**
* 首先要實現org.springframework.cglib.proxy.MethodInterceptor
*
*
* Cglib代理方式總結:
* 1.目標類不能爲final,否則拋異常報錯
* 2.目標類的方法如果爲final, static, 則子類無法重寫,則不會被代理對象攔截。
*
*
* Create by Administrator on 2020/5/14 10:57
*/
public class CglibProxyFactory implements MethodInterceptor {
private Object target;
//通過構造器注入target
public CglibProxyFactory(Object target){
this.target = target;
}
@Override
public Object intercept(Object o,
Method method,
Object[] args,
MethodProxy methodProxy) throws Throwable {
System.out.println("目標方法執行前的處理。。。。。。。。。。。。。。");
Object returnValue = method.invoke(target, args);
System.out.println("目標方法執行後的處理。。。。。。。。。。");
return returnValue;
}
/**
* 創建cglib代理對象
* @return
*/
public Object getProxyInstance(){
//使用org.springframework.cglib.proxy.Enhancer
Enhancer enhancer = new Enhancer();
//設置父類類型 -> 對應核心理解中的爲target創建子類
enhancer.setSuperclass(target.getClass());
//設置回調方法,則調用時會觸發該類中的intercept方法
enhancer.setCallback(this);
//創建代理對象 即子類
return enhancer.create();
}
}
Cglib測試
UserDao2 與UserDao一致,唯一區別是UserDao2沒有實現接口,因爲Cglib不需要target實現接口
public class CglibTest {
public static void main(String[] args) {
//生成一個目標對象
UserDao2 target = new UserDao2();
System.out.println("userDao.getClass() = " + target.getClass());
//創建代理對象
UserDao2 proxy = (UserDao2) new CglibProxyFactory(target).getProxyInstance();
System.out.println("proxy.getClass() = " + proxy.getClass());
//執行代理對象的方法
proxy.save();
}
}
Cglib結果
userDao.getClass() = class com.coolcoding.boot.proxy.UserDao2
proxy.getClass() = class com.coolcoding.boot.proxy.UserDao2$$EnhancerByCGLIB$$74c2897e
目標方法執行前的處理。。。。。。。。。。。。。。
保存數據....cglib....
目標方法執行後的處理。。。。。。。。。。
Cglib總結:
-
Cglib中target不能爲final類型,因爲final類型的類無法爲其創建子類
-
target中的方法不能爲final或者static,雖然可以正常運行,但是由於方法無法被子類重寫,而不會被代理對象攔截。