设计模式之代理模式

静态代理

静态代理的特点

  • 代理对象被代理对象实现同一个接口
  • 代理对象持有被代理对象的引用
  • 调用时,代理对象调用被代理对象的相应方法,可以在调用的前后,增加额外功能

静态代理的实例

​ 实例作用:保存数据时,进行事务控制

原方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Service
public class UserService {

@Resource
private UsUserMapper usUserMapper;

public void insertUser(){
//用户信息
UsUser usUser = new UsUser();
usUser.setUserId(1234567912);
usUser.setUserAccount("测试用户");
//插入
usUserMapper.insert(usUser);
}
}

静态代理后

1.增加接口

1
2
3
public interface UserServiceI {
void insertUser();
}

2.被代理对象实现接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Service
public class UserService implements UserServiceI{

@Resource
private UsUserMapper usUserMapper;

@Override
public void insertUser(){
//用户信息
UsUser usUser = new UsUser();
usUser.setUserId(1234567912);
usUser.setUserAccount("测试用户");
//插入
usUserMapper.insert(usUser);
}
}

3.代理对象实现接口,同时持有被代理对象的引用

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

@Resource
private UserService userService;
@Resource
PlatformTransactionManager platformTransactionManager;
@Resource
TransactionDefinition transactionDefinition;

@Override
public void insertUser() {
//开启事务
TransactionStatus transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);
try {
userService.insertUser();
//未发生异常,事务提交
platformTransactionManager.commit(transactionStatus);
}finally {
//发生异常,回滚事务
platformTransactionManager.rollback(transactionStatus);
}
}
}

4.使用方式:注入代理类的bean,由代理类持有被代理类的引用来调用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@RestController
public class TestController {

@Resource
private UserStaticProxyService userStaticProxyService;

@ApiOperation(value = "第一次测试接口",notes = "@author jungle")
@GetMapping("/test/one")
public ResponseEntity<String> testOne(){

//代理类调用
userStaticProxyService.insertUser();

return ResponseEntity.ok().build();
}
}

动态代理

静态代理和动态代理的区别

  1. 静态代理在编译生成class文件,动态代理在运行时生成字节码加载到JVM
  2. 静态代理代理对象被代理对象都需要实现同一接口,动态代理则无此限制

JDK动态代理

实现方式

  • 需要代理的类需要实现一个接口中的所有抽象方法
  • 通过Proxy类的newProxyInstance指定类加载器需要代理的类接口执行代理类中方法的执行器
  • 执行代理类中方法的执行器需要实现InvocationHandler接口,执行调用逻辑

Proxy : 负责动态生成代理

  • newProxyInstance
    • ClassLoader 被代理类的类加载器
    • Class<?>[] 代理类要实现的接口
    • InvocationHandler 代理类中需要加入的功能

InvocationHandler:代理类新增的功能

  • invoke
    • Object proxy 代理的实例
    • Method 被拦截的方法
    • Object[] args 方法的参数

实例

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
46
47
48
49
50
51
52
53
54
55
//代理工厂
public class ProxyFactory {
public static Object createProxy(Object target){

ClassLoader classLoader = target.getClass().getClassLoader();
//找到被代理类的所有接口
Class<?>[] interfaces = target.getClass().getInterfaces();
//找到代理逻辑
PerformanceMonitorHandler h = new PerformanceMonitorHandler(target);
return Proxy.newProxyInstance(classLoader,interfaces,h);
}
}
public interface NuclearWeaponBoomService {
public void startBoom();
}
//代理类
public class NuclearWeaponBoomServiceImpl implements NuclearWeaponBoomService {
@Override
public void startBoom() {
for (int i = 10; i> 0 ; i--) {
System.out.println(i);
try {
Thread.sleep(1000);
}catch (InterruptedException e){
throw new RuntimeException("线程异常",e);
}
}
System.out.println("boom");
}
}
//代理逻辑
public class PerformanceMonitorHandler implements InvocationHandler {
private Object target;
public PerformanceMonitorHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//前置处理
long before = System.currentTimeMillis();
//执行方法调用
Object result = method.invoke(target, args);
//后置处理
long after = System.currentTimeMillis();
System.out.println("方法:"+method.getName()+"共执行了:"+(after-before)+"毫秒");
return result;
}
}
public class PerfApp {
public static void main(String[] args) {
NuclearWeaponBoomService nwbs = new NuclearWeaponBoomServiceImpl();
NuclearWeaponBoomService proxy = (NuclearWeaponBoomService)ProxyFactory.createProxy(nwbs);
proxy.startBoom();
}
}

示例:JDK动态代理实现Redis降级

CGlib动态代理

实习方式

  • cglib : code generation lib 代码生成库
  • 使用继承的方式来生成代理类
  • 代理类继承被代理类,覆盖被代理类的方法,在实现中调用父类的相应方法

实例

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
46
47
48
49
50
51
52
//生产工厂
public class CglibProxyFactory {
public static Object createProxy(Object target){
//创建增强器
Enhancer enhancer = new Enhancer();
//设置被代理类,也就是父类
enhancer.setSuperclass(target.getClass());
//设置方法拦截器,也就是代理逻辑
enhancer.setCallback(new PerformanceMonitorInterceptor());
return enhancer.create();
}
}
//代理类
public class NuclearWeaponBoomServiceImpl extends Nuclear {
public void startBoom() {
for (int i = 10; i> 0 ; i--) {
System.out.println(i);
try {
Thread.sleep(1000);
}catch (InterruptedException e){
throw new RuntimeException("线程异常",e);
}
}
System.out.println("boom");
}
}
//代理逻辑
public class PerformanceMonitorInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//前置处理
long before = System.currentTimeMillis();
//调用父类相应方法
Object result = methodProxy.invokeSuper(target, args);
//后置处理
long after = System.currentTimeMillis();
System.out.println("方法:"+method.getName()+"共执行了:"+(after-before)+"毫秒");
return result;
}
}
//原方法
public class Nuclear {
public void startBoom(){};
}
//测试类
public class PerfApp {
public static void main(String[] args) {
Nuclear nwbs = new NuclearWeaponBoomServiceImpl();
Nuclear proxy = (Nuclear)CglibProxyFactory.createProxy(nwbs);
proxy.startBoom();
}
}

JDK、CGlib动态代理的区别

  • jdk使用接口的方式实现动态代理
  • cglib使用继承的方式实现动态代理
  • 性能:jdk1.8之前 cglib 胜出 、 jdk1.8之后 jdk方式胜出
  • spring 默认是用jdk动态代理生成代理类,但是也支持cglib