Dynamic proxy (the core principle of spring AOP)

Article directory

1, Proxy mode

The agent mode is also one of the 23 re design modes. Let's learn:

Proxy mode definition:
A proxy object is provided to the target object, and the proxy object controls the reference to the target object.
Generally speaking, agency mode is the common intermediary in our life.

                     . From official purchase, in programming, this process refers to obtaining this function directly from the class providing this function.
                           .

Objective:
1. Through the introduction of proxy object to indirectly access the target object, to prevent the unnecessary complexity of the system caused by direct access to the target object; function providing classes can focus more on the implementation of the main functions.

                             .

2. Enhance the original business by proxy object;

                             .

General class diagram of agent mode:

Subject
Abstract object: it can be an abstract class or an interface. Abstract topic is a common business type, which declares the common interface between real object and proxy object.
For example, selling train tickets is a public interface. The ticket office of China Railway Administration is the real object, and the small supermarket next to the ticket office is the agent object.

RealSubject
Real object: also known as the agent object, which is the specific executor of business logic. Must implement the specification (Interface) given by abstract object

Proxy
Proxy object: also called proxy class, it must implement the specification (Interface) given by the abstract object. It is responsible for the application of the real role, limiting all the methods defined by the interface to the real object implementation, and pre-processing and post-processing before and after the specific real object processing. For example: the agent does not produce train tickets, but only the porters who sell train tickets.

Be careful:
1. Real object and proxy object must implement common interface
2. A proxy class can represent multiple proxies or real objects, so which specific subject role a proxy class represents is determined by the scene class. Generally, an interface only needs one proxy class, and the specific proxy implementation class is determined by the high-level module.

2, Static and dynamic agents:

According to the period of agent creation, it can be divided into static agent and dynamic agent

2.1 static agent

A static agent is a source code created by a programmer or generated automatically by a specific tool when it is compiled. The agent class. Class file has been created before the programmer runs it

The following code:

Interface class (abstract object):

Real object:

Proxy object:

package test;
//Small shops, supermarkets. In addition to selling train tickets, it also provides services such as selling goods, cigarettes and alcohol. If you buy a fake train ticket, you can return it
//Proxy object: the proxy object contains real objects, enhances the service of real objects, and inherits from the same interface with real objects

public class Store implements TrainTicketFactory{
    //1. Include real objects
    public TrainTicketFactory factory;

    //You can act for yourself
    public Store(){
        this.factory = new Store();
    }

    //Agency of train tickets for passengers
    public Store(TrainTicketFactory factory){
        this.factory = factory;
    }

    @Override
    public void sale_TrainTickets(String number) {
        before();
        factory.sale_TrainTickets(number);
        after();
    }

    //Pretreatment. (you can also buy alcohol and tobacco)
    private void before() {
        System.out.println("Brother to pack cigarettes, the train will inevitably be lonely!");
    }

    //Postprocessing. (if you find fake tickets, cigarettes or wine, you can go to a small store to help you deal with them.)
    private void after() {
        System.out.println("Excuse me, brother. I've got a pack of fake cigarettes for you. I'll change them for you again");
    }
}

Test class:

Result:

Advantages and disadvantages of static agent:
Advantage:
   business classes only need to pay attention to the business logic itself to ensure the reusability of business classes. This is the common advantage of agents. The agent makes the main() method not need to know what the implementation class is and how to do it, while the client only needs to know the agent (uncoupling). For the above client code, TrainTicketFactory Factory = new realSaleFactory();
Store store = new Store(Factory);
You can apply the factory method to hide it.

Disadvantages:
The proxy class implements the same interface as the delegate class, and the proxy class implements the same method through the delegate class. This leads to a lot of code duplication. If a method is added to the interface, all the proxy classes need to implement this method in addition to all the implementation classes. It increases the complexity of code maintenance.
   proxy objects only serve one type of object, if you want to serve multiple types of objects. It is bound to proxy for every object. Static proxy is not competent when the program scale is a little large. The above code only provides a proxy for the access of the TrainTicketFactory class, but if you want to provide a proxy for other classes, such as realsalefactory [b], you need to add the proxy class of the proxy store [b] again. As a result, the number of objects, code and performance continue to decline.
   due to this disadvantage of static agents, dynamic agents are needed.

