目录  一、前言 二、案例 1 代码 2 自定义代理类【静态代理】 2.1 一个接口多个实现,到底注入哪个依赖呢? 2.1.1 @Primary注解 2.1.2 @Resource注解(指定name属性) 2.1.3 @Qualifier注解   2.2 面向接口编程 2.3 如果没接口咋办呢?     3 动态代理     
  
 
 一、前言  
在【对AOP的理解】中,提到过代理模式。 本篇文章进一步谈谈我对代理模式的理解。   
 二、案例  
 1 代码  
@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 自定义代理类【静态代理】  
@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   
@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注解  
@Slf4j 
@Service 
@Primary 
public  class  UserProxyServiceImpl  implements  UserService  { . . . 
} 
  
 2.1.2 @Resource注解(指定name属性)  
@RestController 
@RequestMapping ( "/user" ) 
public  class  UserController  { @Resource ( name =  "userProxyServiceImpl" ) private  UserService  userService; . . . 
} 
  
IDEA的友好提示:   妈妈再也不担心我注不对bean了:)   
 2.1.3 @Qualifier注解  
@Resource(name = “userProxyServiceImpl”)相当于:   
@Autowired 
@Qualifier ( "userProxyServiceImpl" ) 
  
@RestController 
@RequestMapping ( "/user" ) 
public  class  UserController  { @Autowired @Qualifier ( "userProxyServiceImpl" ) private  UserService  userService; . . . 
} 
  
同样,IDEA提供了友好的提示:     
 2.2 面向接口编程  
我们通过改变使用的bean:从UserServiceImpl换成了UserProxyServiceImpl,就新增了一些逻辑,例如,记录“登录”消耗的时间。 对调用者完全是无感的。  这就是通过接口来解耦了调用方和实现方:调用方–接口–实现方。     
 2.3 如果没接口咋办呢?  
 2.3.1 示例  
@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 继承  
@RestController 
@RequestMapping ( "/user" ) 
public  class  UserController  { 
@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 动态代理