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.
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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
