Spring4新特性——核心容器的其他改进

2019-03-19 08:54|来源: 开涛

1、Map依赖注入:

Java代码
  1. @Autowired  

  2. private Map<String, BaseService> map;  

这样会注入:key是bean名字;value就是所有实现了BaseService的Bean,假设使用上一篇的例子,则会得到:

{organizationService=com.sishuok.spring4.service.OrganizationService@617029, userService=com.sishuok.spring4.service.UserService@10ac73b}

2、List/数组注入:

Java代码
  1. @Autowired  

  2. private List<BaseService> list;  

这样会注入所有实现了BaseService的Bean;但是顺序是不确定的,如果我们想要按照某个顺序获取;在Spring4中可以使用@Order或实现Ordered接口来实现,如:

Java代码
  1. @Order(value = 1)  

  2. @Service  

  3. public class UserService extends BaseService<User> {  

  4. }  

这种方式在一些需要多态的场景下是非常有用的。

3、@Lazy可以延迟依赖注入:

Java代码
  1. @Lazy  

  2. @Service  

  3. public class UserService extends BaseService<User> {  

  4. }  

Java代码
  1. @Lazy  

  2. @Autowired  

  3. private UserService userService;  

我们可以把@Lazy放在@Autowired之上,即依赖注入也是延迟的;当我们调用userService时才会注入。即延迟依赖注入到使用时。同样适用于@Bean。

4、@Conditional

@Conditional类似于@Profile(一般用于如我们有开发环境、测试环境、正式机环境,为了方便切换不同的环境可以使用@Profile指定各个环境的配置,然后通过某个配置来开启某一个环境,方便切换,但是@Conditional的优点是允许自己定义规则。可以指定在如@Component、@Bean、@Configuration等注解的类上,以绝对Bean是否创建等。首先来看看使用@Profile的用例,假设我们有个用户模块:

1、在测试/开发期间调用本机的模拟接口方便开发;

2、在部署到正式机时换成调用远程接口;

Java代码
  1. public abstract class UserService extends BaseService<User> {  

  2. }  

  3.  

  4. @Profile("local")  

  5. @Service  

  6. public class LocalUserService extends UserService {  

  7. }  

  8.  

  9. @Profile("remote")  

  10. @Service  

  11. public class RemoteUserService extends UserService {  

  12. }  

我们在写测试用例时,可以指定我们使用哪个Profile:

Java代码
  1. @ActiveProfiles("remote")  

  2. @RunWith(SpringJUnit4ClassRunner.class)  

  3. @ContextConfiguration(locations =  "classpath:spring-config.xml")  

  4. public class ServiceTest {  

  5.  

  6.    @Autowired  

  7.    private UserService userService;  

  8. }  

 这种方式非常简单。如果想自定义如@Profile之类的注解等,那么@Conditional就派上用场了;假设我们系统中有好多本地/远程接口,那么我们定义两个注解@Local和@Remote注解要比使用@Profile方便的多;如:

Java代码
  1. @Retention(RetentionPolicy.RUNTIME)  

  2. @Target({ElementType.TYPE, ElementType.METHOD})  

  3. @Conditional(CustomCondition.class)  

  4. public @interface Local {  

  5. }  

  6.  

  7. @Retention(RetentionPolicy.RUNTIME)  

  8. @Target({ElementType.TYPE, ElementType.METHOD})  

  9. @Conditional(CustomCondition.class)  

  10. public @interface Remote {  

  11. }  

Java代码
  1. public class CustomCondition implements Condition {  

  2.  

  3.    @Override  

  4.    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {  

  5.        boolean isLocalBean = metadata.isAnnotated("com.sishuok.spring4.annotation.Local");  

  6.        boolean isRemoteBean = metadata.isAnnotated("com.sishuok.spring4.annotation.Remote");  

  7.        //如果bean没有注解@Local或@Remote,返回true,表示创建Bean  

  8.        if(!isLocalBean && !isRemoteBean) {  

  9.            return true;  

  10.        }  

  11.  

  12.        boolean isLocalProfile = context.getEnvironment().acceptsProfiles("local");  

  13.  

  14.        //如果profile=local 且 bean注解了@Local,则返回true 表示创建bean;  

  15.        if(isLocalProfile) {  

  16.            return isLocalBean;  

  17.        }  

  18.  

  19.        //否则默认返回注解了@Remote或没有注解@Remote的Bean  

  20.        return isRemoteBean;  

  21.    }  

  22. }  

然后我们使用这两个注解分别注解我们的Service:

Java代码
  1. @Local  

  2. @Service  

  3. public class LocalUserService extends UserService {  

  4. }  

Java代码
  1. @Remote  

  2. @Service  

  3. public class RemoteUserService extends UserService {  

  4. }  

首先在@Local和@Remote注解上使用@Conditional(CustomCondition.class)指定条件,然后使用@Local和@Remote注解我们的Service,这样当加载Service时,会先执行条件然后判断是否加载为Bean。@Profile就是这样实现的,其Condition是:org.springframework.context.annotation.ProfileCondition。可以去看下源码,很简单。

5、基于CGLIB的类代理不再要求类必须有空参构造器了:

这是一个很好的特性,使用构造器注入有很多好处,比如可以只在创建Bean时注入依赖,然后就不变了,如果使用setter注入,是允许别人改的。当然我们可以使用spring的字段级别注入。如果大家使用过如Shiro,我们可能要对Controller加代理。如果是类级别代理,此时要求Controller必须有空参构造器,有时候挺烦人的。spring如何实现的呢?其借鉴了objenesis类库的思想,可以去其官网看看介绍。这样就支持如下方式的构造器注入了:

Java代码
  1. @Controller  

  2. public class UserController {  

  3.    private UserService userService;  

  4.    @Autowired  

  5.    public UserController(UserService userService) {  

  6.        this.userService = userService;  

  7.    }  

  8. }  

org.springframework.cglib.proxy.Enhancer在其github和maven仓库中的source中竟然木有,其github:https://github.com/spring-projects/spring-framework/tree/master/spring-core/src/main/java/org/springframework/cglib;难道忘了吗?

转在:http://jinnianshilongnian.iteye.com/blog/1989379


相关问答

更多