@Configuration用于定义配置类,标注在类上,至关于把该类做为spring的xml配置文件中的<beans>
,做用是用于配置spring容器(应用上下文)css
实例说明:html
配置类java
@Configuration
public class MyConfig {
public MyConfig() {
System.out.println("TestConfig容器初始化!!!!");
}
}
复制代码
至关于xmlmysql
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
复制代码
测试类web
public class TestConfig {
@Test
public void test(){
AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyConfig.class);
}
}
复制代码
运行结果spring
TestConfig容器初始化!!!!sql
@Bean标注在方法上,至关于xml配置配置中的</bean>
编程
代码实例说明:后端
注册类数组
public class ConfigurationBean {
public void start() {
System.out.println("容器初始化开始!!!!!");
}
}
复制代码
配置类
@Configuration
public class MyConfiguration {
//能够指定bean 的名称,也能够指定注入容器以前加载的方法
@Bean()
public ConfigurationBean configurationBean(){
return new ConfigurationBean();
}
}
复制代码
至关于xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="configurationBean" class="zfcoding.bean.ConfigurationBean" ></bean>
</beans>
复制代码
测试方法
@Test
public void ConfigurationTest(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfiguration.class);
ConfigurationBean configurationBean=(ConfigurationBean) applicationContext.getBean("configurationBean");
System.out.println(configurationBean);
}
复制代码
运行结果:
容器初始化开始!!!!!
zfcoding.bean.ConfigurationBean@77be656f
说明几点:
(1)、@Bean注解标注在方法上,若是未经过@Bean指定bean的名称,则默认与标注的方法名相同;
(2)、@Bean注解标注在方法上,若是想经过@Bean指定bean的名称,例如 @Bean("configurationBean1"),那么容器中实例化bean的名称为configurationBean1;
容器bean管理的生命周期:
咱们能够自定义初始化和销毁的方法,容器在bean进行到当前生命周期的时候来调用咱们自定义的初始化和销毁的方法。
@Bean 中指定初始化和销毁的方法。
@Bean(initMethod = "init",destroyMethod = "destory")
xml
<bean id="student" class="zfcoding.bean.Student" init-method="init" destroy-method="destory"></bean>
复制代码
@Scope
默认状况下: singleton 单实例(默认值),ioc容器启动会调用方法建立对象放到ioc容器当中。之后每次获取直接获取对象。
prototype :多实例,ioc容器启动并不会调用方法建立对象放到容器当中。,而是每次获取的时候才会调用方法建立对象,调用一次建立一次。
@Lazy 懒加载,
针对单实例bean,默认在容器启动的时候建立对象
懒加载:容器启动不创键对象,第一次使用Bean建立对象,并初始化。
实例说明:
public class Student {
public Student() {
System.out.println("Student容器加载开始!!!!");
}
//对象建立完成,并赋值好,调用初始化方法......
public void init() {
System.out.println("Student.init()初始化开始.....");
}
//销毁针对(单实例),容器关闭的时候调用。
//多实例(@Scope("prototype")):容器不会管理这个bean,容器也不回调用销毁的方法。
public void destory() {
System.out.println("Student.destory().........");
}
}
复制代码
@Configuration
public class MyConfigStudent {
//@Lazy
//@Scope("prototype")
@Bean(value = "student",initMethod = "init",destroyMethod = "destory")
public Student student(){
return new Student();
}
}
复制代码
测试类一
public class MyConfigStudentTest {
@Test
public void test(){
AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyConfigStudent.class);
}
}
复制代码
运行结果
Student容器加载开始!!!!
Student.init()初始化开始…..
那么说明,ioc容器启动会调用方法建立对象放到ioc容器当中。之后每次获取直接获取对象。
若是咱们使用@Lazy注解(单实例),容器启动不创键对象,测试结果没有输出信息。
测试类二,咱们第一次使用Bean建立对象,并初始化
public class MyConfigStudentTest {
@Test
public void test(){
AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyConfigStudent.class);
Student student = (Student)applicationContext.getBean("student");
System.out.println(student);
applicationContext.close();
}
}
复制代码
运行结果
Student容器加载开始!!!!
Student.init()初始化开始.....
zfcoding.bean.Student@7364985f
Student.destory().........
复制代码
说明懒加载(@Lazy):当容器启动不创键对象,而是第一次使用Bean建立对象,并初始化。
InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候都会执行该方法。
测试类
public class TestInitializingBean implements InitializingBean {
public void afterPropertiesSet() throws Exception {
System.out.println("TestInitializingBean.afterPropertiesSet()......");
}
public void init() {
System.out.println("TestInitializingBean.init().......");
}
}
复制代码
配置类
@Configuration
public class MyInitializingBeanConfig {
@Bean(value = "testInitializingBean",initMethod = "init")
public TestInitializingBean testInitializingBean(){
return new TestInitializingBean();
}
}
复制代码
测试类
public class TestnitializingBeanConfig {
@Test
public void test(){
AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyInitializingBeanConfig.class);
}
}
复制代码
运行结果:
TestInitializingBean.afterPropertiesSet()……
TestInitializingBean.init()…….
从结果能够看出,在Spring初始化bean的时候,若是该bean实现了InitializingBean接口,而且同时在配置文件中指定了init-method,系统则是先调用afterPropertieSet()方法,而后再调用init-method中指定的方法。
那么这种方式在spring中是怎么实现的呢,经过查看Spring加载bean的源码类AbstractAutowiredCapableBeanFactory类中的invokeInitMethods(),以下:
protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
throws Throwable {
判断该bean是否实现了实现了InitializingBean接口,若是实现了InitializingBean接口,则只掉调用bean的afterPropertiesSet方法
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws Exception {
((InitializingBean) bean).afterPropertiesSet();
return null;
}
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
((InitializingBean) bean).afterPropertiesSet();
}
}
if (mbd != null) {
String initMethodName = mbd.getInitMethodName();
if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
复制代码
总结:
Spring为bean提供了两种初始化bean的方式,一种是实现InitializingBean接口,重写afterPropertiesSet方法,另外一种四在配置文件中经过init-method指定,可是若是同时指定的话是先调用InitializingBean实现的方法。
能够使用JS250,@PostConstruct 标注在方法上面,bean建立完成而且属性赋值完成,来执行初始化方法
@PreDestroy ,在容器销毁bean以前通知咱们进行清理工做.
@Configuration+@ComponentScan 注入组件, 只是把标注了注解为@Controller ,@Service, @Repository, @Component 加入到Spring容器中.
xml 方式的配置
<context:component-scan base-package="zfcoding"></context:component-scan>
复制代码
代码实例说明:
须要注入的类
@Component
public class PersonComponent {
}
@Repository
public class PersonDao {
}
@Service
public class PersonService {
}
@Controller
public class PersonController {
}
复制代码
配置类
@Configuration
//@ComponentScan(basePackages = "zfcoding",
//// includeFilters =@ComponentScan.Filter(type=FilterType.ANNOTATION,classes = Controller.class),useDefaultFilters=false )
@ComponentScan(basePackages = "zfcoding",
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, Service.class}))
public class MyCompoentScan {
}
复制代码
测试的方法
@Test
public void ComponentSanTest(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyCompoentScan.class);
String[] definitionNames = applicationContext.getBeanDefinitionNames();
for (String definitionName:definitionNames){
System.out.println(definitionName);
}
}
复制代码
运行结果
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
myCompoentScan
personComponent
personDao
复制代码
说明:
excludeFilters 排除不须要加载的组件
includeFilters 只包含须要加载的组件,使用的时候须要把 useDefaultFilters=false,才能生效
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, Service.class}),指定的过滤规则,排除注解标注是@Controller和@Service 类。
@Conditional-设置组件做用域
按照必定的条件进行判断,知足条件给容器中注册bean
自定义Conditional条件须要编写一个类,这个类须要实现Condition接口,咱们直接使用就@Conditional({WindowsEnvironment.class})
@Conditional既能够定义在方法上,也能够定义在类上,代码实现自定义条件须要实现Condition接口
条件类
public class WindowsEnvironment implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
BeanDefinitionRegistry registry = context.getRegistry();
if(property.contains("Windows 10")){
return true;
}
return false;
}
}
复制代码
public class LiunxEnvironment implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
if (environment.containsProperty("Liunx")){
return true;
}
return false;
}
}
复制代码
配置类
@Configuration
public class MyCondition {
@Conditional({WindowsEnvironment.class})
@Bean
public ConfigurationBean configurationBean(){
return new ConfigurationBean("张三");
}
@@Conditional({LiunxEnvironment.class})
@Bean
public ConfigurationBean configurationBean1(){
return new ConfigurationBean("李四");
}
}
复制代码
测试方法
public class CoditionTest {
@Test
public void test(){
AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyCondition.class);
String[] beanNamesForType = applicationContext.getBeanNamesForType(ConfigurationBean.class);
for (String s:beanNamesForType){
System.out.println(s);
}
}
}
复制代码
运行结果
configurationBean
一、@Import(要导入的组件),容器就会自动注册这个组件,id默认是全类名。
二、ImportSelector , 自定义逻辑返回要导入的组件 编写的类学要实现 ImportSelector 接口,返回须要导入的主键的全类名数组。
三、ImportBeanDefinitionRegistrar,自定义逻辑须要编写类实现ImportBeanDefinitionRegistrar接口,手动注册bean到容器当中。
代码实例
public class Red {
}
public class Blue {
}
复制代码
//
@Import({Red.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
public class MyImport {
}
复制代码
public class MyImportSelector implements ImportSelector {
//AnnotationMetadata :当前标注@Import 注解类的全部注解信息
//返回类的全类名
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"zfcoding.bean.Blue"};
}
}
复制代码
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
//importingClassMetadata:当前类的注解信息
//BeanDefinitionRegistry BeanDefinition注册类,把全部须要添加到容器中的bean,BeanDefinitionRegistry.registerBeanDefinition手工注册
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean red = registry.containsBeanDefinition("zfcoding.bean.Red");
if (red){
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Blue.class);
registry.registerBeanDefinition("blue",rootBeanDefinition);
}
}
}
复制代码
测试类
public class ImportTest {
@Test
public void test(){
AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyImport.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String s:beanDefinitionNames){
System.out.println(s);
}
}
}
复制代码
运行结果
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactorymyImport
zfcoding.bean.Red
zfcoding.bean.Blue
blue
总结:
给容器中导入组件:
一、@Configuration&@Bean给容器中注册组件
二、@ComponentScan&@Configuration(@Controller ,@Service, @Repository, @Component)
三、@Import 快速的给容器中导入一个组件
@Value
一、基本数值
二、SPEL #{}
三、 能够写
使用@PropertySource去取外部配置文件中的值保存到运行的环境当中。加载完尾部的配置文件以后,使用
{}取出配置文件中的值.代码实例:
代码省略setter(),getter(),toString()方法。
public class Person {
@Value("张三")
private String name;
@Value("#{20-1}")
private Integer age;
@Value("${person.sex}")
private String sex;
}
复制代码
//<context:property-placeholder location="person.properties"></context:property-placeholder>
@PropertySource("classpath:person.properties")
@Configuration
public class MyConfigValue {
@Bean
public Person person(){
return new Person();
}
}
复制代码
person.properties
person.sex=1
复制代码
测试类
@Test
public void test1(){
AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyConfigValue.class);
Person person = (Person) applicationContext.getBean("person");
System.out.println(person);
}
复制代码
运行结果
Person{name='张三', age=19, sex='1'}
复制代码
自动装配:
@Autowired自动注入,单个默认优先按照类型去容器中找到对应的组件,默认必定要将属性赋值好,没有找到就会报错,能够使用@Autowired (required=false),非必须的 ;
@Qualifier ,指定须要装配的组件id ;
@Primary 让Spring 进行自动装配的时候, 首选装配的组件(配置类中),也能够使用@Qualifier指定须要装配的bean的名字.
Spring还支持@Resource(JSR250)和@Inject (JSR330){java规范的注解}
@Resource 和@Autowired同样实现自动装配的功能,默认按照组件的名称进行装配。
@Inject须要导入javax.inject的包,和@Autowired功能同样,没有required=false的功能。
Spring 为咱们提供能够根据当前环境,动态的激活和切换一系列组件的功能
下面咱们来使用数据源说明这个问题:
@Profile指定哪一个环境下才能被注册到容器当中,加了@Profile,只有这个环境激活的时候才能注册到spring容器当中,默认是defalut
代码实例:
@PropertySource("classpath:db.properties")
@Configuration
public class MyConfigProfile implements EmbeddedValueResolverAware {
@Value("${db.username}")
private String username;
@Value("${db.password=}")
private String password;
private StringValueResolver stringValueResolver;
private String driveClass;
@Profile("default")
@Bean("testDataSource")
public DataSource dataSource() throws PropertyVetoException {
ComboPooledDataSource dataSource=new ComboPooledDataSource();
dataSource.setUser(username);
dataSource.setPassword(password);
dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/springboot?serverTimezone=UTC&useSSL=false&autoReconnect=true&tinyInt1isBit=false&useUnicode=true&characterEncoding=utf8");
dataSource.setDriverClass(driveClass);
return dataSource;
}
@Profile("dev")
@Bean("devDataSource")
public DataSource devDataSource() throws PropertyVetoException {
ComboPooledDataSource dataSource=new ComboPooledDataSource();
dataSource.setUser(username);
dataSource.setPassword(password);
dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/springboot?serverTimezone=UTC&useSSL=false&autoReconnect=true&tinyInt1isBit=false&useUnicode=true&characterEncoding=utf8");
dataSource.setDriverClass(driveClass);
return dataSource;
}
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.stringValueResolver=resolver;
driveClass = stringValueResolver.resolveStringValue("db.driverClass");
}
}
复制代码
db.properties
db.username=root
db.password=root
db.driverClass=com.mysql.jdbc.Driver
复制代码
测试代码:
public class DataSourceProfileTest {
@Test
public void test() {
AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MyConfigProfile.class);
String[] beanNamesForType = applicationContext.getBeanNamesForType(DataSource.class);
for (String s:beanNamesForType){
System.out.println(s);
}
}
/* 使用无参构造器
* 设置激活的环境
* 注册主配置类
* 启动刷新容器
*/
@Test
public void test1(){
//使用无参构造器
AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext();
//设置激活的环境
applicationContext.getEnvironment().setActiveProfiles("dev");
//注册主配置类
applicationContext.register(MyConfigProfile.class);
//启动刷新容器
applicationContext.refresh();
String[] beanNamesForType = applicationContext.getBeanNamesForType(DataSource.class);
for (String s:beanNamesForType){
System.out.println(s);
}
}
}
复制代码
测试案例运行结果
testDataSource
devDataSource
小结:
@Profile还能够标记在类上,表示只有指定环境的时候,整个配置文件里面的配置才能开始生效。没有标注环境表示的bean,在任何环境下都是加载的(前提是配置类是生效的),还能够经过命令行参数:-Dspring.profiles.active=test
激活对应的环境。
我是阿福,公众号「阿福聊编程」做者,对后端技术保持学习爱好者,我会常常更新JAVA技术文章,在进阶的路上,共勉!
欢迎你们关注个人公众号,后台回复666,领取福利,你懂的。