代理模式 Proxy
代理模式是一种结构型设计模式, 让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。
这有什么好处呢?
如果需要在类的主要业务逻辑前后执行一些工作
, 你无需修改类就能完成这项工作
。 由于代理实现的接口与原类相同, 因此你可将其传递给任何一个使用实际服务对象的客户端。
总结:在目标对象实现的基础上,增强额外的 功能操作,扩展目标对象的功能
三种:
静态代理
动态代理(JDK代理,接口代理)
Cglib代理(可以在内存中动态的创建对象,而不需要实现接口)
真实世界类比
信用卡和现金在支付过程中的用处相同。
信用卡是银行账户的代理, 银行账户则是一大捆现金的代理。
它们都实现了同样的接口, 均可用于进行支付。 消费者会非常满意, 因为不必随身携带大量现金; 商店老板同样会十分高兴, 因为交易收入能以电子化的方式进入商店的银行账户中, 无需担心存款时出现现金丢失或被抢劫的情
况。
代理模式结构
-
服务接口 (Service Interface) 声明了服务接口。 代理必须遵循该接口才能伪装成服务对象。
-
服务 (Service) 类提供了一些实用的业务逻辑。
-
代理 (Proxy) 类包含一个指向服务对象的引用成员变量。 代理完成其任务 (例如延迟初始化、 记录日志、 访问控制和缓存等) 后会将请求传递给服务对象。
通常情况下, 代理会对其服务对象的整个生命周期进行管理。
-
客户端 (Client) 能
通过同一接口与服务或代理进行交互
, 所以你可在一切需要服务对象的代码中使用代理。
静态代理
静态代理在使用的时候,需要定义接口或者父类
,被代理对象,与代理对象一起实现相同的接口或者是继承相同的父类
服务接口
public interface ITeacherDao {
/**
* teach方法
*/
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) {
super();
this.target = target;
}
/**
* 这里在重写接口方法的同时,还可以对方法进行增强
*/
@Override
public void teach() {
System.out.println("代理开始before....");
target.teach();
System.out.println("代理结束after....");
}
}
客户端
public class Client {
public static void main(String[] args) {
// 创建目标对象
TeacherDao teacherDao = new TeacherDao();
// 创建代理对象,同时将目标对象传递给代理对象
TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao);
// 通过代理对象,调用被代理对象TeacherDao的方法
teacherDaoProxy.teach();
}
}
代码运行结果
动态代理
代理对象,不需要实现接口,但是目标对象要实现接口,否则不能使用动态代理
代理对象的生成,是利用JDK的API,动态地在内存中构建代理对象
动态代理也叫做:JDK代理,接口代理
java.lang.reflect.Proxy;
服务接口
public interface ITeacherDao {
/**
* teach方法
*/
void teach();
/**
*
* @param name
*/
void sayHello(String name);
}
代理类
public class TeacherDao implements ITeacherDao {
@Override
public void teach() {
System.out.println("老师正在授课~~~");
}
@Override
public void sayHello(String name) {
System.out.println("你好啊~"+name);
}
}
代理工厂
public class ProxyFactory {
/**
* 维护一个目标对象
*/
private Object target;
/**
* 构造器,对target进行初始化
* @param target
*/
public ProxyFactory(Object target) {
this.target = target;
}
/**
*
* @return
*/
public Object getProxyInstance(){
/**
* public static Object newProxyInstance(ClassLoader loader,
* Class<?>[] interfaces,
* InvocationHandler h)
* ClassLoader loader,指定当前对象的类加载器
* Class<?>[] interfaces, 目标对象的实现类接口,使用泛型来确认类型
* InvocationHandler h,处理事件,执行目标对象的方法时,会触发事件处理器的方法,在这里写:如何去增强原来的方法,如何调用
*/
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("利用JDK动态代理来对原来的方法进行增强~~~");
// 利用反射机制来调用目标对象的方法
Object invoke = method.invoke(target, args);
System.out.println("增强完毕~动态代理结束~~~~");
// 返回调用的结果
return invoke;
}
});
}
}
代理类——由代理工厂动态生成
动态生成————class com.sun.proxy.$Proxy0
public class Client {
public static void main(String[] args) {
// 创建目标对象(被代理的对象)
ITeacherDao target = new TeacherDao();
// 给目标对象创建代理对象,强转为ITeacherDao
ITeacherDao proxyInstance = (ITeacherDao) new ProxyFactory(target).getProxyInstance();
System.out.println("proxyInstance--------->" + proxyInstance.getClass());
// proxyInstance--------->class com.sun.proxy.$Proxy0
}
}
客户端
public class Client {
public static void main(String[] args) {
// 创建目标对象(被代理的对象)
ITeacherDao target = new TeacherDao();
// 给目标对象创建代理对象,强转为ITeacherDao
ITeacherDao proxyInstance = (ITeacherDao) new ProxyFactory(target).getProxyInstance();
// 通过代理对象,调用被代理对象TeacherDao的方法
proxyInstance.teach();
proxyInstance.sayHello("ZQH");
}
}
代码结果:
Cglib代理
静态代理和JDK代理模式都
要求目标对象实现一个接口
,有时候目标对象只是一个单独的对象,并没有实现任何的接口
,这个时候可以使用目标对象子类来实现代理——这就是Cglib代理Cglib页叫做子类代理,归属于动态代理
Cglib是一个强大的高性能代码生成包,可以再运行时期动态扩展Java类和实现Java接口,广泛用于AOP框架,实现方法拦截
如何选择代理模式:
- 目标对象需要实现接口,使用JDK代理
- 目标对象不需要实现接口,使用Cglib代理
Cglib包的底层是通过使用字节码处理器ASM来转换字节码,并生成新的类
服务类
public class TeacherDao {
public void teach() {
System.out.println("老师正在授课.....我是Cglib代理,不需要实现接口");
}
}
代理工厂
public class ProxyFactory implements MethodInterceptor { //实现 MethodInterceptor接口
/**
* 目标对象
*/
private Object target;
/**
* 构造器,传入目标对象
* @param target
*/
public ProxyFactory(Object target) {
this.target = target;
}
public Object getProxyInstance(){
// 创建一个工具类型
Enhancer enhancer = new Enhancer();
// 设置父类
enhancer.setSuperclass(target.getClass());
// 设置回调函数
enhancer.setCallback(this);
// 创建子类对象,即代理对象
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Cglib代理模式开始......");
Object invoke = method.invoke(target, objects);
System.out.println("Cglib代理模式提交......");
return invoke;
}
}
客户端
public class Client {
public static void main(String[] args) {
// 创建目标对象(被代理的对象)
TeacherDao target = new TeacherDao();
// 给目标对象创建代理对象,把目标对象传递给代理对象
TeacherDao proxyInstance = (TeacherDao) new ProxyFactory(target).getProxyInstance();
// 通过代理对象,调用被代理对象TeacherDao的方法
proxyInstance.teach();
}
}
运行结果:
Spring的动态代理
参考:https://blog.csdn.net/shijinjins/article/details/124423859
Spring如何选择是用JDK还是cglib?
1、当bean实现接口时,会用JDK代理模式
2、当bean没有实现接口,用cglib实现