设计模式是一套被反复使用,多数人知晓,经过分类编目的,代码设计的总结,也可以说是前人的智慧结晶。学习设计模式能让我们对一些应用场景使用相同的套路达到很好的效果,我会不定时更新一些自己对设计模式的理解的文章,从定义,实现,应用场景来说说设计模式,今天我要说的对象是代理模式
一:定义
代理模式:给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。代理模式是常用的结构型设计模式之一,当无法直接访问某个对象或不适合直接访问某个对象时可以通过一个代理对象来间接访问,代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。这么做也服务开闭原则(对扩展开放对修改关闭)的要求。代理模式的关键点是:代理对象会调用被代理对象,代理对象对被代理对象的功能做了扩展。
打个比方,我们想要购买国外的某些商品,由于各种成本问题,直接去一趟国外不划算,于是我们找了代购帮我们购买,这里的代购帮我们到国外的实体店完成了购买的动作,他就是实体店的代理,而实体店是被代理,而我们和代购直接的沟通交涉,相当于是对被代理功能的扩展,而这一步是在我们和代理直接完成,不会影响被代理的商店
二:实现
从实现上来说,代理模式可以分为静态,动态代理和子类代理
静态代理:静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类,所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。下面展示代码实现
//定义接口
interface UserDao{
void save();
}
//被代理类实现接口
class UserService implements UserDao{
@Override
public void save() {
System.out.println("这是需要被代理的保存方法");
}
}
//代理类代理相关的方法
class UserDaoProxy implements UserDao{
//在代理类中引用被代理类,这里只是使用最简单的方法
private UserDao iUserDao;
public UserDaoProxy(UserDao iUserDao){
this.iUserDao=iUserDao;
}
//引用并扩展被代理类的方法
@Override
public void save() {
System.out.println("这里编写需要扩展的内容");
iUserDao.save();
System.out.println("保存成功或者失败之后的操作");
}
//测试一下
public static void main(String[] df){
UserDaoProxy proxy=new UserDaoProxy(new UserService()); proxy.save();
}
}
动态代理:代理对象是动态生成的,不需要实现父类接口
动态的生成一个对象,很自然的就想到反射。JDK中有动态生成代理的包(java.lang.reflect.Proxy),是专门为创建动态代理对象存在的,它的内部实现是使用了反射技术,JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是: static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为: ● ClassLoader loader,:指定当前目标对象使用类加载器,获取加载器的方法是固定的 ● Class<?>[] interfaces,:目标对象实现的接口的类型,使用泛型方式确认类型 ● InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入。
这里使用一个工厂类创建动态代理对象来代替上面的代理对象,其他方法不变
class ProxyFactory{ public static Object getProxyInstance(Object target){ 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("这是代理对象扩展的内容"); Object object=method.invoke(target,args); return object; } }); }
//测试一下
public static void main(String[] df){
UserDao object=(UserDao)ProxyFactory.getProxyInstance(new UserService());
object.save();
}
}
子类代理:
上面的静态代理和动态代理模式都是要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理
Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.
● JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现.
● Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)
● Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.
Cglib子类代理实现方法:
1.需要引入cglib的jar文件,但是Spring的核心包中已经包括了Cglib功能,所以直接引入pring-core-3.2.5.jar即可.
2.引入功能包后,就可以在内存中动态构建子类
3.代理的类不能为final,否则报错
4.目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.
//被代理对象,没有实现任何接口
class UserDao {
public void save() {
System.out.println("----已经保存数据!----");
}
}
class ProxyFactory implements MethodInterceptor{
//给目标对象创建一个代理对象
public Object getProxyInstance(Object target){
//1.工具类
Enhancer en = new Enhancer();
//2.设置父类
en.setSuperclass(target.getClass());
//3.设置回调函数
en.setCallback(this);
//4.创建子类(代理对象)
return en.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("这是需要扩展的内容...");
//执行目标对象的方法
Object returnValue = method.invoke(target, args);
System.out.println("这是需要扩展的内容...");
return returnValue;
}
//测试一下
public static void main(String[] arg){
//被代理对象
UserDao target = new UserDao();
//代理对象
UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();
//执行代理对象的方法
proxy.save();
}
}
三:应用场景
从应用场景来说,代理模式可以分为远程代理,缓冲代理,智能引用代理,虚拟代理,保护代理等等
(1) 当客户端对象需要访问远程主机中的对象时可以使用远程代
(2) 当需要用一个消耗资源较少的对象来代表一个消耗资源较多的对象,从而降低系缩短运行时间时可以使用虚拟代理,例如一个对象需要很长时间才能完成加
(3) 当需要为某一个被频繁访问的操作结果提供一个临时存储空间,以供多个客户端共享访问这些结果时可以使用缓冲代理。通过使用缓冲代理,系统无须在客户端每一次访问时都重新执行操作,只需直接从临时缓冲区获取操作结果即可。
(4) 当需要控制对一个对象的访问,为不同用户提供不同级别的访问权限时可以使用保护代理。
(5) 当需要为一个对象的访问(引用)提供一些额外的操作时可以使用智能引用代理。