Skip to main content

Joinpoint

Please refer to spring-framework under org.springframework.mylearntest package for code related to this article (from official source spring-test module).

AOP Implementation Ways in JAVA

Languages capable of implementing AOP are collectively called AOL (Aspect-Oriented Language). Besides AspectJ:

  • AspectC
  • AspectC++
  • Aspect.Net
  • AspectL(Lisp)
  • AspectPHP
  • ......

AOP in JAVA

  1. Dynamic Proxy
  • During runtime, dynamically generate corresponding proxy objects for corresponding interfaces, encapsulate cross-cutting concern logic into InvocationHandler of dynamic proxy, then during system runtime, weave cross-cutting logic into corresponding proxy classes according to module positions where cross-cutting concern needs to be woven.
  1. Dynamic Bytecode Enhancement
  • Use Java tool libraries like ASM or CGLIB to dynamically construct class files of bytecode during program runtime.
  • Weave cross-cutting logic through dynamic bytecode enhancement technology during runtime, generate corresponding subclasses for these system module classes, and add cross-cutting logic to these subclasses, so that these dynamically generated subclasses are used during application execution, thereby achieving the purpose of weaving cross-cutting logic into the system.
  • If classes or instance methods in classes that need to be extended are declared as final, they cannot be extended by subclassing. When Spring AOP cannot use dynamic proxy mechanism to extend AOP functions, it will use dynamic bytecode enhancement technology of CGLIB library to implement AOP extension.
  1. Java Code Generation
  • EJB container provides weaving information based on deployment descriptor file, will generate corresponding java code for corresponding functional module classes according to information provided by descriptor, then compile java code to generate corresponding java classes through deployment tools or deployment interfaces. Afterward, functional module classes deployed to EJB container can work normally.
  1. Custom Class Loader
  • All class files of java programs must be loaded into Java Virtual Machine by corresponding Classloader before they can run. Default Classloader will read class bytecode file, then parse and load these class files into virtual machine for running according to class bytecode specification. If I can weave cross-cutting logic into class file during this class loading into virtual machine for running, wouldn't it complete the integration of AOP and OPP?
  • We can complete weaving of cross-cutting logic into system by custom class loader. Custom class loader reads weaving rules and necessary information prescribed by external files, can add cross-cutting logic to existing logic of system module classes during class file loading, then hand over modified class to java virtual machine for running. Through class loader, we can basically weave most classes and corresponding instances, function is naturally much more powerful than previous ways. However, the biggest problem with this way is use of class loader itself. Some application servers control the entire class loading system, so certain problems may occur in such scenarios.
  • Jboss AOP and AspectWerkz framework which has been merged into AspectJ project both use custom class loader way to implement.
  1. AOL Extension
  • AOL extension is the most powerful and most difficult to master way, AspectJ mentioned before belongs to this way. In this way, various concepts of AOP mostly have one-to-one corresponding entities in AOL. We can use extended AOL to implement any AOP concept entities and even OPP concept entities, such as Aspect and Class. All AOP concepts get the most perfect expression in AOL.
  • Using extended AOL, it is quite powerful in expression of AOP concepts, making all cross-cutting concern logic involved in AOP can live freely in its own "kingdom" before weaving. And features like "compiling to static class can improve system running performance", "java virtual machine can load file where woven AO component is located and run it like loading normal class". Using this way requires learning an extended AOL language.

Joinpoint

  • Joinpoint

  • Pointcut Expression

    • Directly specify method name where Joinpoint is located
    • Regular Expression: Supported by Jboss, Spring AOP, AspectWerkz etc.
    • Use specific Pointcut expression language: After Spring 2.0, with help of AspectJ's Pointcut expression language interpreter, supports using AspectJ's Pointcut expression language to specify Pointcut.

Static Proxy

IRequestable
package org.springframework.mylearntest.aop.staticproxy;

public interface IRequestable {
void request();
}
package org.springframework.mylearntest.aop.staticproxy;

