一.引出代理
生活中,常见的代理有中介,各种产品销售门店...等等。比如,“小米工厂”只负责生产手机,把产品交给“小米之家”去销售。这里“小米之家”就是代理,“小米工厂”就是被代理的对象。为什么要用代理呢?第一:如果客户都直接去工厂拿货,那么工厂既要负责生产又要负责销售,太混乱了不容易管理。第二:这对工厂来说也不安全,哪家工厂还没点商业机密呢,哪能轻易让外人进去。第三:对客户而言,如果在购买手机的时候能多来点优惠,或者赠送耳机呀啥的最好了,那么对于手机工厂来说还要去生产耳机嘛,而代理就可以很好的实现这一点。编程思想来源于生活,因此java中也引进了代理这一概念。
二. 代理的好处
- 职责清晰,如:工厂只负责生产,销售交给其他部门。也符合单一职责的设计思想
- 可以实现方法增强。这里方法增强的意思是在不改变原方法的情况下,代理可以实现更多的功能。比如工厂卖手机只卖手机,而代理在卖手机的基础上可以加送耳机。
- 实现对委托方的保护,在代理中可以进行判断是否要去执行委托方的业务逻辑,起到隔离的作用。
- 低耦合高内聚,扩展性强,不同的代理可以有不同的代理操作。比如小米之家代理卖小米送耳机;淘宝代理卖小米,可以送钢化膜。 讲动态代理前,先简单看看什么是静态代理。
三. 静态代理
- 静态代理需要程序员自己编写。
静态代理和动态代理的区别可以理解为:静态创建对象和动态创建对象的区别。通过硬编码写死在程序中的叫静态,如Person p = new Person; 通过反射创建的对象叫动态。
举例:
interface Sell{
//money指的是买手机的钱
void sellPhone(Double money);
}
class MiFactory implements Sell{
@Override
public void sellPhone(Double money) {
System.out.println("卖手机赚了" + money);
}
}
//MiHome是MiFactory的静态代理
class MiHome implements Sell{
//被代理的对象
private MiFactory factory = new MiFactory();
@Override
public void sellPhone(Double money) {
//隔离,如果金额非法就没必要执行MiFactory的业务逻辑
if(money < 0){
return;
}
factory.sellPhone();
//方法增强
System.out.println("促销送耳机");
}
}
public class StaticProxyTest{
public static void main(String[] args) {
MiHome miHome = new MiHome();
miHome.sellPhone();
}
}
- 静态代理缺点
- 静态代理只服务一种类型的对象,如果很多不同类型对象都需要代理,那么会程序中会存在很多个代理,显得代码臃肿不易维护。如小米工厂代理,华为工厂代理,苹果工厂代理...
- 当接口增加了一个方法,那么所有实现类都要重写这个方法,代理类也要重写增加程序员负担
- 当某个对象的多个方法都需要同一种增强形式时,如事务,代理对象中会有大量重复代码。
三. 动态代理
通过反射创建的代理叫做动态代理,动态代理和委托方的关系是在运行时确定的(代理对象在程序的运行过程中创建);而静态代理和委托方的关系在编译时就确定了。有两种方式实现动态代理。
- 基于接口的动态代理:jdk
- 基于子类的动态代理:cglib
3.1 jdk动态代理
通过Proxy.newProxyInstance(...)创建代理。要求委托方至少实现一个接口,否则不能使用jdk动态代理。
- newProxyInstance()参数说明
- ClassLoader loader:类加载器,把委托方加载进内存才能通过反射获取其要增强的方法
- Class<?>[] interfaces:代理类要实现的接口,用于让代理对象和委托方有相同的方法,因此这里一般传入委托方实现的接口。
- InvocationHandler : 实现业务增强的核心,通常情况下都是匿名内部类。
public class JdkProxyTest {
public static void main(String[] args) {
//factory委托方
MiFactory factory = new MiFactory();
//proxy代理对象
Sell proxy = (Sell) Proxy.newProxyInstance(
MiFactory.class.getClassLoader(), //通过类加载器把要增强的方法加载进内存
factory.getClass().getInterfaces(), //获取委托方实现的接口
new InvocationHandler() {//匿名内部类
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("sellPhone".equals(method.getName())) {
double money = Double.parseDouble(args[0].toString());
///隔离,如果金额非法就没必要执行MiFactory的业务逻辑
if (money < 0) {
return null;
}
//反射获取委托方要增强的方法
method.invoke(factory,money);
//方法增强
System.out.println("促销送耳机");
}
return null;
}
});
proxy.sellPhone(2000D);
}
}
3.2 cglib动态代理
没有实现接口的普通类要使用代理的话,可以通过cglib来实现。刚才使用的基于接口的动态代理是JDK官方支持的,但是现在要讲的基于子类的动态代理需要第三方的支持,因此导入第三方依赖如下:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.1_3</version>
</dependency>
实现:使用Enhancer类中的create方法创建代理。要求委托方不能是最终类,即不能被final修饰(final修饰的类不可被继承)
- create()参数说明
- Class type:用于指定委托方的字节码
- Callback callback:用于提供增强的代码,一般写的都是该接口的子接口实现类MethodInterceptor。 代码实现:
class AppleFactory{
public void sellPhone(Double money){
System.out.println("卖苹果手机");
}
}
public class cglibProxyTest {
public static void main(String[] args) {
AppleFactory factory = new AppleFactory();
AppleFactory proxy = (AppleFactory) Enhancer.create(factory.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
double money = Double.parseDouble(objects[0].toString());
if (money < 0) {
return null;
}
method.invoke(factory,money);
System.out.println("买苹果手机送耳机");
return null;
}
});
proxy.sellPhone(1000d);
}
}
动态代理的缺点:硬要说它的缺点的话,应该就是效率了吧。因为动态代理用到了反射,而反射比起直接创建对象效率是更低的,因为反射基本上是解释执行。