什么是代理?
举个栗子,生活中的外卖平台,店铺是制作外卖的,然后放到平台上售卖。这里的店铺就是真实角色,为了能够让店铺不用担心销售等问题,从而能够更加专注做外卖,所以店铺的外卖都会放到平台上面。这里平台就是代理。平台代店家出售,而店家只需要做外卖就好了。
再比如程序中,当程序做出了一个操作的时候,应该要写一个相应的日志,代理的作用就是负责如日志相关的公共类业务,而程序本身自需要负责自身的业务就可以了,不必关心日志等公共业务的问题。
使用静态代理的优点:
- 使真实角色的业务更加简洁,没有多余的公共类业务,真实角色只负责本身的业务。
- 公共类业务全部由代理完成,使真实角色和代理类分工合作。
- 当公共类业务发生改变只需要改变代理类而不用去改变真实角色的代码。有利于程序的维护。
缺点:
- 每个真实角色都需要相应的代理类---类的数量变多。
- 编写代理类的数量多开发效率降低了。
编写一个静态代理
Interface
public void sellRice(); |
真实对象—SellRiceImpl
/*店铺类---真实对象*/ public class SellRiceImpl implements SellRice{ public SellRiceImpl() { // TODO Auto-generated constructor stub } public void sellRice() { System.out.println("店铺出售快餐"); } } |
代理类—MeiTuan
public class MeiTuan implements SellRice {
SellRiceImpl sr=new SellRiceImpl();
public void sellRice() { getNeed(); sr.sellRice(); shouFei(); }
public void getNeed() { System.out.println("接收用户订单"); } public void shouFei() { System.out.println("向店铺收取提成"); } } |
客户使用平台
public class KeHu { public static void main(String[] args) { MeiTuan mt=new MeiTuan(); mt.sellRice(); } } |
如上不更改真实对象的代码的情况下,使用代理实现了真实对象的功能,还实现了真实对象所不具备的功能。
动态代理
- 动态代理与静态代理的角色是一样的。
- 动态代理的代理类是动态生成的
- 动态代理有静态代理的好处,没有静态代理那样庞大的工作量
- 动态代理分为两类一类基于接口的动态代理一类基于类的动态代理
- 基于接口动态代理————jdk动态代理
- 基于类的动态代理————cglib
Jdk的动态代理 Proxy类和InvocatioHandler接口
以下是JDK API中相关的描述
- Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。
为某个接口创建代理Foo :
InvocationHandler handler = new MyInvocationHandler(...);
Class<?> proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class);
Foo f = (Foo) proxyClass.getConstructor(InvocationHandler.class).
newInstance(handler);
或更简单地:
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
new Class<?>[] { Foo.class },
handler);
动态代理类 (以下简称为代理类 )是一个实现在类创建时在运行时指定的接口列表的类,具有如下所述的行为。 代理接口是由代理类实现的接口。 代理实例是代理类的一个实例。 每个代理实例都有一个关联的调用处理程序对象,它实现了接口InvocationHandler 。 通过其代理接口之一的代理实例上的方法调用将被分派到实例调用处理程序的invoke方法,传递代理实例, java.lang.reflect.Method被调用方法的java.lang.reflect.Method对象以及包含参数的类型Object Object的数组。 调用处理程序适当地处理编码方法调用,并且返回的结果将作为方法在代理实例上调用的结果返回。
创建一个动态代理类的实例
这里将会用到文章开头提到的例子2的情况
首先创建操作数据库的接口
/*数据库的增删改方法*/ public interface Database { public void add(); public void delete(); public void update(); } |
真实对象---DatabaseImp
/*真实对象*/ public class DatabaseImp implements Database{ @Override public void add() { System.out.println("insert into"); } @Override public void delete() { System.out.println("delete"); } @Override public void update() { System.out.println("update"); } } |
Proxy动态代理类
public class ProxyinvocationHandler implements InvocationHandler { private Database db;
public void setDb(Database db) { this.db = db; }
/* 生成代理类 */ public Object getProxy() { return Proxy.newProxyInstance (this.getClass().getClassLoader(), db.getClass().getInterfaces(), this); } /* * proxy是代理类 * method是代理类的调用处理程序的方法对象 * */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object request=method.invoke(db, args); return request; } } |
Test—通过代理类实现真实对象的功能
public static void main(String[] args) { DatabaseImp dbi=new DatabaseImp(); ProxyinvocationHandler pih=new ProxyinvocationHandler(); pih.setDb(dbi); Database db=(Database)pih.getProxy(); db.add(); db.delete(); db.update(); } |
运行:
动态代理实现添加日志操作
再代理类中添加日志的方法
修改以下代码(新增和修改的地方为红色字体)
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object request=method.invoke(db, args); log(method.getName()); return request; }
public void log(String methodName) { System.out.println("日志:"+new Date()+"执行"+methodName+"方法"); } |
修改动态代理类后的运行结果
如果使用静态代理类实现以上结果就需要在静态代理中写下每个方法
StaticProxy
public class StaticProx implements Database{ DatabaseImp dbi=new DatabaseImp(); public void add() { dbi.add(); System.out.println("日志:"+new Date()+"执行add方法"); }
public void delete() { dbi.delete(); System.out.println("日志:"+new Date()+"执行delete方法"); }
public void update() { dbi.update(); System.out.println("日志:"+new Date()+"执行update方法"); } } |
对比上面的动态代理,静态代理工作量就显得庞大。