The principle and practice of static proxy and dynamic proxy in Spring AOP

Regarding the recent enthusiasm of bloggers to write blogs, I am also thinking: Why do you write blogs? In the Internet age, whether you are a master or a rookie, you have the right to express your own opinions. Whether you are right or wrong, you will find answers on this platform. So, I will try my best to write the content I am interested in, no matter positive or negative news, I will reply to each of my readers as much as possible. Even if I only have one reader, I will keep writing. Isn't it a joy to have a platform to express yourself and record your own moments? Similarly, understanding technology is a process of in-depth and expansion, which requires a person's clear and rigorous logical thinking. Sometimes, blogging is more like taking notes for yourself and consolidating scattered knowledge!


In the previous article, I analyzed and discussed the source code of spring, and I won't repeat it here. Interested students can look at bowing to the spring boss--a large number of source code outflow analysis . This article is a supplement to the previous article. . Back to the topic, why is there Aspect Oriented Programming (AOP)? We know that java is an object-oriented (OOP) language, but it has some drawbacks, such as when we need to introduce a common behavior for multiple objects without inheritance relationship, such as logging, permission verification, transaction and other functions, only Referencing the common behavior in every object is not easy to maintain, and there is a lot of duplication of code. The emergence of AOP makes up for this deficiency of OOP.

In order to clarify spring AOP, we will discuss the following aspects:

  1. proxy mode.
  2. The principle and practice of static proxy.
  3. Principle and practice of dynamic agency.
  4. Spring AOP principle and actual combat.



1. Proxy mode.

Proxy mode: Provides a proxy for other objects to control access to this object. This passage is more official, but I prefer to understand it in my own language: For example, if object A wants to do something, before there is no proxy, it will do it by itself. After proxying to A, it will be done by the proxy class B of A. The proxy actually adds a layer of processing before and after the original instance, which is also the primary outline of AOP.



2. The principle and practice of static proxy.

Static proxy mode: To put it bluntly, static proxy means that the bytecode file of the proxy class already exists before the program runs, and the relationship between the proxy class and the original class has been determined before running. Without further ado, let's take a look at the code. For the convenience of reading, the blogger merges the separate class file into the interface, and the reader can directly copy the code to run:

package test.staticProxy;
// 接口
public interface IUserDao {
	void save();
	void find();
}
//目标对象
class UserDao implements IUserDao{
	@Override
	public void save() {
		System.out.println("模拟:保存用户!");
	}
	@Override
	public void find() {
		System.out.println("模拟:查询用户");
	}
}
/**
    静态代理
          特点:
	1. 目标对象必须要实现接口
	2. 代理对象,要实现与目标对象一样的接口
 */
class UserDaoProxy implements IUserDao{
	// 代理对象,需要维护一个目标对象
	private IUserDao target = new UserDao();
	@Override
	public void save() {
		System.out.println("代理操作: 开启事务...");
		target.save();   // 执行目标对象的方法
		System.out.println("代理操作:提交事务...");
	}
	@Override
	public void find() {
		target.find();
	}
}

Test Results:

Although the static proxy ensures that the business class only needs to focus on the logic itself, one interface of the proxy object only serves one type of object. If there are many methods to be proxyed, it is necessary to proxy for each method. Furthermore, if a method is added, in addition to the implementation class that needs to implement this method, all proxy classes also need to implement this method. Increased code maintenance costs. So how to solve it? The answer is to use dynamic proxies.


3. The principle and practice of dynamic agency

Dynamic proxy mode: The source code of the dynamic proxy class is dynamically generated during the running of the program through mechanisms such as JVM reflection, and the relationship between the proxy class and the delegate class is determined at runtime. Examples are as follows:

package test.dynamicProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 接口
public interface IUserDao {
	void save();
	void find();
}
//目标对象
 class UserDao implements IUserDao{
	@Override
	public void save() {
		System.out.println("模拟: 保存用户!");
	}
	@Override
	public void find() {
		System.out.println("查询");
	}
}
/**
 * 动态代理:
 *    代理工厂,给多个目标对象生成代理对象!
 *
 */
class ProxyFactory {
	// 接收一个目标对象
	private Object target;
	public ProxyFactory(Object target) {
		this.target = target;
	}
	// 返回对目标对象(target)代理后的对象(proxy)
	public Object getProxyInstance() {
		Object proxy = Proxy.newProxyInstance(
			target.getClass().getClassLoader(),  // 目标对象使用的类加载器
			target.getClass().getInterfaces(),   // 目标对象实现的所有接口
			new InvocationHandler() {			// 执行代理对象方法时候触发
				@Override
				public Object invoke(Object proxy, Method method, Object[] args)
						throws Throwable {

					// 获取当前执行的方法的方法名
					String methodName = method.getName();
					// 方法返回值
					Object result = null;
					if ("find".equals(methodName)) {
						// 直接调用目标对象方法
						result = method.invoke(target, args);
					} else {
						System.out.println("开启事务...");
						// 执行目标对象方法
						result = method.invoke(target, args);
						System.out.println("提交事务...");
					}
					return result;
				}
			}
		);
		return proxy;
	}
}


The test results are as follows:


Create test class object code in run test class

IUserDao proxy = (IUserDao)new ProxyFactory(target).getProxyInstance();

In fact, jdk dynamically generates a class to implement the interface, hiding this process:

class $jdkProxy implements IUserDao{}

The premise of using the dynamic proxy generated by jdk is that the target class must have an implemented interface . But here is another problem. If a class does not implement the interface, the jdk dynamic proxy cannot be used, so the Cglib proxy solves this problem.

