代理模式

定义

为其他对象提供一种代理,以控制对这个对象的访问
代理对象在客户端和目标对象之间起到中介的作用

优点和缺点

优点

  • 代理模式能将代理对象与真实被调用的目标对象分离
  • 一定程度上降低了系统的耦合度,扩展性好
  • 保护目标对象
  • 增强目标对象

缺点

  • 代理模式会造成系统设计中类的数目增加
  • 在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢
  • 增加系统的复杂度

静态代理

JDk的静态代理要求目标对象和代理对象都要实现相同的接口。然后提供给客户端使用。这个代理对客户端是可见的。

代码实现

假设一个场景,现在我们要保存一个用户信息,想要在保存用户之前或者之后做一些校验处理
建一个接口,里面是我们要实现的方法

1
2
3
public interface UserService {
int saveUser(int age,String name);
}

创建一个接口的实现,也就是要被代理的方法

1
2
3
4
5
6
7
public class UserServiceImpl implements UserService {
@Override
public int saveUser(int age, String name) {
System.out.println("用户名:" + name + " 用户年龄:" + age);
return 1;
}
}

再创建一个接口的实现,这个就是我们的代理类,这里将被代理的类通过构造函数传入,如果集成了spring,可以通过注入的方式来做

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class UserServiceImplProxy implements UserService {

private UserService userService;

public UserServiceImplProxy(UserService userService) {
this.userService = userService;
}

@Override
public int saveUser(int age, String name) {
before();
int saveUser = userService.saveUser(age, name);
after();
return saveUser;
}

private void before() {
System.out.println("前置处理");
}

private void after() {
System.out.println("后置处理");
}
}

调用

1
2
3
4
5
6
public class Test {
public static void main(String[] args) {
UserService userService = new UserServiceImplProxy(new UserServiceImpl());
userService.saveUser(18, "小明");
}
}

结果展示,可以在调用被代理的方法之前或者之后做一些处理

1
2
3
前置处理
用户名: 小明 用户年龄: 18
后置处理

UML类图

JDk动态代理

JDk的动态代理要求目标对象必须实现接口,因为它创建代理对象的时候是根据接口创建的,如果不实现接口,jdk无法给目标对象创建代理对象,被代理对象可以实现多个接口,创建代理时指定创建某个接口的代理对象就可以调用该接口中定义的方法了。

代码实现

同样以上面的例子为例,我们要在保存用户信息之前以及之后做一些校验

建一个接口,里面是我们要实现的方法

1
2
3
public interface UserService {
int saveUser(int age,String name);
}

创建一个接口的实现,也就是要被代理的方法

1
2
3
4
5
6
7
public class UserServiceImpl implements UserService {
@Override
public int saveUser(int age, String name) {
System.out.println("用户名:" + name + " 用户年龄:" + age);
return 1;
}
}

JDk动态代理,需要代理类实现InvocationHandler接口,并实现里面的invoke方法,invoke方法里面有三个参数,分别是当前代理类对象,要代理的方法,方法的参数
通过build()方法获取当前的代理类,会通过Proxy的静态方法生成一个代理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class UserServiceImplProxy implements InvocationHandler {

private Object target;

public UserServiceImplProxy(Object target) {
this.target = target;
}


public Object build(){
Class<?> aClass = target.getClass();
return Proxy.newProxyInstance(aClass.getClassLoader(), aClass.getInterfaces(), this);
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

before();
Object obj = method.invoke(target, args);
after();
return obj;
}

private void before() {
System.out.println("前置处理");
}

private void after() {
System.out.println("后置处理");
}
}

运行结果

1
2
3
前置处理
用户名: 小明 用户年龄: 18
后置处理

CGLib动态代理

jdk给目标类提供动态代理要求目标类必须实现接口,当一个目标类不实现接口时,jdk是无法为其提供动态代理的,cglib却能给这样的类提供动态代理。Spring在给某个类提供动态代理时,会自动在jdk动态代理和cglib动态代理中动态选择。

依赖

需要先添加maven依赖(如果直接导入jar包的话,可能会有一些关联的jar包无法引入从而引起一些错误)

1
2
3
4
5
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>

代码实现

同样使用上述的例子,不过CGLIB代理主要用于被代理类没有实现接口的情况下,我们被代理类不再实现接口

1
2
3
4
5
6
public class UserServiceImpl {
public int saveUser(int age, String name) {
System.out.println("用户名:" + name + " 用户年龄:" + age);
return 1;
}
}

编写代理类,实现MethodInterceptor接口,并重写里面的intercept方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class UserServiceImplProxy implements MethodInterceptor {

private Object target;

public UserServiceImplProxy(Object target) {
this.target = target;
}

public Object build() {
Enhancer enhancer = new Enhancer();
//设置父类,因为cglib是针对指定的类生成一个子类,所以需要指定父类
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);//设置回调
Object obj = enhancer.create();//创建并返回代理对象
return obj;
}

private void before() {
System.out.println("前置处理");
}

private void after() {
System.out.println("后置处理");
}

@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object invoke = method.invoke(target, objects);
after();
return invoke;
}
}

具体调用

1
2
3
4
5
6
7
8
public class Test {

public static void main(String[] args) {
UserServiceImpl userService = (UserServiceImpl) new UserServiceImplProxy(new UserServiceImpl()).build();
userService.saveUser(18, "小明");
}

}

输出结果

1
2
3
前置处理
用户名: 小明 用户年龄: 18
后置处理