设计模式-代理模式

静态代理

原理

在不改变原始类(或叫被代理类)的情况下,通过引入代理类来给原始类附加功能。一般情况下,我们让代理类和原始类实现同样的接口。但是,如果原始类并没有定义接口,并且原始类代码并不是我们开发维护的。在这种情况下,我们可以通过让代理类继承原始类的方法来实现代理模式。

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public interface UserService {

public void login(String username, String password);

}
public class UserServiceImpl implements UserService {

@Override
public void login(String username, String password) {
System.out.println("User ["+username+"] logins!");
}
}
public class UserServiceStaticProxy extends UserServiceImpl {

@Override
public void login(String username, String password) {
System.out.println("login start:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
super.login(username, password);
System.out.println("login end:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
}
}

动态代理

原理

静态代理需要针对每个类都创建一个代理类,并且每个代理类中的代码都有点像模板式的“重复”代码,增加了维护成本和开发成本。对于静态代理存在的问题,我们可以通过动态代理来解决。我们不事先为每个原始类编写代理类,而是在运行的时候动态地创建原始类对应的代理类,然后在系统中用代理类替换掉原始类。

Java实现动态代理采用的是反射。

实现

动态代理有两种,一种是被代理的类是实体类,而不是接口,这种情况在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。

java代理

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
34
35
36
37
38
39
40
41
42
43
44
45
public class DynamicInvocationHandler implements InvocationHandler {

private final Object proxiedObject;

public DynamicInvocationHandler(Object proxiedObject) {
this.proxiedObject = proxiedObject;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("login start:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
Object result = method.invoke(proxiedObject, args);
System.out.println("login end:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
return result;
}
}

public class UserServiceDynamicProxy {

private final UserServiceImpl userService;

public UserServiceDynamicProxy() {
this.userService = new UserServiceImpl();
}

public Object createProxy(Object proxiedObject) {
Class<?>[] interfaces = proxiedObject.getClass().getInterfaces();
DynamicInvocationHandler handler = new DynamicInvocationHandler(userService);
return Proxy.newProxyInstance(proxiedObject.getClass().getClassLoader(), interfaces, handler);
}

}

public class Application {

public static void main(String[] args) {
UserService userService = new UserServiceStaticProxy();
userService.login("username","password");
UserServiceDynamicProxy userServiceDynamicProxy = new UserServiceDynamicProxy();
UserService userService1 = (UserService) userServiceDynamicProxy.createProxy(new UserServiceImpl());
userService1.login("username","password");

}

}

CGLIB代理

动态代理需要被代理类实现接口,如果被代理类没有实现接口,就需要用到CGLib了。这种代理方式就叫做CGlib代理。

Cglib代理也叫作子类代理,他是通过在内存中构建一个子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,然后加入自己需要的操作。因为使用的是继承的方式,所以不能代理final 类。

导入依赖:

1
2
3
4
5
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
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
public class CglibProxyFactory<T> implements MethodInterceptor {

private final T target;

public CglibProxyFactory(T target) {
this.target = target;
}

public Object getProxyInstance() {
// cglib工具类
Enhancer enhancer = new Enhancer();
// 设置父类
enhancer.setSuperclass(this.target.getClass());
// 设置回调函数
enhancer.setCallback(this);

return enhancer.create();
}

@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("login start:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
Object result = method.invoke(target, args);
System.out.println("login end:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
return result;
}
}

第二种:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class CglibProxyInterfaceFactory implements MethodInterceptor {

public <T> T getProxyInstance(Class<T> clz) {
return (T) Enhancer.create(clz, this);
}

@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("login start:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
Object result = methodProxy.invokeSuper(o, args);
System.out.println("login end:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
return result;
}
}