preface
Proxy Pattern is one of the 23 commonly used design patterns of object-oriented software. The function of Proxy Pattern is to provide a Proxy for other objects to control the access to this object. Frankly speaking, it is a middleman or a daigou
As shown in the figure, the client sends a request to the interface. Normally, the impL of the interface implementation class is used to call the method to complete the request. However, with the addition of the proxy class, the method of the interface implementation class can be directly called by the proxy instance, and additional functions can be added.
Why add a layer of proxy classes? There are two advantages:
-
Hide and protect interface implementation classes and control direct access to interface implementation class objects
-
New functions can be added to improve the scalability of the system
JDK dynamic proxy is one of the most commonly used proxy mechanisms, mainly implemented through reflection. To implement JDK dynamic proxy, you must implement the InvocationHandler interface, which requires overriding the invoke() method, as shown in the parameter list below
-
Proxy: dynamic proxy class (the person who buys for you)
-
Method: indicates the method to be called
-
Args: Arguments to the method being called
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
Copy the code
practice
Use the JDK dynamic proxy step
- Create proxied interfaces and classes;
- Create an implementation class for the InvocationHandler interface that implements the proxy logic in the Invoke method;
- Create a Proxy object through the Proxy’s static method newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)
- Use proxy objects.
Let’s write an example to show the dynamic proxy call process
The first is the dynamic proxy interface, which writes a method and overrides it
public interface TicketProvider {
public void getTrainTicket(a);
public void getTrainTicket(int count);
}
Copy the code
Then there’s its implementation class
public class TicketProviderImpl implements TicketProvider {
@Override
public void getTrainTicket(a) {
System.out.println("Buying train tickets");
}
@Override
public void getTrainTicket(int count) {
System.out.println("Buying train tickets"+count+"Zhang"); }}Copy the code
Then there is the proxy class, which implements the InvocationHandler interface and overrides the Invoke method
public class TicketProviderProxy implements InvocationHandler {
// The actual object, that is, the shop that the daigou go to
private Object target;
// Constructor assignment
public TicketProviderProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("-----before in method invoke-----");
// The invoke() method finds the corresponding method by passing the actual object and parameters
method.invoke(target, args);
System.out.println("Add functionality to the method");
System.out.println("-----after in method invoke-----");
return null; }}Copy the code
Let’s test the proxy
public class ProxyTest {
public static void main(String[] args) {
// Interface implementation class
TicketProvider ticketProvider = new TicketProviderImpl();
//InvocationHandler implements the class using the constructor just defined
InvocationHandler handler = new TicketProviderProxy(ticketProvider);
//newProxyInstance () generates an instance of the dynamic proxy class, args[0] is the implementation class, args[1] is the interface class,
// Args [2] is the InvocationHandler implementation class, that is, the handler, which processes incoming real objects that need to be propped
TicketProvider ticketProviderProxy = (TicketProvider) Proxy.newProxyInstance(
ticketProvider.getClass().getClassLoader(),
ticketProvider.getClass().getInterfaces(),
handler);
// Pass in a parameter test
ticketProviderProxy.getTrainTicket();
ticketProviderProxy.getTrainTicket(5); }}Copy the code
You can see that the output is
As you can see, the proxy object created with the newProxyInstance() method can call the methods of the interface implementation class and also output the added methods. Dynamic proxy is then implemented.
The source code
Proxy.newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler H) generates the Proxy object, so we move on to the implementation of newProxyInstance:
public static Object newProxyInstance(ClassLoader loader, Class
[] interfaces, InvocationHandler h)
throws IllegalArgumentException
{
// check that h is not null
Objects.requireNonNull(h);
// Copy the interface's class object
finalClass<? >[] intfs = interfaces.clone();// Perform some security checks
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);
}
// Get the constructor of the proxy object. The constructor takes the parameters specified by constructorParams and takes the constants: private static final Class
[] constructorParams = { InvocationHandler.class };
finalConstructor<? > cons = cl.getConstructor(constructorParams);final InvocationHandler ih = h;
if(! Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run(a) {
cons.setAccessible(true);
return null; }}); }New Object[]{h}
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 newInternalError(t.toString(), t); }}catch (NoSuchMethodException e) {
throw newInternalError(e.toString(), e); }}Copy the code
GetProxyClass0 (loader, INTFS) to get the Class object of the proxy Class, and then get the constructor from the Class object, and then create the proxy object.
The use of reflection is in the generation of the dynamic proxy class, the newProxyInstance() method, which implements the second argument, the interface class. Inside the implementation of the interface method, the invoke() method of the handler is then invoked by reflection. The invoke() method validates the arguments passed through its internal method.invoke() method and then invokes the methods of the interface implementation class based on the arguments.
The same is true of SpringAOP, except that cglib dynamic proxies are used to proxy objects that do not implement interfaces. Beans, for example, are proxied using Cglib.
My wechat official number: Java Architect Advanced programming focus on sharing Java technology dry products, including JVM, SpringBoot, SpringCloud, database, architecture design, interview questions, e-books, etc., looking forward to your attention!