Day4 AOP and dynamic agent

AOP

AOP (Aspect Oriented Programming, Aspect Oriented Programming), is one of two core of Spring.

AOP Overview

Issues into

  1. Programmers Xiaoqiang method maintains one thousand a day, the boss asked one thousand Xiaoqiang this method should be added transaction code (Uniform Code);
  2. Xiaoqiang bite the bullet, add a new method, and then let the thousand ways to call this method affairs, to solve the current problem.
  3. But after a while, my boss ran and Xiaoqiang said that one thousand methods have to call the method function log printing, while also adding a user to verify the legitimacy.

This time Xiaoqiang have two options:

  1. The boss said: roll, then cool flash on foot.
  2. Using AOP technology to achieve this function, after the boss just added can easily get.

What is AOP?

Simple to understand: Method enhanced.

AOP can enhance the function of the method, without modifying the original business code.

Depth understanding

  1. From the perspective of a programming language: Different size processing
    AOP is an extension of OOP, OOP maximum particle size (i.e., object-oriented programming) that can be handled is object, OOP, when needed enhancement method must be modified class definition. The size AOP can be processed deep into the object may be a method or field (Spring does not support field enhancement), so we do not have AOP can redefine class, while enhancing the original method.
  2. From the perspective of design pattern: AOP further reduces the coupling between the modules
    so that the business code, and the code-functional, to reduce the coupling between them.

    Functionality of the code, such as: transaction processing, parameter validation, logging, monitoring, load balancing

  3. From the perspective of liberation programmer productivity: less code
    AOP requires only a small amount of configuration or annotation can be completed
  4. From the perspective of code readability: clearer business logic
    programmer may be more focused on the business logic.

AOP realization of the principle: dynamic proxy

Proxy mode
Proxy mode is commonly used java design pattern, which is characterized by the proxy class delegate class has the same interface, the proxy class is responsible for the pre-message delegate class, filtered message, the message is forwarded to the delegate, and the message post-processing Wait. When we visit the actual object, the object is accessed through a proxy.

What is the dynamic proxy?
General agency model (a static proxy) we need to define the relevant proxy class, while the dynamic proxy class using some tools to help us in running, automatically generated proxy class.

The dynamic proxy AOP
dynamic proxy AOP is an implementation, AOP weaving may also be used in a static manner.
Spring dynamic proxies to implement AOP.

Recalling the IOC and DI: DI is an implementation of IOC, IOC can also use the DL ways.
IOC Spring is achieved using DI. IOC and AOP are two of the most central characteristic Spring.

Here a simple example, each using a non-proxy mode, normal mode and the dynamic proxy proxy mode to achieve.

And which has a an interface implementation class UserService UserServiceImpl, now needs to each of the methods in interface adds a function (printing run time).

Non-proxy

Non-proxy mode, you need to modify the original implementation class, or redefine an implementation class to add new functionality requirements.
Original interface and implementation class:

public interface UserService {

    void getUser();
    void createUser();
    void updateUser();
    void deleteUser();
}

public class UserServiceImpl implements UserService {

    @Override
    public void getUser() {
        System.out.println("getUser");
    }

    @Override
    public void createUser() {
        System.out.println("createUser");
    }

    @Override
    public void updateUser() {
        System.out.println("updateUser");
    }

    @Override
    public void deleteUser() {
        System.out.println("deleteUser");
    }
}

Define a new implementation class, printing time and method names:

public class UserServiceImpl2 implements UserService {

    private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @Override
    public void getUser() {
        System.out.println("getUser");
        System.out.println(dateFormat.format(new Date()).toString() +  " Log: method getUser invoked.");
    }

    @Override
    public void createUser() {
        System.out.println("createUser");
        System.out.println(dateFormat.format(new Date()).toString() +  " Log: method createUser invoked.");
    }

    @Override
    public void updateUser() {
        System.out.println("updateUser");
        System.out.println(dateFormat.format(new Date()).toString() +  " Log: method updateUser invoked.");
    }

    @Override
    public void deleteUser() {
        System.out.println("deleteUser");
        System.out.println(dateFormat.format(new Date()).toString() +  " Log: method deleteUser invoked.");
    }
}

test:

    public static void main(String[] args) {
        UserService userDao = new UserServiceImpl2();
        userDao.getUser();
        userDao.createUser();
        userDao.updateUser();
        userDao.deleteUser();
    }

result:

getUser
2019-12-22 10:29:26: method getUser invoked
createUser
2019-12-22 10:29:26: method createUser invoked
updateUser
2019-12-22 10:29:26: method updateUser invoked
deleteUser
2019-12-22 10:29:26: method deleteUser invoked

Proxy mode

Proxy mode, also need to define the proxy class that implements the interface, but does not need to override the original method, but is a combination of a proxy object.
Acting categories:

public class UserServiceProxy implements UserService {

