[Java School Recruitment Interview] Basic Knowledge (2) - Spring Framework & AOP


foreword

This article mainly introduces Spring Frameworkand AOPanalyzes source code for aspect-oriented programming.

"Basic knowledge" is the first part of this column. This blog post is the second blog post. If necessary, you can:

  1. Click here to return to the index articles of this column
  2. Click here to return to the previous article "[Java School Recruitment Interview] Basic Knowledge (1) - Java Common Class Libraries"
  3. Click here to go to the next article "[Java School Recruitment Interview] Basic Knowledge (3) - Multithreading and Concurrency"

1. Basic knowledge of Spring Framewwork

1. The relationship between IoC, DL, and DI
insert image description here
IoC means inversion of control, DL means dependency lookup, and DI means dependency injection

As can be seen from the above figure, DL and DI are two implementations of IoC. DL has been abandoned because it is invasive to business code. The way to achieve DIis divided into Set注入, 接口注入, 注解注入and 构造器注入4 kinds.

2. The IoC container
insert image description here
can be seen from the figure:

  1. The IoC container first reads the configuration information of the Bean from various configuration files and annotations, and stores it in Bean定义注册表.
  2. Then instantiate the Bean according to the Bean Registry.
  3. Then store the instantiated Bean instance in Bean缓存池.
  4. When the application needs to use the Bean instance, Bean缓存池the corresponding instance is taken out and injected into it.

3. BeanFactory
is the core interface of the Spring framework, and its functions are as follows:

  • Provide IoC configuration mechanism;
  • Contains the definitions of various beans, which is convenient for instantiating beans;
  • Establish dependencies between beans;
  • Control the life cycle of beans.

4. ApplicationContext interface
It inherits multiple interfaces:

  • BeanFactory: manage and assemble Bean;
  • ResourcePatternResolver: load resource files;
  • MessageSource: realize internationalization and other functions;
  • ApplicationEventPublisher: Register the listener to implement the listening mechanism.

5. Bean scope

  • singleton: Spring's default scope, there is only one Bean instance in the container;
  • prototype: For each getBean request, the container will create a Bean instance;
  • request: A Bean instance will be created for each Http request;
  • session: A Bean instance will be created for each session;
  • globalSession: A Bean instance will be created for each global Http Session, and this scope is only valid for Portlets.

6. The life cycle of Bean
insert image description here
As can be seen from the figure, the life cycle of Bean is divided into 4three parts:
1) Instantiation of Bean
2) Initialization of Bean
3) Use of Bean
4) Destruction of Bean


2. Basic concepts of Spring AOP

The emergence of AOP is not to completely replace OOP, but only as a beneficial supplement to OOP. The application of AOP is limited, and it is generally only suitable for those applications with cross-cutting logic: such as performance monitoring, access control, transaction management, and logging.

The following are some basic concepts of Spring AOP:

1. Aspect

1) Static aspect: when the proxy object is generated, it is determined whether the enhancement needs to be woven into the target class connection point;
2) Dynamic aspect: it must be judged whether the enhancement needs to be woven according to the value of the method input parameter at runtime to the target class connection point;
3) Process cut point: represents other methods directly or indirectly invoked by a certain method, and the process cut point is composed of process cut point and corresponding enhancement;
4) Composite cut point: Sometimes, a cut point is difficult To describe the information of the target connection point, we need to create a composite pointcut aspect;
5) Introduction aspect: It is a special type of enhancement. It does not weave enhancements around the target method, but creates new methods and methods for the target class. Attributes, so the link point that introduces enhancements is at the class level, not at the method level. By introducing enhancement, we can add an interface implementation to a target class.

2. Weaving

Weaving is the process of adding enhancements to specific join points of the target class. AOP has 3 weaving methods:
1) Compiler weaving: This requires the use of a special Java compiler;
2) Class loading period weaving: This requires the use of a special class loader;
3) Dynamic proxy weaving: In The way to add enhancements to generate subclasses for the target class at runtime.
Adopted by Spring Framework 动态代理织入.

3. Advice

Enhancement means that outside of some business logic, it needs to be written frequently 非业务逻辑代码. For example, some methods must be executed before execution 通用的初始化步骤; some methods must be executed after execution 通用的反初始化步骤; some methods even require both. These common codes are: performance monitoring, access control, transaction management, and logging.

