Spring的两种代理方式
在使用Spring框架时,我们常常会使用到AOP(面向切面编程)的相关技术,而代理是AOP中必不可少的一个环节。在Spring中,支持两种代理方式:JDK动态代理和CGLIB动态代理。这两种代理方式都有各自的特点和优劣,具体使用哪种方式则要根据具体的情况而定。
JDK动态代理
JDK动态代理是基于接口的代理,它要求目标对象必须实现一个接口。Spring提供JDK动态代理主要是为了解决AOP中的问题,例如安全控制、事务管理等等。JDK动态代理通过Proxy类来实现,它不需要任何第三方依赖,只需要一个接口即可。
JDK动态代理的核心类是java.lang.reflect.Proxy
,使用方式如下:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkProxyDemo {
public static void main(String[] args) {
// 创建目标对象
UserService userService = new UserServiceImpl();
// 创建InvocationHandler对象
InvocationHandler handler = new MyInvocationHandler(userService);
// 创建代理对象
UserService proxy = (UserService) Proxy.newProxyInstance(
userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(),
handler
);
// 调用代理对象的方法
proxy.saveUser();
}
}
interface UserService {
void saveUser();
}
class UserServiceImpl implements UserService {
public void saveUser() {
System.out.println("保存用户信息");
}
}
class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法调用前");
Object result = method.invoke(target, args);
System.out.println("方法调用后");
return result;
}
}
上述代码中,我们创建了一个UserService接口及其实现类UserServiceImpl。MyInvocationHandler是我们自己实现的InvocationHandler接口的实现类。在main方法中,我们创建了一个UserService的代理对象,当我们调用代理对象的saveUser方法时,实际上是通过invoke方法来执行了UserServiceImpl的saveUser方法,并在调用前后分别输出了一句话。
CGLIB动态代理
CGLIB动态代理是基于类的代理,它要求目标对象不需要实现接口。它通过动态生成一个子类来覆盖目标对象的方法,并使用字节码技术生成被代理对象的子类。
CGLIB动态代理的核心类是net.sf.cglib.proxy.Enhancer
,使用方式如下:
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CglibProxyDemo {
public static void main(String[] args) {
// 创建目标对象
UserService userService = new UserServiceImpl();
// 创建MethodInterceptor对象
MethodInterceptor interceptor = new MyMethodInterceptor();
// 创建代理对象
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserServiceImpl.class);
enhancer.setCallback(interceptor);
UserService proxy = (UserService) enhancer.create();
// 调用代理对象的方法
proxy.saveUser();
}
}
class MyMethodInterceptor implements MethodInterceptor {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("方法调用前");
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("方法调用后");
return result;
}
}
上述代码中,我们使用CGLIB动态代理实现了一个UserService的代理对象,方法调用前后分别输出了一句话。
选择合适的代理方式
JDK动态代理和CGLIB动态代理各有优劣,应该根据具体情况选择合适的代理方式。一般来说,如果目标对象实现了接口,那么使用JDK动态代理;如果目标对象没有实现接口,那么使用CGLIB动态代理。
示例一
假设我们有一个接口UserService
和它的实现类UserServiceImpl
,我们想在UserServiceImpl
中增加一个方法,记录方法调用次数。
我们可以先定义一个计数器类Counter
,然后定义一个切面类LogAspect
,在其中通过反射获取了目标方法的信息,并记录了方法调用次数。
import java.lang.reflect.Method;
public class Counter {
private static int count;
public synchronized static void addCount() {
count++;
}
public static void printCount() {
System.out.println("方法调用次数:" + count);
}
}
public class LogAspect {
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("调用方法:" + method.getName());
Counter.addCount();
}
public void after(Method method, Object[] args, Object target) throws Throwable {
Counter.printCount();
}
}
接下来,我们使用JDK动态代理和CGLIB动态代理实现对UserServiceImpl
的代理。
使用JDK动态代理的代码如下:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkProxyExample {
public static void main(String[] args) {
// 创建目标对象
UserService userService = new UserServiceImpl();
// 创建InvocationHandler对象
InvocationHandler handler = new MyInvocationHandler(userService);
// 创建代理对象
UserService proxy = (UserService) Proxy.newProxyInstance(
userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(),
handler
);
// 调用代理对象的方法
proxy.saveUser();
}
}
interface UserService {
void saveUser();
}
class UserServiceImpl implements UserService {
public void saveUser() {
System.out.println("保存用户信息");
}
}
class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
LogAspect aspect = new LogAspect();
aspect.before(method, args, target);
Object result = method.invoke(target, args);
aspect.after(method, args, target);
return result;
}
}
使用CGLIB动态代理的代码如下:
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) {
// 创建目标对象
UserServiceImpl userServiceImpl = new UserServiceImpl();
// 创建MethodInterceptor对象
MethodInterceptor interceptor = new MyMethodInterceptor();
// 创建代理对象
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserServiceImpl.class);
enhancer.setCallback(interceptor);
UserServiceImpl proxy = (UserServiceImpl) enhancer.create();
// 调用代理对象的方法
proxy.saveUser();
}
}
class MyMethodInterceptor implements MethodInterceptor {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
LogAspect aspect = new LogAspect();
aspect.before(method, objects, o);
Object result = methodProxy.invokeSuper(o, objects);
aspect.after(method, objects, o);
return result;
}
}
这两个示例中,我们使用相同的切面实现了对UserServiceImpl
方法调用次数的监控。可以看到,使用JDK动态代理时我们需要实现接口,而使用CGLIB动态代理则不需要实现接口,但需要实现类必须支持继承。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解Spring的两种代理方式:JDK动态代理和CGLIB动态代理 - Python技术站