Realization of JDK dynamic proxy in Spring AOP

Realization of JDK dynamic proxy in Spring AOP

Dynamic proxy mechanism

  • Create your own invocation handler by implementing the InvocationHandler interface

  • Create a dynamic proxy class by specifying a ClassLoader object and a set of interfaces for the Proxy class

  • The constructor of the dynamic proxy class is obtained through the reflection mechanism, and the only parameter type is the call processor interface type

  • The dynamic proxy class instance is created through the constructor, and the processor object is called as a parameter during construction.

AOP thinking

  • OOP introduces concepts such as encapsulation, inheritance, and polymorphism to establish a hierarchy of objects to simulate a collection of public behaviors.

  • The AOP technology uses a technique called "cross-cutting" to dissect the inside of the encapsulated object and encapsulate those common behaviors that affect multiple classes into a reusable module, which can reduce the duplication of the system code and reduce the inter-module The degree of coupling is conducive to future operability and maintainability.

  • AOP divides the software system into two parts: core concerns and cross-cutting concerns. The main process of business processing is the core concern, and the part that has little to do with it is the cross-cutting concern.

Dynamic proxy source code to implement AopProxyFactory

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
    
    

	@Override
	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    
    
		if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
    
    
			Class<?> targetClass = config.getTargetClass();
			if (targetClass == null) {
    
    
				throw new AopConfigException("TargetSource cannot determine target class: " +
						"Either an interface or a target is required for proxy creation.");
			}
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
    
    
				// 表示在有接口实现的时候采用JDK动态代理
                return new JdkDynamicAopProxy(config);
			}
            // 在没有接口实现的时候采用Cglib动态代理
			return new ObjenesisCglibAopProxy(config);
		}
		else {
    
    
			return new JdkDynamicAopProxy(config);
		}
	}

Implementation of AOP in Spring

The program is executed from top to bottom, and has little relationship with the main business logic. AOP aspect-oriented programming is to separate the main business from the cross-cutting code and achieve decoupling.

Common implementations are:

  • Unified log processing
  • Unified exception handling
  • Spring transaction management

Code

Define the interface

/**
 * @ClassName: BuyService
 * @Description: 购买基础接口
 * @Author: 尚先生
 * @CreateDate: 2019/6/17 14:52
 * @Version: 1.0
 */
public interface BuyService {
    
    

    String buyPhone(BuyService buyService);

    String buyComputer(BuyService buyService);

}

Define the implementation class

/**
 * @ClassName: BusiServiceImpl
 * @Description: 购买实现类
 * @Author: 尚先生
 * @CreateDate: 2019/6/17 14:53
 * @Version: 1.0
 */
@Service
public class BuyServiceImpl implements BuyService {
    
    

    @Intercept("buyPhone")
    @Override
    public String buyPhone(BuyService buyService) {
    
    
        try {
    
    
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("==========当前类描述 " + buyService.getClass().getName() + "=============" + " buyPhone");
        this.buyComputer(this);
        return "buy phone";
    }

    @Intercept("buyComputer")
    @Override
    public String buyComputer(BuyService buyService) {
    
    
        try {
    
    
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("==========当前类描述 " + buyService.getClass().getName() + "=============" + " buyComputer");
        return "buy computer";
    }
}

Custom block annotation

/**
 * @ClassName: Intercept
 * @Description: 自定义拦截器注解
 * @Author: 尚先生
 * @CreateDate: 2019/6/17 16:28
 * @Version: 1.0
 */
@Target({
    
    ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Intercept {
    
    

    String value() default "";
}

Custom interceptor implementation

/**
 * @ClassName: Interceptor
 * @Description: 拦截器实现类
 * @Author: 尚先生
 * @CreateDate: 2019/6/17 15:12
 * @Version: 1.0
 */
@Component
@Aspect
public class Interceptor {
    
    
    @Pointcut(value = "@annotation(com.learn.demo.java.proxy.Intercept)")
    public void buySomething() {
    
    
        System.out.println("===========自定义切入点===============");
    }
    @Around("buySomething()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
    
    
        try {
    
    
            //通过获取 Intercept 注解
            Method proxyMethod = ((MethodSignature) point.getSignature()).getMethod();
            Method targetMethod = point.getTarget().getClass().getMethod(proxyMethod.getName(), proxyMethod.getParameterTypes());
            Intercept intercept = targetMethod.getAnnotation(Intercept.class);
            String methodName = targetMethod.getName();
            //处理注解逻辑
            String value = intercept.value();
            System.err.println("=========== " + methodName + " 获取前置拦截信息 ===========" + value);
            return point.proceed();
        } catch (Throwable e) {
    
    
            System.out.println("执行异常"+ e.getMessage());
        }finally {
    
    
            System.err.println("=========== " + " 后置处理结果返回 ===========");
        }
        return "执行异常,请查看详细日志信息";
    }
}

Custom interceptor configuration class

/**
 * @ClassName: AspectJConfig
 * @Description: 开启Spring对AspectJ的支持
 * @Author: 尚先生
 * @CreateDate: 2019/6/17 18:39
 * @Version: 1.0
 */
@Configuration
@ComponentScan("com.learn.demo.java.proxy")
@EnableAspectJAutoProxy
public class AspectJConfiguration {
    
    
}

Start the boot class

/**
 * @ClassName: Bootstrap
 * @Description: 启动测试类
 * @Author: 尚先生
 * @CreateDate: 2019/6/17 14:58
 * @Version: 1.0
 */
public class Bootstrap {
    
    
    public static void main(String[] args) {
    
    
        // spring 采用的 jdk 动态代理
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

        context.scan("com.learn.demo.java.proxy");

        context.register(Interceptor.class);

        context.refresh();

        BuyService bean = context.getBean("buyServiceImpl", BuyService.class);

        String phone = bean.buyPhone(bean);

        System.err.println("=========Bootstrap.class============== " + phone);

        // 输出代理对象 class 文件
        createProxyClassFile();
    }

    /**
     * 生成代理文件
     */
    private static void createProxyClassFile() {
    
    
        String name = "ProxyBuyService";
        byte[] data = ProxyGenerator.generateProxyClass(name,
                new Class[] {
    
    BuyService.class});
        FileOutputStream out = null;
        try {
    
    
            out = new FileOutputStream("D://" + name + ".class");
            out.write(data);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            if (null != out)
                try {
    
    
                    out.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }
        }
    }
}

Results of the

=========== buyPhone 获取前置拦截信息 ===========buyPhone
==========当前类描述 com.sun.proxy.$Proxy22============= buyPhone
===========  后置处理结果返回 ===========
=========Bootstrap.class============== buy phone
==========当前类描述 com.learn.demo.java.proxy.BuyServiceImpl============= buyComputer

Process analysis

  • From the execution results, you can clearly see the object com.sun.proxy.$Proxy22 executed in buyPhone(), and then execute this.buyComputer(this); the executed object becomes com.learn.demo.java.proxy.BuyServiceImpl , So only the buyPhone() method will be intercepted in aspect-oriented programming.

  • The main reason is that the generated proxy object is not the same as the proxy object, so the latter calls the class method as if it is a direct call, without going across the board.

  • The code has been updated in GitHub . For more details, please follow dwyanewede . For more JDK dynamic proxy, please refer to the previous article: JDK dynamic proxy implementation principle

More excellent articles

JDK dynamic proxy implementation principle
https://blog.csdn.net/shang_xs/article/details/92772437
Primary school students in java
https://blog.csdn.net/shang_xs

Official account recommendation

Insert picture description here

Guess you like

Origin blog.csdn.net/shang_xs/article/details/92778364