Backend Development 9 min read

Mastering Spring AOP: XML, Annotations, and ProxyFactoryBean Deep Dive

This article explains Spring AOP fundamentals, demonstrates XML‑based and annotation‑based configurations, and provides a comprehensive guide to using ProxyFactoryBean—including its properties, proxy‑interface and proxy‑class scenarios, CGLIB considerations, and wildcard interceptor matching—complete with runnable code examples.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Mastering Spring AOP: XML, Annotations, and ProxyFactoryBean Deep Dive

1. Introduction

In the Spring framework, AOP (Aspect‑Oriented Programming) enables adding cross‑cutting concerns such as logging, transaction management, and security without modifying existing code.

XML‑Based Configuration

Early Spring versions often used XML files to declare aspects, advices, and pointcuts. The key tags are <aop:config> , <aop:aspect> , and <aop:before> (among others).

<code>&lt;aop:config&gt;
  &lt;aop:aspect id="myAspect" ref="aBean"&gt;
    &lt;aop:pointcut id="businessService" expression="execution(* com.pack.service.*.*(..))"/&gt;
    &lt;aop:before pointcut-ref="businessService" method="monitor"/&gt;
  &lt;/aop:aspect&gt;
&lt;/aop:config&gt;</code>

Annotation‑Based Configuration

Using annotations such as @Aspect , @Before , and @After makes AOP definitions more concise and readable.

<code>@Component
@Aspect
public class LogAspect {
    @Pointcut("execution(* save(..))")
    private void logPc() {}

    @Around("logPc()")
    public Object process(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("before log...");
        Object ret = pjp.proceed();
        System.out.println("after log...");
        return ret;
    }
}</code>

2. Practical Example – ProxyFactoryBean

ProxyFactoryBean offers a flexible way to create AOP proxies programmatically, especially when you need custom control over bean creation.

2.1 Configurable Properties

proxyTargetClass : Set to true to create a CGLIB proxy for the target class instead of an interface‑based JDK proxy.

optimize : Enables aggressive optimizations for CGLIB proxies (do not use lightly).

frozen : When true, the proxy configuration cannot be changed after creation.

exposeProxy : Exposes the current proxy via AopContext.currentProxy() when true.

proxyInterface : Array of interface names to expose.

interceptorNames : Array of advisor or interceptor bean names to apply.

2.2 Proxying an Interface

Define a target bean, an interceptor, and configure ProxyFactoryBean to expose the interface.

<code>public interface ICommonDAO {
    void save();
}

@Component("commonDAOTarget")
public class CommonDAOImpl implements ICommonDAO {
    @Override
    public void save() {
        System.out.println("save operator...");
    }
}

@Component
public class LogInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("before log...");
        Object ret = invocation.proceed();
        System.out.println("after log...");
        return ret;
    }
}

@Configuration
public class AppConfig {
    @Bean
    @Primary
    public ProxyFactoryBean commonDAO(@Qualifier("commonDAOTarget") CommonDAOImpl commonDAOTarget) throws Exception {
        ProxyFactoryBean proxy = new ProxyFactoryBean();
        proxy.setProxyInterfaces(new Class<?>[] { ICommonDAO.class });
        proxy.setTarget(commonDAOTarget);
        proxy.setInterceptorNames("logInterceptor");
        return proxy;
    }
}</code>

Test:

<code>ICommonDAO dao = context.getBean(ICommonDAO.class);
dao.save();
// Output:
// before log...
// save operator...
// after log...</code>

2.3 Proxying a Class (CGLIB)

If the target does not implement an interface, enable CGLIB proxying by setting proxyTargetClass to true.

<code>@Component("commonDAOTarget")
public class CommonDAO {
    public void save() {
        System.out.println("save operator...");
    }
}

@Bean
@Primary
public ProxyFactoryBean commonDAO(@Qualifier("commonDAOTarget") CommonDAO commonDAOTarget) throws Exception {
    ProxyFactoryBean proxy = new ProxyFactoryBean();
    proxy.setTarget(commonDAOTarget);
    proxy.setInterceptorNames("logInterceptor");
    proxy.setProxyTargetClass(true);
    return proxy;
}</code>

Inspecting the bean class shows a CGLIB‑generated subclass.

<code>class com.pack.aop.create.ProxyFactoryBeanTest2$CommonDAO$$SpringCGLIB$$1</code>

2.4 Limitations of CGLIB Proxies

Final classes cannot be proxied.

Final methods cannot be advised.

Private methods cannot be intercepted.

Package‑private methods from a different package are effectively private and cannot be advised.

2.5 Wildcard Interceptor Matching

Interceptor names can be specified with a trailing wildcard, allowing all beans whose names start with a given prefix to be applied.

<code>@Component("global_log")
public class LogInterceptor implements MethodInterceptor {}

@Component("global_auth")
public class AuthInterceptor implements MethodInterceptor {}

public ProxyFactoryBean commonDAO() throws Exception {
    ProxyFactoryBean proxy = new ProxyFactoryBean();
    // The wildcard must be at the end
    proxy.setInterceptorNames("global_*");
    return proxy;
}</code>

During initialization, Spring automatically discovers all beans whose names begin with global_ and registers them as interceptors.

AOPSpringAnnotationsXMLCGLIBProxyFactoryBean
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login 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.