public class RequestableImpl implements IRequestable{
@Override
public void request() {
System.out.println(" request process in RequestableImpl");
}
}
ServiceControlRequestableProxy
package org.springframework.mylearntest.aop.staticproxy;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ServiceControlRequestableProxy implements IRequestable{
private static final Logger logger = LoggerFactory.getLogger(ServiceControlRequestableProxy.class);
private IRequestable requestable;

public ServiceControlRequestableProxy(IRequestable target) {
this.requestable = target;
}

@Override
public void request() {
System.out.println("request process in ServiceControlRequestableProxy");
requestable.request();
}

public static void main(String[] args) {
IRequestable target = new RequestableImpl();// Object to be proxied
IRequestable proxy = new ServiceControlRequestableProxy(target); // Pass object to be proxied into proxy via constructor
proxy.request();// Let proxy handle request
}
}

Dynamic Proxy

Dynamic proxy mechanism mainly consists of a class and an interface, namely java.lang.reflect.Proxy class and java.lang.reflect.InvocationHadler interface.

RequestCtrlInvocationHandler
package org.springframework.mylearntest.aop.dynamicproxy;

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

public class RequestCtrlInvocationHandler implements InvocationHandler {
private Object target;

public RequestCtrlInvocationHandler(Object target) {
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("reflect invoke before target method");
if ("request".equals(method.getName())) {
System.out.println("dynamic proxy target method");
return method.invoke(target, args);
}
return null;
}
}
Test4DynamicProxy
package org.springframework.mylearntest.aop.dynamicproxy;

import org.springframework.mylearntest.aop.staticproxy.IRequestable;
import org.springframework.mylearntest.aop.staticproxy.RequestableImpl;

import java.lang.reflect.Proxy;

@SuppressWarnings("rawtypes")
public class Test4DynamicProxy {
public static void main(String[] args) {
// arg1:ClassLoader arg2:Interface info arg3:InvocationHandler implementation class and pass in object to be proxied
IRequestable requestable = (IRequestable) Proxy.newProxyInstance(
Test4DynamicProxy.class.getClassLoader(),
new Class[]{IRequestable.class},
new RequestCtrlInvocationHandler(new RequestableImpl()));
requestable.request();
}
}

If you want to understand dynamic proxy deeply, please read "Java Reflection in Action".

CGLIB Bytecode Generation

To use CGLIB extension subclass, first need to implement a net.sf.cglib.proxy.Callback, but more often, we will directly use net.sf.cglib.proxy.MethodInterceptor interface (MethodInterceptor extends Callback interface).

Requestable
package org.springframework.mylearntest.aop.CGLIBClassGenerate;

public class Requestable {
public void request(){
System.out.println("req in requestable without implement any interface");
}
}
RequestCtrlCallback
package org.springframework.mylearntest.aop.CGLIBClassGenerate;

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class RequestCtrlCallback implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
if (method.getName().equals("request")) {
System.out.println("proxy generated by cglib intercept method request");
return methodProxy.invokeSuper(o, objects);
}
return null;
}
}
Test4CGLIB
package org.springframework.mylearntest.aop.CGLIBClassGenerate;

import org.springframework.cglib.proxy.Enhancer;

public class Test4CGLIB {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Requestable.class);
enhancer.setCallback(new RequestCtrlCallback());

Requestable proxy = (Requestable) enhancer.create();
proxy.request();
}
}
Agreement
The code part of this work is licensed under Apache License 2.0 . You may freely modify and redistribute the code, and use it for commercial purposes, provided that you comply with the license. However, you are required to:
  • Attribution: Retain the original author's signature and code source information in the original and derivative code.
  • Preserve License: Retain the Apache 2.0 license file in the original and derivative code.
The documentation part of this work is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License . You may freely share, including copying and distributing this work in any medium or format, and freely adapt, remix, transform, and build upon the material. However, you are required to:
  • Attribution: Give appropriate credit, provide a link to the license, and indicate if changes were made.
  • NonCommercial: You may not use the material for commercial purposes. For commercial use, please contact the author.
  • ShareAlike: If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original.