使用代理模式创建代理对象,让代理对象控制目标对象的访问(目标对象可以是远程的对象、创建开销大的对象或需要安全控制的对象),并且可以在不改变目标对象的情况下添加一些额外的功能。
我们用一个例子来解释静态代理:车展中有许多车,因此创建一个buy_car的接口,其中包含BuyCar的方法,而顾客customer继承买车的接口,Override->BuyCar的方法,同时具有Money属性,而销售方则作为代理,判断你是否有足够的Money购买车辆。
静态代理与动态代理的区别:所谓静态代理,其实质是自己手写(或者用工具生成)代理类,也就是在程序运行前就已经存在的编译好的代理类。但是,如果我们需要很多的代理,每一个都这么去创建实属浪费时间,而且会有大量的重复代码,此时我们就可以采用动态代理,动态代理可以在程序运行期间根据需要动态的创建代理类及其实例来完成具体的功能。总的来说,根据代理类的创建时机和创建方式的不同,我们可以将代理分为静态代理和动态代理两种形式。
上述的买车案例中,我们可以直接去柜台结账买车,而通过与销售方的接触,将我们的具体实现与调用方(买车方)进行了解耦,通过面向接口进行编码完全将具体的实现(买车过程)隐藏在内部(销售方)。
代理模式设计一下角色:
二、静态代理案例Subject(抽象主题类):接口或者抽象类,声明真实主题与代理的共同接口方法。
RealSubject(真实主题类):也叫做被代理类或被委托类,定义了代理所表示的真实对象,负责具体业务逻辑的执行,客户端可以通过代理类间接的调用真实主题类的方法。
Proxy(代理类):也叫委托类,持有对真实主题类的引用,在其所实现的接口方法中调用真实主题类中相应的接口方法执行。
Client(客户端类):使用代理模式的地方。
1、新建一个买车的接口:
public interface buy_car {
public void buy_mycar();
}
2、新建一个people人类,具有买车的行为,所以实现接口buy_car
class people implements buy_car {
private int cash;
private String vip;
private String username;
@Override
public void buy_mycar() {
// TODO Auto-generated method stub
System.out.print(username+"是vip 客户,可以直接购买新车!");
}
public int getCash(){
return cash;
}
public void setCash(int cash){
this.cash = cash;
}
public String getUsername(){
return username;
}
public void setUsername(String username){
this.username = username;
}
public String getVip(){
return vip;
}
public void setVip(String vip){
this.vip = vip;
}
}
3、people类不能拥有车,必须经过proxy代理类的认证,符合条件之后才可以拥有车辆,新建一个代理,这个代理类来考察当前的people是否有资格进行买车:
class proxyclass implements buy_car {
private people people;
public people getPeople(){
return people;
}
public void setPeople(people people){
this.people = people;
}
@Override
public void buy_mycar() {
// TODO Auto-generated method stub
if (people.getVip() == "vip"){
people.buy_mycar();
return ;
}
if(people.getCash()>=50000){
System.out.println(people.getUsername()+"买了新车,交易结束!");
}
else
{
System.out.println(people.getUsername()+"钱不够,不能买车,继续比赛!");
}
}
}
4、最后创建一个客户端,用来模拟买车的行为:
public class run_main {
public static void main(String[] args) {
// TODO Auto-generated method stub
people people_1 =new people();
people_1.setCash(60000);
people_1.setUsername("jeck");
people people_2 =new people();
people_2.setCash(40000);
people_2.setUsername("rose");
people people_3 =new people();
people_3.setCash(0);
people_3.setUsername("tom");
people_3.setVip("vip");
proxyclass proxy_buy = new proxyclass();
proxy_buy.setPeople(people_1);
proxy_buy.buy_mycar();
proxy_buy.setPeople(people_2);
proxy_buy.buy_mycar();
proxy_buy.setPeople(people_3);
proxy_buy.buy_mycar();
}
}
三、动态代理
动态代理可以在程序运行期间根据需要动态的创建代理类及其实例来完成具体的过程
动态代理分为两大类:基于接口的动态代理,基于类的动态代理
-
基于接口--jdk动态代理
-
基于类:cglib
-
java字节码实现:javasis
使用动态代理,需要了解两个类:proxy->代理,invocationHandler:调用处理程序
1). 抽象主题(接口)
同样地,首先得有一个接口,通用的接口是代理模式实现的基础。
package cn.inter;
public interface Subject {
public void doSomething();
}
2). 被代理角色(目标类)
然后,我们要有一个真正的实现这个 Subject 接口的类,以便代理。
package cn.impl;
import cn.inter.Subject;
public class RealSubject implements Subject {
public void doSomething() {
System.out.println("call doSomething()");
}
}
3). 代理角色(代理类)与客户端
在动态代理中,代理类及其实例是程序自动生成的,因此我们不需要手动去创建代理类。在Java的动态代理机制中,InvocationHandler(Interface)接口和Proxy(Class)类是实现我们动态代理所必须用到的。事实上,Proxy通过使用InvocationHandler对象生成具体的代理代理对象,下面我们看一下对InvocationHandler接口的实现:
package cn.handler;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ProxyHandler implements InvocationHandler {
private Object proxied; // 被代理对象
public ProxyHandler(Object proxied) {
this.proxied = proxied;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 在转调具体目标对象之前,可以执行一些功能处理
System.out.println("前置增强处理: yoyoyo...");
// 转调具体目标对象的方法(三要素:实例对象 + 实例方法 + 实例方法的参数)
Object obj = method.invoke(proxied, args);
// 在转调具体目标对象之后,可以执行一些功能处理
System.out.println("后置增强处理:hahaha...");
return obj;
}
}
4)在实现了InvocationHandler接口后,我们就可以创建代理对象了。在Java的动态代理机制中,我们使用Proxy类的静态方法newProxyInstance创建代理对象,如下:
package cn.client;
import java.lang.reflect.Proxy;
import cn.handler.ProxyHandler;
import cn.impl.RealSubject;
import cn.inter.Subject;
public class Test {
public static void main(String args[]) {
// 真实对象real
Subject real = new RealSubject();
// 生成real的代理对象
Subject proxySubject = (Subject) Proxy.newProxyInstance(
Subject.class.getClassLoader(), new Class[] { Subject.class },
new ProxyHandler(real));
proxySubject.doSomething();
System.out.println("代理对象的类型 : " + proxySubject.getClass().getName());
System.out.println("代理对象所在类的父类型 : " + proxySubject.getClass().getGenericSuperclass());
}
}
狂神案例:
1、接口
public interface Rent{
public void rent();
}
2、实现类:
public class Host implements Rent{
public void rent()
{
System.out.println("出租房屋");
}
}
3、自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Rent rent;
//set方法 注入业务
public void setRent(Rent rent) {
this.rent = rent;
}
//生成代理类
//获取当前类的加载器,获取业务的接口,当前对象
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
rent.getClass().getInterfaces(),this);
}
//处理业务,并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
Object result = method.invoke(Target, args);
return result;
}
//添加日志
public void log(String msg){
System.out.println("[debug]调用了"+msg+"方法");
}
}
4、Client
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//代理角色,不存在,找他的处理程序
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setTarget(host);//设置要代理的对象
//动态生成代理类
Object proxy = (Object) pih.getProxy();
proxy.rent();
}
}
改写3和4步来变成万能的自动代理
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Object target;
//set方法 注入业务
public void setTarget(Object target) {
this.target = target;
}
//生成代理类
//获取当前类的加载器,获取业务的接口,当前对象
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(),this);
}
//处理业务,并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
Object result = method.invoke(Target, args);
return result;
}
//添加日志
public void log(String msg){
System.out.println("[debug]调用了"+msg+"方法");
}
}
四、静态代理vs动态代理
静态代理的缺点:
动态代理的优点:静态代理如果接口新增一个方法,除了所有实现类(真实主题类)需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。代理对象只服务于一种类型的对象,如果要服务多类型的对象。必须要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了
动态代理的缺点:可以通过一个代理类完成全部的代理功能,接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。当接口方法数量较多时,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。动态代理的应用使我们的类职责更加单一,复用性更强。
优缺点分析不能对类进行代理,只能对接口进行代理,如果我们的类没有实现任何接口,那么就不能使用这种方式进行动态代理(因为$Proxy()这个类集成了Proxy,Java的集成不允许出现多个父类)。
优点:
代理作为调用者和真实主题的中间层,降低了模块间和系统的耦合性。可以以一个小对象代理一个大对象,达到优化系统提高运行速度的目的。代理对象能够控制调用者的访问权限,起到了保护真实主题的作用。
缺点:
适用场景由于在调用者和真实主题之间增加了代理对象,因此可能会造成请求的处理速度变慢。实现代理模式需要额外的工作(有些代理模式的实现非常复杂),从而增加了系统实现的复杂度。
当一个对象不能或者不想直接访问另一个对象时,可以通过一个代理对象来间接访问。为保证客户端使用的透明性,委托对象和代理对象要实现同样的接口。被访问的对象不想暴露全部内容时,可以通过代理去掉不想被访问的内容。
根据适用范围,代理模式可以分为以下几种:
-
远程代理:为一个对象在不同的地址空间提供局部代表,这样系统可以将Server部分的事项隐藏。
-
虚拟代理:如果要创建一个资源消耗较大的对象,可以先用一个代理对象表示,在真正需要的时候才真正创建。
-
保护代理:用代理对象控制对一个对象的访问,给不同的用户提供不同的访问权限。
-
智能引用:在引用原始对象的时候附加额外操作,并对指向原始对象的引用增加引用计数。