    private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    private UserService userService;

    public UserServiceProxy(UserService userService) {
        this.userService = userService;
    }

    @Override
    public void getUser() {
        userService.getUser();
        System.out.println(dateFormat.format(new Date()).toString() +  " Log: method getUser invoked.");
    }

    @Override
    public void createUser() {
        userService.createUser();
        System.out.println(dateFormat.format(new Date()).toString() +  " Log: method createUser invoked.");
    }

    @Override
    public void updateUser() {
        userService.updateUser();
        System.out.println(dateFormat.format(new Date()).toString() +  " Log: method updateUser invoked.");
    }

    @Override
    public void deleteUser() {
        userService.deleteUser();
        System.out.println(dateFormat.format(new Date()).toString() +  " Log: method deleteUser invoked.");
    }
}

test:

    public static void main(String[] args) {
        UserService userServiceOriginal = new UserServiceImpl();
        UserService userServiceProxy = new UserServiceProxy(userServiceOriginal);
        userServiceProxy.getUser();
        userServiceProxy.createUser();
        userServiceProxy.updateUser();
        userServiceProxy.deleteUser();
    }

result:

getUser
2019-12-22 11:34:49 Log: method getUser invoked.
createUser
2019-12-22 11:34:49 Log: method createUser invoked.
updateUser
2019-12-22 11:34:49 Log: method updateUser invoked.
deleteUser
2019-12-22 11:34:49 Log: method deleteUser invoked.

JDK dynamic proxies

JDK agent does not need to rewrite dynamic implementation class, do not need to define a UserService proxy class that implements the interface. But the use of the Proxy class to help us dynamically generated proxy class.

public class UserServiceProxy {
        private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    /**
     * 返回动态代理对象
     * @param userService 被代理对象
     * @return 代理对象
     */
    public static UserService newProxyInstance(UserService userService) {
            /*
             * Proxy.newProxyInstance方法返回动态生成的代理类
             * 参数:
             *  userService.getClass().getClassLoader():被代理类的类加载器;
             *  userService.getClass().getInterfaces():被代理类的接口,重要!说明JDK动态代理是基于代理接口的。
             *  第三个参数定义了一个内部类,实现了代理逻辑,内部参数:
             *      o:代理类对象;
             *      method:被代理方法;
             *      objects:被代理方法的参数;
             */
            return (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),
                    userService.getClass().getInterfaces(), (o, method, objects) -> {
                        // 调用被代理方法
                        Object ret = method.invoke(userService, objects);
                        // 编写增强功能
                        System.out.println(dateFormat.format(new Date()) + ": method " + method.getName() + " invoked");
                        return ret;
                    });
        }
}

test:

    public static void main(String[] args) {
        // 保存动态生成的代理类的字节码
        System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        UserService userService = UserServiceProxy.newProxyInstance(new UserServiceImpl());
        userService.getUser();
        userService.createUser();
        userService.updateUser();
        userService.deleteUser();
    }

result:

getUser
2019-12-22 11:48:44: method getUser invoked
createUser
2019-12-22 11:48:44: method createUser invoked
updateUser
2019-12-22 11:48:44: method updateUser invoked
deleteUser
2019-12-22 11:48:44: method deleteUser invoked

JDK dynamic proxy resolution

JDK dynamic proxy class is generated at runtime, the default will not be saved to a local file, unless the property is set as follows:
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
dynamically generated proxy class path: "Project directory /aop/dynamic_proxy/cglibproxy/$Proxy0.class",$ Proxy0 is the class name of the proxy class.

JDK dynamic proxy uses reflection to getUser () method, for example, we can view the stored proxy class, which is defined as:

    public final void getUser() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

The key sentence:super.h.invoke(this, m3, (Object[])null);

  1. invoke is our definition:
(o, method, objects) -> {
    // 调用被代理方法
    Object ret = method.invoke(userService, objects);
    // 编写增强功能
    System.out.println(dateFormat.format(new Date()) + ": method " + method.getName() + " invoked");
    return ret;
}
  1. m3 define proxy class is dynamically generated in:
m3 = Class.forName("com.bailiban.day4.aop.dynamic_proxy.noproxy.UserService").getMethod("getUser");

Cglib dynamic proxy

public class UserServiceCglibProxy {

    private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    /**
     * 返回动态代理对象
     * @param userService 被代理对象
     * @return 代理对象
     */
    public static UserService newProxyInstance(UserService userService) {

        /*
         * Enhancer.create方法返回动态生成的代理类
         * 参数:
         *  userService.getClass():被代理类的类;
         *  第二个参数定义了一个内部类,实现了代理逻辑,内部参数:
         *      o:代理类对象;
         *      method:被代理方法;
         *      objects:被代理方法的参数;
         *      methodProxy:方法代理;
         */
        return (UserService) Enhancer.create(userService.getClass(), (MethodInterceptor) (o, method, objects, methodProxy) -> {
            // 调用被代理方法
            Object ret = methodProxy.invokeSuper(o, objects);
            // 编写增强功能
            System.out.println(dateFormat.format(new Date()) + ": method " + method.getName() + " invoked");
            return ret;
        });
    }
}

