0. 代理模式
- 作用:在不改变原来代码的基础上,对方法进行增强。
1. 动态代理
- 动态代理分为两种,基于接口的动态代理,基于子类的动态代理
基于接口—jdk的Proxy | 基于子类—cglib的Enhancer | |
---|---|---|
方法 | Proxy.newProxyInstance | Enhancer.create |
方法核心匿名内部类 | new InvocationHandler | new MethodInterceptor |
1. 需求背景(代码演示)
-
先准备好一个接口,以及实现类
// 生产者的接口 public interface IProducer { void saleSomeThing(float money); }
public class Producer implements IProducer { @Override public void saleSomeThing(float money) { System.out.println("卖出物品,赚" + money + "元"); } }
-
写个测试代码,调用Producer.saleSome 得到 赚的钱:
public class test { public static void main(String[] args) { Producer producer = new Producer(); producer.saleSomeThing(1000f); } }
-
实际上,生产者并不会卖多少钱,赚多少钱,还需要减成本,才能得到净利润,设成本需要20%
需求:那要如何不改原代码的基本上获取到净利润?
2. 基于接口的动态代理
-
先说概念:
基于接口的动态代理: 涉及的类:Proxy 提供者:JDK官方 如何创建代理对象: 使用Proxy类中的newProxyInstance方法 创建代理对象的要求: 被代理类最少实现一个接口,如果没有则不能使用 newProxyInstance方法的参数: ClassLoader:类加载器 它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。 Class[]:字节码数组,注意是接口的字节码 它是用于让代理对象和被代理对象有相同方法。固定写法。 InvocationHandler:用于提供增强的代码 它是让我们写如何代理,通常情况下都是匿名内部类,但不是必须的。
-
代码演示:
public class InterfaceProxy { public static void main(String[] args) { Producer producer = new Producer(); // newProxyInstance 的三个参数: // 第一个:只要给类加载器就行了,你也可以写InterfaceProxy.class.getClassLoader() // 第二个:字节码数组,注意是接口的字节码,也可以写成 new Class[] {IProducer.class} // 第三个:用于提供增强的代码,也可以写成匿名内部类 IProducer proxyInstance = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(), producer.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //proxy 代理对象的引用 //method 当前执行的方法 //args 当前执行方法所需的参数 if ("saleSomeThing".equals(method.getName())) { // 执行的方法是 saleSomeThing的时候,就会进入到这里,将money * 0.8 // method.invoke 执行原来方法逻辑 return method.invoke(producer, (float) args[0] * 0.8f); } // 这里写null 会出现一种现象,就是执行其他方法的时候,返回值都是null return null; } }); // 执行代理对象中的方法的时候,会先去 执行 new InvocationHandler内部类的invoke方法 // 若要调用原来被代理对象的方法,即 需要调用 method.invoke()方法 proxyInstance.saleSomeThing(1000f); } }
3. 基于子类的动态代理
-
先说概念
基于子类的动态代理: 涉及的类:Enhancer 提供者:第三方cglib库 如何创建代理对象: 使用Enhancer类中的create方法 创建代理对象的要求: 被代理类不能是最终类 create方法的参数: Class:字节码 它是用于指定被代理对象的字节码 Callback:用于提供增强的代码 它是让我们写如何代理。通常情况下都是匿名内部类,但不是必须的。
-
代码演示:
public class Producer { // 注意 producer 并没有实现 接口了 public void saleSomeThing(float money) { System.out.println("卖出物品,赚" + money + "元"); } }
public class SubclassProxy { public static void main(String[] args) { Producer producer = new Producer(); Producer producerProxy = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { // 入参的参数都一样意思, // 而methodProxy:当前执行方法的代理对象 Object value = null; if ("saleSomeThing".equals(method.getName())) { value = method.invoke(producer, (float) args[0] * 0.8f); } return value; } }); producerProxy.saleSomeThing(1000f); } }
2. 静态代理
-
概念:静态代理在使用时,需要定义接口或者父类,被代理对象(目标对象)与代理对象一起实现相同的接口或者是继承相同父类。
-
代码例子:
public interface IUserDao { void sayName(String name); } public class UserDao implements IUserDao{ @Override public void sayName(String name) { System.out.println("我的名字是" + name); } } public class ProxyUserDao implements IUserDao { private IUserDao target; public ProxyUserDao(IUserDao target) { this.target = target; } @Override public void sayName(String name) { System.out.println("hello,大家早上好"); target.sayName(name); System.out.println("演讲完成,大家再见"); } } public class test { public static void main(String[] args) { IUserDao userDao = new UserDao(); ProxyUserDao proxyUserDao = new ProxyUserDao(userDao); proxyUserDao.sayName("小明"); } }