Konsep
Agen: Untuk mengawal objek A, objek B baharu dicipta dan objek B melakukan semua operasi objek A sebaliknya, yang dipanggil ejen. Penubuhan sistem agensi melibatkan tiga peranan yang mengambil bahagian: objek sebenar (A), objek proksi (B), dan pelanggan.
Objek proksi (B) memainkan peranan perantara, menghubungkan objek sebenar (A) dan klien Jika dikembangkan lagi, objek proksi boleh melaksanakan logik yang lebih kompleks, seperti kawalan akses untuk objek sebenar.
Kes
Keperluan: Antara muka lapisan perniagaan pekerja memerlukan kebenaran pentadbir untuk memanggil simpan dan senarai panggilan tidak memerlukan kebenaran Pengecualian akan dilemparkan apabila membuat panggilan tanpa kebenaran.
Proksi statik
1 2 3 4 5 6 7 8 |
public interface IEmployeeService {
void save();
void list();
}
|
Salin selepas log masuk
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class EmployeeServiceImpl implements IEmployeeService {
@Override
public void save() {
System.out.println( "EmployeeServiceImpl-正常的save...." );
}
@Override
public void list() {
System.out.println( "EmployeeServiceImpl-正常的list...." );
}
}
|
Salin selepas log masuk
1 2 3 4 5 6 7 8 9 10 11 12 |
public class SessionHolder {
private static String currentUser;
public static String getCurrentUser(){
return currentUser;
}
public static void setCurrentUser(String currentUser){
SessionHolder.currentUser = currentUser;
}
}
|
Salin selepas log masuk
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public class EmployeeProxy implements IEmployeeService {
private EmployeeServiceImpl employeeService;
public EmployeeProxy(EmployeeServiceImpl employeeService){
this.employeeService = employeeService;
}
@Override
public void save() {
if ( "admin" .equals(SessionHolder.getCurrentUser())){
employeeService.save();
} else {
throw new RuntimeException( "当前非admin用户,不能执行save操作" );
}
}
@Override
public void list() {
employeeService.list();
}
}
|
Salin selepas log masuk
1 2 3 4 5 6 7 8 9 10 11 12 13 | public class App {
public static void main(String[] args) {
System.out.println( "----------------真实对象--------------------" );
EmployeeServiceImpl employeeService = new EmployeeServiceImpl();
employeeService.list();
employeeService.save();
System.out.println( "----------------代理对象--------------------" );
SessionHolder.setCurrentUser( "dafei" );
EmployeeProxy employeeProxy = new EmployeeProxy(employeeService);
employeeProxy.list();
employeeProxy.save();
}
}
|
Salin selepas log masuk
e
1 2 3 4 5 6 7 8 | ----------------真实对象--------------------
EmployeeServiceImpl-正常的list....
EmployeeServiceImpl-正常的save....
----------------代理对象--------------------
EmployeeServiceImpl-正常的list....
Exception in thread "main" java.lang.RuntimeException: 当前非admin用户,不能执行save操作
at com.langfeiyes.pattern.proxy.demo.EmployeeProxy.save(EmployeeProxy.java:20)
at com.langfeiyes.pattern.proxy.demo.App.main(App.java:16)
|
Salin selepas log masuk
Apabila dipanggil terus menggunakan objek sebenar EmployeeServiceImpl, kedua-dua senarai dan simpan boleh diakses terus, tetapi ia tidak memenuhi sekatan kebenaran pentadbir pada keperluan. Jika anda menggunakan objek proksi EmployeeProxy, anda boleh melengkapkan keperluan.
Lengkapkan logik proksi dengan terus mencipta kelas objek proksi baharu Kaedah ini dipanggil mod proksi statik.
Mod proksi dinamik JDK
Mod proksi dinamik yang biasa digunakan dalam Java termasuk proksi dinamik JDK dan proksi dinamik cglib Di sini kita memfokuskan pada proksi dinamik JDK
Ia masih lagi keperluan asal , IEmployeeService EmployeeServiceImpl SessionHolder tidak berubah Tambah pengawal proksi JDK baharu-EmployeeInvocationHandler
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public class EmployeeInvocationHandler implements InvocationHandler {
private Object target;
public EmployeeInvocationHandler(Object target){
this.target = target;
}
public Object getProxy(){
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
if ( "save" .equals(name) && ! "admin" .equals(SessionHolder.getCurrentUser())){
throw new RuntimeException( "当前非admin用户,不能执行save操作" );
}
return method.invoke(target, args);
}
}
|
Salin selepas log masuk
Kelas Apl ujian diubah sedikit:
rreee. kod boleh sama Perbezaan antara keperluan pelaksanaan dan proksi statik ialah kurang objek proksi dicipta. Terdapat soalan pada ketika ini. Tiada objek proksi dibuat. Mengapakah panggilan kelas proksi boleh dilaksanakan? ?
Analisis Prinsip
Pertama, mari kita buat kesimpulan Prinsip pelaksanaan asas proksi dinamik JDK: menggunakan kaedah pelaksanaan antara muka, pada masa jalan, kelas dibina secara dinamik dalam memori, dan kemudian. disusun dan dilaksanakan. Kelas ini digunakan sekali sahaja Apabila JVM dihentikan, kelas proksi akan hilang.
Peranan yang mengambil bahagian Untuk memahami prinsip proksi dinamik JDK, anda mesti terlebih dahulu memahami kelas yang terlibat dalam proksi dinamik JDK

