动态代理
- 何为动态代理?
答: 可以简单理解为在不改变原本类中代码的情况下,为类中的方法(可以为一个或者多个)提供更多的功能,美滋滋。
- 为什么要使用动态代理而不使用静态代理?
先定义两个说法:1.哪个类中的方法想要被加强,我称这个类为被代理类 2.使拥某个类来加强被代理类中的方法,这个类我称为代理类(很容易理解,别被绕晕了)
答:使用动态代理可以使程序员在运行期动态地获取到被代理类中的所有方法(注意,是可以获得被代理类中的所有方法!),获得方法之后便能在一个代理类中对这些方法进行加强
实现动态代理
实现动态代理,我们做好三步,一步一步套着来,就能完成
- 代理类要实现一个InvocationHandle接口
- 使用Proxy类的newProxyInstance()方法创建一个动态代理对象
这里有点小讲究 newProxyInstance()方法需要三个参数,newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) //loader - 定义被代理类的类加载器 //interfaces - 代理类要实现的接口列表 //h - 指派方法调用的调用处理程序
这里不知道填什么没关系,我们先往后看
这里是我的代码文件结构:
damain 为JavaBean包, proxy包为代理类包,service为实现功能的包(在本例中即为被代理类包)
User类就是一个基本的JavaBean
UserService是一个功能接口
UserServiceImpl实现了这个功能接口
准备好这些之后,重点就是我们的代理类了
public class UserProxy implements InvocationHandler {
private UserService userService;
public UserProxy(UserService userService) {
this.userService = userService;
}
//当login方法执行的时候会回调该方法,所以实际增强的效果是在这里
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object obj = null;
if("login".equals(method.getName())) {
System.out.println("登录功能执行前的记录日志....");
obj = method.invoke(userService, args);
if(obj != null) {
System.out.println("恭喜您登录成功");
}
}
return obj;
}
public UserService loginPlus() {
//这里先准备好Proxy.newProxyInstance()方法所需要的三个参数
//loader - 定义被代理类的类加载器(哪个类要被增强,就是哪个类的类加载器)
ClassLoader loader = userService.getClass().getClassLoader();
//classes - 代理类要实现的接口列表
Class[] classes = new Class[]{UserService.class};
Object obj = Proxy.newProxyInstance(loader, classes, this);
UserService userService = (UserService) obj;
return userService;
}
}
代码很多,不要急,一个一个来解释
这一步是把被代理类对象放进来,为什么要放进来呢?因为就是要对被代理类中的方法进行增强嘛,当然要把方法的类对象引进来,不然怎么获得到类中的方法呢?
这个方法,是在获取到加强后的UserService类对象,这里最重要也是最复杂的一个地方,就是
Proxy.newProxyInstance(loader, classes, this);
this参数传的就是UserProxy这个类对象,因为这个参数需要一个InvocationHandle的类对象,而正好UserProxy实现了这个接口,因此可以直接将本类的对象传过去
调用这个方法会返回一个Object类型的值,其实,Proxy.newProxyInstance(loader, classes, this);这个方法底层就是调用了上边的invoke方法,invoke方法如下图
这个方法中有参数method,就是值被代理类中的方法。Object[]数组里边装的是被代理类方法的所需要的参数,method.invoke()方法所需要的参数为(传进此代理类的被代理类对象,要被加强的方法所需的参数,直接填args即可),这样一来就可以获得一个Object对象,将此对象 return给对象它的Proxy.newProxyInstance(),因此Proxy.newProxyInstance()可以获得一个Object类对象,而这个Object类对象,实际上就是被增强后的被代理类对象。
最后,在测试类中完成测试,先实例化一个UserProxy对象,再用这个对象去调用loginPlus()来获得被加强后的被代理对象,此时再用被代理对象去调用login登录方法,即完成了方法的增强
而下面两句代码的输出结果可以看出来,被代理类对象传进代理类的加强方法中之后,返回的对象已经发生了改变(这种查物理地址的方法还是阳哥教我的/—\)。
System.out.println(System.identityHashCode(service));
System.out.println(System.identityHashCode(userService));