问题
I've been studying the proxy classes and I don't get the whole idea of designing it.
From what I learned so far it is a wrapper object that can control access to the original object. But if we want to control it why can't we design the original class with those access mechanisms.
I read that these proxy objects are useful for tracing method calls, routing method calls to remote servers.
But I searched for a question that would explain this to me in java but I didn't find any.
I'll illustrate the code for a method tracing program which was in the book that I was referring.
public class ProxyTest {
public static void main(String[] args) throws ClassNotFoundException {
var elements = new Object[1000];
// fill elements with proxies for the integers 1 . . . 1000
for (int i = 0; i < elements.length; i++) {
Integer value = i + 1;
var handler = new TraceHandler(value);
Object proxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Comparable.class}, handler);
elements[i] = proxy;
}
// construct a random integer
Integer key = new Random().nextInt(elements.length) + 1;
// search for the key
int result = Arrays.binarySearch(elements, key);
// print match if found
if (result >= 0)
System.out.println(elements[result]);
}
}
/**
* An invocation handler that prints out the method name and parameters, then
* invokes the original method
**/
class TraceHandler implements InvocationHandler{
private Object target;
/**
* Constructs a TraceHandler
* @param t the implicit parameter of the method call
**/
public TraceHandler(Object t){
target = t;
}
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
// print implicit argument
System.out.print(target);
// print method name
System.out.print("." + m.getName() + "(");
// print explicit arguments
if (args != null){
for (int i = 0; i < args.length; i++){
System.out.print(args[i]);
if (i < args.length - 1)
System.out.print(", ");
}
}
System.out.println(")");
// invoke actual method
return m.invoke(target, args);
}
}
Can someone please point out to me what is going on with this proxy design pattern, what does it do in this particular program and its advantages?
回答1:
Very wide question:
there are several different options around this:
- Proxy pattern - a way of manipulation of one object by another thought third one
- also lazy loading could also be part of that discussion
- finally most popular - dynamic proxies and code enhancement after compilation. A lot of top famous frameworks work that way (e.g. spring, hibernate, selenium). That allows achieve more readable code and increase its quality (less bugs). Dynamic proxies bring dynamic lazy init possibility, code enhancements, more declarative code
E.g. spring transactional annotation in work
class UsersDao {
@Transactional
public void method() {
// DO SOME STUFF
}
}
Spring is creating dynamic proxy that "extends" UsersDao but realy does redirect all method invocations to a specific object that implement InvocationHandler interface
example of InvocationHandler
public interface InvocationHandler {
/**
* Processes a method invocation on a proxy instance and returns
* the result. This method will be invoked on an invocation handler
* when a method is invoked on a proxy instance that it is
* associated with.
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
Inside "invoke" spring is doing something like that:
Transaction tx = TransactionManager.createTransaction()
try {
// execute method
method.invoke();
tx.commit()
}
catch (Exception e) {
// execute callback
tx.rollback()
}
finally () {
// do clean up
// tx.flush()
}
That is achieved by a magic of dynamic proxies
Tools for dynamic proxying:
https://github.com/cglib/cglib/wiki
https://www.javassist.org/
https://docs.oracle.com/javase/6/docs/api/java/lang/reflect/Proxy.html
回答2:
Proxy classes are extremely useful when you are dealing with code from other teams or third parties and can be used for all sorts of diagnostics or enhanced operations.
I've used them with database vendor JDBC Connection jars and fault tolerant Remote server calls where the proxy deals with errors or re-connections and the application logic is cleaner.
Your TraceHandler could be a self contained class by adding the method:
@SuppressWarnings("unchecked")
public static <T> T create(final T impl, final Class<?>... interfaces)
{
final Class<?> cls = impl.getClass();
return (T)Proxy.newProxyInstance(cls.getClassLoader(), interfaces, new TraceHandler(impl));
}
Then you could use to TraceHandler to monitor / log ANY interface used by your application:
SomeObject x = TraceHandler.create(x, SomeObject.class);
FileVisitor myvisitor = TraceHandler.create(visitor, FileVisitor.class)
Hopefully it will then be clearer how proxy classes can be helpful, and an example as follows:
public class ProxyTest
{
public static void main(String[] args) {
var elements = new Integer[1000];
for (int i = 0; i < elements.length; i++) {
elements[i] = Integer.valueOf(i);
}
// construct a random integer
Integer key = new Random().nextInt(elements.length) + 1;
Comparator<Integer> comparator = Integer::compare;
Comparator<Integer> comparator2 = TraceHandler.create(comparator, Comparator.class);
// search for the key
System.out.println("Search for "+key+" without proxy:");
int result = Arrays.binarySearch(elements, key, comparator);
// print match if found
if (result >= 0)
System.out.println(elements[result]);
System.out.println("Search "+key+" with proxy:");
int result2 = Arrays.binarySearch(elements, key, comparator2);
if (result2 >= 0)
System.out.println(elements[result]);
}
}
回答3:
Let's remove all the hocus-pocus of nomenclatures and just understand it in the real world using an example-
- Bob and his 10,000 friends go to watch a soccer match.
- Due to security reasons, it is mandatory for the guards at the entry gates to check tickets of all the people and then let them enter.
- There are also pizza and beer stalls inside the stadium but you can only buy them if you have a valid ticket.
- Now since we know that the guards did there duty right and no one without a valid ticket could enter the stadium, the pizza and beer vendors don't have to check tickets of every person themselves.
Now let's see-
- The guards are acting as a proxy layer between the stadium stalls and the rest of the world.
- If the guards were not there, the pizza and beer vendors would all need to write the ticket checking code or at least make a call to that piece of code themselves. This might cause problems in the future because what if a new stall opens in the stadium and they forget to call the piece of code which checks tickets of the people.
Advantages-
- Proxies allow you to do something with the object, using the object that you are using while allowing you to write a cleaner, SOLID code with ease.
- Had this been a real-world scenario, neither would have I told the guards about the stalls in the stadium nor would have I told the vendors about the whole ticketing system of the stadium. These 2 operations could grow independently on their own.
These 2 articles give a good intro about proxies-
- https://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch08s06.html
- https://spring.io/blog/2012/05/23/transactions-caching-and-aop-understanding-proxy-usage-in-spring
回答4:
Proxy classes act as an interface to access an object, usually adding additional functionality.
They are somewhat similar to Wrapper classes but they don't contain the object that they interface with and they are needed when you have to add additional behaviour to a class which you can't change the code of.
来源:https://stackoverflow.com/questions/61615058/what-is-the-actual-purpose-of-designing-a-proxy-class