代理模式
代理模式又名 委托模式
作用:为其他对象提供一种代理以控制对这个对象的访问
通俗解释:对象A 想做一件事,让 代理对象B 代理,这件事就由 对象B 代替 对象A 做
本质:在原实例前后加一层处理(AOP的初级轮廓),实现增强
代理模式分为 静态代理 与 动态代理
静态代理
在程序运行前就已经存在代理类的字节码文件,代理类和原始类的关系在运行前就已经确定
现在有一个接口:用户的业务操作
public interface UserService {
void add();
}
存在一个真实角色,即被代理对象:接口的实现类
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("增加了一个用户");
}
}
如果这时候想要给业务方法添加日志功能,则需要修改每个方法
@Override
public void add() {
System.out.println("使用了add方法");
System.out.println("增加了一个用户");
}
如果现在有 n 个实现类,需要把所有东西都改一遍,代码量巨大,这种写法将浪费大量时间,且不合理
为了不改变原有代码而对方法进行增强,增加一个代理对象,并在其中维护一个真实角色
public class UserServiceProxy implements UserService {
private UserServiceImpl userServiceImpl;
public UserServiceProxy(UserServiceImpl userServiceImpl) {
this.userServiceImpl = userServiceImpl;
}
@Override
public void add() {
log("add");
userService.add();
}
//日志方法
public void log(String msg) {
System.out.println("使用了" + msg + "方法");
}
}
创建一个真实对象,并创建一个代理对象,把真实对象托管给代理对象,让代理对象来执行方法
public static void main(String[] args) {
// 0.创建一个真实角色
UserServiceImpl userService = new UserServiceImpl();
// 1.把真实对象交给代理对象管理,获得代理角色
UserServiceProxy proxy = new UserServiceProxy(userService);
proxy.add();
}
代理角色与真实角色要实现同一个接口
静态代理的优缺点
优点:
- 静态代理使业务类只需要关心逻辑本身
- 使真实角色的操作更加纯粹,不用关注一些公共的业务
缺点:
- 一个真是角色,需要一个代理角色,编写的代码量大
- 如果接口新添加一个方法,除了实现类需要实现这个方法之外,所有的代理类都要实现此方法,这种做法添加了代码的维护成本,解决方法为:使用动态代理
动态代理
在静态代理中,对于每个代理角色,都需要我们自动手写一个真实角色类,并对方法进行增强,而动态代理的代理类是动态生成的,其原理是利用 反射机制 完成
动态代理的实现:
- JDK 动态代理:基于接口的动态代理
- CGLIB 动态代理:没有接口的动态代理
这里主要讲解基于接口的 JDK 动态代理
主要步骤:使用 Proxy 的 newProxyInstance 方法获取代理类
代理对象和被代理对象要实现同一个接口,如果被代理对象没有实现任何接口,是无法为其创建代理对象的
newProxyInstance
Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
- ClassLoader loader:真实对象的类加载器
- Class<?>[] interfaces:真实对象所实现的接口
- InvocationHandler h:方法执行器,代理对象调用所有方法,都会执行该处理器,并对方法进行增强
具体实现
定义一个接口
public interface UserDao {
int add(int a, int b);
}
现在接口有一个实现类
public class UserDaoImpl implements UserDao{
@Override
public int add(int a, int b) {
return a + b;
}
}
正常使用:
public class Demo {
public static void main(String[] args) {
final UserDao userDao = new UserDaoImpl();
System.out.println(userDao.add(1, 2)); // 3
}
}
执行结果:
3
使用动态代理实现日志的添加:通过 Proxy 类的 newProxyInstance 静态方法创建一个代理对象
public class Demo {
public static void main(String[] args) {
final UserDao userDao = new UserDaoImpl();
// 通过 Proxy 类创建代理对象
UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(
userDao.getClass().getClassLoader(), // 真实对象的类加载器
userDao.getClass().getInterfaces(), // 真实对象所实现的接口
// 方法处理器,代理对象调用所有方法,都会执行该处理器,并对方法进行增强
new InvocationHandler() {
/**
* @param proxy 代理对象
* @param method 代理对象调用的方法
* @param args 方法传进来的参数
* @return
* @throws Throwable
*/
@Override
// invoke:增强方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// method.getName(): 获取调用方法名
// 通过方法名, 进行不同操作
if (method.getName().equals("add")) {
System.out.println("添加方法被执行了");
int ans = (int)args[0] + (int)args[1];
System.out.println("执行结果 " + args[0]
+ " + " + args[1] + " = " + ans);
Object result = method.invoke(userDao, args);
System.out.println("方法执行完毕");
// 利用反射执行目标方法
return result;
}
return null;
}
}
);
userDaoProxy.add(1, 2);
}
}
执行结果:
添加方法被执行了
执行结果 1 + 2 = 3
方法执行完毕
工具化:创建工厂,专门用来获取代理对象
public class UserProxyFactory {
public static UserDao getProxy(final UserDao userDao) {
Object o = Proxy.newProxyInstance(
userDao.getClass().getClassLoader(),
userDao.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("add")) {
System.out.println("添加方法被执行了");
int ans = (int) args[0] + (int) args[1];
System.out.println("执行结果 " + args[0]
+ " + " + args[1] + " = " + ans);
Object result = method.invoke(userDao, args);
System.out.println("方法执行完毕");
return result;
}
return null;
}
}
);
return (UserDao) o;
}
}
CGLIB
实现方式:以动态生成的子类继承目标的方式实现,在运行期动态的在内存中构建一个子类
使用前提:目标类不能为 final 修饰,因为 final 修饰的最终类不能被继承
public class UserDao{
}
CGLIB 是以动态生成的子类继承目标的方式实现,程序执行时,隐藏了下面的过程
public class $Cglib_Proxy_class extends UserDao{
}