Cglib is implemented in the form of a dynamically generated subclass inheritance target, which dynamically builds a subclass in memory at runtime, as follows:

public class UserDao{}
//Cglib是以动态生成的子类继承目标的方式实现,程序执行时,隐藏了下面的过程
public class $Cglib_Proxy_class  extends UserDao{}

The premise of Cglib use is that the target class cannot be final modified . Because final modified classes cannot be inherited.

Now, we can look at the definition of AOP: Aspect-oriented programming, the core principle is to use the dynamic proxy pattern to add relevant logic before and after method execution or when an exception occurs.

From the definition and the previous code we can find 3 points:

  1. AOP is based on the dynamic proxy pattern.
  2. AOP is at the method level (the method to be tested cannot be statically modified, because there cannot be static methods in the interface, and the compilation will report an error).
  3. AOP can separate business code and concern code (repetitive code), and dynamically inject concern code when executing business code. Aspects are classes formed by code of interest.



4. Spring AOP principle and actual combat

As mentioned above, jdk proxy and cglib proxy are two dynamic proxies. The excellent spring framework integrates both methods at the bottom layer, so we don't need to worry about dynamically generating proxies by ourselves. So, how does spring generate proxy objects?

  1. When creating a container object, a proxy object is generated according to the class intercepted by the pointcut expression.
  2. If the target object has an implementation interface, use the jdk proxy. If the target object does not implement the interface, the cglib proxy is used. Then get the proxy object from the container, and implant the methods of the "Aspect" class at runtime. By looking at the spring source code, we found such a passage in the DefaultAopProxyFactory class.


Simply from the literal meaning, if there is an interface, use the Jdk proxy, otherwise use Cglib, which just confirms the content stated above. The premise of using spring AOP to integrate the two proxy methods is as follows: If the target class does not implement the interface, and the class is final modified, then spring AOP programming cannot be performed!

Knowing the principle, now we will manually implement spring's AOP by ourselves:

package test.spring_aop_anno;

import org.aspectj.lang.ProceedingJoinPoint;

public interface IUserDao {
	void save();
}
//用于测试Cglib动态代理
class OrderDao {
	public void save() {
		//int i =1/0;用于测试异常通知
		System.out.println("保存订单...");
	}
}
//用于测试jdk动态代理
class UserDao implements IUserDao {
	public void save() {
		//int i =1/0;用于测试异常通知
		System.out.println("保存用户...");
	}
}
//切面类
class TransactionAop {
	public void beginTransaction() {
		System.out.println("[前置通知]  开启事务..");
	}
	public void commit() {
		System.out.println("[后置通知] 提交事务..");
	}
	public void afterReturing(){
		System.out.println("[返回后通知]");
	}
	public void afterThrowing(){
		System.out.println("[异常通知]");
	}
	public void arroud(ProceedingJoinPoint pjp) throws Throwable{
		System.out.println("[环绕前:]");
		pjp.proceed();    			   // 执行目标方法
		System.out.println("[环绕后:]");
	}
}

Spring's xml configuration file:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
	<!-- dao实例加入容器 -->
	<bean id="userDao" class="test.spring_aop_anno.UserDao"></bean>

	<!-- dao实例加入容器 -->
	<bean id="orderDao" class="test.spring_aop_anno.OrderDao"></bean>

	<!-- 实例化切面类 -->
	<bean id="transactionAop" class="test.spring_aop_anno.TransactionAop"></bean>

	<!-- Aop相关配置 -->
	<aop:config>
		<!-- 切入点表达式定义 -->
		<aop:pointcut expression="execution(* test.spring_aop_anno.*Dao.*(..))" id="transactionPointcut"/>
		<!-- 切面配置 -->
		<aop:aspect ref="transactionAop">
			<!-- 【环绕通知】 -->
			<aop:around method="arroud" pointcut-ref="transactionPointcut"/>
			<!-- 【前置通知】 在目标方法之前执行 -->
			<aop:before method="beginTransaction" pointcut-ref="transactionPointcut" />
			<!-- 【后置通知】 -->
			<aop:after method="commit" pointcut-ref="transactionPointcut"/>
			<!-- 【返回后通知】 -->
			<aop:after-returning method="afterReturing" pointcut-ref="transactionPointcut"/>
			<!-- 异常通知 -->
			<aop:after-throwing method="afterThrowing" pointcut-ref="transactionPointcut"/>
		</aop:aspect>
	</aop:config>
</beans>      

Pointcut expressions are not covered here. ref: Spring AOP pointcut expressions

The test results of the code are as follows:

At this point, we have all introduced spring AOP, back to the opening question, what do we do with it?

  1. Spring declarative transaction management configuration, another blogger's article: Distributed system architecture combat demo: ssm+dubbo
  2. Parameter verification of the Controller layer. ref: spring aop intercepts Controller for parameter verification
  3. A case study of MySQL database read and write separation using Spring AOP
  4. Before executing the method, determine whether it has permission.
  5. Log calls to partial functions. Monitor some important functions. If the specified exception is thrown, the relevant personnel can be notified by SMS or email.
  6. Information filtering, page forwarding and other functions, the power of a blogger is limited, and can only list so many. Welcome to the comment area to supplement the article.

What else can spring AOP do, and what magical functions can be achieved, it lies in every ordinary and wise programmer of us!

Reprinted in: https://my.oschina.net/liughDevelop/blog/1457097#comment-list

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325517726&siteId=291194637