静态代理、JDK动态代理、CgLib动态代理

代理模式:

  1. 静态代理
  2. 动态代理
    1. jdk动态代理
    2. cgLib动态代理
      使用目的:解决在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面对对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问,直接访问会给使用者或者系统结构带来很多麻烦

每一个动态代理类中都必须实现Invocation接口,实现其中的invoke方法
JDK的动态代理必须需要目标对象实现接口,否则不能生成代理对象
UserServiceImpl implements UserService

静态代理

静态代理方式是为每个被代理的对象构造对应的代理类。在程序运行前,代理类的class文件已经存在了。但是一个代理类对应一个被代理对象。

public interface UserService {
    void addUser();
}
public class UserServiceImpl implements UserService {
    @Override
    public void addUser() {
        System.out.println("添加一名用户");
    }
}
public class StaticProxy implements UserService{

    private UserServiceImpl userServiceImpl;

    public StaticProxy(UserServiceImpl userServiceImpl) {
        this.userServiceImpl = userServiceImpl;
    }

    @Override
    public void addUser() {
        System.out.println("运行方法前日志");
        userServiceImpl.addUser();
        System.out.println("运行方法后日志");
    }
}
public class StaticProxyFactory {
    public static StaticProxy newInstance(){
        return new StaticProxy(new UserServiceImpl());
    }
}
调用时
StaticProxy staticProxy = StaticProxyFactory.newInstance();
staticProxy.addUser();

这种方式是继承同一个接口,然后内部维护了一个实现类,也可以直接继承该实现类,方法实现用super调用。
优点:业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理模式的共有优点
缺点:

  1. 代理对象的一个接口只服务于一种类型的对象,如果要代理的类型很多,势必要为每一种类型的方法都进行代理
  2. 如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

和装饰设计模式的核心区别:
装设设计模式是对被装饰者对象执行结果(map)的处理
而静态代理是在调用委托类对象方法执行前后做一些其他操作

动态代理

动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。invoke方法采用回调的方式执行,只有在代理对象执行方法时,才会被invoke拦截

JDK动态代理

JDK动态代理所用到的代理类在程序调用到代理类对象时才由JVM真正创建,JVM根据传进来的 业务实现类对象 以及 方法名 ,动态地创建了一个代理类的class文件并被字节码引擎执行,然后通过该代理类对象进行方法调用。
实现步骤

  1. 创建被代理的类以及实现的接口
  2. 创建一个实现接口InvocationHandler的类,必须实现invoke方法
  3. 调用Proxy的newProxyInstance静态方法,创建一个代理类
  4. 通过代理对象调用目标方法
interface Hello {
    void sayHello();
}
class HelloImpl implements Hello {
    @Override
    public void sayHello() {
        System.out.println("hello");
    }
}
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("method :" + method.getName() + " is invoked!");
        return method.invoke(target, args);
    }
}
main{
    Hello helloWorld = (Hello) Proxy.
        newProxyInstance(JDKProxyTest.class.getClassLoader(),
                new Class<?>[]{Hello.class},
                new MyInvocationHandler(new HelloImpl()));
helloWorld.sayHello();
}

实现InvocationHandler接口步骤

  1. 定义含参构造方法,该参数为要代理的实例对象,目的是用于执行method.invoke()方法(也就是执行目标方法)
  2. 实现接口的invoke()方法,该方法用于对目标方法的增强处理,比如记录日志等。该方法的返回值就是代理对象执行目标方法的返回值

具体参数

  1. proxy 动态dialing生成的代理对象

  2. method 目标方法的实例

  3. args 目标方法的参数
    通过Proxy.newProxyInstance方法生成代理对象,具体参数有

  4. loader目标类的类加载器

  5. interfaces 目标类实现的接口

  6. InvocationHandler 调用处理类的实现对象

参数解释,因为JDK动态代理本质是运行的时候动态的去生成了一个类继承了同一接口,然后本地维护了一个被代理对象,因此需要将生成的代理类加载到JVM中需要一个类加载器。最后基于回调,真正还是由内部维护的被代理对象执行方法,实现了一个InvocationHandler接口
注意点:JDK的动态代理只能代理实现了接口的类

cglib动态代理

cglib是针对类来实现代理的,原理是通过字节码对指定的业务类生成一个子类,并覆盖其中业务方法实现代理,顺势织入横切逻辑。因为采用的是继承,所以不能对final修饰的类进行代理。
需要引入依赖

<dependency>
   <groupId>cglib</groupId>
   <artifactId>cglib-nodep</artifactId>
   <version>2.2</version>
</dependency>

例子

//代理类
public class UserServiceCglibMethodInterceptor implements MethodInterceptor {

	@Override
	public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
		System.out.println("Cglib Before add");
		Object result = methodProxy.invokeSuper(object, objects);
		System.out.println("Cglib After add");
		return result;
	}
}
//代理工厂
public class ProxyCglibFactory {

	public static UserService getInstance() {
		Enhancer enhancer = new Enhancer();  
		enhancer.setSuperclass(UserServiceImpl.class);  
		enhancer.setCallback(new UserServiceCglibMethodInterceptor());  
		return (UserServiceImpl)enhancer.create();
	}	
}
//测试
public class TestCglibProxy {

	public static void main(String[] args) {
		UserService userService = ProxyCglibFactory.getInstance();
		User user = new User();
		user.setId(12);
		user.setName("哈哈");
		user.setAge(23);
		userService.add(user);
	}
	
}

代理对象的生成过程由Enhancer类实现,大致步骤如下

  1. 生成代理类Class的二进制字节码
  2. 通过Class.foName加载二进制字节码,生成Class对象
  3. 通过反射机制获取实例构造,并初始化代理类对象

特点

  1. cglib的动态代理是针对类来实现代理
  2. 对指定目标类产生一个子类,通过方法拦截技术拦截所有父类方法的调用
  3. 因为是通过集成实现,final类无法使用cglib

总结JDKProxy和CgLibProxy的区别

  1. jdk动态代理生成的代理类和委托类实现了相同的接口
  2. cglib动态代理中生成的字节码更加复杂,生成的代理类是委托类的子类,且不能处理被final修饰的类
  3. jdk采用反射机制调用委托类的方法,cglib采用类似索引的方式直接调用委托类方法
  4. jdk利用jdk的API,动态的再内存中构建代理对象
  5. cglib在内存中构建一个子类对象
  6. JDK动态代理生成的类为com.sun.proxy.$Proxy0
  7. cglib生成的类为class.cglib.委托类接口$ $ EnhancerByCGLIB$$552188b6
    JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。

//TODO
//JDK动态代理源码分析

参考
JAVA中的静态代理、动态代理以及CGLIB动态代理
JDK动态代理详解
Java的静态代理和动态代理
JAVA中的静态代理、动态代理以及CGLIB动态代理
深入理解[代理模式]原理与技术
Java三种代理模式:静态代理、动态代理和cglib代理
JDK动态代理-超详细源码分析

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值