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.
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><aop:config>
<aop:aspect id="myAspect" ref="aBean">
<aop:pointcut id="businessService" expression="execution(* com.pack.service.*.*(..))"/>
<aop:before pointcut-ref="businessService" method="monitor"/>
</aop:aspect>
</aop:config></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.
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.
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.