How to Handwrite Spring AOP: From Concepts to Code Implementation

This article walks through the design and implementation of a hand‑written Spring AOP module, covering core concepts such as Advice, Pointcut, weaving, proxy creation, and the integration of these components into a bean lifecycle to enable method‑level enhancements without modifying source code.

Java High-Performance Architecture
Java High-Performance Architecture
Java High-Performance Architecture
How to Handwrite Spring AOP: From Concepts to Code Implementation

Spring Source Handwritten Series - Implementing AOP

After implementing a hand‑written IoC and DI, we now add AOP capabilities so users can enhance method behavior without changing source code.

1. AOP Analysis

1. What is AOP?

Aspect‑Oriented Programming (AOP) enables adding functionality to class methods without changing the class code.

2. What are we building?

Based on the previously written IoC/DI, we provide AOP features that allow users to apply advice, pointcuts, and weaving.

3. Requirements

Provide AOP functionality as defined by the concepts above.

2. AOP Concept Explanation

Key concepts include Advice, Pointcut, and Weaving. We examine which parts must be supplied by the user and which are built into the framework.

3. Implementing Advice

1. Advice

1.1 Interface‑Based Design

Advice is supplied by the user. To handle variability we define standard interfaces that users implement.

How to recognize user‑provided components?

How to isolate user variability?

We define a plain interface and let users implement it.

1.2 Advice Characteristics

Advice can run before, after, around, after‑returning, after‑throwing, etc.

Before

AfterReturn

Around

After

Throwing

1.3 Specific Advice Types

1.3.1 Before Advice

Before : executed before method invocation. No return value needed.

public interface MethodBeforeAdvice extends Advice {
    /**
     * Implement this method for before advice
     *
     * @param method the method to be enhanced
     * @param args   method arguments
     * @param target target object
     */
    void before(Method method, Object[] args, Object target) throws Throwable;
}

1.3.2 After Advice

Executed after method execution.

public interface AfterAdvice extends Advice {
    /**
     * Implement this method for after advice
     *
     * @param returnValue the returned value
     * @param method      the enhanced method
     * @param args        method arguments
     * @param target      target object
     */
    void after(Object returnValue, Method method, Object[] args, Object target) throws Throwable;
}

1.3.3 After Returning Advice

public interface AfterReturningAdvice extends Advice {
    void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable;
}

1.3.4 Around Advice

public interface MethodInterceptor extends Advice {
    /**
     * Perform around (before/after) enhancement and exception handling.
     *
     * @param method the enhanced method
     * @param args   method arguments
     * @param target target object
     * @return the method's return value
     */
    Object invoke(Method method, Object[] args, Object target) throws Throwable;
}

1.3.5 Throwing Advice

public interface ThrowsAdvice extends Advice {
    void afterThrowing(Method method, Object[] args, Object target, Exception ex) throws Throwable;
}

1.4 Advice Design

We derive an advice hierarchy diagram (omitted).

2. Pointcut

2.1 Pointcut Characteristics

User‑specified

Flexible

Can match multiple join points

2.2 Pointcut Analysis

Users need to specify method signatures, possibly with wildcards for package, class, method name, and parameter types.

com.boge.spring.aop.Girl.dbj(Boy,Time)

com.boge.spring.aop.Girl.dbj(Boy,Girl,Time)

2.3 Pointcut Design

Define an interface with methods to match classes and methods.

public interface Pointcut {
    boolean matchsClass(Class<?> targetClass);
    boolean matchsMethod(Method method, Class<?> targetClass);
}

AspectJExpressionPointcut implements this interface using AspectJ's parser.

public class AspectJExpressionPointcut implements Pointcut {
    private static PointcutParser pp = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution();
    private String expression;
    private PointcutExpression pe;
    public AspectJExpressionPointcut(String expression) {
        this.expression = expression;
        pe = pp.parsePointcutExpression(expression);
    }
    @Override
    public boolean matchsClass(Class<?> targetClass) {
        return pe.couldMatchJoinPointsInType(targetClass);
    }
    @Override
    public boolean matchsMethod(Method method, Class<?> targetClass) {
        ShadowMatch sm = pe.matchesMethodExecution(method);
        return sm.alwaysMatches();
    }
    public String getExpression() {
        return expression;
    }
}

3. Aspect

Users combine Advice and Pointcut via an Advisor.

4. Advisor

Advisor acts as a façade that groups Advice and Pointcut for easier use.

4. Weaving Implementation

1. Weaving Analysis

Weaving attaches user‑provided enhancements to target methods, typically during bean initialization after the bean is created.

2. Weaving Design

We use a BeanPostProcessor to intercept bean creation, match advisors, and apply proxies.

2.1 BeanPostProcessor Interface

public interface BeanPostProcessor {
    Object postProcessBeforeInitialization(Object bean, String beanName) throws Throwable;
    Object postProcessAfterInitialization(Object bean, String beanName) throws Throwable;
}

2.2 Matching Advisors

During post‑process after initialization, we collect matching advisors and create a proxy if needed.

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws Throwable {
    List<Advisor> matchAdvisors = getMatchedAdvisors(bean, beanName);
    if (CollectionUtils.isNotEmpty(matchAdvisors)) {
        bean = this.createProxy(bean, beanName, matchAdvisors);
    }
    return bean;
}

2.3 Proxy Creation

We generate a dynamic proxy (JDK or CGLIB) that delegates to a chain of advices.

2.3.1 Advice Chain Execution

public static Object applyAdvices(Object target, Method method, Object[] args,
                                 List<Advisor> matchAdvisors, Object proxy, BeanFactory beanFactory) throws Throwable {
    List<Object> advices = AopProxyUtils.getShouldApplyAdvices(target.getClass(), method, matchAdvisors, beanFactory);
    if (CollectionUtils.isEmpty(advices)) {
        return method.invoke(target, args);
    } else {
        AopAdviceChainInvocation chain = new AopAdviceChainInvocation(proxy, target, method, args, advices);
        return chain.invoke();
    }
}

2.3.2 Proxy Factory

public interface AopProxyFactory {
    AopProxy createAopProxy(Object bean, String beanName, List<Advisor> matchAdvisors, BeanFactory beanFactory) throws Throwable;
    static AopProxyFactory getDefaultAopProxyFactory() {
        return new DefaultAopProxyFactory();
    }
}

With these components the hand‑written Spring AOP framework is complete.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaaopBackend Developmentdependency-injectionAspect Oriented Programming
Java High-Performance Architecture
Written by

Java High-Performance Architecture

Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.