2.2 dynamic agent

                   . The java.lang.reflect.Proxy class and the InvocationHandler interface are mainly used. The dynamic Proxy object is created by the Proxy, and the InvocationHandler specifies what the dynamic Proxy object does.

The principle of dynamic agent is: when customers have needs, whether it is to buy train tickets, cigarettes and alcohol, or to buy inflatable dolls, as long as you have needs. According to your dynamic needs, dynamic agent class will help you find a small agent store that can buy all kinds of goods on behalf of you, and then help you to purchase on behalf of you.

Difference: the essential difference between dynamic agent and static agent is. The proxy object changes in real time.

Main methods:
Proxy:
public static InvocationHandler getInvocationHandler(Object proxy) throws IllegalArgumentException
Returns the call handler for the specified proxy instance.

InvocationHandler::
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
Process the method call on the proxy instance and return the result. When a method is called on the proxy instance associated with the method, it is called on the calling handler.

                      . So we think of a way through a proxy class to complete all the proxy functions, then we need to use dynamic proxy
   in the above example, an agent can only proxy one type, and the compiler has already determined the object to be proxied. While dynamic agent is to realize dynamic agent through reflection mechanism at runtime, and it can proxy various types of objects.

To implement the dynamic proxy mechanism in Java, you need to:
java.lang.reflect.InvocationHandler interface
And java.lang.reflect.Proxy classes.

Definition of java.lang.reflect.InvocationHandler interface:
The invoke method is used to enhance the business

package java.lang.reflect;
public interface InvocationHandler {
/**
Parameter interpretation:
	Object proxy Represented object
	Method method Method to call
	Object[] args Parameters required for method call
*/
    public Object (Object proxy, Method method, Object[] args)
        throws Throwable;
}

Definition of java.lang.reflect.Proxy class:

public class Proxy implements java.io.Serializable {

    private static final long serialVersionUID = -2222568056686623797L;

    /** parameter types of a proxy class constructor */
    private static final Class<?>[] constructorParams =
        { InvocationHandler.class };

    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

    /**
     * the invocation handler for this proxy instance.
     * @serial
     */
    protected InvocationHandler h;

    /**
     * Prohibits instantiation.
     */
    private Proxy() {
    }

