学习目标
1,掌握代理模式的应用场景和实现原理
2,了解静态代理模式和动态代理模式的区别
3,了解CGLib和JDK Proxy的根本区别
4,手写实现定义的动态代理
代理模式定义
代理模式(Proxy Pattern)是指为其他对象提供一种代理,以控制对这个对象的访问。
代理对象在客户端跟目标对象之间起到中介作用,属于结构型设计模式。
生活中的代理模式
房产中介
快递小哥
黄牛党
媒婆
代理模式实现方式
方式一:静态代理
按照农村找对象类举例子。
新建一个person.java类
package com.packer.partten.factory.proxy.staticproxy;
/**
* Created by lijianfang on 2021/9/25.
*/
public interface Person {
public void findLove();
}
新建一个Son.java类,实现person类中的找对象要求
package com.packer.partten.factory.proxy.staticproxy;
/**
* Created by lijianfang on 2021/9/25.
*/
public class Son implements Person{
/**
* 找对象的要求:肤白貌美大长腿
*/
public void findLove(){
System.out.println("找对象的要求:肤白貌美大长腿");
}
}
再新建一个Father.java类
package com.packer.partten.factory.proxy.staticproxy;
/**
* Created by lijianfang on 2021/9/25.
*/
public class Father {
private Person person;
public Father(Person person){
this.person = person;
}
public void findLove(){
System.out.println("父亲为儿子物色对象");
this.person.findLove();
System.out.println("双方父母同意");
}
}
最后做一下测试
package com.packer.partten.factory.proxy.staticproxy;
/**
* Created by lijianfang on 2021/9/25.
*/
public class FatherProxyTest {
public static void main(String[] args) {
Father father = new Father((Person) new Son());
father.findLove();
}
}
测试结果如下:儿子通过父亲的代理找到对象。
父亲为儿子物色对象
找对象的要求:肤白貌美大长腿
双方父母同意
Process finished with exit code 0
第二种方式:动态代理模式
动态代理方式一:CGLib动态代理是通过生成一个被代理对象的子类,然后重写父类的方法。
小明通过媒婆找对象。先新建一个xiaoming.java类
package com.packer.partten.factory.proxy.cglib;
/**
* Created by lijianfang on 2021/9/27.
*/
public class xiaoming {
/**
* 找对象的要求:肤白貌美大长腿
*/
public void findLove(){
System.out.println("找对象的要求:肤白貌美大长腿");
}
}
再新建一个媒婆类Meipo.java类
package com.packer.partten.factory.proxy.cglib;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* Created by lijianfang on 2021/9/27.
*/
public class Meipo implements MethodInterceptor{
public Object getInstance(Class clazz) throws Exception{
Enhancer enhancer = new Enhancer();
//把父类设置为谁,这一步就是告诉cglib,生成的字类需要继承那个类
enhancer.setSuperclass(clazz);
//设置回调
enhancer.setCallback(this);
//第一步/生成源代码
//第二不 编译class文件
//第三步 加载到JVM中,并返回被代理的对象
return enhancer.create();
}
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("我是媒婆,的给你找个异性才可以");
System.out.println("开始海选");
System.out.println("--------");
//这个obj的引用是由CGLib给我们new出来的,CGLib new出来的对象,是被代理的对象的子类(继承了我们写的类)
//OOP,在new子类之前,实际上默认先调用了我们的super()方法
//new了子类的同时,必须先new出来父类,这就相当于是间接的持有了我们父类的引用
//子类重写了父类的所有方法,我们改变了子类对象的某些属性,是可以间接的操作父类的属性
Object result = methodProxy.invokeSuper(object, args);
System.out.println("--------");
System.out.println("如果合适的话,就准备办事");
return result;
}
}
最后创建测试类CGLibProxyTest.java
package com.packer.partten.factory.proxy.cglib;
/**
* Created by lijianfang on 2021/9/27.
*/
public class CGLibProxyTest {
public static void main(String[] args) {
//JDK的动态代理是通过接口来进行强制转换的
//生成以后的的代理对象,可以强制转换成接口
//CGLib的动态代理是通过生成一个被代理对象的子类,然后重写父类的方法
//生成以后的对象,可以强制转换为被代理对象(自己写的类)
//子类引用赋值给父类
try{
xiaoming obj = (xiaoming) new Meipo().getInstance(xiaoming.class);
obj.findLove();
}catch (Exception e){
System.out.println("报错了"+e);
}
}
}
测试结果:
我是媒婆,的给你找个异性才可以
开始海选
--------
找对象的要求:肤白貌美大长腿
--------
如果合适的话,就准备办事
Process finished with exit code 0
动态代理方式二:JDK动态代理,JDK的动态代理是通过接口来进行强制转换的,生成以后的的代理对象,可以强制转换成接口。
原理:
1.拿到被代理对象的引用,然后获取它的接口
2.JDK代理重新生成一个类,同时实现我们给的代理对象所实现的接口
3.把被代理对象的引用也拿到了
4.重新动态生成一个class字节码
5.然后编译
我们举一个例子,代码如下:
新建一个person.java类
package com.packer.partten.factory.proxy.jdk;
/**
* Created by lijianfang on 2021/10/2.
*/
public interface Person {
//寻找真爱、相亲
void findLove();
//获取性别
String getSex();
//获取名字
String getName();
}
新建一个小明xiaoming.java类,实现person的接口,代码如下:
package com.packer.partten.factory.proxy.jdk;
/**
* Created by lijianfang on 2021/10/2.
*/
public class xiaoming implements Person {
private String sex = "女";
private String name = "小明";
/**
* 找对象
*/
@Override
public void findLove() {
System.out.println("我叫" + this.name + ",性别:" + this.sex + "我找对象的要求是:");
System.out.println("高富帅");
System.out.println("有房有车的");
System.out.println("身高要求180cm以上,体重70kg");
}
/**
* 获取性别
* @return
*/
@Override
public String getSex() {
return null;
}
/**
* 获取名字
* @return
*/
@Override
public String getName() {
return null;
}
public void setSex(String sex) {
this.sex = sex;
}
public void setName(String name) {
this.name = name;
}
}
在新建meipo.java类,代码如下:
package com.packer.partten.factory.proxy.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* Created by lijianfang on 2021/10/2.
*/
public class Meipo implements InvocationHandler {
private Person target; //被代理对象的引用作为一个成员变量保存下来了
//获取被代理人的个人资料
public Object getInstance(Person target) throws Exception{
this.target = target;
Class clazz = target.getClass();
System.out.println("被代理对象的class是:"+clazz);
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("我是媒婆:" + "得给你找个异性才行");
System.out.println("开始进行海选...");
System.out.println("------------");
//调用的时候
method.invoke(this.target, args);
System.out.println("------------");
System.out.println("如果合适的话,就准备办事");
return null;
}
}
最后新建测试类JdkProxyTest.java类,代码如下:
package com.packer.partten.factory.proxy.jdk;
/**
* Created by lijianfang on 2021/10/2.
*/
public class JdkProxyTest {
public static void main(String[] args) throws Exception {
//原理:
//1.拿到被代理对象的引用,然后获取它的接口
//2.JDK代理重新生成一个类,同时实现我们给的代理对象所实现的接口
//3.把被代理对象的引用也拿到了
//4.重新动态生成一个class字节码
//5.然后编译
Person person = (Person) new Meipo().getInstance(new xiaoming());
System.out.println(person.getClass());
person.findLove();
}
}
测试结果如下:
被代理对象的class是:class com.packer.partten.factory.proxy.jdk.xiaoming
class com.sun.proxy.$Proxy0
我是媒婆:得给你找个异性才行
开始进行海选...
------------
我叫小明,性别:女我找对象的要求是:
高富帅
有房有车的
身高要求180cm以上,体重70kg
------------
如果合适的话,就准备办事
Process finished with exit code 0