Spring基础配置
Spring框架本身有四大原则:
使用POJO进行轻量级和最小侵入式开发。
通过依赖注入和基于接口编程实现松耦合。
通过AOP和默认习惯进行声明式编程。
使用AOP和模板(template)减少模式化代码。
Spring所有功能的设计和实现都是基于此四大原则的。
依赖注入
点睛
我们经常说的控制反转(Inversion of control-IOC)和依赖注入(dependency injection-DI)在Spring环境下世等同的概念,控制反转时通过依赖注入实现的。所谓依赖注入指的是容器负责创建对象和维护对象间的依赖关系,而不是通过对象本身负责自己的创建和解决自己的依赖。
依赖注入的主要目的是为了解耦,体现了一种“组合”的理念。如果你希望你的类具备某项功能的时候,是继承自一个具有此功能的父类好呢?还是组合另外一个具有这个功能的类好呢?答案是不言而喻的,继承一个父类,子类将与父类耦合,组合另外一个类则使耦合度大大降低。
Spring IoC容器(ApplictionContext)负责创建Bean,并通过容器将功能类的Bean注入到你需要的Bean中。Spring提供使用XML、注解、java配置、groovy配置实现Bean的创建和注入。
无论是xml配置、注解配置还是java配置,都被称为配置元数据,所谓元数据即描述数据的数据。元数据本身不具备任何可执行的能力,只能通过外界代码来对这些元数据解析后进行一些有意义操作。Spring容器解析这些配置元数据进行Bean初始化、配置和管理依赖。
声明Bean注解
@Component组件,没有明确的角色。
@Service在业务逻辑层(service层)使用。
@Repository在数据访问层(dao层)使用。
@Controller在展现层(MVC→Spring MVC)使用。
注入Bean的注解,一般情况下通过。
@Autowired:Spring提供的注解
@Inject:JSR-330提供的注解。
@Resource:JSR-250提供的注解。
以上三种注解,可以注解在set方法或者属性上,注解在属性上,优点是代码更少、层次更清晰。
示例
编写功能类的Bean。
package hightlight_spirng4.ch1.d1;
import org.springframework.stereotype.Service;
@Service //1 public class FunctionService { public String sayHello(String word) { return "Hello"+word+"!"; } }
|
代码解释
使用@Service注解声明当前FunctionService类是Spring管理的一个Bean。其中,使用@Component、@Service、@Repository和@Controller是等效的,可根据需要选用。
使用功能类的Bean
package hightlight_spirng4.ch1.d1;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service //1 public class UseFunctionService { @Autowired //2 FunctionService functionService;
public String sayHello(String word) { return functionService.sayHello(word); } }
|
代码解释
使用@Service注解声明当前UseFunctionService类是Spring管理的一个Bean。
使用@Autowired将FunctionService的实体Bean注入的UseFunctionService中,让UseFunctionService具备FunctionService的功能,此处使用JSR-330的@Inject注解或者是JSR-250的@Resource注解是等效的。
配置类
package hightlight_spirng4.ch1.d1;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration;
@Configuration //1 @ComponentScan("hightlight_spirng4.ch1.d1") //2 public class DiConfig {
}
|
代码解释
使用@Configuration声明当前类是一个配置类。
使用@ComponentScan,自动扫描包名下所有使用@Service、@Component、@Repository和@Controller的类,并注册为Bean。
运行
package hightlight_spirng4.ch1.d1;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DiConfig.class); //1 UseFunctionService useFunctionService = context.getBean(UseFunctionService.class); //2 System.out.println(useFunctionService.sayHello("di")); context.close(); } } |
代码解释
使用AnnotationConfigApplicationContext作为Spring容器,接受输入一个配置类作为参数;
获得声明配置的UserFunctionService的Bean。
结果如下所示
9 29, 2017 5:33:44 午後 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh 情報: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@1fc0f2f: startup date [Fri Sep 29 17:33:44 JST 2017]; root of context hierarchy Hellodi! 9 29, 2017 5:33:44 午後 org.springframework.context.annotation.AnnotationConfigApplicationContext doClose 情報: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@1fc0f2f: startup date [Fri Sep 29 17:33:44 JST 2017]; root of context hierarchy |
Java配置
点睛
Java配置是Spring4.x推荐的配置方式,可以完全替代xml配置;Java配置也是Spring Boot推荐的配置方式。
Java偶尔之是通过@Configuration·和@Bean来实现的。
@Configuration声明当前类是一个配置类,相当于一个Spring配置的XML文件。
@Bean注解在方法上,声明当前方法的返回值为一个Bean。
何时使用Java配置或是注解配置呢?外面主要原则是:全局配置实用Java配置(如数据库相关配置、MVC相关配置),业务Bean的配置是使用注解配置(@Service、@Component、@Repository、@Controlle)
示例
编写功能的Bean
package hightlight_spirng4.ch1.javaconfig; //1 public class FunctionService { public String sayHello(String word) { return "Hello"+word+"!"; } }
|
代码解释
此处没有使用@Service声明Bean
使用功能类的Bean
package hightlight_spirng4.ch1.javaconfig;
//1 public class UseFunctionService { //2 FunctionService functionService;
public String sayHello(String word) { return functionService.sayHello(word); }
public void setFunctionService(FunctionService functionService) { this.functionService = functionService; }
} |
代码解释
此处没有使用@Service声明的Bean。
此处没有使用@Autowired注解注入Bean。
配置类
package hightlight_spirng4.ch1.d1;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration;
@Configuration //1 @ComponentScan("hightlight_spirng4.ch1.d1") //2 public class DiConfig { @Bean //2 public FunctionService FunctionService() { return new FunctionService(); }
@Bean //2 public UseFunctionService UseFunctionService() { UseFunctionService useFunctionService=new UseFunctionService();//3 useFunctionService.setFunctionService(FunctionService()); return useFunctionService; }
// @Bean //2 // public UseFunctionService UseFunctionService(UseFunctionService useFunctionService) { //4 // return useFunctionService; // } }
|
代码解释
@Configuration注解表明JavaConfig类是一个配置类,这意味着这个类里可能有零个或多个@Bean注解。
@Bean注解声明当前FunctionService的返回值是一个Bean,Bean的名称是方法名。
注入UseFunctionService的Bean时调用FunctionService()。
两外一种注入的方式,直接将FunctionService作为参数给UseFunctionService(),这也是Spring容器提供的极好的功能。在Spring容器中,只要存在某个Bean,就可以再另一个Bean的声明方法的参数中写入。
运行
package hightlight_spirng4.ch1.javaconfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig.class); UseFunctionService useFunctionService = context.getBean(UseFunctionService.class); System.out.println(useFunctionService.sayHello("JavaConfig")); context.close(); } }
|
使用AnnotationConfigApplicationContext作为Spring容器,接受输入一个配置类作为参数;
获得声明配置的UserFunctionService的Bean。
结果如下所示
10 02, 2017 2:54:47 午後 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh 情報: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@1fc0f2f: startup date [Mon Oct 02 14:54:47 JST 2017]; root of context hierarchy HelloJavaConfig! 10 02, 2017 2:54:48 午後 org.springframework.context.annotation.AnnotationConfigApplicationContext doClose 情報: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@1fc0f2f: startup date [Mon Oct 02 14:54:47 JST 2017]; root of context hierarchy |
AOP
点睛
AOP:面向切面编程,相对于OOP面向对象编程。
Spring的AOP的存在目的是为了解耦。AOP可以让一组类共享相同的行为。在OOP中只能通过继承类和实现接口,来使代码的耦合度增强,且类继承只能为单继承,阻碍更多行为添加到一组类上,AOP弥补了OOP的不足。
Spring支持AspectJ的注解式切面编程。
使用@Aspect声明是一个切面。
使用@After、@Before、@Around定义建言(advice),可直接将拦截规则(切点)作为参数。
------------------------------------华丽的分隔符(AOP未完)-------------------------------------------------
Bean的scope
点睛
Scope描述的是Spring容器如何新建Bean的实例的。Spring的Scope有以下几种,通过@Scope注解来实现。
Singleton:以个Spring容器中只有一个Bean的实例。此为Spring的默认配置,全容器共享一个实例。
Prototype:每次调用新建一个Bean实例。
Request:Web项目中,给每一个http request新建一个Bean实例。
Session:Web项目中,给每一个http session新建一个Bean实例。
GlobalSession:这个只是在portal应用中有用,给每一个global http session新建一个Bean实例。
另外,在Spring Batch中还有一个Scope是使用@SetpScope,外面将在批处理一节介绍这个Scope。
本例简单演示默认的singleton和prototype,分别从Spring容器中获得2次Bean,判断Bean的实例是否相等。
示例
编写Singleton的Bean
package hightlight_spirng4.ch2.scope;
import org.springframework.stereotype.Service;
@Service //1 public class DemoSingletonService {
} |
代码解释
默认为Singleton,相当于@Scope(“singleton”)
编写Prototype的Bean
package hightlight_spirng4.ch2.scope;
import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Service;
@Service @Scope("prototype") //1 public class DemoPrototypeService {
}
|
代码解释
声明Scope为Prototype。
配置类
package hightlight_spirng4.ch2.scope;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration;
@Configuration @ComponentScan("hightlight_spirng4.ch2.scope") public class ScopeConfig {
}
|
运行
package hightlight_spirng4.ch2.scope;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) { AnnotationConfigApplicationContext context =new AnnotationConfigApplicationContext(ScopeConfig.class); DemoSingletonService s1=context.getBean(DemoSingletonService.class); DemoSingletonService s2=context.getBean(DemoSingletonService.class); DemoPrototypeService p1 =context.getBean(DemoPrototypeService.class); DemoPrototypeService p2 =context.getBean(DemoPrototypeService.class); System.out.println("s1 eques s2 ?"+s1.equals(s2)); System.out.println("p1 eques p2 ?"+p1.equals(p2)); context.close(); }
}
|
结果
10 02, 2017 4:26:35 午後 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh 情報: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@1fc0f2f: startup date [Mon Oct 02 16:26:35 JST 2017]; root of context hierarchy 10 02, 2017 4:26:35 午後 org.springframework.context.annotation.AnnotationConfigApplicationContext doClose 情報: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@1fc0f2f: startup date [Mon Oct 02 16:26:35 JST 2017]; root of context hierarchy s1 eques s2 ?true p1 eques p2 ?false |