test:

    public static void main(String[] args) {
        // 保存动态生成的代理类的字节码
//        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "C:\\code");
        UserService userService = UserServiceCglibProxy.newProxyInstance(new UserServiceImpl());
        userService.getUser();
        userService.createUser();
        userService.updateUser();
        userService.deleteUser();
    }

result:

getUser
2019-12-22 12:10:01: method getUser invoked
createUser
2019-12-22 12:10:01: method createUser invoked
updateUser
2019-12-22 12:10:01: method updateUser invoked
deleteUser
2019-12-22 12:10:01: method deleteUser invoked

Cglib dynamic proxy resolution

Cglib generated proxy class also needs to be set to the locally saved:
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "C:\\code");
Cglib dynamically generated code more and more complex.

To getUser () method, for example, we can view the stored proxy class, which is defined as:

    public final void getUser() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            var10000.intercept(this, CGLIB$getUser$0$Method, CGLIB$emptyArgs, CGLIB$getUser$0$Proxy);
        } else {
            super.getUser();
        }
    }

The key code is: var10000.intercept(this, CGLIB$getUser$0$Method, CGLIB$emptyArgs, CGLIB$getUser$0$Proxy);
where:

  1. intercept is our definition:
(o, method, objects, methodProxy) -> {
            // 调用被代理方法
            Object ret = methodProxy.invokeSuper(o, objects);
            // 编写增强功能
            System.out.println(dateFormat.format(new Date()) + ": method " + method.getName() + " invoked");
            return ret;
        }
  1. this: proxy object;
  2. CGLIB$getUser$0$Method
    Proxy method, can be understood as the method is getUser UserServiceImpl in;
  3. CGLIB$emptyArgs: Method parameters;
  4. CGLIB$getUser$0$Proxy
    MethodProxy object type, reflection can be avoided using the method. There is one difference between the use of reflective and Cglib JDK dynamic proxies.

Difference between the two

Simple to understand

  1. scenes to be used
  • JDK dynamic proxy interface for
    example, we want UserServiceImpl agent, the UserServiceImpl must implement an interface, such as UserService interfaces;
  • Cglib for the class or interface
    , for example, we want to proxy UserServiceImpl, need not be concerned whether the class implements the interface.
  1. effectiveness
  • Before Java8, Cglib high efficiency;
  • Java8 start, high efficiency JDK dynamic proxy;

Depth understanding

  1. Why JDK dynamic proxy must have an interface, and you do not need Cglib
    which is determined by their implementation.
  • JDK dynamic proxy class generated inheritance
    JDK dynamic proxy generation code:
public final class $Proxy0 extends Proxy implements UserService {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m6;
    private static Method m0;
    private static Method m4;
    private static Method m5;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void getUser() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void createUser() throws  {
        try {
            super.h.invoke(this, m6, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void updateUser() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void deleteUser() throws  {
        try {
            super.h.invoke(this, m5, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.bailiban.day4.aop.dynamic_proxy.noproxy.UserService").getMethod("getUser");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m6 = Class.forName("com.bailiban.day4.aop.dynamic_proxy.noproxy.UserService").getMethod("createUser");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            m4 = Class.forName("com.bailiban.day4.aop.dynamic_proxy.noproxy.UserService").getMethod("updateUser");
            m5 = Class.forName("com.bailiban.day4.aop.dynamic_proxy.noproxy.UserService").getMethod("deleteUser");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

From the proxy class inheritance look: public final class $Proxy0 extends Proxy implements UserService
you can see, $ Proxy0 inherited the Proxy class, while Java only single inheritance, in order to make use of the local agents and the like, can be used transparently proxy class, so that they can share a common interface. So JDK dynamic proxy support only.

  • Cglib proxy class inheritance
public class UserDaoImpl$$EnhancerByCGLIB$$4a28906a extends UserDaoImpl implements Factory

We can see the agent class does not inherit from other classes, but inherits the proxy class. So Cglib way is not concerned about whether there is a proxy class interfaces.

  1. Efficiency
  • Various different implementations bring efficiency
    • JDK agent using reflection, but before the reflector is "heavy" operation;
    • Cglib using mapping method finds the corresponding method name, then calls without using the reflector;
  • Optimizes the difference between official and unofficial brought
    • Although JDK agent uses the reflection of this "re-operation", but it will be with the official Java language, for JVM, to constantly optimize the JIT-time compiler, and bring enormous efficiency improvements;
    • Cglib unofficial implementation, optimization is not enough;

Guess you like

Origin www.cnblogs.com/cheng18/p/12081185.html