3.数学计算器的改进
在最初,我们需要在实现加减乘除的时候,加入日志,这使得
-
代码混乱:越来越多的非业务需求(日志和验证等)加入后,原有的业务方法急剧膨胀。每个方法在处理核心逻辑的同时还必须兼顾其他多个关注点。
-
代码分散: 以日志需求为例,只是为了满足这个单一需求,就不得不在多个模块(方法)里多次重复相同的日志代码。如果日志需求发生变化,必须修改所有模块。
然后我们通过动态代理的方式进行改进:
动态代理的两种方式:
- 基于接口:jdk动态代理
- 基于继承:Cglib、Javassist动态代理
在此演示基于接口的,基于接口,也就是接口中定义了什么方法,就可以通过代理对象去执行这些方法,而接口中没有定义的,就无法代理;而基于继承的方式,定义了什么方法,就可以全部代理
基于接口:接下来是生成动态代理对象的两种方法
3.1 通过newProxyInstance()生成代理对象
ArithmeticCalculatorProxy.java
package com.atguigu.spring.aop.proxy
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
/**
* 生成代理对象。
*
* JDK的动态代理:
* 1. Proxy : 是所有动态代理类的父类, 专门用户生成代理类或者是代理对象
* public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces)
用于生成代理类的Class对象.
* public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
用于生成代理对象
* 2. InvocationHandler :完成动态代理的整个过程.
* public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
*
*/
public class ArithmeticCalculatorProxy {
//动态代理: 目标对象 如何获取代理对象 代理要做什么
//目标对象
private ArithmeticCalculator target ;
public ArithmeticCalculatorProxy(ArithmeticCalculator target) {
this.target = target ;
}
//获取代理对象的方法
public Object getProxy() {
//代理对象
Object proxy ;
/**
* loader: ClassLoader对象。 类加载器对象. 帮我们加载动态生成的代理类。
*
* interfaces: 接口们. 提供目标对象的所有的接口. 目的是让代理对象保证与目标对象都有接口中想同的方法.
*
* h: InvocationHandler类型的对象.
*/
ClassLoader loader = target.getClass().getClassLoader();
Class [] interfaces = target.getClass().getInterfaces();
proxy = Proxy.newProxyInstance(loader, interfaces, new InvocationHandler() {
/**
* invoke: 代理对象调用代理方法, 会回来调用invoke方法。
*
* proxy: 代理对象 , 在invoke方法中一般不会使用.
*
* method: 正在被调用的方法对象.
*
* args: 正在被调用的方法的参数.
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//将方法的调用转回到目标对象上.
//获取方法的名字
String methodName = method.getName();
//记录日志
System.out.println("LoggingProxy==> The method " + methodName+" begin with "+ Arrays.asList(args));
Object result = method.invoke(target, args); // 目标对象执行目标方法. 相当于执行ArithmeticCalculatorImpl中的+ - * /
//记录日志
System.out.println("LoggingProxy==> The method " + methodName +" ends with :" +result );
return result ;
}
});
return proxy ;
}
}
/**
* 模拟底层生成的动态代理类
* 是在运行过程中动态生成的
*/
class $Proxy0 extends Proxy implements ArithmeticCalculator{
//这个h就是我们实现的匿名对象
protected $Proxy0(InvocationHandler h) {
super(h);
}
@Override
public int add(int i, int j) {
// return super.h.invoke(this,方法对象,方法参数);
return 0 ;
}
@Override
public int sub(int i, int j) {
return 0;
}
@Override
public int mul(int i, int j) {
return 0;
}
@Override
public int div(int i, int j) {
return 0;
}
}
Main.java 同时将动态生成的代理类保存下来
package com.atguigu.spring.aop.proxy;
import java.util.Properties;
public class Main {
public static void main(String[] args) throws Exception{
//将动态生成的代理类保存下来
Properties properties = System.getProperties();
properties.put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
//目标对象
ArithmeticCalculator target = new ArithmeticCalculatorImpl();
//获取代理对象
Object obj = new ArithmeticCalculatorProxy2(target).getProxy();
// 转回具体的类型.
//接口类型
ArithmeticCalculator proxy = (ArithmeticCalculator) obj ;
System.out.println(proxy.getClass().getName());
//
int result = proxy.add(1, 1);
System.out.println("Main Result : " + result );
/**
* 问题:
* 1. 代理对象能否转换成目标对象的类型?
* 2. 代理对象调用代理方法,为什么会执行 InvocationHandler中的invoke 方法 : 见手动模拟底层生成的动态代理类
*/
}
}
3.2 使用getProxyClass()生成代理类的Class对象
package com.atguigu.spring.aop.proxy;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
/**
* 生成代理对象。
*
* JDK的动态代理:
* 1. Proxy : 是所有动态代理类的父类, 专门用户生成代理类或者是代理对象
* public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces)
用于生成代理类的Class对象.
* public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
用于生成代理对象
* 2. InvocationHandler :完成动态代理的整个过程.
* public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
*
*/
public class ArithmeticCalculatorProxy2 {
//动态代理: 目标对象 如何获取代理对象 代理要做什么
//目标对象
private ArithmeticCalculator target ;
public ArithmeticCalculatorProxy2(ArithmeticCalculator target) {
this.target = target ;
}
//获取代理对象的方法
public Object getProxy() throws Exception {
//代理对象
Object proxy ;
ClassLoader loader = target.getClass().getClassLoader();
Class [] interfaces = target.getClass().getInterfaces();
Class proxyClass = Proxy.getProxyClass(loader, interfaces);
//Class 创建对象? newInstance()
Constructor con =
proxyClass.getDeclaredConstructor(InvocationHandler.class);
proxy = con.newInstance(new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//将方法的调用转回到目标对象上.
//获取方法的名字
String methodName = method.getName();
//记录日志
System.out.println("LoggingProxy2==> The method " + methodName+" begin with "+ Arrays.asList(args));
Object result = method.invoke(target, args); // 目标对象执行目标方法. 相当于执行ArithmeticCalculatorImpl中的+ - * /
//记录日志
System.out.println("LoggingProxy2==> The method " + methodName +" ends with :" +result );
return result ;
}
});
return proxy ;
}
}