The types of enhancements are:

  • Method-level enhancements: pre-enhancement, post-enhancement, surround enhancement, exception throwing enhancement
  • Class-Level Enhancements: Introduction to Enhancements

4. Dynamic proxy

Spring AOP uses two proxy mechanisms:

  • JDK-based dynamic proxy;
  • Dynamic proxy based on CGLib.

The reason why two proxy mechanisms are needed is largely because the JDK itself only provides proxy for interfaces and does not support proxy for classes.


3. JDK dynamic proxy

After JDK1.3, Java provides dynamic proxy technology, which allows developers to create proxy instances of interfaces during runtime.

JDK's dynamic proxy mainly involves java.lang.reflecttwo classes in the package: Proxyand InvocationHandler.

  • InvocationHandlerIt is an interface, which can define cross-cutting logic by implementing the interface, and call the code of the target class through the reflection mechanism, and dynamically weave cross-cutting logic and business logic together.
  • ProxyInvocationHandlerA proxy object of the target class is generated by dynamically creating an instance conforming to an interface.

1. Basic Usage

The following is an example of a piece of code for the JDK dynamic proxy:

1)我们自己的,需要织入增强的业务接口:

	public interface Business {
    
    
	    void doBusiness();
	}

2)业务逻辑实现类:

	public class BusinessImpl implements Business {
    
    
	    @Override
	    public void doBusiness() {
    
    
	        System.out.println("I'm doing business");
	    }
	}

In the business logic here, because it is only for demonstration, we only output one sentence I'm doing business, and the business logic in actual development can be any code.

3)业务逻辑代理类:

	public class BusinessProxy implements InvocationHandler {
    
    
		//增强的目标,这里指的是业务逻辑实现类BusinessImpl
	    private Object target;
	    public BusinessProxy(Object target) {
    
    
	        this.target = target;
	    }
		
		//获取代理对象
	    public <T> T getProxy() {
    
    
	        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
	    }
	
		//实现增强逻辑
	    @Override
	    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
	        System.out.println("Doing pre-advice.");
	        Object result = method.invoke(target, args);
	        System.out.println("Doing post-advice.");
	        return result;
	    }
	}

We have implemented one of the enhanced logic here 环绕增强, that is, output a sentence before the business logic Doing pre-advice., and output a sentence after the business logic Doing post-advice.. The enhanced logic in actual development can also be the logic of performance monitoring, access control, transaction management, and logging.

4)业务逻辑调用类:

	public class CallBusiness {
    
    
	    public static void main(String[] args) {
    
    
	        Business business = new BusinessProxy(new BusinessImpl()).getProxy();
	        business.doBusiness();
	    }
	}

5)输出结果

Doing pre-advice.
I’m doing business
Doing post-advice.

It can be seen that through JDK dynamic proxy, we have realized the enhancement of business logic.

2. Principle analysis

1)其大致流程为:
① Create the bytecode file of the proxy class for the interface;
② Use ClassLoader to load the bytecode file into the JVM;
③ Create an instance object of the proxy class and execute the target method of the object.

