A few days ago I wrote Java Annotations that you can learn because annotations are actually part of reflection technology, and THEN I thought, another common concept in reflection technology is dynamic proxy, so I’ll just write another blog post about dynamic proxy.

Let’s analyze the word agent first.

The agent

Proxy is translated from English Proxy. We have seen agents in life, probably the most common is the friends in the circle of friends to sell the mask of the students.

They take the product from the manufacturer, advertise it on their wechat moments, and then sell it to acquaintances.

In theory, customers can buy products directly from the manufacturer, but in reality, there are few such sales model. Generally, manufacturers entrust agents to sell, and customers deal with agents, but not directly with the actual producer of the product.

So the agent has a kind of middleman flavor.

Next, let’s talk about the agent pattern in software.

The proxy pattern

Proxy pattern is a common design pattern in object-oriented programming.

This is a common UML diagram of a common proxy pattern.

Note the following points: 1. Users only care about interface functions, not who provides them. In the figure above, the interface is Subject. 2. The real implementer of the interface is the RealSubject shown above, but it does not interact directly with the user, but through a proxy. 3. The Proxy is the Proxy in the figure above, and because it implements the Subject interface, it can contact users directly. 4. When the user invokes the Proxy, the Proxy invokes the RealSubject internally. So, proxies are intermediaries that can enhance RealSubject operations.

If it’s hard to understand, let me give you an example. It is worth noting that agents can be divided into static agents and dynamic agents. Let’s start with static proxies.

Static agent

When we go to the cinema to see a movie, are there often advertisements at the beginning of the movie?

The movie is commissioned by the movie company to be shown in the cinema, but the cinema can generate some of its own economic income when the movie is shown, such as selling popcorn, coke and so on, and then play some advertisements at the beginning and end of the movie.

Now let’s do the simulation in code.

The first step is to have an interface, and a generic interface is the foundation of proxy pattern implementation. This interface, which we’ll call Movie, represents the ability to play movies.


package com.frank.test;

public interface Movie {
    void play();
}
Copy the code

Then, we’ll have a class that actually implements the Movie interface, and a proxy class that just implements the interface.

