Mastering Spring AOP and JDK Dynamic Proxies: Common Issues and Fixes
This tutorial demonstrates how to create JavaBeans, DAO interfaces, and implementations, use JDK dynamic proxies to expose proxy classes, troubleshoot method‑level proxy failures, and apply Spring AOP with ProxyFactory or @Aspect, while also covering transaction pitfalls and best‑practice configurations.
Environment: Spring Boot 2.3.10
Demo Classes
JavaBean:
public class User {
private Integer id;
private String name;
public User(Integer id, String name) {
this.id = id;
this.name = name;
}
}DAO interface:
public interface UserDAO {
User save(User user);
User findUser(Integer id);
}DAO implementation:
@Component
public class UserDAOImpl implements UserDAO {
@Override
public User save(User user) {
this.findUser(user.getId());
System.out.println("save method : " + user);
return user;
}
@Override
public User findUser(Integer id) {
System.out.println("findUser method invoke...");
return new User(id, "张三" + new Random().nextInt(10000));
}
}JDK Dynamic Proxy
Generate a proxy class to demonstrate method interception within the same class:
public static void main(String[] args) {
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
UserDAO target = new UserDAOImpl();
UserDAO dao = (UserDAO) Proxy.newProxyInstance(
ProxyDemo.class.getClassLoader(),
new Class<?>[] {UserDAO.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
method.invoke(target, args);
return null;
}
}
);
context.set(dao);
dao.save(new User(1, "田七"));
}The property sun.misc.ProxyGenerator.saveGeneratedFiles saves the generated $Proxy0.class under %project_root%\com\sun\proxy. The generated proxy extends Proxy and implements UserDAO. Calls to save are intercepted ("before" is printed), while findUser is not.
To proxy findUser as well, the proxy instance is stored in a ThreadLocal and retrieved inside save:
public User save(User user) {
ProxyDemo.currentProxy().findUser(user.getId());
System.out.println("save method : " + user);
return user;
}Running this version shows both methods being invoked through the proxy.
Spring AOP Dynamic Proxy
Method 1 – Using ProxyFactory (JDK proxy by default, CGLIB if no interface):
public static void main(String[] args) {
ProxyFactory proxyFactory = new ProxyFactory(new UserDAOImpl());
proxyFactory.addInterface(UserDAO.class);
proxyFactory.addAdvice(new MethodBeforeAdvice() {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
logger.info("调用之前");
}
});
UserDAO dao = (UserDAO) proxyFactory.getProxy();
User user = new User(1, "李四");
dao.save(user);
}Result: the "before" advice runs before save.
Method 2 – Defining an @Aspect:
@Aspect
@Component
public class UserAspect {
private static Logger logger = LoggerFactory.getLogger(UserAspect.class);
@Pointcut("execution(* com.pack.dao..*.*(..))")
private void log() {}
@Before("log()")
public void beforeLog() {
logger.info("方法执行之前操作...");
}
}Register UserDAOImpl as a Spring bean and inject UserDAO (which is the proxy) to call other methods via the proxy:
@Component
public class UserDAOImpl implements UserDAO {
@Resource
private UserDAO userDAO;
@Override
public User save(User user) {
userDAO.findUser(user.getId());
System.out.println("save method : " + user);
return user;
}
}Test class:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SpringBootAopApplication.class)
public class SpringBootAopApplicationTests {
@Resource
private UserDAO userDAO;
@Test
public void testAop() {
User user = new User(1, "李四");
userDAO.save(user);
}
}Important configuration: enable proxy exposure with ProxyFactory.setExposeProxy(true) or spring.aop.proxy-target-class=true so that AopContext.currentProxy() can be used.
When a non‑transactional method calls a @Transactional method directly, the transaction will not be applied. Use AOP proxy (e.g., AopContext.currentProxy()) or move the transactional method to another bean.
The next approach is absolutely horrendous, and we hesitate to point it out, precisely because it is so horrendous. You can (painful as it is to us) totally tie the logic within your class to Spring AOP.
Binding business logic tightly to Spring AOP is discouraged due to strong coupling.
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.
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.
