28--静态代理模式和JDK、CGLIB动态代理

版权声明:如有转载,请标明出处,谢谢合作! https://blog.csdn.net/lyc_liyanchao/article/details/83240498

在分析AOP的源码之前,我们需要了解一下代理模式,代理模式可分为静态代理和动态代理两种。

1.静态代理

代理模式是常用设计模式的一种,我们在软件设计时常用的代理一般是指静态代理,也就是在代码中显式指定的代理。

静态代理由 业务实现类、业务代理类 两部分组成。业务实现类 负责实现主要的业务方法,业务代理类负责对调用的业务方法作拦截、过滤、预处理,主要是在方法中首先进行预处理动作,然后调用业务实现类的方法,还可以规定调用后的操作。我们在需要调用业务时,不是直接通过业务实现类来调用的,而是通过业务代理类的同名方法来调用被代理类处理过的业务方法。

  • 代理类和被代理类
package com.lyc.cn.v2.day04.aop.proxy;

/**
 * 账户接口
 */
public interface Count {
	// 查询账户
	void queryCount();

	// 修改账户
	void updateCount();

}
package com.lyc.cn.v2.day04.aop.proxy;

/**
 * @author: LiYanChao
 * @create: 2018-10-21 12:07
 */
public class CountImpl implements Count {
	@Override
	public void queryCount() {
		System.out.println("==查询账户");
	}

	@Override
	public void updateCount() {
		System.out.println("==更新账户");
	}
}

package com.lyc.cn.v2.day04.aop.proxy;

/**
 * 代理类
 * @author: LiYanChao
 * @create: 2018-10-21 12:08
 */
public class CountProxy implements Count {

	private CountImpl countImpl;

	/**
	 * 覆盖默认构造器
	 */
	public CountProxy(CountImpl countImpl) {
		this.countImpl = countImpl;
	}

	@Override
	public void queryCount() {
		System.out.println("==查询账户开始");
		// 调用真正的查询账户方法
		countImpl.queryCount();
		System.out.println("==查询账户结束");
	}

	@Override
	public void updateCount() {
		System.out.println("==更新账户开始");
		// 调用真正的修改账户操作
		countImpl.updateCount();
		System.out.println("==更新账户结束");
	}
}

  • 测试及结果
@Test
public void test3() {
    // 静态代理
    CountImpl countImpl = new CountImpl();
    CountProxy countProxy = new CountProxy(countImpl);
    countProxy.updateCount();
    System.out.println("\n*******\n");
    countProxy.queryCount();
}
==更新账户开始
==更新账户
==更新账户结束

*******

==查询账户开始
==查询账户
==查询账户结束
  • 总结
    一个代理类只能对一个业务接口的实现类进行包装,如果有多个业务接口的话就要定义很多实现类和代理类才行。而且,如果代理类对业务方法的预处理、调用后操作都是一样的(比如:调用前输出提示、调用后自动关闭连接),则多个代理类就会有很多重复代码。这时我们可以定义这样一个代理类,它能代理所有实现类的方法调用:根据传进来的业务实现类和方法名进行具体调用。——那就是动态代理。
2.JKD动态代理

JDK动态代理所用到的代理类在程序调用到代理类对象时才由JVM真正创建,JVM根据传进来的 业务实现类对象 以及 方法名 ,动态地创建了一个代理类的class文件并被字节码引擎执行,然后通过该代理类对象进行方法调用。我们需要做的,只需指定代理类的预处理、调用后操作即可。JDK的动态代理需要实现InvocationHandler接口,并重写invoke方法。

  • 代理类和被代理类
package com.lyc.cn.v2.day04.aop.jdk;

public interface Animal {
	void say();
}

package com.lyc.cn.v2.day04.aop.jdk;

public class Dog implements Animal {

	@Override
	public void say() {
		System.out.println("I am a dog...");
	}

}

package com.lyc.cn.v2.day04.aop.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MyInvocationHandler implements InvocationHandler {

	// 目标对象
	private Object target;

	/**
	 * 构造方法
	 * @param target 目标对象
	 */
	public MyInvocationHandler(Object target) {
		super();
		this.target = target;
	}

	/**
	 * @param proxy  JDK动态生成的最终代理对象
	 * @param method 调用真实对象的某个方法的Method对象
	 * @param args   调用真实对象某个方法时接受的参数
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("==代理方法开始执行");
		Object invoke = method.invoke(target, args);
		System.out.println("==代理方法结束执行");
		return invoke;
	}

	/**
	 * 获取目标对象的代理对象
	 * @return 代理对象
	 */
	public Object getProxy() {
		return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(), this);
	}

}

  • 测试及结果
@Test
public void test4() {
    // JDK动态代理
    MyInvocationHandler handler = new MyInvocationHandler(new Dog());
    Animal proxy = (Animal) handler.getProxy();
    proxy.say();
}
==代理方法开始执行
I am a dog...
==代理方法结束执行
  • 总结
    JDK动态代理的代理对象在创建时,需要使用业务实现类所实现的接口作为参数(因为在后面代理方法时需要根据接口内的方法名进行调用)。如果业务实现类是没有实现接口而是直接定义业务方法的话,就无法使用JDK动态代理了。并且,如果业务实现类中新增了接口中没有的方法,这些方法是无法被代理的(因为无法被调用)。如果没有接口定义又想使用动态代理的话,那么可以使用CGLIB动态代理。
3.CGLIB动态代理

CGLIB是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理。 在使用的时候需要引入cglib和asm的jar包

compile group: 'asm', name: 'asm', version: '3.3.1'
compile group: 'cglib', name: 'cglib', version: '2.2.2'
  • 代理类和被代理类
package com.lyc.cn.v2.day04.aop.cglib;

public class Cat {
	public void say() {
		System.out.println("I am a cat");
	}
}

package com.lyc.cn.v2.day04.aop.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 CglibProxy implements MethodInterceptor {

	private Enhancer enhancer = new Enhancer();

	// 这里的目标类型为Object,则可以接受任意一种参数作为被代理类,实现了动态代理
	public Object getInstance(Class clazz) {
		enhancer.setSuperclass(clazz);
		enhancer.setCallback(this);
		// 返回代理对象
		return enhancer.create();
	}

	@Override
	public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
		System.out.println("==代理方法开始执行");
		Object result = methodProxy.invokeSuper(proxy, args);
		System.out.println("==代理方法结束执行");
		return result;
	}

}

  • 测试类及结果
@Test
public void test5() {
    // CGLIB动态代理
    Cat cat = (Cat) new CglibProxy().getInstance(Cat.class);
    cat.say();
}
==代理方法开始执行
I am a cat
==代理方法结束执行
  • 总结
    1、静态代理是通过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,用户通过代理类调用被包装过的业务方法;
    2、JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;
    3、CGlib动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理;

猜你喜欢

转载自blog.csdn.net/lyc_liyanchao/article/details/83240498