目录 
一、前言  
二、案例  
[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 动态代理