代理模式
代理模式
基本介绍
- 1.代理模式,为目标对象提供一个替身,以控制这个对象的访问。即通过代理对象访问目标对象,这样做的好处是,可以在目标对象功能的基础上,增加额外的操作,即扩展目标对象的功能。
- 2.目标对象(被代理对象)可以是远程对象,创建开销大的对象或者需要安全控制的对象。
- 3.代理模式有不同的三种形式,有静态代理,动态代理(JDK代理,接口代理),Cglib代理(可以在内存中动态的创建对象,而不用实现接口,本身也是动态代理的范畴)
- 4.代理模式示意图
静态代理
基本介绍
静态代理在使用中,代理对象类和目标对象(被代理对象)类,需要实现相同的接口,或继承相同的父类。
案例引入
要求:
- 1.定义一个ITeacherDao接口。
- 2.TeacherDao类和TeacherDaoProxy类都实现这个接口。
- 3.TeacherDaoProxy类中,持有TeacherDao对象。
- 4.使用代理对象的目标方法,完成方法的调用,在代理对象内部还是使用的TeacherDao对象完成,不过可以新增一些额外的操作。
类图
代码
//接口
public interface ITeacherDao {
public void teach();
}
//被代理类
public class TeacherDao implements ITeacherDao{
@Override
public void teach() {
System.out.println(" 老师授课中.... ");
}
}
//代理类
public class TeacherDaoProxy implements ITeacherDao{
private ITeacherDao target;
public TeacherDaoProxy(ITeacherDao target){
this.target = target;
}
@Override
public void teach() {
System.out.println("代理开始...");
target.teach();
System.out.println("代理结束...");
}
}
//测试
public class Client {
public static void main(String[] args) {
TeacherDao teacherDao = new TeacherDao();
TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao);
teacherDaoProxy.teach();
}
}
优缺点分析
- 1.优点,在不修改目标对象的功能前提下,通过代理对象对目标对象的功能进行了扩展。
- 2.缺点,静态代理需要让代理对象和目标对象实现一样的接口,或者继承相同类,如果要对目标对象的功能有不同的扩展,则需要创建很多代理类。
- 3.一旦接口或类增加方法,目标类,和代理类都要维护。
动态代理
基本介绍
- 1.代理对象,不需要实现接口,但是目标对象要实现接口,否则不能使用动态代理。
- 2.代理对象的生成,是利用java中的API,动态的在内存中,创建代理对象。
- 3.动态代理,也叫JDK代理,接口代理。
JDK中生成代理对象的API
- 1.代理类,java.lang.reflect.Proxy。
- 2.JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
案例引入
要求:使用动态代理,实现静态案例中的案例。
类图
代码
public interface ITeachDao {
public void teach();
}
public class TeachDao implements ITeachDao{
@Override
public void teach() {
System.out.println("老师上课..");
}
}
public class ProxyFactory {
private Object target;
public ProxyFactory(TeachDao target){
this.target = target;
}
public Object getProxyInstance(){
/**
* public static Object newProxyInstance(ClassLoader loader,
* Class<?>[] interfaces,
* InvocationHandler h)
* 说明:
* 1.ClassLoader loader: 指定当前目标对象使用的类加载器,获取加载器的方法固定。
* 2.Class<?>[] interfaces: 目标对象实现的接口组的class对象组成的数组,使用泛型方法确认类型。
* 3.InvocationHandler h: 事情处理器,执行目标对象的方法时,会触发当前重写的事情处理器方法,该方法会把当前执行的目标对象(proxy)作为参数传入。
*/
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy,method,args) -> {
System.out.println("JDK代理开始....");
Object result = method.invoke(target, args);
System.out.println("JDK代理结束....");
return result;
});
}
}
public class Client {
public static void main(String[] args) {
TeachDao target = new TeachDao();
ProxyFactory proxyFactory = new ProxyFactory(target);
ITeachDao proxyInstance = (ITeachDao)proxyFactory.getProxyInstance();
proxyInstance.teach();
System.out.println(proxyInstance.getClass());
}
}
Cglib代理
Cglib代理模式的基本介绍
- 1.静态代理和JDK代理都要求目标对象继承类,或实现一个接口,但是很可能目标对象就只是一个单独的类,没有实现任何接口,继承任何类,这个时候可以使用目标对象子类来实现代理,这就是Cglib代理。
- 2.Cglib代理也叫子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展。
- 3.Cglib是一个强大的高性能的代码生成包,它可以在运行期间扩展Java类与实现接口,它广泛的被许多AOP的框架使用,例如Spring AOP实现方法拦截。
- 4.在AOP编程中如何选择代理模式:
- 4.1 目标对象需要实现接口,用JDK代理。
- 4.2 目标对象不需要实现接口,用Cglib代理。
- 5.Cglib包的底层是通过使用字节码处理框架ASM转换字节码并生成新的类。
Cglib代理模式实现步骤
- 1.需要引入Cglib的jar文件,或Maven坐标(Spring框架底层会使用到Cglib框架,可以直接引入Cglib的坐标就可以使用了)。
- 2.在内存中动态构架子类,注意代理的类不能为final,否则报错java.lang.IllegalArgumentException。
- 3.目标对象的方法如果为final/static,那么就不会被拦截,既不会执行目标对象额外的业务方法。
案例引入
要求:将前面的案例用Cglib代理模式实现。
uml类图
public class SunDao {
public void sunLightToEarth(){
System.out.println("太阳普照大地");
}
}
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @author 长名06
* @version 1.0
*/
public class ProxyFactory implements MethodInterceptor {
private SunDao target;
public ProxyFactory(SunDao target){
this.target = target;
}
public Object getProxyInstance(){
//1.获取工具类
Enhancer enhancer = new Enhancer();
//2.设置父类
enhancer.setSuperclass(target.getClass());
//3.设置回调函数
enhancer.setCallback(this);
//4.创建子类对象,即是代理对象
return enhancer.create();
}
//重写intercept方法,代理对象调用方法时,会调用到该方法,类似JDK代理中传入的匿名内部类的InvocationHandler h中重写的invoke方法。
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib代理模式 开始");
//执行代理方法,获取返回值
Object resultVal = method.invoke(target, args);
System.out.println("cglib代理模式 结束");
return resultVal;
}
}
public class Client {
public static void main(String[] args) {
//创建目标对象
SunDao target = new SunDao();
//获取到代理对象,并且将目标对象传递给代理对象
SunDao proxyInstance = (SunDao)new ProxyFactory(target).getProxyInstance();
//执行代理对象的方法,触发intecept方法,从而实现对目标对象的调用
proxyInstance.sunLightToEarth();
}
}
常见的代理模式,介绍几种变体
1.防火墙代理:内网通过代理穿透防火墙,实现对公网的访问。
2.缓存代理:当请求图片文件等资源时,先到缓存代理取,如果取到资源则返回。取不到资源,再到公网或者数据库取,然后缓存。
3.远程代理:远程对象的本地代表,通过它可以把远程对象当本地对象来调用,远程代理通过网络和真正的远程对象沟通信息。
4.同步代理:主要使用在多线程编程中,完成多线程间异步工作。
只是为了记录自己的学习历程,且本人水平有限,不对之处,请指正。