动态代理
Spring AOP(面向切面编程)允许开发者定义横切关注点,如日志记录、事务管理等,并将这些逻辑应用于业务代码中而无需修改原有的业务逻辑。Spring AOP可以使用JDK动态代理和CGLIB来实现AOP的功能。
JDK Proxy
JDK Proxy是Java自带的代理机制,它要求被代理的类必须实现一个或多个接口。JDK Proxy通过在运行时创建实现相同接口的新类来生成代理对象。
例子:
假设我们有一个简单的UserService
接口及其实现:
java
public interface UserService {
void saveUser();
}
public class UserServiceImpl implements UserService {
@Override
public void saveUser() {
System.out.println("Saving user...");
}
}
我们可以创建一个基于JDK Proxy的简单切面:
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkProxyExample {
public static void main(String[] args) {
// 创建目标对象
final UserService userService = new UserServiceImpl();
// 通过JDK Proxy创建代理对象
UserService proxy = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class[]{UserService.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method execution");
Object result = method.invoke(userService, args);
System.out.println("After method execution");
return result;
}
});
// 使用代理对象调用方法
proxy.saveUser();
}
}
CGLIB
CGLIB是一个强大的高性能代码生成库,它可以在运行期扩展Java类与实现Java接口(使用ASM技术,效率是不如JDK Proxy的)。CGLIB适用于没有实现任何接口的目标类(当然如果实现了接口也是可以的,因为本质上是生成目标类的子类,所以必须可继承就可以)。
例子:
我们继续使用上面的UserServiceImpl
,但这次不实现接口。
java
public class UserServiceImpl {
public void saveUser() {
System.out.println("Saving user...");
}
}
然后,使用CGLIB创建代理:
java
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxyExample {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserServiceImpl.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before method execution");
Object result = proxy.invokeSuper(obj, args);
System.out.println("After method execution");
return result;
}
});
UserServiceImpl userService = (UserServiceImpl) enhancer.create();
userService.saveUser();
}
}
Spring AOP
Spring AOP可以自动选择使用JDK Proxy还是CGLIB来创建代理。如果目标对象实现了至少一个接口,则默认使用JDK Proxy;否则使用CGLIB。
Spring AOP利用了AspectJ的规范,然后具体实现动态代理还是依赖JDK Proxy和CGLIB技术。
例子:
首先配置Spring上下文并定义切面:
xml
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.example.UserService"/>
<aop:config>
<aop:aspect ref="loggingAspect">
<aop:before method="logBefore" pointcut="execution(* com.example..*(..))"/>
</aop:aspect>
</aop:config>
<bean id="loggingAspect" class="com.example.LoggingAspect"/>
</beans>
定义切面类:
java
package com.example;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class LoggingAspect {
@Before("execution(* com.example..*(..))")
public void logBefore() {
System.out.println("Before method execution");
}
@Before("execution(* com.example.service.*.*(..))")
public void logBefore2(JoinPoint joinPoint) {
// 获取方法签名
String methodName = joinPoint.getSignature().getName();
// 获取目标类名称
String className = joinPoint.getTarget().getClass().getSimpleName();
// 获取传入参数
Object[] args = joinPoint.getArgs();
System.out.println("Executing: " + className + "." + methodName + "()");
if (args.length > 0) {
System.out.println("Arguments: ");
for (Object arg : args) {
System.out.println("\t" + arg);
}
}
}
}
这样就配置了一个简单的Spring AOP切面,在com.example
包下的所有方法执行前打印一条消息。
以上就是JDK Proxy、CGLIB以及Spring AOP的基本示例。在实际项目中,通常推荐直接使用Spring AOP,因为它提供了更高级别的抽象和便利性。