代理模式是一种结构型设计模式。
思想:给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。
静态代理模式可以在不修改目标对象的功能前提下,对目标功能扩展。
案例:西门庆找潘金莲,那潘金莲不好意思答复呀,咋办,找那个王婆做代理。
KindWomen:抽象角色。
JiaShi:目标角色,委托类。
WangPo:代理角色。
代码实现:
抽象出一种类型的女人,王婆和潘金莲都属于这个类型的女人。
public interface KindWomen {
public void makeEyesWithMan(); //抛媚眼
public void happyWithMan();
}
具体到一种类型的女人,这里是潘金莲,是一个年轻漂亮的姑娘,具体被代理的人。
public class PanJinLian implements KindWomen {
@Override
public void makeEyesWithMan() {
System.out.println("潘金莲抛媚眼");
}
@Override
public void happyWithMan() {
System.out.println("潘金莲在和男人做那个.....");
}
}
定义王婆,王婆也是这种类型的女人,但是她太老了,是个男人都看不上,但是她有智慧有经验呀,她可以作为这一类女人的代理,让年轻的姑娘代替干。即代理类。
public class WangPo implements KindWomen {
private KindWomen kindWomen;
// 她可以是KindWomen的任何一个女人的代理,只要你是这一类型
public WangPo(KindWomen kindWomen){
this.kindWomen = kindWomen;
}
@Override
public void makeEyesWithMan() {
// 王婆这么大年龄了,谁看她抛媚眼,还是让年轻的女人吧
this.kindWomen.makeEyesWithMan();
}
@Override
public void happyWithMan() {
// 自己老了,干不了,可以让年轻的代替
this.kindWomen.happyWithMan();
}
}
定义西门庆,男主角,需要找一个女人HappyHappy。
public class XiMenQing {
public static void main(String[] args) {
// 西门庆不知道去哪里找,于是找到了王婆,王婆找到了潘金莲
WangPo wangPo = new WangPo(new PanJinLian());
// 虽然表面上时王婆在做,实际上爽的是潘金莲
wangPo.makeEyesWithMan();
wangPo.happyWithMan();
}
}
西门庆Happy过程。
假如西门庆想换个新鲜的,再定义一个贾氏,又是一个年轻漂亮的姑娘,具体被代理的人。
public class JiaShi implements KindWomen {
@Override
public void makeEyesWithMan() {
System.out.println("贾氏抛媚眼");
}
@Override
public void happyWithMan() {
System.out.println("贾氏正在Happy中......");
}
}
西门庆又找到王婆,说和潘金莲Happy已经腻了,想换个新的,于是王婆让贾氏做为代理人了。
public class XiMenQing {
public static void main(String[] args) {
// 让王婆作为贾氏的代理人
WangPo wangPo = new WangPo(new PanJinLian());
wangPo.makeEyesWithMan();
wangPo.happyWithMan();
}
}
西门庆再次Happy过程。
总结:代理模式主要使用了 Java 的多态,干活的是被代理类,代理类主要是接活,你让我干活,好,我交给幕后的类去干,你满意就成,那怎么知道被代理类能不能干呢?同根就成,大家知根知底,你能做啥,我能做啥都清楚的很,同一个接口呗。
动态代理类的原理是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理类和目标类(委托类)的关系是在程序运行时确定。
java.lang.reflect.Proxy
是 Java 动态代理机制生成的所有动态代理类的父类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。
java.lang.reflect.Proxy
的newProxyInstance()
方法要求传入3个参数,其中最后一个参数是一个调用处理器对象,即实现了java.lang.reflect.InvocationHandler
这个接口的类的实例。InvocationHandler
定义了一个invoke()
方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对目标类(委托类)的代理访问。每次生成动态代理类对象时都要指定一个对应的调用处理器对象。
步骤
InvocationHandler
接口的实现类,在 invoke
方法中实现代理逻辑;newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h)
创建一个代理对象;代码实现
被代理的接口和接口实现类:
// 抽象角色
public interface Car {
void drive();
}
// 目标类(委托类)
public class KIACar implements Car {
@Override
public void drive() {
System.out.println("起亚汽车");
}
}
动态代理生成工厂类;实现代理逻辑必须实现java.lang.reflect.InvocationHandler
接口:
import java.lang.reflect.Proxy;
public class JDKProxyFactory {
// 封装一个目标对象
private Object target;
// 生成代理类
public Object getProxy(Object target) {
this.target = target;
// 第一个参数:指定代理类的类加载器,传入target自身的类加载器即可;
// 第二个参数:代理类需要实现的接口,传入被代理类实现的接口,这样生成的代理类和被代理类就实现了相同的接口;
// 第二个参数:InvocationHandler的实现类, 即代理类的实现类, 用来编写代理类的业务逻辑并调用被代理类的方法。
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy, method, args)->{
System.out.println("启动车辆");
// 反射调用被代理类的方法
Object object = method.invoke(target, args);
System.out.println("停止车辆");
return object;
}
);
}
public static void main(String[] args) {
JDKProxyFactory factory = new JDKProxyFactory();
// 获取代理类
Car car = (Car)factory.getProxy(new KIACar());
// 通过代理类来调用被代理类方法
car.drive();
}
}
运行结果:
可以看到,代理工厂和目标是完全松耦合的,根据传入的目标的类型不同,代理工厂可以生成不同类型的代理。这解决了静态代理模式代理类和目标耦合性强的问题。
Proxy.newProxyInstance源码分析:
// 方法名和入参
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
// 被代理类接口的Class对象拷贝一份
final Class<?>[] intfs = interfaces.clone();
// 通过类加载器和被代理类接口的Class对象,从缓存中获取或重新生成指定的代理类的Class对象
Class<?> cl = getProxyClass0(loader, intfs);
// 获取代理类的构造函数
final Constructor<?> cons = cl.getConstructor(constructorParams);
// 生成代理对象
return cons.newInstance(new Object[]{h});
总结
通过类加载器和被代理类接口的Class字节码,动态生成被代理类的Class对象字节码,然后通过这个Class对象获取构造函数后,实例化这个代理类并返回。
newInstance()就是把new这个方式分解为两步,首先调用class的加载方法加载某个类,然后实例化。
newInstance案例
// newInstance:弱类型。效率低。只能调用无参构造。----类加载机制
// new:强类型。相对高效。能调用任何public构造。 ----
@Test
public void getInstance() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
String classValue = "com.sunhui.test.Student";
Class clazz = Class.forName(classValue); //初始化该类
Student student = (Student) clazz.newInstance(); //实例化该类
student.setUserName("sunhui");
student.setPassword("sh152332");
System.out.println("UserName:"+student.getUserName()+"\nPassword:"+student.getPassword());
}
Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展。
JDK的动态代理有一个限制,就是使用动态代理的对象必须至少实现一个接口,如果想代理没有实现接口的类,就可以使用Cglib实现。
Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口。它广泛的被许多AOP的框架使用,例如Spring AOP。
Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类。因此,要使用Cglib,必须引入cglib.jar和asm.jar。另外Spring也集成了cglib功能,使用Spring框架的项目中可以直接使用Cglib的功能。
JDK动态代理类是必须实现了接口的,而cglib不需要实现接口,但是必须保证类不含有final关键字,否则是无法代理的,cglib是Spring核心框架提供的。
1、步骤:
2、案例:
被代理类:
public class BydCar {
public void drive() {
System.out.println("比亚迪汽车");
}
}
实现代理逻辑的客户端;实现代理逻辑必须实现 org.springframework.cglib.proxy.MethodInterceptor 接口。
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class CglibProxyHandler implements MethodInterceptor{
private Object target; // 业务类对象,供代理方法中进行真正的业务方法调用
// 生成代理类
public Object getProxy(Object target){
this.target = target; // 给业务对象赋值
Enhancer enhancer = new Enhancer(); // 创建加载器,用来创建动态代理类
enhancer.setSuperclass(this.target.getClass()); // 为加载器指定要代理的业务类
enhancer.setCallback(this); // 设置回调,对于代理类上所有方法的调用,都会调用CallBack,CallBack则需要实现intercept()方法来实现
return enhancer.create(); // 创建动态代理类对象并返回
}
// 实现回调方法
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("汽车启动");
proxy.invokeSuper(obj, args); // 调用真正的业务类方法
System.out.println("汽车停止");
return null;
}
public static void main(String[] args) {
CglibProxyHandler cglibProxyHandler = new CglibProxyHandler();
BydCar car = (BydCar) cglibProxyHandler.getProxy(new BydCar());
car.drive();
}
}
3、cglib动态代理遇到的坑:
在通过MethodProxy调用真正业务类方法的时候不要使用MethodProxy.invoke()方法,这个方法会一直循环调用,导致栈溢出;所以invoke会导致OOM的问题。
一定要使用MethodProxy.invokeSuper()方法来调用真正业务类方法。