代理模式,拿下!!!
代理模式
是给一个对象提供一个代理对象,并由代理对象控制对原对象的引用
。
通俗来讲,代理模式就是我们所熟知的中介。
以我们熟知的商品代购为例:
假如我们需要买一个物品,我们可以直接去工厂里购买;也可以找代购。
如果直接去工厂购买,我们在购买前需要对自己要买的物品做一些调研,然后去工厂直接去提货,这样什么事情都需要自己亲力亲为。
如果我们通过代购购买,我们只需要告诉代购我们需要什么,剩下的事情代购会帮我们处理(调研、拿货),最终给我们需要的相应的物品。
因此,代理模式的目标如下:
(1)通过引用代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来不必要的复杂性。
(2)通过代理对象对原有的业务进行增强。
通常情况下,按照代理的创建时期,一般可以分为两种:
静态代理
静态代理是由程序员或者特定的工具自动生成的源代码,再对其编译,在程序运行之前,代理类编译的生成的.class文件就已经存在了
动态代理
动态代理是在程序运行时,通过反射机制动态创建而成。
1、静态代理模式
静态代理中的代理类和委托类的关系在运行前就确定了,如图所示:
特别注意几个概念:
抽象对象
抽象对象声明了真实对象和代理对象的公共接口
。
真实对象
代理对象所代表的真实对象,最终被引用的对象
。
代理对象
包含真实对象进而操作真实对象,相当于访问者与真实对象直接的中介
。
下面,我们来举个例子:
(1)创建服务类接口
public interface BuyCar {
void buycar();
}
(2)服务实现类
public class BuyCarImpl implements BuyCar{
public void buycar() {
System.out.println("买一辆奥迪");
}
}
(3)创建代理类
public class BuyCarProxy implements BuyCar{
private BuyCar buyCar;
public BuyCarProxy(BuyCar buyCar){
this.buyCar = buyCar;
}
public void buycar() {
System.out.println("买车前的调研......");
buyCar.buycar();
System.out.println("买车后的保养......");
}
}
(4)编写测试类
public class ProxyTest {
public static void main(String[] args) {
BuyCarImpl buyCar = new BuyCarImpl();
BuyCarProxy buyCarProxy = new BuyCarProxy(buyCar);
buyCarProxy.buycar();
}
}
优点:静态代理在不修改目标对象的前提下,可以通过代理对象对目标对象进行扩展。
代理类可以使得客户端不需要知道具体的实现类是什么,怎么做的,客户端只需知道代理即可(解耦合)
缺点:代理类和具体的实现类实现了相同的接口,代理类通过实现类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。
2、动态代理模式
2.1 JDK自带
事实上,单一的代理是不存的,一个代理可以同时身兼数职。既可以代购车,也可以代购房。
在动态代理中我们不再需要手动的创建代理类,我们只需要一个动态处理器
就可以了,而真正的代理对象由JDK运行时动态的创建。
(1)创建服务类接口
//买车接口
public interface BuyCar {
void buycar();
}
//买房接口
public interface BuyHouse {
void buyHouse();
}
(2)服务实现类
//买车接口的实现类
public class BuyCarImpl implements BuyCar {
public void buycar() {
System.out.println("买一辆奥迪");
}
}
//买房接口的实现类
public class BuyHouseImpl implements BuyHouse{
public void buyHouse() {
System.out.println("买一栋大别墅");
}
}
(3)动态代理类
//通过实现 InvocationHandler 接口创建自己的调用处理器;
public class ProxyHandler implements InvocationHandler {
private Object object;
//通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
public ProxyHandler(Object object){
this.object = object;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before invoke "+method.getName());
method.invoke(object,args);
System.out.println("After invoke "+method.getName());
return null;
}
}
(4)测试类
public class DynamicProxyTest {
public static void main(String[] args){
BuyHouse buyHouse = new BuyHouseImpl();
BuyCar buyCar = new BuyCarImpl();
InvocationHandler handler = new ProxyHandler(buyHouse);
InvocationHandler handler1 = new ProxyHandler(buyCar);
/**
* 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
*/
BuyHouse proxyHouse = (BuyHouse) Proxy.newProxyInstance(buyHouse.getClass().getClassLoader(), buyHouse.getClass().getInterfaces(),handler);
BuyCar proxyCar = (BuyCar) Proxy.newProxyInstance(buyCar.getClass().getClassLoader(), buyCar.getClass().getInterfaces(),handler1);
proxyHouse.buyHouse();
proxyCar.buycar();
}
}
注意Proxy.newProxyInstance()
方法接受三个参数:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
ClassLoader loader:指定当前目标对象使用类加载器,获取加载器的方法是固定的 Class<?>[] interfaces:指定目标对象实现的接口的类型,使用泛型方式确认类型 InvocationHandler h:指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法。
2.2 CGLIB
CGLIB
相比于JDK
动态代理更加强大,JDK动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有接口,那么Java动态代理就没法使用了。
在使用cglib
前,需要先添加依赖。
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.12</version>
</dependency>
(1)目标类
Dao
public class Dao {
public void update() {
System.out.println("PeopleDao.update()");
}
}
Dao1
public class Dao1 {
public void select(){
System.out.println("PeopleDao.select");
}
}
(2)代理类
public class DaoProxy implements MethodInterceptor {
public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Befor Metod Invoke");
methodProxy.invokeSuper(object,objects);
System.out.println("After Method Invoke");
return null;
}
}
参数解释:
Object表示要进行增强的对象 Method表示拦截的方法 Object[]数组表示参数列表,基本数据类型需要传入其包装类型,如int-->Integer、long-Long、double-->Double MethodProxy表示对方法的代理,invokeSuper方法表示对被代理对象方法的调用
(3)测试
public class CglibProxyTest {
public static void main(String[] args) {
DaoProxy daoProxy = new DaoProxy();
Enhancer enhancer = new Enhancer();
Enhancer enhancer1 = new Enhancer();
//设置要继承的父类
enhancer.setSuperclass(Dao.class);
enhancer1.setSuperclass(Dao1.class);
//设置回调方法
enhancer.setCallback(daoProxy);
enhancer1.setCallback(daoProxy);
//创建动态代理类
Dao dao = (Dao)enhancer.create();
Dao1 dao1= (Dao1) enhancer1.create();
dao.update();
System.out.println("...................................");
dao1.select();
}
}
公众号回复:Java全套、Java架构、大数据、电子书、算法和刷题笔记、面经,即可获得对应的学习资源。
点个在看你最好看