Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Aspect-Oriented Programming: Proxy Factory and Annotation-Based AOP Implementation

Tech May 17 2

Dynamic Aspect Weaving

AOP fundamentally separates business logic from cross-cutting concerns. This separation provides several advantages:

  1. Aspect code needs to be written only once
  2. Developers focus solely on business logic without duplicating cross-cutting code
  3. Aspects are dynamically woven into business code at runtime

This article explores two approaches to implementing this separation.

AOP Implementation Using Proxy Factory

The following demonstrates implementing AOP with Spring and JDK dynamic proxy.

Step 1: Create the Aspect Class

package com.example.aop;

import org.springframework.stereotype.Component;

@Component
public class TransactionAspect {
    
    public void startTransaction() {
        System.out.println("Transaction started");
    }
    
    public void endTransaction() {
        System.out.println("Transaction completed");
    }
}

Step 2: Define the Service Interface and Implementation

package com.example.aop;

public interface UserService {
    void create();
    void remove();
}
package com.example.aop;

import javax.annotation.Resource;
import org.springframework.stereotype.Component;

@Component
public class UserServiceImpl implements UserService {

    @Resource
    private TransactionAspect transactionAspect;
    
    @Override
    public void create() {
        System.out.println("Business logic: create user");
    }

    @Override
    public void remove() {
        System.out.println("Business logic: remove user");
    }
}

Step 3: Create the Proxy Factory

package com.example.aop;

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

public class ServiceProxyFactory {

    static Object target;
    static TransactionAspect aspect;
    
    public static Object createProxy(Object targetObject, TransactionAspect targetAspect) {
        target = targetObject;
        aspect = targetAspect;
        
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(), 
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args)
                            throws Throwable {
                        aspect.startTransaction();
                        Object result = method.invoke(target, args);
                        aspect.endTransaction();
                        return result;
                    }
                });
    }
}

Step 4: Configure Spring XML

<!-- Enable annotation scanning -->            
<context:component-scan base-package="com.example.aop"></context:component-scan>
        
<!-- Configure proxy bean -->
<bean id="userServiceProxy" class="com.example.aop.ServiceProxyFactory" factory-method="createProxy">
    <constructor-arg index="0" ref="userServiceImpl"></constructor-arg>
    <constructor-arg index="1" ref="transactionAspect"></constructor-arg>
</bean>

Step 5: Test the Proxy

@Test
public void testProxy() {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserService service = (UserService) ctx.getBean("userServiceProxy");
    service.create();
    service.remove();
}

Annotation-Based AOP

Spring provides robust annotation support for AOP, enabling declarative aspect configuraton.

Step 1: Add Dependencies

Include the following JARs: spring-aop-x.x.x.RELEASE.jar, aopalliance.jar, aspectjweaver.jar, and aspectjrt.jar.

Step 2: Configure Spring XML

<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    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
        ">
                
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
        
        <context:component-scan base-package="com.example.aop.annotation"></context:component-scan>

</beans>

Step 3: AOP Annotations Reference

Annotation Purpose
@Aspect Marks a class as an aspect
@Pointcut("execution(* com.example.aop.annotation..(..))") Defines pointcut expression
@Before("pointCut()") Executes before target method
@After("pointCut()") Executes after target method (always)
@AfterReturning("pointCut()") Executes after method returns (not on exception)
@AfterThrowing("pointCut()") Executes when exception occurs
@Around("pointCut()") Wraps around target method execution

Step 4: Create the Aspect Class

package com.example.aop.annotation;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class LoggingAspect {
    
    @Pointcut("execution(* com.example.aop.annotation.*.*(..))")
    public void allMethods() { }
    
    @Before("allMethods()")
    public void beforeAdvice() {
        System.out.println("Before: Starting operation");
    }
    
    @After("allMethods()")
    public void afterAdvice() {
        System.out.println("After: Operation completed");
    }
    
    @AfterReturning("allMethods()")
    public void afterSuccess() {
        System.out.println("AfterReturning: Normal completion");
    }
    
    @AfterThrowing("allMethods()")
    public void afterException() {
        System.out.println("AfterThrowing: Exception occurred");
    }
    
    @Around("allMethods()")
    public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("Around: Before proceed");
        Object result = pjp.proceed();
        System.out.println("Around: After proceed");
        return result;
    }
}

Step 5: Define the Service

package com.example.aop.annotation;

public interface UserService {
    void create();
    void remove();
}
package com.example.aop.annotation;

import javax.annotation.Resource;
import org.springframework.stereotype.Component;

@Component
public class UserServiceImpl implements UserService {
    
    @Override
    public void create() {
        System.out.println("Business logic: create user");
    }

    @Override
    public void remove() {
        System.out.println("Business logic: remove user");
    }
}

Step 6: Verify Execution Order

@Test
public void testAnnotationAop() {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserService service = (UserService) ctx.getBean("userServiceImpl");
    System.out.println(service.getClass());
    service.create();
    service.remove();
}

Execution Order Analysis

The actual execution sequence when all advice types are present:

  1. Around (before proceed)
  2. Before
  3. Target method executes
  4. After
  5. AfterReturning
  6. Around (after proceed)

Key observations:

  • Around (before) executes before Before
  • Around (after) executes after AfterReturning
  • After always executes regardless of success or failure
  • AfterReturning executes only on normal completion
  • AfterThrowing executes only when an exception is thrown

Proxy selection behavior: When the target object implements an interface, Spring defaults to JDK dynamic proxy. When no interface exists, Spring uses CGLIB proxy.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.