Java中代理模式有两种:动态代理和静态代理。静态代理有JDK静态代理,而动态代理分为JDK动态代理和cglib动态代理。在Spring的AOP实现中,主要应用了JDK动态代理以及CGLIB动态代理。记录一下今天学习的收获。
代理一般就是给目标类的方法在执行之前处理消息,过滤消息,也就是通常说的增强目标类的方法。之后还能进行消息的后置处理。代理类类本身不实现服务,而是通过调用真实目标类中的方法来提供服务。
JDK静态代理源码的实现:
1、业务接口:
package com.static_agent;
public interface TargetInterface {
public void addBook();
}
2、业务实现:
package com.static_agent;
public class Target implements TargetInterface {
@Override
public void addBook() {
System.out.println("增加一本图书成功------");
}
}
3、代理类:
package com.static_agent;
public class Proxy implements TargetInterface{
//真实目标对象
private Target target;
public Proxy(Target target) {
this.target = target;
}
@Override
public void addBook() {
System.out.println("代理类方法,进行了增强。。。");
System.out.println("事务开始。。。");
// 调用目标类的方法;
target.addBook();
System.out.println("处理结束。。。");
}
}
4、测试代码:
package com.static_agent;
public class StaticProxyTest {
public static void main(String[] args) {
//创建真实对象
Target target = new Target();
//创建代理对象
Proxy proxy = new Proxy(target);
//使用代理对象进行方法的执行
proxy.addBook();
}
}
5、执行结果
JDK动态代理源码的实现
1、业务接口:
package com.dynamic_agent;
public interface TargetInterface {
public void save();
}
2、业务实现:
package com.dynamic_agent;
public class Target implements TargetInterface {
@Override
public void save() {
System.out.println("save Running-----");
}
}
3、增强实现类:
package com.dynamic_agent;
public class Advice {
public void before(){
System.out.println("前置增强--------");
}
public void afterReturning(){
System.out.println("后置增强--------");
}
}
4、测试代码:
package com.dynamic_agent;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DynamicProxyTest {
public static void main(String[] args) {
//创建目标对象
Target target = new Target();
//增强对象
Advice advice = new Advice();
//返回值就是动态代理生成的对象
/* 目标对象和代理对象是兄弟关系,俩都是接口的子类*/
TargetInterface proxy= (TargetInterface) Proxy.newProxyInstance(
target.getClass().getClassLoader(), //目标对象类加载器
target.getClass().getInterfaces(),//目标对象相同的接口字节码对象数组
new InvocationHandler() {
//调用代理对象的任何方法,实质执行的都是 invoke() 方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//前置增强
advice.before();
//执行目标方法
Object invoke = method.invoke(target, args);
//后置增强
advice.afterReturning();
return invoke;
}
}
);
//调用代理对象的方法
proxy.save();
//以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。
System.out.println(proxy.getClass().getName());
}
}
5、执行结果
cglib动态代理源码的实现
1、导入依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.6</version>
</dependency>
2、目标类:
package com.dynamic_cglib;
public class Target{
public void save() {
System.out.println("cglib save Running-----");
}
}
3、增强实现类:
package com.dynamic_cglib;
public class Advice {
public void before(){
System.out.println("cglib 前置增强--------");
}
public void afterReturning(){
System.out.println("cglib 后置增强--------");
}
}
4、测试代码:
package com.dynamic_cglib;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class DynamicCglibProxy {
public static void main(String[] args) {
//创建目标对象
Target target = new Target();
//增强对象
Advice advice = new Advice();
//返回值就是动态代理生成的对象 基于cglib
//1、创建一个增强器
Enhancer enhancer = new Enhancer();
//2、设置父类(目标类)
enhancer.setSuperclass(target.getClass());
//3、设置回调函数
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
advice.before();//前置
Object invoke = method.invoke(target, args);//执行目标
advice.afterReturning();//后置
return invoke;
}
});
//4、创建代理对象
Target proxy = (Target) enhancer.create();
proxy.save();
System.out.println(proxy.getClass().getName());
}
}
5、执行结果
关于动态代理总结:1、原理
jdk静态代理直接是代理类实现目标对象的接口,完成代理,耦合度较高。
jdk动态代理是接口代理,目标接口需要有一个类来实现自己的抽象方法,代理类和目标类是兄弟关系。
jdk动态代理会根据目标对象生成一个代理类,并实现了该业务接口的jdk代理类,该类的字节码会被传进去的ClassLoader加载,创建了jdk代理对象实例,
jdk代理对象实例在创建时,业务代理对象实例会被赋值给Proxy类,jdk代理对象实例也就有了业务代理对象实例,同时jdk代理对象实例通过反射根据被代理类的业务方法创建了相应的Method对象m(可能有多个)。当jdk代理对象实例调用业务方法,如proxy.saver();这个会先把对应的m对象作为参数传给invoke()方法(就是invoke方法的第二个参数),调用了jdk代理对象实例的invoke()回调方法,在invoke方法里面再通过反射来调用被代理对象的因为方法,也就是Object invoke = method.invoke(target, args);。
cglib动态代理是继承代理,通过ASM字节码框架修改字节码生成新的子类,重写并增强方法的功能。
2、优缺点
jdk静态代理类只能为一个被代理类服务,如果需要代理的类比较多,就会出现代码冗余的问题。jdk静态代理在编译时产生class文件,运行时无需产生,可直接使用,效率好。
jdk动态代理必须实现接口,通过反射来动态代理方法,消耗系统性能。但是无需产生过多的代理类,避免了重复代码的产生,系统更加灵活。
cglib动态代理无需实现接口,通过生成子类字节码来实现,比反射快一点,没有性能问题。但是由于cglib会继承被代理类,所以被代理类不能是final类,被代理方法不能是final,并且会带着一点侵入性。
因此,cglib的应用更加广泛一点。