    /**
     * Constructs a new {@code Proxy} instance from a subclass
     * (typically, a dynamic proxy class) with the specified value
     * for its invocation handler.
     *
     * @param  h the invocation handler for this proxy instance
     *
     * @throws NullPointerException if the given invocation handler, {@code h},
     *         is {@code null}.
     */
    protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }

    /**
     * Returns the {@code java.lang.Class} object for a proxy class
     * given a class loader and an array of interfaces.  The proxy class
     * will be defined by the specified class loader and will implement
     * all of the supplied interfaces.  If any of the given interfaces
     * is non-public, the proxy class will be non-public. If a proxy class
     * for the same permutation of interfaces has already been defined by the
     * class loader, then the existing proxy class will be returned; otherwise,
     * a proxy class for those interfaces will be generated dynamically
     * and defined by the class loader.
     *
     * <p>There are several restrictions on the parameters that may be
     * passed to {@code Proxy.getProxyClass}:
     *
     * <ul>
     * <li>All of the {@code Class} objects in the
     * {@code interfaces} array must represent interfaces, not
     * classes or primitive types.
     *
     * <li>No two elements in the {@code interfaces} array may
     * refer to identical {@code Class} objects.
     *
     * <li>All of the interface types must be visible by name through the
     * specified class loader.  In other words, for class loader
     * {@code cl} and every interface {@code i}, the following
     * expression must be true:
     * <pre>
     *     Class.forName(i.getName(), false, cl) == i
     * </pre>
     *
     * <li>All non-public interfaces must be in the same package;
     * otherwise, it would not be possible for the proxy class to
     * implement all of the interfaces, regardless of what package it is
     * defined in.
     *
     * <li>For any set of member methods of the specified interfaces
     * that have the same signature:
     * <ul>
     * <li>If the return type of any of the methods is a primitive
     * type or void, then all of the methods must have that same
     * return type.
     * <li>Otherwise, one of the methods must have a return type that
     * is assignable to all of the return types of the rest of the
     * methods.
     * </ul>
     *
     * <li>The resulting proxy class must not exceed any limits imposed
     * on classes by the virtual machine.  For example, the VM may limit
     * the number of interfaces that a class may implement to 65535; in
     * that case, the size of the {@code interfaces} array must not
     * exceed 65535.
     * </ul>
     *
     * <p>If any of these restrictions are violated,
     * {@code Proxy.getProxyClass} will throw an
     * {@code IllegalArgumentException}.  If the {@code interfaces}
     * array argument or any of its elements are {@code null}, a
     * {@code NullPointerException} will be thrown.
     *
     * <p>Note that the order of the specified proxy interfaces is
     * significant: two requests for a proxy class with the same combination
     * of interfaces but in a different order will result in two distinct
     * proxy classes.
     *
     * @param   loader the class loader to define the proxy class
     * @param   interfaces the list of interfaces for the proxy class
     *          to implement
     * @return  a proxy class that is defined in the specified class loader
     *          and that implements the specified interfaces
     * @throws  IllegalArgumentException if any of the restrictions on the
     *          parameters that may be passed to {@code getProxyClass}
     *          are violated
     * @throws  SecurityException if a security manager, <em>s</em>, is present
     *          and any of the following conditions is met:
     *          <ul>
     *             <li> the given {@code loader} is {@code null} and
     *             the caller's class loader is not {@code null} and the
     *             invocation of {@link SecurityManager#checkPermission
     *             s.checkPermission} with
     *             {@code RuntimePermission("getClassLoader")} permission
     *             denies access.</li>
     *             <li> for each proxy interface, {@code intf},
     *             the caller's class loader is not the same as or an
     *             ancestor of the class loader for {@code intf} and
     *             invocation of {@link SecurityManager#checkPackageAccess
     *             s.checkPackageAccess()} denies access to {@code intf}.</li>
     *          </ul>

     * @throws  NullPointerException if the {@code interfaces} array
     *          argument or any of its elements are {@code null}
     */
    @CallerSensitive
    public static Class<?> getProxyClass(ClassLoader loader,
                                         Class<?>... interfaces)
        throws IllegalArgumentException
    {
        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        return getProxyClass0(loader, intfs);
    }

    /*
     * Check permissions required to create a Proxy class.
     *
     * To define a proxy class, it performs the access checks as in
     * Class.forName (VM will invoke ClassLoader.checkPackageAccess):
     * 1. "getClassLoader" permission check if loader == null
     * 2. checkPackageAccess on the interfaces it implements
     *
     * To get a constructor and new instance of a proxy class, it performs
     * the package access check on the interfaces it implements
     * as in Class.getConstructor.
     *
     * If an interface is non-public, the proxy class must be defined by
     * the defining loader of the interface.  If the caller's class loader
     * is not the same as the defining loader of the interface, the VM
     * will throw IllegalAccessError when the generated proxy class is
     * being defined via the defineClass0 method.
     */
    private static void checkProxyAccess(Class<?> caller,
                                         ClassLoader loader,
                                         Class<?>... interfaces)
    {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            ClassLoader ccl = caller.getClassLoader();
            if (VM.isSystemDomainLoader(loader) && !VM.isSystemDomainLoader(ccl)) {
                sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
            }
            ReflectUtil.checkProxyPackageAccess(ccl, interfaces);
        }
    }

    /**
     * Generate a proxy class.  Must call the checkProxyAccess method
     * to perform permission checks before calling this.
     */
    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);
    }

    /*
     * a key used for proxy class with 0 implemented interfaces
     */
    private static final Object key0 = new Object();

    /*
     * Key1 and Key2 are optimized for the common use of dynamic proxies
     * that implement 1 or 2 interfaces.
     */

    /*
     * a key used for proxy class with 1 implemented interface
     */
    private static final class Key1 extends WeakReference<Class<?>> {
        private final int hash;

        Key1(Class<?> intf) {
            super(intf);
            this.hash = intf.hashCode();
        }

        @Override
        public int hashCode() {
            return hash;
        }

        @Override
        public boolean equals(Object obj) {
            Class<?> intf;
            return this == obj ||
                   obj != null &&
                   obj.getClass() == Key1.class &&
                   (intf = get()) != null &&
                   intf == ((Key1) obj).get();
        }
    }

    /*
     * a key used for proxy class with 2 implemented interfaces
     */
    private static final class Key2 extends WeakReference<Class<?>> {
        private final int hash;
        private final WeakReference<Class<?>> ref2;

        Key2(Class<?> intf1, Class<?> intf2) {
            super(intf1);
            hash = 31 * intf1.hashCode() + intf2.hashCode();
            ref2 = new WeakReference<Class<?>>(intf2);
        }

        @Override
        public int hashCode() {
            return hash;
        }

        @Override
        public boolean equals(Object obj) {
            Class<?> intf1, intf2;
            return this == obj ||
                   obj != null &&
                   obj.getClass() == Key2.class &&
                   (intf1 = get()) != null &&
                   intf1 == ((Key2) obj).get() &&
                   (intf2 = ref2.get()) != null &&
                   intf2 == ((Key2) obj).ref2.get();
        }
    }

    /*
     * a key used for proxy class with any number of implemented interfaces
     * (used here for 3 or more only)
     */
    private static final class KeyX {
        private final int hash;
        private final WeakReference<Class<?>>[] refs;

        @SuppressWarnings("unchecked")
        KeyX(Class<?>[] interfaces) {
            hash = Arrays.hashCode(interfaces);
            refs = (WeakReference<Class<?>>[])new WeakReference<?>[interfaces.length];
            for (int i = 0; i < interfaces.length; i++) {
                refs[i] = new WeakReference<>(interfaces[i]);
            }
        }

        @Override
        public int hashCode() {
            return hash;
        }

        @Override
        public boolean equals(Object obj) {
            return this == obj ||
                   obj != null &&
                   obj.getClass() == KeyX.class &&
                   equals(refs, ((KeyX) obj).refs);
        }

        private static boolean equals(WeakReference<Class<?>>[] refs1,
                                      WeakReference<Class<?>>[] refs2) {
            if (refs1.length != refs2.length) {
                return false;
            }
            for (int i = 0; i < refs1.length; i++) {
                Class<?> intf = refs1[i].get();
                if (intf == null || intf != refs2[i].get()) {
                    return false;
                }
            }
            return true;
        }
    }

    /**
     * A function that maps an array of interfaces to an optimal key where
     * Class objects representing interfaces are weakly referenced.
     */
    private static final class KeyFactory
        implements BiFunction<ClassLoader, Class<?>[], Object>
    {
        @Override
        public Object apply(ClassLoader classLoader, Class<?>[] interfaces) {
            switch (interfaces.length) {
                case 1: return new Key1(interfaces[0]); // the most frequent
                case 2: return new Key2(interfaces[0], interfaces[1]);
                case 0: return key0;
                default: return new KeyX(interfaces);
            }
        }
    }

    /**
     * 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<?>>
    {
        // prefix for all proxy class names
        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());
            }
        }
    }

    /**
     * Returns an instance of a proxy class for the specified interfaces
     * that dispatches method invocations to the specified invocation
     * handler.
     *
     * <p>{@code Proxy.newProxyInstance} throws
     * {@code IllegalArgumentException} for the same reasons that
     * {@code Proxy.getProxyClass} does.
     *
     * @param   loader the class loader to define the proxy class
     * @param   interfaces the list of interfaces for the proxy class
     *          to implement
     * @param   h the invocation handler to dispatch method invocations to
     * @return  a proxy instance with the specified invocation handler of a
     *          proxy class that is defined by the specified class loader
     *          and that implements the specified interfaces
     * @throws  IllegalArgumentException if any of the restrictions on the
     *          parameters that may be passed to {@code getProxyClass}
     *          are violated
     * @throws  SecurityException if a security manager, <em>s</em>, is present
     *          and any of the following conditions is met:
     *          <ul>
     *          <li> the given {@code loader} is {@code null} and
     *               the caller's class loader is not {@code null} and the
     *               invocation of {@link SecurityManager#checkPermission
     *               s.checkPermission} with
     *               {@code RuntimePermission("getClassLoader")} permission
     *               denies access;</li>
     *          <li> for each proxy interface, {@code intf},
     *               the caller's class loader is not the same as or an
     *               ancestor of the class loader for {@code intf} and
     *               invocation of {@link SecurityManager#checkPackageAccess
     *               s.checkPackageAccess()} denies access to {@code intf};</li>
     *          <li> any of the given proxy interfaces is non-public and the
     *               caller class is not in the same {@linkplain Package runtime package}
     *               as the non-public interface and the invocation of
     *               {@link SecurityManager#checkPermission s.checkPermission} with
     *               {@code ReflectPermission("newProxyInPackage.{package name}")}
     *               permission denies access.</li>
     *          </ul>
     * @throws  NullPointerException if the {@code interfaces} array
     *          argument or any of its elements are {@code null}, or
     *          if the invocation handler, {@code h}, is
     *          {@code null}
     */
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        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);
            }

            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);
        }
    }

    private static void checkNewProxyPermission(Class<?> caller, Class<?> proxyClass) {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            if (ReflectUtil.isNonPublicProxyClass(proxyClass)) {
                ClassLoader ccl = caller.getClassLoader();
                ClassLoader pcl = proxyClass.getClassLoader();

                // do permission check if the caller is in a different runtime package
                // of the proxy class
                int n = proxyClass.getName().lastIndexOf('.');
                String pkg = (n == -1) ? "" : proxyClass.getName().substring(0, n);

                n = caller.getName().lastIndexOf('.');
                String callerPkg = (n == -1) ? "" : caller.getName().substring(0, n);

                if (pcl != ccl || !pkg.equals(callerPkg)) {
                    sm.checkPermission(new ReflectPermission("newProxyInPackage." + pkg));
                }
            }
        }
    }

    /**
     * Returns true if and only if the specified class was dynamically
     * generated to be a proxy class using the {@code getProxyClass}
     * method or the {@code newProxyInstance} method.
     *
     * <p>The reliability of this method is important for the ability
     * to use it to make security decisions, so its implementation should
     * not just test if the class in question extends {@code Proxy}.
     *
     * @param   cl the class to test
     * @return  {@code true} if the class is a proxy class and
     *          {@code false} otherwise
     * @throws  NullPointerException if {@code cl} is {@code null}
     */
    public static boolean isProxyClass(Class<?> cl) {
        return Proxy.class.isAssignableFrom(cl) && proxyClassCache.containsValue(cl);
    }

    /**
     * Returns the invocation handler for the specified proxy instance.
     *
     * @param   proxy the proxy instance to return the invocation handler for
     * @return  the invocation handler for the proxy instance
     * @throws  IllegalArgumentException if the argument is not a
     *          proxy instance
     * @throws  SecurityException if a security manager, <em>s</em>, is present
     *          and the caller's class loader is not the same as or an
     *          ancestor of the class loader for the invocation handler
     *          and invocation of {@link SecurityManager#checkPackageAccess
     *          s.checkPackageAccess()} denies access to the invocation
     *          handler's class.
     */
    @CallerSensitive
    public static InvocationHandler getInvocationHandler(Object proxy)
        throws IllegalArgumentException
    {
        /*
         * Verify that the object is actually a proxy instance.
         */
        if (!isProxyClass(proxy.getClass())) {
            throw new IllegalArgumentException("not a proxy instance");
        }

        final Proxy p = (Proxy) proxy;
        final InvocationHandler ih = p.h;
        if (System.getSecurityManager() != null) {
            Class<?> ihClass = ih.getClass();
            Class<?> caller = Reflection.getCallerClass();
            if (ReflectUtil.needsPackageAccessCheck(caller.getClassLoader(),
                                                    ihClass.getClassLoader()))
            {
                ReflectUtil.checkPackageAccess(ihClass);
            }
        }

        return ih;
    }

    private static native Class<?> defineClass0(ClassLoader loader, String name,
                                                byte[] b, int off, int len);
}

