簡介
- 給目標對象(被代理對象)提供一個代理對象,並由代理對象控制對目標對象(被代理對象)的引用,代理對象相當於一箇中介。
- 通過引入代理對象的方式來間接訪問目標對象,防止直接訪問對象給系統帶來不必要的複雜性。
- 相當於,代理對象擁有被代理對象的所有功能,並且代理對象還添加了額外的功能來控制被代理對象。
靜態代理
代理類與被代理類實現同一個接口,將被代理類組合到代理類中並添加控制操作。
實例:
共同的接口
public interface Sourceable {
public void method();
}
被代理類
public class Source implements Sourceable {
public void method() {
System.out.println("the original method!");
}
}
代理類
public class Proxy implements Sourceable {
private Source source;
public Proxy(Source source){
this.source = source;
}
public void method() {
before();
source.method();
atfer();
}
private void atfer() {
System.out.println("after proxy!");
}
private void before() {
System.out.println("before proxy!");
}
}
測試類
public class ProxyTest {
public static void main(String[] args) {
Source target = new Source();
Sourceable proxy = new Proxy(target);
proxy.method();
}
}
動態代理
設計動態代理類時,不需要顯式實現與被代理類相同的接口,而是將這種實現推遲到程序運行時由JVM來實現。
通過Java反射機制的method.invoke(),通過調用動態代理類對象方法,從而自動調用目標對象的方法。
實例:假設一個系統中有三個簡單角色,”遊客”,”會員”,”管理員”,當用一個用戶User進行查看其它用戶信息功能showUsersInfo的時候,但是隻有管理員纔能有權限使用這個功能。
用戶類
public class User {
String name;
String id;
public User(String name, String id) {
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public String toString() {
return "User{" +
"namae='" + name + '\'' +
", id='" + id + '\'' +
'}';
}
}
用戶操作接口
public interface UserService {
public void showUsersInfo(User user);
}
用戶操作類(被代理類)
public class UserServiceIpm implements UserService {
private User user;
public UserServiceIpm(User user) {
this.user = user;
}
@Override
public void showUsersInfo(User user) {
System.out.println(user);
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
用戶操作代理類(代理類)
public class UserServiceProxy implements InvocationHandler{
private Object target; //被代理對象
public UserServiceProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理");
target = (UserServiceIpm)target;
String name = ((UserServiceIpm) target).getUser().name;
if (name=="管理員"){
Object o = method.invoke(target,args);
return o;
}else{
System.out.println("沒有權限");
return null;
}
}
}
測試類
public class Test {
public static void main(String[] args) {
User u1 = new User("遊客","2");
// User u1 = new User("管理員","2");
//創建被代理對象target
UserService target = new UserServiceIpm(u1);
//獲取被代理對象類類型
Class<? extends UserService> c = target.getClass();
//獲取被代理對象的類加載器
ClassLoader classLoader = c.getClassLoader();
//獲取被代理對象實現的接口
Class<?>[] interfaces = c.getInterfaces();
//創建代理類對象h,傳被代理類對象target過去
InvocationHandler h = new UserServiceProxy(target);
//被代理類創建代理
UserService proxy = (UserService) Proxy.newProxyInstance(classLoader, interfaces, h);
User user = new User("會員","1");
target.showUsersInfo(user);
System.out.println("--------");
proxy.showUsersInfo(user);
}
}
兩者的區別
- 靜態代理
- 代理單一目標對象
- 在代理類實現時就指定與被代理類相同的接口(運行前)
- 優點
- 降低了系統的耦合度
- 代理對象作爲客戶端和目標對象之間的中介,起到了保護目標對象的作用
- 缺點
- 增加了代理對象,因此會造成請求的處理速度變慢
- 增加了系統實現的複雜度
- 動態代理
- 代理多個目標對象
- 不需要顯式實現與被代理類相同的接口,而是將這種實現推遲到程序運行時由JVM來實現。(運行時)
- 優點
- 只需要1個動態代理類就可以解決創建多個靜態代理的問題
- 靈活性強,在使用時(調用被代理類對象)纔會動態創建動態代理類實例,不需要事先實例化
- 缺點
- 效率低,需要先通過Java反射機制,間接調用目標對象方法。
- 只能針對接口創建代理類,不能針對類創建代理類,即只能動態代理 實現了接口的類。
- 具體應用場景:面向切面編程(AOP),日誌記錄、性能統計、安全控制、異常處理等