代理模式(Proxy):为其他对象提供一种代理以控制对这个对象的访问。 代理模式说白了就是“真实对象”的代表,在访问对象时引入一定程度的间接性,因为这种间接性可以附加多种用途。
生活中,代理无处不在:
1、代理销售
2、代理记账
3、代理找对象(百合网、珍爱网)
4、代理找房子(中介)
我们假设出一种场景:
操作员查询销售订单的功能,为了提高查询性能,我们接入缓存功能,如果缓存中有数据,那么直接返回 缓存中的数据,如果缓存中没有数据,那么查询订单数据后,把数据加入到缓存,这样在下次查询订单 数据时,就可以从缓存中读取了。
那我们应该如何来设计这个功能的代码结构呢?
静态代理类的需要:
public class Order {
private int oid;
private String orderInfo;
private float total;
private String orderDate;
public Order() {
}
public Order(int oid, String orderInfo, float total, String orderDate) {
this.oid = oid;
this.orderInfo = orderInfo;
this.total = total;
this.orderDate = orderDate;
}
@Override
public String toString() {
return "Order{" +
"oid=" + oid +
", orderInfo='" + orderInfo + '\'' +
", total=" + total +
", orderDate='" + orderDate + '\'' +
'}';
}
public int getOid() {
return oid;
}
public void setOid(int oid) {
this.oid = oid;
}
public String getOrderInfo() {
return orderInfo;
}
public void setOrderInfo(String orderInfo) {
this.orderInfo = orderInfo;
}
public float getTotal() {
return total;
}
public void setTotal(float total) {
this.total = total;
}
public String getOrderDate() {
return orderDate;
}
public void setOrderDate(String orderDate) {
this.orderDate = orderDate;
}
}
public class DB {
private static List<Order> list = new ArrayList<>();
static {
list.add(new Order(1,"毛巾3条",45,"2020-2-1"));
list.add(new Order(2,"小纸巾10包",12,"2020-1-21"));
list.add(new Order(3,"洗发水1瓶",32,"2020-1-30"));
list.add(new Order(4,"红牛1箱",36,"2020-2-2"));
list.add(new Order(5,"哈脾2箱",120,"2020-2-3"));
}
public Order getOrder(int oid){
System.out.println("从数据库中查找数据...");
for (Order order:list){
if (order.getOid()==oid) {
return order;
}
}
return null;
}
}
public class Cache {
private Cache(){};
private Map<Integer, Order> map = new HashMap<>();
private volatile static Cache cache;
public static Cache getInstance(){
if (cache == null) {
synchronized (Cache.class){
if (cache == null) {
cache = new Cache();
}
}
}
return cache;
}
//把订单添加到缓存中
public void putCache(int key,Order value){
System.out.println("把订单数据添加到缓存中.");
map.put(key,value);
}
//从缓存中获取订单
public Order getCache(int key){
System.out.println("从缓存中查询订单数据...");
return map.get(key);
}
}
public interface OrderDao {
public Order queryOrder(int oid);
}
public class OrderDaoImpl implements OrderDao {
DB db=new DB();
@Override
public Order queryOrder(int oid) {
Order order = db.getOrder(oid);
return order;
}
}
public class OrderProxy implements OrderDao{
private OrderDao target;//被代理对象
public OrderProxy(OrderDao target){
this.target = target;
}
//用于实现目标对象的控制
//先从缓存中取数据,如果缓存中有,直接返回,如果缓存中没有,那么去数据库取数据,再把数据存入缓存
@Override
public Order queryOrder(int oid) {
Cache cache = Cache.getInstance();
Order order = cache.getCache(oid); //从缓存中查询订单
if (null==order) {
order = target.queryOrder(oid);// 从数据库中查询订单
cache.putCache(order.getOid(),order); //把订单放入缓存中
}
return order;
}
}
public class Test {
public static void main(String[] args) {
OrderDao dao = new OrderDaoImpl();
Order order = dao.queryOrder(2);
System.out.println(order);
System.out.println("-------------");
//查缓存
OrderProxy proxy = new OrderProxy(dao);
Order order1 = proxy.queryOrder(2);
System.out.println(order1);
System.out.println("------第二次查询------");
Order order2 = proxy.queryOrder(2);
System.out.println(order2);
}
}
注意:在第二次查询时,由于缓存区已经存在数据,所以我们是直接从缓存区中获取数据,并不是从DB(伪数据库)中获取数据。
动态代理: 顾名思义代理对象的类是通过动态方式来自动生成的,这样的好处是,我们不需要每次为被代理对象单独创建代理类,JDK API 中,对动态代理模式提供了支持
现在我们使用动态代理改进操作员查询销售订单功能
//InvocationHandler 接口用来实现 代理对象要处理的事情
public class CreateProxyFactory implements InvocationHandler {
private Object target;//被代理对象
//创建代理对象的方法
public Object create(Object target){
this.target = target;
//JDK 提供动态创建代理类对象的方法(代理对象类加载器,代理对象的接口集合,InvocationHandler(代理对象的处理器))
Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
return proxy;
}
/**
* //生成的代理对象要执行的代理业务方法
* @param proxy 代理类对象
* @param method 被代理对象的方法
* @param args 被代理对象方法的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Order order = null;
if (args != null) {
int oid = (int)args[0];
order = Cache.getInstance().getCache(oid);
if (null == order){
//调用真实的业务方法
order = (Order)method.invoke(target, args);
Cache.getInstance().putCache(order.getOid(),order);
}
}
return order;
}
}
public class TestDy {
public static void main(String[] args) {
OrderDao dao = new OrderDaoImpl();
CreateProxyFactory createProxyFactory = new CreateProxyFactory();
OrderDao proxy = (OrderDao) createProxyFactory.create(dao);
Order order = proxy.queryOrder(3);
System.out.println(order);
System.out.println("------第二次查询-------");
Order order1 = proxy.queryOrder(3);
System.out.println(order1);
}
}
CGLIB是高性能的代码生成包。可以为没有实现接口的类提供代理,JDK动态代理必须要有接口的补充。 通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。
通过 IDEA添加 CGLIB 包 项目 -> Open Module Settings -> Libraries -> 中间栏的 + 号-> From Maven -> 在输入框输入 cglib -> 等待下拉列表出现 后->选择cglib:cglib:3.3.0
也可以直接用lib导入jar包。
public class CglibProxy implements MethodInterceptor {
private Object target;
public Object create(Object target){
this.target = target;
Enhancer enhancer = new Enhancer(); //生成代理对象的增强类(工具类)
enhancer.setSuperclass(this.target.getClass());//设置代理类的父类
enhancer.setCallback(this);//设置代理类的处理方法
enhancer.setClassLoader(this.target.getClass().getClassLoader());//设置类加载器
Object obj = enhancer.create(); //创建代理对象
return obj;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Order order = null;
if (objects != null) {
int oid = (int)objects[0];
order = Cache.getInstance().getCache(oid);
if (null == order){
//调用真实的业务方法
order = (Order)method.invoke(target, objects);
Cache.getInstance().putCache(order.getOid(),order);
}
}
return order;
}
}
public class OrderDaoObject {
public Order queryOrder(int oid) {
//根据oid查询数据库,返回订单对象
DB db = new DB();
Order order = db.getOrder(oid);
return order;
}
}
public class TetstCglib {
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
OrderDaoObject odo = new OrderDaoObject();
OrderDaoObject odoProxy = (OrderDaoObject) cglibProxy.create(odo);
Order order = odoProxy.queryOrder(4);
System.out.println(order);
System.out.println("-------第二次查询-------");
Order order1 = odoProxy.queryOrder(4);
System.out.println(order1);
}
}