Source code analysis of Spring Aop

Keywords: Java JDK Spring IntelliJ IDEA

1. Dynamic agents: Jdk and Cglib

I. principle difference:

java dynamic proxy uses reflection mechanism to generate an anonymous class that implements the proxy interface. It calls InvokeHandler before calling the specific method.

cglib dynamic proxy uses asm open source package to load the class file of proxy object class and modify its bytecode to generate subclass.

1. If the target object implements the interface, the dynamic agent of JDK will be used to implement AOP by default.
2. If the target object implements the interface, CGLIB can be used to implement AOP.

3. If the target object does not implement the interface, CGLIB library must be used, and spring will automatically convert between JDK dynamic agent and CGLIB.

package com.lvyuanj.core.service;

public interface UserService{
	void login(String username);
	void exit(String username);
}

package com.lvyuanj.core.service.Impl;

import com.lvyuanj.core.service.UserService;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

	@Override
	public void login(String username) {
		System.out.println(username+"Perform login business verification");
	}

	@Override
	public void exit(String username) {
		System.out.println("exit---->"+username);
	}
}

package com.lvyuanj.core.service;

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

public class MyInvocationHandler implements InvocationHandler {

	private Object target;

	public MyInvocationHandler(Object target){
		super();
		this.target = target;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("start------->");
		Object result = method.invoke(target, args);
		System.out.println("end---------<");
		return result;
	}

	public Object getProxy(){
		return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),target.getClass().getInterfaces(),this);
	}
}
package com.lvyuanj.core.service;

import com.lvyuanj.core.service.Impl.UserServiceImpl;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import sun.misc.ProxyGenerator;

import java.io.FileOutputStream;
import java.io.IOException;

@EnableAspectJAutoProxy
@ComponentScan
public class SpringAopDemo {

	public static void main(String[] args) throws IOException {

		UserService userService0 = new UserServiceImpl();
		MyInvocationHandler myInvocationHandler = new MyInvocationHandler(userService0);
		UserService myProxyUserService = (UserService) myInvocationHandler.getProxy();
		myProxyUserService.login("zhangsan");
	}
}

2. Source code analysis of spring AOP
package com.lvyuanj.core.service;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class AuthAspect {

	@Around("execution(* com.lvyuanj.core.service.Impl.UserServiceImpl.*(..))")
	public void around(ProceedingJoinPoint joinPoint) throws Throwable {
		System.out.println("start ....");
		long l = System.currentTimeMillis();
		joinPoint.proceed();
		System.out.println("Time consuming:"+ (System.currentTimeMillis()-l));
	}
}
package com.lvyuanj.core.service;

import com.lvyuanj.core.service.Impl.UserServiceImpl;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import sun.misc.ProxyGenerator;

import java.io.FileOutputStream;
import java.io.IOException;

@EnableAspectJAutoProxy
@ComponentScan
public class SpringAopDemo {

	public static void main(String[] args) throws IOException {
		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringAopDemo.class);

		UserService userService = applicationContext.getBean(UserService.class);
		String arvin = "arvin";
		userService.login(arvin);
	}
}

  1. Sequence diagram:
new AnnotationConfigApplicationContext
	> org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
		>org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
		>org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)
		>org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization
			>org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization
			>org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary
			>org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#createProxy
				>org.springframework.aop.framework.ProxyFactory#getProxy(java.lang.ClassLoader)
					>org.springframework.aop.framework.ProxyCreatorSupport#createAopProxy
						>org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy   --- Judgement and use JdkDynamicAopProxy  and ObjenesisCglibAopProxy agent;Whether the interface is implemented
							>org.springframework.aop.framework.JdkDynamicAopProxy#getProxy(java.lang.ClassLoader)   -- Get proxy object
								>java.lang.reflect.Proxy#newProxyInstance
								>java.lang.reflect.Proxy#getProxyClass0
								>java.lang.reflect.Proxy.ProxyClassFactory#apply  
								---->byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags); --- Proxy class name getclassByte
								---->defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);  -- Generated by byte file Class
package com.lvyuanj.core.service;

import com.lvyuanj.core.service.Impl.UserServiceImpl;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import sun.misc.ProxyGenerator;

import java.io.FileOutputStream;
import java.io.IOException;

@EnableAspectJAutoProxy
@ComponentScan
public class SpringAopDemo {

	public static void main(String[] args) throws IOException {
		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringAopDemo.class);

		UserService userService = applicationContext.getBean(UserService.class);
		String arvin = "arvin";
		userService.login(arvin);
byte[] userServices = ProxyGenerator.generateProxyClass("UserServiceImpl", new Class[]{UserServiceImpl.class});

		FileOutputStream fileOutputStream = new FileOutputStream("D://UserServiceImpl.class");

		fileOutputStream.write(userServices);

		fileOutputStream.close();
	}
}

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

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

public final class UserServiceImpl extends Proxy implements com.lvyuanj.core.service.Impl.UserServiceImpl {
    private static Method m1;
    private static Method m3;
    private static Method m8;
    private static Method m2;
    private static Method m6;
    private static Method m5;
    private static Method m7;
    private static Method m9;
    private static Method m0;
    private static Method m4;

    public UserServiceImpl(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 login(String var1) throws  {
        try {
            super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void notify() throws  {
        try {
            super.h.invoke(this, m8, (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 wait(long var1) throws InterruptedException {
        try {
            super.h.invoke(this, m6, new Object[]{var1});
        } catch (RuntimeException | InterruptedException | Error var4) {
            throw var4;
        } catch (Throwable var5) {
            throw new UndeclaredThrowableException(var5);
        }
    }

    public final void wait(long var1, int var3) throws InterruptedException {
        try {
            super.h.invoke(this, m5, new Object[]{var1, var3});
        } catch (RuntimeException | InterruptedException | Error var5) {
            throw var5;
        } catch (Throwable var6) {
            throw new UndeclaredThrowableException(var6);
        }
    }

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

    public final void notifyAll() throws  {
        try {
            super.h.invoke(this, m9, (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 wait() throws InterruptedException {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | InterruptedException | 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.lvyuanj.core.service.Impl.UserServiceImpl").getMethod("login", Class.forName("java.lang.String"));
            m8 = Class.forName("com.lvyuanj.core.service.Impl.UserServiceImpl").getMethod("notify");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m6 = Class.forName("com.lvyuanj.core.service.Impl.UserServiceImpl").getMethod("wait", Long.TYPE);
            m5 = Class.forName("com.lvyuanj.core.service.Impl.UserServiceImpl").getMethod("wait", Long.TYPE, Integer.TYPE);
            m7 = Class.forName("com.lvyuanj.core.service.Impl.UserServiceImpl").getMethod("getClass");
            m9 = Class.forName("com.lvyuanj.core.service.Impl.UserServiceImpl").getMethod("notifyAll");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            m4 = Class.forName("com.lvyuanj.core.service.Impl.UserServiceImpl").getMethod("wait");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

Then cast the resulting $Proxy0 instance to UserServiceImpl and assign the reference to the subject. When the userService.login() method is executed, the login() method in the $Proxy0 class is called, and then the invoke() method of h in the parent class Proxy is called. That is, InvocationHandler.invoke()

1. Why do java dynamic proxy objects have to be interfaces?
Answer: because java is a single inheritance and multiple implementations, the source code of UserServiceImpl Proxy class can be obtained by decompiling above. UserServiceImpl already inherits the Proxy class by default, so the Proxy object must have an implementation interface;

Posted by Grande on Sat, 26 Oct 2019 07:14:54 -0700