Among them, the most important methods are:
Using proxy (dynamic proxy) to generate dynamic proxy objects

/**
Parameter Description:
ClassLoader loader Class loader
Class<?>[] interfaces Get all interfaces
InvocationHandler h Get an instance of the subclass of the InvocationHandler interface
*/
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException

Direct code:

1. Abstract class

2. Real object:

3. Dynamically creating classes for proxy objects
   dynamic proxy classes can only proxy interfaces (abstract classes are not supported). Proxy classes need to implement the InvocationHandler class and the invoke method. The invoke method is required to call all methods of the proxy interface, and the returned value is an implementation class of the proxy interface

4. Test:

                                  . Through the newProxyInstance function, we get a dynamic proxy object.

Abstract object:

Real object (represented object)

Dynamic proxy object tool class:

package test;

        import java.lang.reflect.InvocationHandler;
        import java.lang.reflect.Method;
        import java.lang.reflect.Proxy;

//Tool class for dynamically creating proxy objects
public class StoreHandler implements InvocationHandler {

    //Represented object (real object)
    private Object target;

    //Used to set up different proxy objects
    public void setTarget(Object target) {
        this.target = target;
    }

    /**
     * Binding relationship, that is, when the methods associated with which interface (bound with specific implementation class) will be called, the invoke method is executed
     * @param  target Binding specific dynamic instances
     * @return Dynamic agent instance
     */
    public Object getProxyInstance(Object target) {
        this.target = target;
        /*
        This method is used to generate a dynamic proxy class instance for a specified class loader, a set of interfaces, and a calling processor.
        The first parameter specifies the classloader that generates the proxy object, which needs to be specified as the same classloader as the target object.
        The second parameter implements the same interface as the target object, so you only need to get the public implementation interface of the target object.
        The third parameter indicates which InvocationHandler's invoke method these intercepted methods need to execute when intercepted
        Returns a proxy object based on the target passed in
        */
        Object result = Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);