2)生成的目标字节码文件内容:

	package com.sun.proxy;
	
	import java.lang.reflect.InvocationHandler;
	import java.lang.reflect.Method;
	import java.lang.reflect.Proxy;
	import java.lang.reflect.UndeclaredThrowableException;
	import test.jdkproxytest.Business;
	
	public final class $Proxy0 extends Proxy implements Business {
    
    
	    private static Method m1;
	    private static Method m2;
	    private static Method m0;
	    private static Method m3;
	
	    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 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 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 doBusiness() throws  {
    
    
	        try {
    
    
	            super.h.invoke(this, m3, (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"));
	            m2 = Class.forName("java.lang.Object").getMethod("toString");
	            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
	            m3 = Class.forName("test.jdkproxytest.Business").getMethod("doBusiness");
	        } catch (NoSuchMethodException var2) {
    
    
	            throw new NoSuchMethodError(var2.getMessage());
	        } catch (ClassNotFoundException var3) {
    
    
	            throw new NoClassDefFoundError(var3.getMessage());
	        }
	    }
	}

Based on the generated bytecode files, we draw the following conclusions:

①The generated proxy class inherits the proxy class we defined and implements the interface to be proxied. Since Java does not support multiple inheritance, the JDK dynamic proxy cannot proxy the class; ②There is a static code block, through
reflection or all methods of the proxy class ;
③ Execute the target method in the proxy class through invoke;


Four, CGLib dynamic proxy

Creating a proxy with the JDK has a limitation that it can only create proxy instances for interfaces. For classes that do not define business methods through interfaces, CGLib fills this gap as a substitute.

CGLib uses very low-level bytecode technology. It can create subclasses for a class, and use method interception technology in the subclasses to intercept all parent class method calls, and weave cross-cutting logic along the way.

1. Basic Usage

Similarly, the following is an example of a piece of code for the JDK dynamic agent:

业务接口and 业务逻辑实现类follow the code from the previous article

1)业务逻辑代理类

	public class BusinessProxy implements MethodInterceptor {
    
    
	    private Enhancer enhancer = new Enhancer();
	    public Object getProxy(Class clazz) {
    
    
	        enhancer.setSuperclass(clazz);
	        enhancer.setCallback(this);
	        return enhancer.create();
	    }
	
	    @Override
	    public Object intercept(Object o, Method method, Object[] os, MethodProxy mp) throws Throwable {
    
    
	        System.out.println("Doing pre-advice");
	        Object result = mp.invokeSuper(o, os);
	        System.out.println("Doing post-advice");
	        return result;
	    }
	}

2)业务逻辑调用类:

	public class CallBusiness {
    
    
		public static void main(String[] args) {
    
    
	        BusinessProxy proxy = new BusinessProxy();
	        BusinessImpl businessImpl = (BusinessImpl) proxy.getProxy(BusinessImpl.class);
	        businessImpl.doBusiness();
	    }
	}

输出结果Same, no more details here.

2. Principle analysis

1)生成的代理类:
After calling CallBusiness, three proxy class Class files will be generated:
insert image description here
let's open the second file first:

	public class BusinessImpl$$EnhancerByCGLIB$$d53a0f55 extends BusinessImpl implements Factory {
    
    
	    ......
	    private MethodInterceptor CGLIB$CALLBACK_0;  //拦截器
	    ......
	    private static final Method CGLIB$doBusiness$0$Method;  //被代理方法
	    private static final MethodProxy CGLIB$doBusiness$0$Proxy;  //代理方法
	    ......
	
	    static void CGLIB$STATICHOOK1() {
    
    
			......
			//代理类
	        Class var0 = Class.forName("test.cglibproxytest.BusinessImpl$$EnhancerByCGLIB$$d53a0f55");
	        //被代理类
	        Class var1;
	        ......
	        CGLIB$doBusiness$0$Method = ReflectUtils.findMethods(new String[]{
    
    "doBusiness", "()V"}, (var1 = Class.forName("test.cglibproxytest.BusinessImpl")).getDeclaredMethods())[0];
	        CGLIB$doBusiness$0$Proxy = MethodProxy.create(var1, var0, "()V", "doBusiness", "CGLIB$doBusiness$0");
	    }
	}

As you can see from the source code of the proxy class, the proxy class will obtain all the methods inherited from the parent class, and there will be MethodProxy corresponding to it, such as Method: , CGLIB$doBusiness$0$MethodMethodProxy:CGLIB$doBusiness$0$Proxy

2)方法的调用

		//代理方法(methodProxy.invokeSuper会调用)
	    final void CGLIB$doBusiness$0() {
    
    
	        super.doBusiness();
	    }
		
		//被代理方法(methodProxy.invoke会调用,在拦截器中调用methodProxy.invoke会死循环,因为其会调用拦截器)
	    public final void doBusiness() {
    
    
	        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
	        if (this.CGLIB$CALLBACK_0 == null) {
    
    
	            CGLIB$BIND_CALLBACKS(this);
	            var10000 = this.CGLIB$CALLBACK_0;
	        }
	
	        if (var10000 != null) {
    
    
	        	//调用拦截器
	            var10000.intercept(this, CGLIB$doBusiness$0$Method, CGLIB$emptyArgs, CGLIB$doBusiness$0$Proxy);
	        } else {
    
    
	            super.doBusiness();
	        }
	    }

