控制对象访问权限
- 概念
- 类图
- 代码实例
- 和装饰者区别
概念
为其他对象提供一种代理以控制这个对象的访问,在某些情况下一个对象不能直接访问那个对象时,代理就起到了客户端和被代理对象(委托类)中介作用。 代理类和委托类都有同样接口。
好处:可以不用动原来类的逻辑,再次增加一些功能,符合开闭原则。真正的业务还是交给被代理对象处理的,因此在其委托被代理对象处理业务前后实现一些公共逻辑服务,例如加入缓存或日志等功能,无须修改原来的类就可以使用代理进行实现。
类图
Subject: 代理类和被代理类实现同样的接口
Proxy:代理类,里面有被代理类,具体逻辑委托被代理类进行处理
RealSubject:被代理类,可以在其内做一些访问权限控制,额外的业务处理
Client:看到的是代理类,并不知道具体处理业务逻辑的类,降低耦合性
代码实例
UserDAO 代理和被代理的公共的接口(Subject)
/**
* @author duanyimiao
* @create 2018-09-22 10:47 AM
* @description 用户数据持久化逻辑接口
**/
public interface UserDAO {
boolean insert(String name,int age);
}
UserDAOImpl 被代理类(RealSubject)
/**
* @author duanyimiao
* @create 2018-09-22 10:47 AM
* @description 用户数据持久具体实现
**/
public class UserDAOImpl implements UserDAO {
@Override
public boolean insert(String name, int age) {
System.out.println("insert to database name="+name +" age="+age);
return true;
}
}
UserDAOProxyByInterface 代理类,通过实现接口方式实现代理方式(Proxy)
/**
* @author duanyimiao
* @create 2018-09-22 10:50 AM
* @description 用户持久数据的代理类(和被代理类实现同个接口方式)
**/
public class UserDAOProxyByInterface implements UserDAO {
private UserDAOImpl conreteUserDAO;
public UserDAOProxyByInterface() {
}
public UserDAOProxyByInterface(UserDAOImpl conreteUserDAO) {
this.conreteUserDAO = conreteUserDAO;
}
@Override
public boolean insert(String name, int age) {
System.out.println("before insert handle some logic by interface");
return conreteUserDAO.insert(name, age);
}
}
UserDAOProxyByExtend 代理类,通过继承被代理类实现的代理方式(Proxy)
/**
* @author duanyimiao
* @create 2018-09-22 10:50 AM
* @description 用户持久数据的代理类(继承被代理类方式)
**/
public class UserDAOProxyByExtend extends UserDAOImpl {
private UserDAOImpl conreteUserDAO;
public UserDAOProxyByExtend() {
}
public UserDAOProxyByExtend(UserDAOImpl conreteUserDAO) {
this.conreteUserDAO = conreteUserDAO;
}
@Override
public boolean insert(String name, int age) {
System.out.println("before insert handle some logic by extend");
return conreteUserDAO.insert(name, age);
}
}
Client 获取代理对象客户端(Client)
/**
* @author duanyimiao
* @create 2018-09-22 10:52 AM
* @description
**/
public class Client {
public static void main(String[] args) {
UserDAOImpl conreteUserDAO = new UserDAOImpl();
UserDAOProxyByInterface userDAOProxyByInterface = new UserDAOProxyByInterface(conreteUserDAO);
//和被代理类实现同个接口方式进行代理
userDAOProxyByInterface.insert("dynamo", 18);
//通过继承被代理类方式进行代理
UserDAOProxyByExtend userDAOProxyByExtend = new UserDAOProxyByExtend(conreteUserDAO);
userDAOProxyByExtend.insert("dynamo", 18);
}
}
输出结果:
before insert handle some logic by interface
insert to database name=dynamo age=18
before insert handle some logic by extend
insert to database name=dynamo age=18
和装饰者模式区别
代理模式关注的是控制对对象的访问,一般具体的被代理对象对Client是不可见的,直接在创建代理对象时就创建了被代理对象,也就是编译时就确定了。 但是装饰者模式:更关注的是在一个对象方法上动态添加修饰逻辑,但是这些逻辑修饰在运行时才能确定的,通过构造方法传入具体的装饰者对象,执行时递归调用修饰对象的方法。
动态代理
静态代理和动态代理:
1、静态代理:代理类由程序员创建的然后编译成.class文件。但是其中缺点是,具有重复代码,灵活性不好,例如在执行接口A中所有方法之前加上日志逻辑,那么使用静态代理的话,在代理类中每个方法都得加,如果我想add* 开头方法加上一种逻辑,select* 开头方法加上另一种逻辑,那么就很难去实现和维护了,想解决以上困惑就要使用动态代理了。
2、动态代理:是在运行的时候,通过jvm中的反射进行动态创建对象,生成字节码对象(构造方法参数 InvocationHandler h类型),传入由我们实现InvocationHandler接口的对象,通过反射创建代理对象。 然后当调用代理对象的任何方法都会调用h中的 invoke(Object proxy,Method method,Object[] args)传入当前代理对象、当前调用的方法、方法参数值。
JDK动态代理实现原理
要求被代理类必须要实现接口,因为JDK动代理实现是通过实现接口方式来实现的。需要实现 InvocationHandler接口(在invoke方法中实现一些额外的逻辑,添加一些新功能),通过Proxy.newProxyInstance(ClassLoader classLoader,Class<?> interfaces,InvocationHandler h)。
实现步骤
1.定义一个接口ProxyObj
2.编写该接口I的实现类ProxyObjImpl
3.编写InvocationHandler接口的实现类InvokcationInvokeHandler,构造h类对象的时候可以把要代理的对象target传入,target完成实际的动作。在里面的invoke方法里编写自己想实现的逻辑,然后再调用实际要完成的动作就可以。
4.调用Proxy.newProxyInstance方法,传递的三个参数分别是代理类的类加载器(可以用Impl实例的getClass().getClassLoader())
、代理类要实现的接口列表(可以用Impl实例getClass().getInterfaces())、InvocationHandler实现类的实例。
这样就生成了 P r o x y 0 类 的 对 象 , 由 于 Proxy0类的对象,由于 Proxy0类的对象,由于Proxy0类实现了ProxyObj接口,所以可以将对象强制转型成ProxyObj。
再说一下Proxy.newProxyInstance方法的实际过程:
1.使用传入的InvocationHandler实例参数将Proxy类的h实例初始化,注意,如果传入空对象的话,会抛出空指针错误,即h不能为空。
2.运行时生成代理Class,即$Proxy0
3.利用上面动态生成的$Proxy0类,构造出该类的对象,并返回。
实现代码
ProxyObj 代理类和被代理类实现的公共接口
public interface ProxyObj {
public void setName(String name);
}
ProxyObjImpl 被代理类
public class ProxyObjImpl implements ProxyObj,ProxyObj1{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("set name="+name);
this.name = name;
}
@Override
public void setAge(Integer age) {
System.out.println("set age="+age);
}
}
InvokcationInvokeHandler 实现InvocationHandler接口
public class InvokcationInvokeHandler implements InvocationHandler {
//真实的对象
private ProxyObj proxyPbj;
public InvokcationInvokeHandler(ProxyObj proxy) {
this.proxyPbj = proxy;
}
/**
*
* @param proxy jvm生成的动态代理对象 $Proxy0
* @param method 当前代理对象调用的方法对象,是代理对象实现接口中的方法
* @param args 调用对象传入的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before call set proxy" + proxy.getClass