        return result;
    }

    /**
     * The method of the associated implementation class will be executed when called. Method of the InvocationHandler interface.
     *
     * @param proxy  agent
     * @param method The method to which the original object is called. (method to be added)
     * @param args   Method parameters
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //Preprocessing logic added before TODO original object method call

        Object ret = null;
        try {
            before();//Preamplifier

            //Call target method (call method in target object)
            ret = method.invoke(target, args);

            after(); //Post enhancement
        } catch (Exception e) {
            //log.error("exception occurred in calling {}. {}", target.getClass().getName(), method.getName(), e);
            throw e;
        }
        //Postprocessing logic added after TODO original object method call
        return ret;
    }

    //Preprocessing (pre enhancement).
    private void before() {
        System.out.println("Deep Market Research for what you buy");
    }

    //Post processing (post enhancement).
    private void after() {
        System.out.println("Exquisite package, postal package and one-stop service");
    }
}

Test class:

Result:

   you can see that we can proxy different types of objects through ProxyHandler. If we implement external interfaces through dynamic proxy, all function calls will be forwarded through the invoke function. Therefore, we can do some operations we want to do here, such as log system, transaction, interceptor, permission control, etc.
At present, AOP is very popular. The core of AOP is dynamic agent mechanism.

Remarks:
The first condition to implement dynamic proxy: the class to be proxy must implement an interface to be proxy.
(now, CGLIB can realize dynamic proxy without interface. Follow up study)

Advantages and disadvantages of dynamic agent:

Advantage:
                          . In this way, when there are a large number of interface methods, we can handle them flexibly without the need to transit every method like a static agent. Moreover, the application of dynamic agent makes our class responsibilities more single and more reusable.

Disadvantages:
The   Proxy has been designed beautifully, but there is still a little bit of regret: it can never get rid of the shackles of only supporting interface Proxy, because its design is doomed to this regret. The inheritance diagram of dynamically generated Proxy classes is doomed to have a common parent class called Proxy. The inheritance mechanism of Java makes it impossible for these dynamic Proxy classes to realize the dynamic Proxy of class, because multi inheritance is essentially not feasible in Java.

3, CGLib dynamic agent

This section will be updated in the future. Please look forward to it.
Recommend you to check the article first: https://www.cnblogs.com/wyq1995/p/10945034.html

Published 16 original articles, won praise 5, visited 594
Private letter follow

Tags: Java PCL Programming REST

Posted on Thu, 05 Mar 2020 23:13:12 -0800 by mdkelly_komex