设计模式之代理模式
初识代理模式
定义:为其他对象提供一种代理以控制这个对象的访问。
结构和说明
Proxy 代理对象
- 实现与具体对象一样的接口,这样就可以使用代理来代理具体的目标对象
- 保存一个指向具体目标对象的引用,可以在需要的是后使用调用具体的目标对象。
- 控制对具体目标的访问,并可能 负责创建和删除它。
Subject
- 目标接口,定义代理和具体目标对象的接口,这样就可以在任何使用具体目标对象的地方使用代理对象。
RealSubject
- 具体目标对象,真正实现目标接口要求的动能。
//目标统一接口
public inferace subject {
public void request();
}
//具体目标对象
public class RealSubject implements Subject{
public void request(){
//功能处理
}
}
public class Proxy implements Subject {
private RealSubject realSubject;
public Proxy(ResalSubject realSubject) {
this.realSubject = realSubject;
}
public void request(){
realSubject.request();
}
}
理解代理模式
代理模式是通过创建一个代理对象,用这个代理对象去代表真实的对象,客户端得到这个对象过后,对客户端没什么影响,犹如得到真实的对象一样来使用。
实例一:大数据量逻辑数据查询。
/**
* @description : 定义用户对象的接口
**/
public interface UserModelApi {
String getUserId();
void setUserId(String userId);
String getName();
void setName (String name);
String getDepId();
void setDepId (String depId);
String getSex();
void setSex (String sex);
}
public class UserModel implements UserModelApi{
private String userId;
private String name;
private String depId;
private String sex;
//getter,setter 略
}
public class UserProxy implements UserModelApi {
private boolean load = false;
private UserModel userModel;
public UserProxy(UserModel userModel) {
this.userModel = userModel;
}
@Override
public String getUserId() {
return userModel.getUserId();
}
@Override
public void setUserId(String userId) {
userModel.setUserId(userId);
}
@Override
public String getName() {
return userModel.getName();
}
@Override
public void setName(String name) {
userModel.setName(name);
}
@Override
public String getDepId() {
if (!this.load) {
reload();
this.load = true;
}
return userModel.getDepId();
}
@Override
public void setDepId(String depId) {
userModel.setDepId(depId);
}
@Override
public String getSex() {
if (!this.load) {
reload();
this.load = true;
}
return userModel.getSex();
}
@Override
public void setSex(String sex) {
userModel.setSex(sex);
}
private void reload(){
/***
* Connection conn = null;
* conn = this.getConnection();
* String sql = 'Select * from tb_user1 where userId = ?'
* PreparedStatement ps = conn.prepareStatement(sql);
* ps.setString(1,userModel.getUserId());
* ResultSet rs = ps.executeQuery();
* if (rs.next()) {
* userModel.setDepId(rs.getString("depId"));
* userModel.setSex(rs.getString("sex"));
* }
* rs.close();
* ps.close();
*/
}
}
public class UserManager {
public Collection<UserModelApi> getUserByDepId( String depId) throws Exception {
Collection<UserModelApi> col = new ArrayList<>();
Connection connection = null;
String sql = "select u.userId,u.name from tb_user u,tb_dep d where u.depId = d.depId like ?";
PreparedStatement preparedStatement = null;
preparedStatement.setString(1,depId + "%");
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
//代理对象而不直接使用userModel
UserProxy userProxy = new UserProxy(new UserModel());
userProxy.setUserId(resultSet.getString("userId"));
userProxy.setName(resultSet.getString("name"));
col.add(userProxy);
}
return col;
}
}
public class Client {
public static void main (String []args) {
UserManager userManager = new UserManager();
try {
Collection<UserModelApi> collections = userManager.getUserByDepId("01011");
//只显示客户名称不需要重新查询
for (UserModelApi api : collections) {
}
//显示用户编号和其他属性 需要重新查询
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 以时间换取空间
* 1+N次查询问题
*
* Lazy Load (Hibernate)实现机制是动态代理。过程类似上述过程
*/
}
保护代理
保护代理是一种控制原始对象访问的代理。多用于对象应该有不同的访问权限时候,保护代理会检查调用者是否有访问的权限,没有权限就不会调用目标对象。
实例二 :订单系统,要求一旦订单被创建,只有订单的创建人才可以修改,其他人不能修改。
//订单OrderApi 接口
public interface OrderApi {
String getOrderName();
void setOrderName(String user,String orderName);
int getOrderNum ();
void setOrderNum (String user,int orderNum);
String getOrderUser();
void setOrderUser(String user,String orderUser);
}
//订单实体对象
public class Order implements OrderApi{
private String orderName;
private int orderNum;
private String orderUser;
public Order(String orderName, int orderNum, String orderUser) {
this.orderName = orderName;
this.orderNum = orderNum;
this.orderUser = orderUser;
}
@Override
public String getOrderName() {
return orderName;
}
public void setOrderName(String user,String orderName) {
this.orderName = orderName;
}
@Override
public int getOrderNum() {
return orderNum;
}
public void setOrderNum(String user,int orderNum) {
this.orderNum = orderNum;
}
@Override
public String getOrderUser() {
return orderUser;
}
public void setOrderUser(String user,String orderUser) {
this.orderUser = orderUser;
}
}
/**
* 保护代理 (控制访问)
*
* @author carro
**/
public class OrderProxy implements OrderApi {
private Order order;
public OrderProxy(Order order) {
this.order = order;
}
@Override
public String getOrderName() {
return order.getOrderName();
}
@Override
public void setOrderName(String user, String orderName) {
if (user != null && this.order.getOrderUser().equalsIgnoreCase(user)) {
order.setOrderName(user,orderName);
} else {
//TODO:throw exception
}
}
@Override
public int getOrderNum() {
return order.getOrderNum();
}
@Override
public void setOrderNum(String user, int orderNum) {
if (user != null && this.order.getOrderUser().equalsIgnoreCase(user)) {
order.setOrderNum(user,orderNum);
} else {
//TODO:throw exception
}
}
@Override
public String getOrderUser() {
return order.getOrderUser();
}
@Override
public void setOrderUser(String user, String orderUser) {
if (user != null && this.order.getOrderUser().equalsIgnoreCase(user)) {
order.setOrderUser(user,orderUser);
} else {
//TODO:throw exception
}
}
}
public class Client {
public static void main(String[]args){
OrderApi orderApi = new OrderProxy(
new Order("设计模式",100,"战三"));
//修改失败
orderApi.setOrderNum("立体",123);
//修改C成功
orderApi.setOrderNum("战三",123);
}
}
Java中的代理
Java静态代理(虚代理模式): 如果Subject 接口变化,那么代理类和具体的目标实现都要变化,不是很灵活。
Java动态代理 :实现机制 反射和动态生成Class 。
JDK动态代理所用到的代理类在程序调用到代理类对象时才由JVM真正创建,JVM根据传进来的 业务实现类对象 以及 方法名 ,动态地创建了一个代理类的class文件并被字节码引擎执行,然后通过该代理类对象进行方法调用。我们需要做的,只需指定代理类的预处理、调用后操作即可。JDK动态代理的代理对象在创建时,需要使用业务实现类所实现的接口作为参数(因为在后面代理方法时需要根据接口内的方法名进行调用)。如果业务实现类是没有实现接口而是直接定义业务方法的话,就无法使用JDK动态代理了。并且,如果业务实现类中新增了接口中没有的方法,这些方法是无法被代理的(因为无法被调用)。
实例三:java jdk 中动态代理使用(以上述订单为例)
public interface OrderApi {
String getOrderName();
void setOrderName(String user, String orderName);
int getOrderNum();
void setOrderNum(String user, int orderNum);
String getOrderUser();
void setOrderUser(String user, String orderUser);
}
public class Order implements OrderApi {
private String orderName;
private int orderNum;
private String orderUser;
public Order(String orderName, int orderNum, String orderUser) {
this.orderName = orderName;
this.orderNum = orderNum;
this.orderUser = orderUser;
}
@Override
public String getOrderName() {
return orderName;
}
public void setOrderName(String user,String orderName) {
this.orderName = orderName;
}
@Override
public int getOrderNum() {
return orderNum;
}
public void setOrderNum(String user,int orderNum) {
this.orderNum = orderNum;
}
@Override
public String getOrderUser() {
return orderUser;
}
public void setOrderUser(String user,String orderUser) {
this.orderUser = orderUser;
}
}
/**
* 动态代理 使用jdk InvocationHandler
* @author carro
**/
public class DynamicProxy implements InvocationHandler{
//定义是接口
private OrderApi order = null;
public OrderApi getProxyInterface(Order order) {
this.order = order;
//通过反射机制,创建一个代理类对象实例并返回。用户进行方法调用时使用
//创建代理对象时,需要传递该业务类的类加载器(用来获取业务实现类的元数据,在包装方法是调用真正的业务方法)、接口、handler实现类
OrderApi orderApi = (OrderApi) Proxy.newProxyInstance(
order.getClass().getClassLoader(),
order.getClass().getInterfaces(),
this
);
return orderApi;
}
/**
* 包装调用方法:进行预处理、调用后处理
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().startsWith("set")) {
if (order.getOrderUser() != null && order.getOrderUser().equals(args[1])){
return method.invoke(order,args);
} else{
throw new Exception(".......");
}
} else{
return method.invoke(order,args);
}
}
}
public class Client {
public static void main(String[]args){
Order order = new Order("设计模式",123,"xuks");
DynamicProxy dynamicProxy = new DynamicProxy();
OrderApi orderApi = dynamicProxy.getProxyInterface(order);
orderApi.setOrderName("历史","学习");
}
}
代理模式优缺点
虚代理:根据需求来创建大对象,只有到必须创建对象的时候,虚代理才会创建对象,从而加快了程序运行和节省资源;
保护代理可以在在访问一个对前后,执行很多附加的操作;可以通过代理来给目标对象增加功能。
智能指引:引用计数等。代理模式本质
控制对象访问使用场景
需要创建开销很大的对象的时候 使用 虚代理
需要控制对象访问权限的时候使用保护代理
需要在访问对象的执行一些附加操作的时候可以使用代理模式。思考使用 继承方式实现代理?
扩展cglib 动态代理
cglib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理。
/**
* 动态代理 使用cglib MethodInterceptor
* @author carro
**/
public class DynamicCGLibProxy implements MethodInterceptor{
//业务类对象,供代理方法中进行真正的业务方法调用
private OrderApi order;
public OrderApi getProxyInstance(Order order) {
this.order = order;//给业务对象赋值
Enhancer enhancer = new Enhancer(); //创建加强器,用来创建动态代理类
enhancer.setSuperclass(this.target.getClass()); //为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
//设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
enhancer.setCallback(this);
// 创建动态代理类对象并返回
return enhancer.create();
}
/**
* // 实现回调方法
*/
@Override
public Object intercept(Object obj, Method method, Object[] args,MethodProxy proxy) throws Throwable {
if (method.getName().startsWith("set")) {
if (order.getOrderUser() != null && order.getOrderUser().equals(args[1])){
return proxy.invokeSuper(obj, args); //调用业务类(父类中)的方法;
} else{
throw new Exception(".......");
}
} else{
return proxy.invokeSuper(obj, args); //调用业务类(父类中)的方法;
}
}
}
public class Client {
public static void main(String[]args){
Order order = new Order("设计模式",123,"xuks");
DynamicProxy dynamicProxy = new DynamicCGLibProxy();
OrderApi orderApi = dynamicProxy.getProxyInstance(order);
orderApi.setOrderName("历史","学习");
}
}
过程
1.首先定义业务类,无需实现接口(实现接口也可以);
2.实现 MethodInterceptor方法代理接口,创建代理类 ;
3.创建业务类和代理类对象,然后通过 代理类对象.getInstance(业务类对象) 返回一个动态代理类对象(它是业务类的子类,可以用业务类引用指向它)。最后通过动态代理类对象进行方法调用;比较
静态代理是通过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,用户通过代理类调用被包装过的业务方法;
JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;
CGlib动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理。