0、静态代理
- 参考链接:https://www.yusian.com/blog/?p=1978
-
简单地说:一个对象实现了另一个对象的功能,我们就将前者称为后者的代理。然而单纯地实现原始对象的功能没有太大的意义,一般而言,代理的目的是在原始对象功能的基础上进行额外的扩展,我们称之为附加功能。
-
静态代理很好地诠释了代理的作用,但由于静态代理的缺陷,利用反射机制对其进行改造便有了动态代理。
-
只要是代理,其核心目的都是在原始对象的基础上扩展附加功能,这一点始终不变。
- 常用的动态代理有2种,一种JDK自带的Proxy类及对其进行封装的相关类,另一种是以CGLib为代表的第三方库
1、Proxy
1.1、基本原理
- Proxy基于接口,动态生成字节码文件给JVM,得到该接口另一个实现类的实例,由于都是对一个接口的实现,利用多态的特性,可以代理原始对象完成接口所有方法的调用。
- 调用过程中通过对所有方法的拦截,实现附加功能的扩展;
- 这个拦截的过程通过接口的方式实现;
1.2、两个重要的方法
-
生成代理类的方法:
/** * classLoader: 生成字节码文件需要的类加载器,由于该类是动态生成的,没有类加载器,这里可以写任意一个存在的类的类加载器 * interfaces: 原始对象的接口,代理对象需要实现与原始相同的接口 * invocationHandler: 实现拦截方法的对象 */ Proxy.newProxyInstance(classLoader, interfaces, invocationHandler)
- 拦截方法
/** * 原始方法对应的参数在该方法中通过参数的方式传递进来,当前方法的返回值将替代原始方法的返回值 * 可在当前方法中执行原始方法,得到原始方法的结果,从而又可以在原始方法的调用前后进行附加功能扩展 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return ret; }
1.3、示例说明
UserService
package com.yusian.service;
import com.yusian.domain.User;
public interface UserService {
void register(User user);
boolean login(String username, String password);
}
UserServiceImpl
package com.yusian.service;
import com.yusian.domain.User;
public class UserServiceImpl implements UserService {
@Override
public void register(User user) {
System.out.println("UserServiceImpl.register--------原始功能");
}
@Override
public boolean login(String username, String password) {
System.out.println("UserServiceImpl.login--------原始功能");
return false;
}
}
Proxy代理
UserService userService = new UserServiceImpl();
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(UserService.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before....");
Object ret = method.invoke(userService, args);
System.out.println("after....");
return ret;
}
});
userServiceProxy.login("yusian", "123456");
执行结果:
before...
UserService.login
after...
Process finished with exit code 0
2、CGLib
2.1、基本原理
- 对标Proxy通过实现原始中对象相同的接口来获取相同的接口调用能力,CGLib通过继承原始对象的类获取相同的接口调用能力。
- CGLib的使用与Proxy高度类似;
- 相对Proxy,通过继承的方式来代理原始对象更具有通用性;
2.2、几个重要的方法
- 初始化一个Enhancer工具类的实例对象
-
指定类加载器方法
setClassLoader
,与Proxy类似; -
指定父类
setSuperclass
,指定原始对象的类型,相当于Proxy的interfaces; -
指定回调
setCallback
,参数是一个接口,与Proxy类似; -
创建代理对象
create
,生成一个代理对象,类似Proxy的newProxyInstance; -
拦截方法
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { return null; }
2.3、示例说明
UserService
package com.yusian.service;
public class UserService {
/**
* 登录
* @param username 用户名
* @param password 密码
*/
public void login(String username, String password) {
System.out.println("UserService.login------原始方法");
}
}
CGLib代理
UserService userService = new UserService();
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(UserService.class.getClassLoader());
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before...");
Object ret = method.invoke(userService, objects);
System.out.println("after...");
return ret;
}
});
UserService userServiceProxy = (UserService) enhancer.create();
userServiceProxy.login("yusian", "123456");
执行结果
before...
UserService.login------原始方法
after...
Process finished with exit code 0