spring代理模式分为动态代理和静态代理。
动态代理又分为JDK动态代理和CGLIB动态代理。
首先,先定义一个公共接口Subject:
public interface Subject {
void request();
}
然后,定义一个具体主题类RealSubject,实现Subject接口:
public class RealSubject implements Subject {
public void request() {
System.out.println("RealSubject's request() method is called.");
}
}
接下来是静态代理的实现:
public class ProxySubject implements Subject {
private RealSubject realSubject;
public ProxySubject(RealSubject realSubject) {
this.realSubject = realSubject;
}
public void request() {
System.out.println("Before RealSubject's request() method is called.");
realSubject.request();
System.out.println("After RealSubject's request() method is called.");
}
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
ProxySubject proxySubject = new ProxySubject(realSubject);
proxySubject.request();
}
}
可以看到,代理类ProxySubject实现了Subject接口,并在构造方法中接受一个RealSubject对象,然后在代理方法request中调用RealSubject的方法,并在方法执行前后加入了额外的逻辑。
最后,在main方法中,创建了RealSubject对象和ProxySubject对象,通过调用ProxySubject的request方法实现了对RealSubject对象的代理调用。
静态代理的优点在于实现简单、易于理解,并且可以在编译期间就生成代理类,运行效率高。缺点在于当需要代理的接口或类过多时,需要手动编写大量的代理类,工作量较大。
DK动态代理是Java动态代理的默认实现,它通过java.lang.reflect.Proxy类来实现。JDK动态代理要求目标类必须实现一个或多个接口,代理类实现目标类实现的所有接口,并在代理类的方法中调用目标对象的方法。
DK动态代理的优点在于可以代理接口,实现接口之间的代理,同时不需要额外的依赖,可以在运行时实现动态代理。缺点是只能代理实现了接口的类,不能代理没有实现接口的类。
下面是一个简单的示例:
public interface Subject {
void request();
}
public class RealSubject implements Subject {
public void request() {
System.out.println("RealSubject's request() method is called.");
}
}
public class ProxySubject implements InvocationHandler {
private Object target;
public ProxySubject(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before " + method.getName() + " is called.");
Object result = method.invoke(target, args);
System.out.println("After " + method.getName() + " is called.");
return result;
}
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
ProxySubject proxySubject = new ProxySubject(realSubject);
Subject subject = (Subject)Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), proxySubject);
subject.request();
}
}
CGLIB(Code Generation Library)动态代理是一种基于ASM(Java字节码操纵框架)开发的动态代理技术。CGLIB动态代理是在运行期间动态生成代理类,通过继承目标类并重载目标方法的方式实现代理,可以代理没有实现接口的类。
CGLIB动态代理的优点在于可以代理没有实现接口的类,灵活度更高,同时它的速度比JDK动态代理要快一些。缺点是它是基于继承实现的,如果目标对象中存在final类或方法,则无法代理。
下面是一个简单的示例:
public class RealSubject {
public void request() {
System.out.println("RealSubject's request() method is called.");
}
}
public class ProxySubject implements MethodInterceptor {
private RealSubject target;
public ProxySubject(RealSubject target) {
this.target = target;
}
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before " + method.getName() + " is called.");
Object result = proxy.invokeSuper(obj, args);
System.out.println("After " + method.getName() + " is called.");
return result;
}
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealSubject.class);
enhancer.setCallback(new ProxySubject(new RealSubject()));
RealSubject subject = (RealSubject) enhancer.create();
subject.request();
}
}
在以上示例中,我们定义了一个RealSubject类和一个ProxySubject类,RealSubject类并没有实现任何接口,ProxySubject类在构造方法中接受一个RealSubject对象,并实现了MethodInterceptor接口,通过设置CGLIB Enhancer类的Superclass和Callback属性,可以生成代理类并实现对目标对象的代理调用。
综上,JDK动态代理和CGLIB动态代理各有优缺点,使用场景和方式不同,需要根据具体的情况选择合适的方式来实现动态代理。在Spring AOP中,通常采用JDK动态代理执行接口代理和CGLIB动态代理执行类代理。
1、实现方式
JDK动态代理是基于接口实现的代理,它要求目标类必须实现至少一个接口,然后通过Java反射机制在运行时生成动态代理类,代理类实现了目标类实现的所有接口,并在代理类的方法中调用目标对象的方法。
CGLIB动态代理则是基于继承实现的代理,它不要求目标类必须实现接口,而是通过ASM字节码处理框架直接对目标类的字节码进行修改,生成一个目标类的子类,代理类继承自目标类的子类,并重写目标类的方法,在重写的方法中调用目标对象的方法。
因此,JDK动态代理只能代理实现了接口的类,而CGLIB动态代理可以代理没有实现接口的类。
2、使用场景
JDK动态代理由于要实现接口,只能代理某些具有共同接口的类,对于没有实现接口的类,它则无法完成对这些类的代理,所以JDK动态代理在实际开发中的使用场景比CGLIB动态代理更加局限。
CGLIB动态代理则在无法使用JDK动态代理的场合下发挥着重要的作用。比如,当目标类没有实现接口或者只想代理目标类的某一个方法时,就可以使用CGLIB动态代理。
3、性能
JDK动态代理的实现是基于Java反射机制的动态代理,所以在创建代理对象的时候,会耗费比较长的时间来做类型检查、安全检查等操作,相对比较慢。
CGLIB动态代理采用ASM字节码框架,并通过在运行时动态生成字节码,比较快。但是,由于CGLIB动态代理是基于继承实现的,所以对目标类中使用final关键字修饰的方法则无法对其进行代理。