Spring 4.0已经发布RELEASE版本,不仅支持Java8,而且向下兼容到JavaSE6/JavaEE6,并移出了相关废弃类,新添加如Java8的支持、Groovy式Bean定义DSL、对核心容器进行增强、对Web框架的增强、Websocket模块的实现、测试的增强等。其中两个我一直想要的增强就是:支持泛型依赖注入、对cglib类代理不再要求必须有空参构造器了。具体更新请参考:

http://docs.spring.io/spring/docs/4.0.0.RELEASE/spring-framework-reference/htmlsingle/#new-in-4.0

1、相关代码:

1.1、Entity

1. public class User implements Serializable {  
2. private Long id;  
3. private String name;  
4. }  
5.   
6. public class Organization implements Serializable {  
7. private Long id;  
8. private String name;  
9. }


1.2、Repository


1. public abstract class BaseRepository<M extends Serializable> {  
2. public void save(M m) {  
3. "=====repository save:" + m);  
4.     }  
5. }  
6.   
7. @Repository  
8. public class UserRepository extends BaseRepository<User> {  
9. }  
10.   
11. @Repository  
12. public class OrganizationRepository extends BaseRepository<Organization> {  
13. }

对于Repository,我们一般是这样实现的:首先写一个模板父类,把通用的CRUD等代码放在BaseRepository;然后子类继承后,只需要添加额外的实现。


1.3、Service

1.3.1、以前Service写法

1. public abstract class BaseService<M extends Serializable> {  
2. private BaseRepository<M> repository;  
3. public void setRepository(BaseRepository<M> repository) {  
4. this.repository = repository;  
5.     }  
6. public void save(M m) {  
7.         repository.save(m);  
8.     }  
9. }  
10. @Service  
11. public class UserService extends BaseService<User> {  
12. @Autowired  
13. public void setUserRepository(UserRepository userRepository) {  
14.         setRepository(userRepository);  
15.     }  
16. }  
17.   
18. @Service  
19. public class OrganizationService extends BaseService<Organization> {  
20. @Autowired  
21. public void setOrganizationRepository(OrganizationRepository organizationRepository) {  
22.         setRepository(organizationRepository);  
23.     }  
24. }

 

可以看到,以前必须再写一个setter方法,然后指定注入的具体类型,然后进行注入;


1.3.2、泛型Service的写法

1. public abstract class BaseService<M extends Serializable> {  
2. @Autowired  
3. protected BaseRepository<M> repository;  
4.   
5. public void save(M m) {  
6.         repository.save(m);  
7.     }  
8. }  
9.   
10. @Service  
11. public class UserService extends BaseService<User> {  
12. }  
13.   
14. @Service  
15. public class OrganizationService extends BaseService<Organization> {  
16. }

大家可以看到,现在的写法非常简洁。支持泛型式依赖注入。

这个也是我之前非常想要的一个功能,这样对于那些基本的CRUD式代码,可以简化更多的代码。

如果大家用过Spring data jpa的话,以后注入的话也可以使用泛型限定式依赖注入 :

1. @Autowired  
2. private Repository<User> userRepository;

1.4 Map依赖注入:

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}

List/数组注入:

1. @Autowired  
2. private List<BaseService> list;

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

1. @Order(value = 1)  
2. @Service  
3. public class UserService extends BaseService<User> {  
4. }

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


1.6 @Lazy可以延迟依赖注入:

1. @Lazy  
2. @Service  
3. public class UserService extends BaseService<User> {  
4. } 
5. @Lazy  
6. @Autowired  
7. private UserService userService;

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


1.7 @Conditional

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

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

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

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:

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方便的多;如:

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. }


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:

1. @Local  
2. @Service  
3. public class LocalUserService extends UserService {  
4. }


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。可以去看下源码,很简单。