为什么会写这篇文章
因为在背aop的八股文的时候,了解到aop的底层是动态代理
又因为设计模式也是必须要掌握的知识点,因此此文章作为初次学习设计模式之代理模式的学习笔记
什么是代理模式
- 结构型设计模式
什么是结构型设计模式?
提供一个代理类,调用目标方法时,不直接对方法进行调用,而是通过代理类调用
代理可以在不修改原来业务代码的基础上实现对原业务的增强
总之就是:原本需要由目标对象做的事,现在目标对象不用亲自做了,由代理对象来替其完成
静态代理
下面的静态代理实现了在不改变原有登陆业务代码的基础上对登陆业务的增强
interface UserService {
void login();
}
class UserServiceImpl implements UserService {
@Override
public void login() {
System.out.println("登录业务");
}
}
// 或许这个类叫 UserServiceProxy 更合适
class Proxy implements UserService {
private UserService u;
Proxy(UserService u) {
this.u = u;
}
@Override
public void login() {
System.out.println("登陆前");
u.login();
System.out.println("登录后");
}
}
public class StaticProxyTest {
// 客户端创建目标对象,并传递给代理类的构造函数:
// 由客户端手动创建目标对象,并将目标对象传递给代理类的构造函数。
public static void main(String[] args) {
UserService u = new UserServiceImpl();
Proxy p = new Proxy(u);
p.login();
}
}
静态代理代码书写注意点
实现接口, 成员变量, 构造函数, 重写方法
使用了静态代理的运行结果
可以把增强部分的代码提取成一个增强类,在代理类使用构造函数同目标对象一起注入代理类
代理类也可以这样写
代理类内部直接创建目标对象:在代理类内部直接创建目标对象,无需由客户端手动创建目标对象并传递给代理类的构造函数。
class Proxy implements UserService {
// 在代理类内部直接创建目标对象
UserService u = new UserServiceImpl();
@Override
public void login() {
System.out.println("登陆前");
u.login();
System.out.println("登录后");
}
}
对应的测试方法也要改为
public static void main(String[] args) {
// 无需由客户端手动创建目标对象并传递给代理类的构造函数
UserService u = new Proxy();
u.login();
}
UserService u = new UserServiceImpl()
和
UserServiceImpl u = new UserServiceImpl()
有什么区别
前者是面向接口编程的方式,通过使用接口变量来引用UserServiceImpl
对象,实现了多态性和松耦合性。这种写法符合面向对象设计原则,使代码更具灵活性和可扩展性。
静态代理确实是对方法实现了增强,但是当我们有很多方法且增强逻辑都一样的时候,代理类需要对每个方法重写进行增强,这样 相同的代码就会被书写很多次,例如
class Proxy implements UserService {
UserService u = new UserServiceImpl();
@Override
public void login() {
System.out.println("打印开始日志");
u.login();
System.out.println("打印结束日志");
}
@Override
public void logout() {
System.out.println("打印开始日志");
u.logout();
System.out.println("打印结束日志");
}
@Override
public void upload() {
System.out.println("打印开始日志");
u.upload();
System.out.println("打印结束日志");
}
}
为了解决这一问题,可以使用动态代理
动态代理
1.jdk动态代理
先不说那么多理论的东西,先来看一个jdk动态代理的实现示例:
- 流程:
1.1 定义一个接口及其实现类
1.2 定义一个类实现InvocationHandler接口,并重写invoke方法
1.3 通过Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
方法创建代理对象
1.4 测试 - 示例:
同样也是模拟对登录业务的增强
// 2.1 定义一个接口及其实现类
interface UserService {
void login();
}
class UserServiceImpl implements UserService {
@Override
public void login() {
System.out.println("登录业务");
}
}
// 定义一个类实现InvocationHandler接口,并重写invoke方法
// invoke方法调用目标方法,并实现业务增强逻辑
class DynamicProxy implements InvocationHandler {
// 这里也是和静态代理一样,把目标对象传进来
// 不一样的是,这里传的是Object
private Object object;
DynamicProxy(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("登陆前");
Object result = method.invoke(object, args);
System.out.println("登陆后");
return result;
}
}
// 创建一个动态代理工厂类
// 主要用于封装动态代理对象的创建过程
class DynamicProxyFactory {
public static Object getProxy(Object target) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 目标类的类加载
target.getClass().getInterfaces(), // 代理需要实现的接口,可指定多个
new DynamicProxy(target) // 代理对象对应的自定义
);
}
}
// 测试
public class DynamicProxyTest {
public static void main(String[] args) {
UserService userService = (UserService) DynamicProxyFactory.getProxy(new UserServiceImpl());
userService.login();
}
}
若想对一个接口中的多个方法实现增强,只需要
在客户端(也就是DynamicProxyTest测试类)里调用方法即可,如uservice.logout();