package com.frank.test; public class RealMovie implements Movie { @Override public void play() { // TODO Auto-generated method stub System.out.println(" You are watching the shawshank Redemption "); }}Copy the code

This is the real movie. It implements the Movie interface, and the Movie starts playing when the play() method is called. What about a Proxy?

package com.frank.test; public class Cinema implements Movie { RealMovie movie; public Cinema(RealMovie movie) { super(); this.movie = movie; } @Override public void play() { guanggao(true); movie.play(); guanggao(false); } public void guanggao(Boolean isStart){if (isStart){system.out.println (" the movie is about to start. ); } else {system.out.println (" The movie will end soon, 10% off popcorn, Coke, chewing gum, buy it home and eat it!" ); }}}Copy the code

Cinema is the Proxy Proxy object and it has a play() method. But when the Play () method is called, it does something of interest, which is advertising. Now, let’s write the test code.

package com.frank.test; public class ProxyTest { public static void main(String[] args) { RealMovie realmovie = new RealMovie(); Movie movie = new Cinema(realmovie); movie.play(); }}Copy the code

Then observe the results:

The movie is about to start, 10% off popcorn, coke, chewing gum, come and buy it! You are watching the movie "Shawshank Redemption" the movie will end soon, popcorn, coke, chewing gum 10% off, buy home to eat!Copy the code

You can now see that the proxy pattern can add and enhance functionality by extending the proxy class without modifying the proxied object. It is important to note that the proxied and proxied classes should implement an interface or inherit from a class in common.

The above is static proxy content, why is it called static? Because its type is predetermined, like the Cinema class in the code above. The next step is dynamic proxy.

A dynamic proxy

Since it is a proxy, so it and static proxy function and purpose is no difference, the only difference is dynamic and static difference.

So where is this dynamic in dynamic proxy?

In the previous section, the Cinema class was the proxy. We need to manually write code to make Cinema implement the Movie interface. In the dynamic proxy, we can automatically create an in-memory proxy to implement the Movie interface when the program runs, without defining the Cinema class. That’s why it’s called dynamic.

Maybe the concept is abstract. Now give an example.

Suppose you have a big shopping mall with lots of counters, one of which sells Maotai liquor. We simulate the code.

package com.frank.test;

public interface SellWine {

     void mainJiu();

}Copy the code

SellWine is an interface that you can understand as a license for selling wine.

package com.frank.test; public class MaotaiJiu implements SellWine { @Override public void mainJiu() { // TODO Auto-generated method stub Println (" I sell Maotai liquor." ); }}Copy the code

Then create a class called MaotaiJiu, which, yes, means Maotai.

We also need a counter to sell alcohol:

package com.frank.test; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class GuitaiA implements InvocationHandler { private Object pingpai; public GuitaiA(Object pingpai) { this.pingpai = pingpai; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// TODO auto-generated method stub system.out.println (" sales start counter is:  "+this.getClass().getSimpleName()); method.invoke(pingpai, args); System.out.println(" end of sale "); return null; }}Copy the code

GuitaiA implements a class called InvocationHandler. What does this class mean? Don’t panic. I’ll explain later.

After that, we can sell wine.

package com.frank.test; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class Test { public static void main(String[] args) { // TODO Auto-generated method stub MaotaiJiu maotaijiu = new MaotaiJiu(); InvocationHandler jingxiao1 = new GuitaiA(maotaijiu); SellWine dynamicProxy = (SellWine) Proxy.newProxyInstance(MaotaiJiu.class.getClassLoader(), MaotaiJiu.class.getInterfaces(), jingxiao1); dynamicProxy.mainJiu(); }}Copy the code

Here, we have a new concept, it doesn’t matter, forget about it, let’s see the result.

Sales start at: GuitaiA. I sell Maotai. End of the salesCopy the code

See, I didn’t implement a proxy class for the SellWine interface as a static proxy does, but it still ends up doing the same thing. The difference is what makes a dynamic proxy “dynamic” as discussed earlier.

Dynamic proxy syntax

Relax, let’s start with the syntax, which is very simple.

Dynamic code involves a very important class Proxy. It is through the static method newProxyInstance of Proxy that the Proxy is created dynamically.

Proxy

public static Object newProxyInstance(ClassLoader loader, Class<? >[] interfaces, InvocationHandler h)Copy the code

The following are the meanings of its three parameters.

  • Loader is naturally a class loader
  • Interfaces that the interfaces code will use to proxy
  • H An InvocationHandler object

The InvocationHandler should be new to beginners, and I’ll get to that in a minute.

InvocationHandler

InvocationHandler is an interface. The official documentation explains that each instance of a proxy has an InvocationHandler implementation class associated with it. If a proxy method is called, the proxy notifies and forwards to the internal InvocationHandler implementation class. It decides what to do.

public interface InvocationHandler {

    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}
Copy the code

Inside the InvocationHandler is just an invoke() method that determines how to handle method calls passed by the proxy.

  • Proxy Proxy object
  • Method Method called by the proxy object
  • Parameters in the method called by args

Because proxies dynamically generate proxies that call the InvocationHandler implementation class, the InvocationHandler is the actual executor.

public class GuitaiA implements InvocationHandler { private Object pingpai; public GuitaiA(Object pingpai) { this.pingpai = pingpai; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// TODO auto-generated method stub system.out.println (" sales start counter is:  "+this.getClass().getSimpleName()); method.invoke(pingpai, args); System.out.println(" end of sale "); return null; }}Copy the code

GuitaiA is where the wine is actually sold.

Now, we increase the difficulty, we not only want to sell Maotai, but also want to sell Wuliangye.

package com.frank.test; public class Wuliangye implements SellWine { @Override public void mainJiu() { // TODO Auto-generated method stub System.out.println(" I sell wuliangye." ); }}Copy the code

The Wuliangye class also implements SellWine, which means that it has a license to sell alcohol and also sells it on GuitaiA.

public class Test { public static void main(String[] args) { // TODO Auto-generated method stub MaotaiJiu maotaijiu = new MaotaiJiu(); Wuliangye wu = new Wuliangye(); InvocationHandler jingxiao1 = new GuitaiA(maotaijiu); InvocationHandler jingxiao2 = new GuitaiA(wu); SellWine dynamicProxy = (SellWine) Proxy.newProxyInstance(MaotaiJiu.class.getClassLoader(), MaotaiJiu.class.getInterfaces(), jingxiao1); SellWine dynamicProxy1 = (SellWine) Proxy.newProxyInstance(MaotaiJiu.class.getClassLoader(), MaotaiJiu.class.getInterfaces(), jingxiao2); dynamicProxy.mainJiu(); dynamicProxy1.mainJiu(); }}Copy the code

Let’s look at the results:

Sales start at: GuitaiA. I sell Maotai. End of Sale Start of sale Counter is: GuitaiA I'm selling Wuliangye. End of the salesCopy the code

What’s the difference between dynamicProxy and dynamicProxy1? They are dynamically generated agents, they are salespeople, and they have technical certificates to sell wine.

I am now expanding the business of the market, in addition to selling alcohol, but also to sell cigarettes.

First, also create an interface as a license to sell cigarettes.

package com.frank.test;

public interface SellCigarette {
    void sell();
}
Copy the code

Then, what kind of cigarettes? I’m from Hunan, so the Lotus King is fine.

public class Furongwang implements SellCigarette { @Override public void sell() { // TODO Auto-generated method stub System.out.println(" Selling the authentic Lotus King, you can scan the barcode to verify." ); }}Copy the code

Then test again to verify:

package com.frank.test; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class Test { public static void main(String[] args) { // TODO Auto-generated method stub MaotaiJiu maotaijiu = new MaotaiJiu(); Wuliangye wu = new Wuliangye(); Furongwang fu = new Furongwang(); InvocationHandler jingxiao1 = new GuitaiA(maotaijiu); InvocationHandler jingxiao2 = new GuitaiA(wu); InvocationHandler jingxiao3 = new GuitaiA(fu); SellWine dynamicProxy = (SellWine) Proxy.newProxyInstance(MaotaiJiu.class.getClassLoader(), MaotaiJiu.class.getInterfaces(), jingxiao1); SellWine dynamicProxy1 = (SellWine) Proxy.newProxyInstance(MaotaiJiu.class.getClassLoader(), MaotaiJiu.class.getInterfaces(), jingxiao2); dynamicProxy.mainJiu(); dynamicProxy1.mainJiu(); SellCigarette dynamicProxy3 = (SellCigarette) Proxy.newProxyInstance(Furongwang.class.getClassLoader(), Furongwang.class.getInterfaces(), jingxiao3); dynamicProxy3.sell(); }}Copy the code

Then, look at the results:

Sales start at: GuitaiA. I sell Maotai. End of Sale Start of sale Counter is: GuitaiA I'm selling Wuliangye. End of Sale The beginning of sale counter is: GuitaiA sells authentic Hibiscus King, which can be verified by scanning the barcode. End of the salesCopy the code

The results were in line with expectations. If you look at the code carefully, the same proxy.newProxyinstance () method generates the implementation class Proxy of SellWine and SellCigarette interfaces, which is the magic of dynamic Proxy.

The secret of dynamic proxy

For those of you who are interested in why proxies can dynamically generate proxies of different interface types, my guess is that they must generate an interface instance dynamically through the incoming interface and then reflection. For example, if SellWine is an interface, then proxy.newProxyInstance () must have one inside it


new SellWine();Copy the code

This code does the same thing, but it’s created by reflection. So is this the case? Just look at their source code. It should be noted that the source code I am currently viewing is version 1.8.

public static Object newProxyInstance(ClassLoader loader, Class<? >[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); final Class<? >[] intfs = interfaces.clone(); /* * Look up or generate the designated proxy class. */ Class<? > cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try { 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); }}Copy the code

NewProxyInstance does create an instance, which is generated by reflection from the constructor of the cl Class file. Cl is obtained by getProxyClass0().

private static Class<? > getProxyClass0(ClassLoader loader, Class<? >... interfaces) { if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } // If the proxy class defined by the given loader implementing // the given interfaces exists, this will simply return the cached copy; // otherwise, it will create the proxy class via the ProxyClassFactory return proxyClassCache.get(loader, interfaces); }Copy the code

Get it directly from the cache. If not, the comment says it will be generated by ProxyClassFactory.

/** * A factory function that generates, defines and returns the proxy class given * the ClassLoader and array of interfaces. */ private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<? >[], Class<? >> {private static final String proxyClassNamePrefix = "$Proxy"; // next number to use for generation of unique proxy class names private static final AtomicLong nextUniqueNumber = new AtomicLong(); @Override public Class<? > apply(ClassLoader loader, Class<? >[] interfaces) { Map<Class<? >, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); for (Class<? > intf : interfaces) { /* * Verify that the class loader resolves the name of this * interface to the same Class object. */ Class<? > interfaceClass = null; try { interfaceClass = Class.forName(intf.getName(), false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass ! = intf) { throw new IllegalArgumentException( intf + " is not visible from class loader"); } /* * Verify that the Class object actually represents an * interface. */ if (! interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } /* * Verify that this interface is not a duplicate. */ if (interfaceSet.put(interfaceClass, Boolean.TRUE) ! = null) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } } String proxyPkg = null; // package to define proxy class in int accessFlags = Modifier.PUBLIC | Modifier.FINAL; /* * Record the package of a non-public proxy interface so that the * proxy class will be defined in the same package. Verify that * all non-public proxy interfaces are in the same package. */ for (Class<? > intf : interfaces) { int flags = intf.getModifiers(); if (! Modifier.isPublic(flags)) { accessFlags = Modifier.FINAL; String name = intf.getName(); int n = name.lastIndexOf('.'); String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); if (proxyPkg == null) { proxyPkg = pkg; } else if (! pkg.equals(proxyPkg)) { throw new IllegalArgumentException( "non-public interfaces from different packages"); } } } if (proxyPkg == null) { // if no non-public proxy interfaces, use com.sun.proxy package proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } /* * Choose a name for the proxy class to generate. */ long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num; /* * Generate the specified proxy class. */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { /* * A ClassFormatError here means that (barring bugs in the * proxy class generation code) there was some other * invalid aspect of the arguments supplied to the proxy * class creation (such as virtual machine limitations * exceeded). */ throw new IllegalArgumentException(e.toString()); }}}Copy the code

The class annotation says that the proxy class is generated using factory methods from the specified ClassLoader and interface array. Then the name of the proxy class is:

// Proxy class prefix is "$Proxy", private static final String proxyClassNamePrefix = "$Proxy"; long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num;Copy the code

So, the dynamically generated Proxy class name is the package name +$Proxy+ ID number.

The generation process, the core code is as follows:

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);


return defineClass0(loader, proxyName,
                    proxyClassFile, 0, proxyClassFile.length);
Copy the code

These two methods, which I didn’t follow up on, defineClass0() are even a native method. Dynamic proxy creation is all we need to know.

Now we need to do some validation. I want to check that the name of the dynamically generated Proxy class is not the package name +$Proxy+ ID number.

public class Test { public static void main(String[] args) { // TODO Auto-generated method stub MaotaiJiu maotaijiu = new MaotaiJiu(); Wuliangye wu = new Wuliangye(); Furongwang fu = new Furongwang(); InvocationHandler jingxiao1 = new GuitaiA(maotaijiu); InvocationHandler jingxiao2 = new GuitaiA(wu); InvocationHandler jingxiao3 = new GuitaiA(fu); SellWine dynamicProxy = (SellWine) Proxy.newProxyInstance(MaotaiJiu.class.getClassLoader(), MaotaiJiu.class.getInterfaces(), jingxiao1); SellWine dynamicProxy1 = (SellWine) Proxy.newProxyInstance(MaotaiJiu.class.getClassLoader(), MaotaiJiu.class.getInterfaces(), jingxiao2); dynamicProxy.mainJiu(); dynamicProxy1.mainJiu(); SellCigarette dynamicProxy3 = (SellCigarette) Proxy.newProxyInstance(Furongwang.class.getClassLoader(), Furongwang.class.getInterfaces(), jingxiao3); dynamicProxy3.sell(); System.out.println("dynamicProxy class name:"+dynamicProxy.getClass().getName()); System.out.println("dynamicProxy1 class name:"+dynamicProxy1.getClass().getName()); System.out.println("dynamicProxy3 class name:"+dynamicProxy3.getClass().getName()); }}Copy the code

The results are as follows:

Sales start at: GuitaiA. I sell Maotai. End of Sale Start of sale Counter is: GuitaiA I'm selling Wuliangye. End of Sale The beginning of sale counter is: GuitaiA sells authentic Hibiscus King, which can be verified by scanning the barcode. DynamicProxy class name:com.sun. Proxy.$Proxy0 dynamicProxy1 class name:com.sun name:com.sun.proxy.$Proxy1Copy the code

The proxy class name of the SellWine interface is com.sun.proxy.$Proxy0 The proxy class name of the SellCigarette interface is com.sun.proxy.$Proxy1

This shows that the dynamically generated Proxy class is the same package as the proxy.

Here is a diagram to remind you of the roles involved in dynamic proxy.



The red box$Proxy0It’s dynamically generated by Proxy.

$Proxy0Implements the interface to be proxied.

$Proxy0By calling theInvocationHandlerTo carry out the mission.

The role of agency

Some students may ask, you’ve learned about agents, but what are they good for?

The main function is to enhance the function without modifying the source code of the proxy object.

This is often seen in AOP faceted programming.

In the software industry, AOP for the abbreviation of Aspect Oriented Programming, meaning: section-oriented Programming, through pre-compilation and runtime dynamic proxy to achieve unified maintenance of program functions of a technology. AOP is a continuation of OOP, a hot topic in software development, and an important content in Spring framework. It is a derivative paradigm of functional programming. Using AOP, each part of the business logic can be isolated, thus reducing the degree of coupling between each part of the business logic, improving the reusability of the program, and improving the efficiency of development.

Main functions logging, performance statistics, security control, transaction processing, exception handling and so on.

The above reference is baidu Encyclopedia for AOP explanation, as for, how to use proxy to log function, performance statistics and so on, this we can refer to the relevant source of AOP, and then carefully consider.

Again, a lot of you might be wondering, when do I use a proxy?

It depends on what you want to do. You’ve learned the syntax, the rest depends on the business requirements. This is suitable for a framework that implements logging.

This concludes the discussion of static and dynamic proxies.

conclusion

  1. Agents are classified into static agents and dynamic agents.
  2. Static proxy, proxy class need to write their own code.
  3. Dynamic Proxy, the Proxy class is generated through proxy.newinstance ().
  4. Both the agent and the proxied implement two interfaces, whether static or dynamic. Their essence is interface oriented programming.
  5. The difference between static and dynamic proxies is whether the developer has to define the Proxy class himself.
  6. The dynamic Proxy generates the Proxy class dynamically through the Proxy, but it also specifies an implementation class for InvocationHandler.
  7. The proxy pattern is essentially designed to enhance the functionality of existing code.