InvocationHandler : pengendali invocation kaedah objek sebenar, kaedah invoke terbina dalam, fungsinya: sesuaikan logik proksi untuk objek sebenar
EmployeeInvocationHandler: pengendali invocation kaedah objek sebenar perkhidmatan pekerja , kelas ini mempunyai 3 kegunaan: 1>Tetapkan objek sebenar
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public class App {
public static void main(String[] args) {
System.out.println( "----------------真实对象--------------------" );
EmployeeServiceImpl employeeService = new EmployeeServiceImpl();
employeeService.list();
employeeService.save();
System.out.println( "----------------代理对象--------------------" );
SessionHolder.setCurrentUser( "dafei" );
EmployeeInvocationHandler handler =
new EmployeeInvocationHandler(employeeService);
IEmployeeService proxy = (IEmployeeService) handler.getProxy();
proxy.list();
proxy.save();
}
}
|
Salin selepas log masuk
2> Sesuaikan kaedah proksi untuk melaksanakan logik
Tambah logik pengesahan kebenaran untuk kaedah simpan objek sebenar
1 2 3 4 5 | private Object target;
public EmployeeInvocationHandler(Object target){
this.target = target;
}
|
Salin selepas log masuk
3> Kembalikan Selepas kaedah proksi
dilaksanakan, kelas proksi bernama: $ProxyX (dengan X ialah nombor siri, secara lalai kepada 0) dikembalikan secara dinamik oleh JDK itu.
1 2 3 4 5 6 7 8 9 10 11 12 13 | @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
if ( "save" .equals(name) && ! "admin" .equals(SessionHolder.getCurrentUser())){
throw new RuntimeException( "当前非admin用户,不能执行save操作" );
}
return method.invoke(target, args);
}
|
Salin selepas log masuk
Proksi: Kelas kawalan proksi dinamik, iaitu kelas induk bagi kelas $ProxyX yang dijana secara dinamik oleh JDK Fungsinya adalah seperti berikut: 1> dengan memanggil Kelas kaedah pembina kelas ProxyBuilder
1 2 3 4 5 6 7 | public Object getProxy(){
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
|
Salin selepas log masuk
2> mengembalikan tika kelas $ProxyX melalui kaedah ProxyInstance baharu
1 2 3 4 5 6 7 8 | private static Constructor<?> getProxyConstructor(Class<?> caller,
ClassLoader loader,
Class<?>... interfaces){
return proxyCache.sub(intf).computeIfAbsent(
loader,
(ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
);
}
|
Salin selepas log masuk
$Proxy0: Apabila kelas App ialah berjalan, kelas proksi yang dibina secara dinamik oleh JDK diwarisi kepada kelas Proksi
1 2 3 4 5 | public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) {
}
|
Salin selepas log masuk
Secara lalai, JVM tidak menyimpan objek kod bait kelas proksi yang dibuat secara dinamik Anda boleh mengkonfigurasi parameter proksi dalam kaedah utama untuk mengekalkan bytecode.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public class App {
public static void main(String[] args) {
System.setProperty( "jdk.proxy.ProxyGenerator.saveGeneratedFiles" , "true" );
System.out.println( "----------------真实对象--------------------" );
EmployeeServiceImpl employeeService = new EmployeeServiceImpl();
employeeService.list();
employeeService.save();
System.out.println( "----------------代理对象--------------------" );
SessionHolder.setCurrentUser( "dafei" );
EmployeeInvocationHandler handler =
new EmployeeInvocationHandler(employeeService);
IEmployeeService proxy = (IEmployeeService) handler.getProxy();
proxy.list();
proxy.save();
}
}
|
Salin selepas log masuk
Selepas pelaksanaan, Objek bytecode kelas proksi akan dijana dalam direktori akar projek.

Untuk memudahkan tafsiran, selepas mengalih keluar beberapa kaedah yang tidak perlu
kelas $Proxy0
1 2 3 4 | System.setProperty( "sun.misc.ProxyGenerator.saveGeneratedFiles" , "true" );
System.setProperty( "jdk.proxy.ProxyGenerator.saveGeneratedFiles" , "true" );
|
Salin selepas log masuk
Daripada kod sumber, ciri-ciri $Proxy0:
- 1> Mewarisi kelas Proksi dan melaksanakan antara muka IEmployeeService
- 2> Cerminkan kaedah simpan dan senaraikan antara muka IEmployeeService melalui blok statik untuk mendapatkan objek kaedahnya Kaedah
- 3>
- 4> Menulis semula kaedah senarai simpan antara muka IEmployeeService bergantung pada atribut h bagi kaedah Proxy.invoke kelas induk
Kebenaran didedahkan
Rajah berikut menunjukkan semua kelas yang mengambil bahagian dalam proksi dinamik:

Rajah berikut ialah rajah jujukan operasi bagi rajah di atas, ikut sahaja

Atas ialah kandungan terperinci Bagaimana untuk melaksanakan proksi dinamik JDK di Java. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!