目录
- 一、前言
- 二、案例
-
- [1 代码](#1 代码)
- [2 自定义代理类【静态代理】](#2 自定义代理类【静态代理】)
-
- [2.1 一个接口多个实现,到底注入哪个依赖呢?](#2.1 一个接口多个实现,到底注入哪个依赖呢?)
-
- [2.1.1 @Primary注解](#2.1.1 @Primary注解)
- [2.1.2 @Resource注解(指定name属性)](#2.1.2 @Resource注解(指定name属性))
- [2.1.3 @Qualifier注解](#2.1.3 @Qualifier注解)
- [2.2 面向接口编程](#2.2 面向接口编程)
- [2.3 如果没接口咋办呢?](#2.3 如果没接口咋办呢?)
-
- [2.3.1 示例](#2.3.1 示例)
- [2.3.2 继承](#2.3.2 继承)
- [3 动态代理](#3 动态代理)
一、前言
- 在【对AOP的理解】中,提到过代理模式。
- 本篇文章进一步谈谈我对代理模式的理解。
二、案例
1 代码
java
复制代码
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserService userService;
@PostMapping("/login")
public UserVO login(@RequestBody LoginRequest loginRequest) {
UserDO userDO = userService.login(loginRequest.getUsername(), loginRequest.getPassword());
return UserVO.builder()
.username(userDO.getUsername())
.password(userDO.getPassword())
.build();
}
}
public interface UserService {
UserDO login(String username, String password);
}
@Service
public class UserServiceImpl implements UserService {
@Resource
private LoginProcess loginProcess;
@Override
public UserDO login(String username, String password) {
return loginProcess.login(username, password);
}
}
@Component
public class LoginProcess {
public UserDO login(String username, String password) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return new UserDO()
.setUsername("forrest")
.setPassword("123456");
}
}
- 我们想知道"登录"过程耗费的时间,即
loginProcess.login(username, password);
耗费的时间。
- 我们希望通过自定义代理类来实现。
2 自定义代理类【静态代理】
java
复制代码
@Slf4j
@Service
public class UserProxyServiceImpl implements UserService {
@Resource
private UserServiceImpl userServiceImpl;
@Override
public UserDO login(String username, String password) {
long startTimestamp = System.currentTimeMillis();
UserDO userDO = userServiceImpl.login(username, password);
log.info("login cost {} ms", System.currentTimeMillis() - startTimestamp);
return userDO;
}
}
- 如果这么写,很显然,启动时会报错:
No qualifying bean of type 'structure.proxy.example3.service.UserService' available: expected single matching bean but found 2: userProxyServiceImpl,userServiceImpl
java
复制代码
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserService userService;
...
}
- UserService是接口,有两个实现类,Spring不知道到底要注入哪个bean,因此报错了。
2.1 一个接口多个实现,到底注入哪个依赖呢?
- 在Spring框架中,当存在多个相同类型的bean时,可以通过三种主要方式来指定注入哪一个bean:使用
@Primary注解
、@Resouce注解(指定name属性)
和@Qualifier注解
。
2.1.1 @Primary注解
java
复制代码
@Slf4j
@Service
@Primary
public class UserProxyServiceImpl implements UserService {
...
}
2.1.2 @Resource注解(指定name属性)
java
复制代码
@RestController
@RequestMapping("/user")
public class UserController {
@Resource(name = "userProxyServiceImpl")
private UserService userService;
...
}
- IDEA的友好提示:
- 妈妈再也不担心我注不对bean了:)
2.1.3 @Qualifier注解
- @Resource(name = "userProxyServiceImpl")相当于:
java
复制代码
@Autowired
@Qualifier("userProxyServiceImpl")
java
复制代码
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
@Qualifier("userProxyServiceImpl")
private UserService userService;
...
}
- 同样,IDEA提供了友好的提示:
2.2 面向接口编程
- 我们通过改变使用的bean:从UserServiceImpl换成了UserProxyServiceImpl,就新增了一些逻辑,例如,记录"登录"消耗的时间。
- 对调用者完全是无感的。
- 这就是通过接口来解耦了调用方和实现方:调用方--
接口
--实现方。
2.3 如果没接口咋办呢?
2.3.1 示例
java
复制代码
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserServiceImpl userService;
@PostMapping("/login")
public UserVO login(@RequestBody LoginRequest loginRequest) {
UserDO userDO = userService.login(loginRequest.getUsername(), loginRequest.getPassword());
return UserVO.builder()
.username(userDO.getUsername())
.password(userDO.getPassword())
.build();
}
}
@Service
public class UserServiceImpl {
@Resource
private LoginProcess loginProcess;
public UserDO login(String username, String password) {
return loginProcess.login(username, password);
}
}
2.3.2 继承
java
复制代码
@RestController
@RequestMapping("/user")
public class UserController {
// @Resource
// private UserServiceImpl userService;
@Resource
private UserProxyServiceImpl userService;
...
}
@Slf4j
@Service
public class UserProxyServiceImpl extends UserServiceImpl {
@Resource
private UserServiceImpl userServiceImpl;
@Override
public UserDO login(String username, String password) {
long startTimestamp = System.currentTimeMillis();
UserDO userDO = userServiceImpl.login(username, password);
log.info("login cost {} ms", System.currentTimeMillis() - startTimestamp);
return userDO;
}
}
- 很显然,所有用到UserServiceImpl的地方,都要换成UserProxyServiceImpl。麻烦啊!
- 因此,如果依赖的实现方可能变化,一定要面向接口编程啊!
- 如果第三方没提供接口,也要自定义一个接口来解耦调用方和实现方!
3 动态代理