1. Spring AOP

Spring is a lightweight container, and the core concepts of the whole Spring family are IoC and AOP. As you can see, AOP is one of the cores of the Spring framework and plays a very important role in your application, as well as the foundation for the rest of Spring’s components. AOP (Aspect Oriented Programming), namely Aspect Oriented Programming, can be said to be OOP (Object Oriented Programming) complement and perfect. OOP introduces concepts such as encapsulation, inheritance, and polymorphism to build a hierarchy of objects that simulate a collection of common behaviors. OOP, however, allows developers to define vertical relationships, but not horizontal ones, such as logging.

Instead of focusing on the basics of AOP, let’s focus on the underlying implementation mechanism of AOP’s core functionality: the implementation principle of dynamic proxies. AOP’s interception capabilities are implemented by dynamic proxies in Java. An enhanced object class is generated by adding aspect logic to the object class (this aspect logic is executed either before, after, or when the object class function throws an exception. Different Interceptor timing corresponds to different types of interceptors, such as BeforeAdviseInterceptor, AfterAdviseInterceptor, and ThrowsAdviseInterceptor.

So how does a dynamic proxy achieve weaving aspect logic into a target class method? Let’s take a closer look at and implement two types of dynamic proxies used in AOP.

The AOP source code uses two dynamic proxies to implement the interception function: JDK dynamic proxy and Cglib dynamic proxy. Both methods exist at the same time, and each has its advantages and disadvantages. The JDK dynamic proxy is implemented by reflection mechanism in Java, and the cglib dynamic proxy is implemented by ASM. In general, reflection is more efficient at generating classes, while ASM is more efficient at executing classes after they are generated. (This can be done by caching ASM-generated classes.)

Let’s take an example of implementing each of these approaches.

2. JDK dynamic proxy

2.1 Define interfaces and implementation classes

public interface OrderService {
    public void save(UUID orderId, String name);

    public void update(UUID orderId, String name);

    public String getByName(String name);
}
Copy the code

The code above defines an intercepted object interface, known as a crosscutting concern. The following code implements the intercepted object interface.

public class OrderServiceImpl implements OrderService {

    private String user = null;

    public OrderServiceImpl(a) {}public OrderServiceImpl(String user) {
        this.setUser(user);
    }

	/ /...
	
    @Override
    public void save(UUID orderId, String name) {
        System.out.println("Call save() method,save:" + name);
    }

    @Override
    public void update(UUID orderId, String name) {
        System.out.println("Call Update () method");
    }

    @Override
    public String getByName(String name) {
        System.out.println("Call getByName() method");
        return "aoho"; }}Copy the code

2.2 JDK dynamic proxy classes

public class JDKProxy implements InvocationHandler {
	// Target object to be proxied
    private Object targetObject;
    
    public Object createProxyInstance(Object targetObject) {
        this.targetObject = targetObject;
        return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),
                this.targetObject.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    	// Proxy object
        OrderServiceImpl bean = (OrderServiceImpl) this.targetObject;
        Object result = null;
        // Advise, here before the target class code executes
        System.out.println("---before invoke----");
        if(bean.getUser() ! =null) {
            result = method.invoke(targetObject, args);
        }
        System.out.println("---after invoke----");
        return result;
    }

	/ /...

}
Copy the code

The above code implements the dynamic proxy class JDKProxy, implements the InvocationHandler interface, and implements the Invoke method in the interface. When the client invokes the business method of the proxy object, the proxy object executes an invoke method that delegates the call to targetObject, which is equivalent to calling the targetObject’s method. The invoke method determines the permission before delegating and implements the method’s interception.

2.3 test

public class AOPTest {
    public static void main(String[] args) {
        JDKProxy factory = new JDKProxy();
        //Proxy dynamically creates a Proxy instance that conforms to an interface for the InvocationHandler implementation class
        OrderService orderService = (OrderService) factory.createProxyInstance(new OrderServiceImpl("aoho"));
		// The orderService agent executes the program by dynamically generated proxy objects
        orderService.save(UUID.randomUUID(), "aoho"); }}Copy the code

The results are as follows:

-- Before invoke---- call save() method,save: AOho -- After invoke----Copy the code

3. CGLIB bytecode generation

3.1 Classes to be proxied

CGLIB can generate proxies for both classes of the interface. In the example, implement a proxy to a class.

public class OrderManager {
    private String user = null;

    public OrderManager(a) {}public OrderManager(String user) {
        this.setUser(user);
    }

	/ /...

    public void save(UUID orderId, String name) {
        System.out.println("Call save() method,save:" + name);
    }

    public void update(UUID orderId, String name) {
        System.out.println("Call Update () method");
    }

    public String getByName(String name) {
        System.out.println("Call getByName() method");
        return "aoho"; }}Copy the code

The implementation of this class is the same as the interface implementation above, for consistency.

3.2 CGLIB dynamic proxy classes

public class CGLibProxy implements MethodInterceptor {
    	// CGLib needs the target object of the proxy
    	private Object targetObject;

       public Object createProxyObject(Object obj) {
        this.targetObject = obj;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(obj.getClass());
        The intercept method in CglibProxy is called by the intercept method in CglibProxy
        enhancer.setCallback(this);
        // Enhanced target class
        Object proxyObj = enhancer.create();
        // Return the proxy object
        return proxyObj;
    }

    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        Object obj = null;
        // Advise, here before the target class code executes
        System.out.println("---before intercept----");
        obj = method.invoke(targetObject, args);
        System.out.println("---after intercept----");
        returnobj; }}Copy the code

The above implements methods for creating subclasses and methods for proxies. The getProxy(superClass.class) method creates a proxy object by extending the class of the parent class by taking in the bytecode of the parent class. The Intercept () method intercepts all calls to the target class method. Obj represents the instance of the target class, method is the reflection object of the target class method, args is the dynamic input parameter to the method, and methodProxy is the proxy class instance. Method.invoke (targetObject, args) calls a method in the parent class via the proxy class.

3.3 test

public class AOPTest {
    public static void main(String[] args) {
        OrderManager order = (OrderManager) new CGLibProxy().createProxyObject(new OrderManager("aoho"));
        order.save(UUID.randomUUID(), "aoho");
    }
Copy the code

The results are as follows:

-- Before Intercept ---- Call Save () method,save: AOho --after Intercept ----Copy the code

4. To summarize

This article mainly introduces two ways of implementing Spring Aop dynamic proxy, and introduces their advantages and disadvantages respectively. JDK dynamic proxies are used only if the target class is based on a unified interface. Without this premise, the JDK dynamic proxy cannot be applied. It can be seen that JDK dynamic proxy has certain limitations. Cglib, a third-party class library implemented dynamic proxy, is more widely used and has more advantages in efficiency.

JDK dynamic proxy mechanism is a delegate mechanism, do not need to third-party libraries, as long as the JDK environment can be proxy, dynamic implementation of the interface class, in the dynamically generated implementation class delegate for Hanlder to call the original implementation class method; CGLib must depend on the CGLib class library, using the inheritance mechanism, is the proxied class and the proxied class inheritance relationship, so the proxy class can be assigned to the proxied class, if the proxied class has an interface, then the proxy class can also be assigned to the interface.

Subscribe to the latest articles, welcome to follow my official account

reference

  1. JDK dynamic proxy and cglib proxy principle exploration
  2. The underlying implementation of AOP -CGLIB dynamic proxy and JDK dynamic proxy