From this, we can see the call chain of the method: the proxy object calls this.doBusinessthe method --> calls 拦截器--> methodProxy.invokeSuper--> --> the method CGLIB$doBusiness$0of the proxy objectdoBusiness

3. MethodProxy

In the interceptor MethodInterceptor, the invokeSuper method of MethodProxy invokes the proxy method. MethodProxy is very critical, so we need to analyze what it does.

1)创建MethodProxy

	public class MethodProxy {
    
    
	    private Signature sig1;
	    private Signature sig2;
	    private MethodProxy.CreateInfo createInfo;
	    private final Object initLock = new Object();
	    private volatile MethodProxy.FastClassInfo fastClassInfo;
	    /** 
	     * @param c1 被代理对象Class
	     * @param c2 代理对象Class
	     * @param desc 入参类型列表字符串
	     * @param name1 被代理方法名
	     * @param name2 代理方法名
	     */
	    public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
    
    
	        MethodProxy proxy = new MethodProxy();
	        // 被代理方法签名
	        proxy.sig1 = new Signature(name1, desc);
	        // 代理方法签名
	        proxy.sig2 = new Signature(name2, desc);
	        proxy.createInfo = new MethodProxy.CreateInfo(c1, c2);
	        return proxy;
	    }
	
		private static class CreateInfo {
    
    
		    Class c1;
		    Class c2;
		    NamingPolicy namingPolicy;
		    GeneratorStrategy strategy;
		    boolean attemptLoad;
		
		    public CreateInfo(Class c1, Class c2) {
    
    
		        this.c1 = c1;
		        this.c2 = c2;
		        AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();
		        if(fromEnhancer != null) {
    
    
		            this.namingPolicy = fromEnhancer.getNamingPolicy();
		            this.strategy = fromEnhancer.getStrategy();
		            this.attemptLoad = fromEnhancer.getAttemptLoad();
		        }
		    }
		}
	}

2)invokeSuper调用

		public Object invokeSuper(Object obj, Object[] args) throws Throwable {
    
    
		        try {
    
    
		            this.init();
		            MethodProxy.FastClassInfo fci = this.fastClassInfo;
		            return fci.f2.invoke(fci.i2, obj, args);
		        } catch (InvocationTargetException var4) {
    
    
		            throw var4.getTargetException();
		        }
		    }
		
		private static class FastClassInfo {
    
    
		    FastClass f1;  //被代理类FastClass
		    FastClass f2;  //代理类FastClass
		    int i1;   //被代理类的方法签名(index)
		    int i2;  //代理类的方法签名
		
		    private FastClassInfo() {
    
    }
		}

The above code calling process is to obtain the FastClass corresponding to the proxy class and execute the proxy method. Before generating 3 class files:

  • the first is代理类的FastClass
  • the third is被代理类的FastClass

4. FastClass mechanism

The reason why Cglib's dynamic proxy execution proxy method is more efficient than JDK is because Cglib adopts the FastClass mechanism. Its principle is simply: generate a Class for the proxy class and the proxy class, and this Class will be the proxy class or the proxy class. The method of the class assigns an index (int type).

This index is used as an input parameter, and FastClass can directly locate the method to be called and call it directly, which saves the reflection call, so the call efficiency is higher than that of the JDK dynamic proxy call through reflection.

public class BusinessImpl$$FastClassByCGLIB$$e73ca2d3 extends FastClass {
    
    
    public BusinessImpl$$FastClassByCGLIB$$e73ca2d3(Class var1) {
    
    
        super(var1);
    }

    public int getIndex(Signature var1) {
    
    
        String var10000 = var1.toString();
        switch(var10000.hashCode()) {
    
    
        case 765831722:
            if (var10000.equals("doBusiness()V")) {
    
    
                return 0;
            }
            break;
		......
        return -1;
    }

    public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
    
    
        BusinessImpl var10000 = (BusinessImpl)var2;
        int var10001 = var1;

        try {
    
    
            switch(var10001) {
    
    
            case 0:
                var10000.doBusiness();
                return null;
            ......
            }
        } catch (Throwable var4) {
    
    
            throw new InvocationTargetException(var4);
        }
        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }
}

FastClass is not generated together with the proxy class, but is generated and placed in the cache when MethodProxy invoke/invokeSuper is executed for the first time.

		// MethodProxy invoke/invokeSuper都调用了init()
		private void init() {
    
    
	        if(this.fastClassInfo == null) {
    
    
	            Object var1 = this.initLock;
	            synchronized(this.initLock) {
    
    
	                if(this.fastClassInfo == null) {
    
    
	                    MethodProxy.CreateInfo ci = this.createInfo;
	                    MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
						// 如果缓存中就取出,没有就生成新的FastClass
	                    fci.f1 = helper(ci, ci.c1);
	                    fci.f2 = helper(ci, ci.c2);
						// 获取方法的index
	                    fci.i1 = fci.f1.getIndex(this.sig1);                      					fci.i2 = fci.f2.getIndex(this.sig2);
	                    this.fastClassInfo = fci;
	                    this.createInfo = null;
	                }
	            }
	        }
	    }

5. The difference between SDK dynamic proxy and CGLib dynamic proxy

  1. The JDK dynamic proxy implements the interface of the proxy object, and the CGLib dynamic proxy inherits the proxy object;
  2. Both generate bytecodes at runtime, JDK directly writes Class bytecodes, CGLib uses the ASM framework to write Class bytecodes, CGLib is more complex to implement, and the efficiency of generating proxy classes is lower than that of JDK;
  3. When calling the proxy method, JDK calls it through the reflection mechanism, and CGLib directly calls the method through the FastClass mechanism, and the execution efficiency is higher than that of JDK.

6. AspectJ

It is a Java-based AOP framework that is more complex and performant than Spring AOP.

1. Basic Usage

Similarly, the following is an example of a piece of code for AspectJ:

业务接口Follow the code from the previous article

1)业务逻辑切面类

	@Aspect
	public class BusinessAspect {
    
    
	    @Before("execution(* doBusiness(..))")
	    public void beforeDoingBusiness(){
    
    
	        System.out.println("Doing pre-advice");
	    }
	    ......
	}

These are @Before("execution(* doBusiness(..))")called 前置增强annotations. The expression means that in execution:执行, *:任何类的, doBusiness(..):方法, @Before:之前, all execute the following beforeDoingBusinessmethods.

The code of the same @AfterReturning后置增强annotation is omitted.

2)业务逻辑调用类

	public class CallBusiness {
    
    
	    public static void main(String[] args) {
    
    
	        BusinessImpl business = new BusinessImpl();
	        AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
	        proxyFactory.setTarget(business);
	        proxyFactory.addAspect(BusinessAspect.class);
	        BusinessImpl proxy = proxyFactory.getProxy();
	        proxy.doBusiness();
	    }
	}

输出结果Same, no more details here.

It can be seen that in the usage of AspectJ, the code is more concise and easy to remember. The only difficulty is: the imported aspect rule syntax requires a certain amount of learning and memory.

2. Several enhanced annotations provided by AspectJ

  1. @Before: Pre-enhancement, equivalent to the function of BeforeAdvice;
  2. @AfterReturning: Post enhancement, equivalent to AfterReturningAdvice;
  3. @Around: surround enhancement, equivalent to MethodInterceptor;
  4. @AfterThrowing: Throw enhancement, equivalent to ThrowsAdvice;
  5. @After: Final enhancement, regardless of throwing an exception or exiting normally, the enhancement will be executed. This enhancement has no corresponding enhancement interface. It can be regarded as a mixture of ThrowsAdvice and AfterReturningAdvice. It is generally used to release resources, which is equivalent to try{} The control flow of finally{};
  6. @DeclareParents: Introduction enhancement, equivalent to IntroductionInterceptor.

postscript

There is a lot of content in this article, but most of it is code examples.

For Spring Framework-related content, my free column "Spring Study Notes" has more detailed concepts, principles and practical methods, and can be moved if necessary.

In this article:
Spring Framework part: only lists the core concepts.

Spring AOP part: Concepts and principles should be known to deal with interviews, but here is also of high practical value. Some scenarios are listed: such as using surround enhancement to implement interface time-consuming statistics, using post-enhancement to report interface calls, android users click The ratio of some function buttons to report statistics, etc. Although AOP aspect-oriented programming cannot replace OOP object-oriented programming, as a supplement, it can omit a lot of repeated codes, which is very useful.

Guess you like

Origin blog.csdn.net/Mr_Megamind/article/details/130449290