spring强化学习系列-容器1-----组件注册,生命周期,属性赋值,自动装配

组件注册

1. Configuration:告诉spring为配置类,bean:注册组件
以前使用xxx.XML文件启动spring容器。

<bean id="person" class="com.atguigu.bean.Person">
		<property name="age" value="18"></property>
		<property name="name" value="zhangsan"></property>
	</bean>
------------------------------------------------------
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
		Person bean = (Person) applicationContext.getBean("person");
		System.out.println(bean);

现在使用配置类,启动容器。通过注解Configuration告诉spring这是一个配置类
使用beans注册组件,通过beans的属性可以修改ID

@Configuration   //告诉Spring这是一个配置类
public class MainConfig {
	
	//给容器中注册一个Bean;类型为返回值类型,id默认使用方法名作为 id
	@Bean()
	//@Bean("person01")(可以指定id)
	public Person person(){
		return new Person("lisi",20);
	}
}
-------------------------------------------------------
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
		Person bean = applicationContext.getBean(Person.class);
		System.out.println(bean);

2. ComponentScan:指定扫描包。
在配置类上使用ComponentScan来指定扫描包,通过设置属性可以添加条件来扫描包
以前配置模式(在配置文件中):
//包扫描,只要标注了@Controller,@Service,@Repository@Component都会被扫描自动加入容器中 <context:component-scan base-package="com.atguigu"></context:component-scan>
使用配置类:
通过ComponentScan注解,value指定要扫描的包
可以通过指定Filter指定各种规则扫描,例:按注解排除(包含),按类排除,按正则表达式排除等等

//配置类==配置文件
@Configuration   //告诉Spring这是一个配置类
@ComponentScan(value="com.atguigu",includeFilters = {
		@Filter(type=FilterType.ANNOTATION,classes={Controller.class})
},useDefaultFilters = false)   

//@ComponentScan  value:指定要扫描的包
	//excludeFilters = Filter[] 指定扫描的时候按照什么规则排除什么组件
	//includeFilters = Filter[] 指定扫描的时候只需要包含什么组件 (默认规则是扫描所有,
//								 		使用此注解时需要修改ComponentScan的useDefaultFilters属性为false)
//FilterType.ANNOTATION:按照注解
//FilterType.ASSIGNABLE_TYPE:按照给定的类型
//FilterType.ASPECTJ:使用ASPECTJ表达式
//FilterType.REGEX:使用正则表达式
//FilterType.CUSTOM:使用自定义规则
-------------------------------------------------------
使用自定义规则需要实现TypeFilter接口:

public class MyTypeFilter implements TypeFilter {

	/*
	*通过match方法返回的Boolean值判断是否过滤
	 * metadataReader:读取到的当前正在扫描的类的信息
	 * metadataReaderFactory:可以获取到其它任何类的信息
	 * 
	 */
	@Override
	public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
		
		//获取当前类注解类的信息
		AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
		//获取当前正在扫描的类的类信息
		ClassMetadata classMetadata = metadataReader.getClassMetadata();
		//获取当前类资源(类的路径)
		Resource resource = metadataReader.getResource();
		
		String className = classMetadata.getClassName();
		System.out.println("----->"+className);
		if (className.contains("er")) {
			return true;
		}
		return false;
	}
}

3. scope:在配置类中使用来控制组件的作用范围(单例多例)lazy:懒加载,针对单实例bean


	//默认是单实例的
	/**
	 * ConfigurableBeanFactory#SCOPE_PROTOTYPE    
	 * @see ConfigurableBeanFactory#SCOPE_SINGLETON  
	 * @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST  request
	 * @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION	 sesssion
	 * @return\
	 * @Scope:调整作用域
	 * prototype:多实例的:ioc容器启动并不会去调用方法创建对象放在容器中。
	 * 					每次获取的时候才会调用方法创建对象;
	 * singleton:单实例的(默认值):ioc容器启动会调用方法创建对象放到ioc容器中。
	 * 			以后每次获取就是直接从容器(map.get())中拿,
	 * request:同一次请求创建一个实例
	 * session:同一个session创建一个实例
	 * 
	 * 懒加载:
	 * 		单实例bean:默认在容器启动的时候创建对象;
	 * 		懒加载:容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化;
	 * 
	 */
//	@Scope("prototype")
	@Lazy
	@Bean("person")
	public Person person(){
		System.out.println("给容器中添加Person....");
		return new Person("张三", 25);
	}

4. Conditional: 按照一定的条件进行判断,满足条件给容器中注册bean

/**
	 * @Conditional({Condition}) : 按照一定的条件进行判断,满足条件给容器中注册bean
	 * 
	 * 如果系统是windows,给容器中注册("bill")
	 * 如果是linux系统,给容器中注册("linus")
	 */
	//@Conditional(WindowsCondition.class)
	@Bean("bill")
	public Person person01(){
		return new Person("Bill Gates",62);
	}
	
	@Conditional(LinuxCondition.class)
	@Bean("linus")
	public Person person02(){
		return new Person("linus", 48);
	}
-------------------------------------
也可以加在类上面
//类中组件统一设置。满足当前条件,这个类中配置的所有bean注册才能生效;
@Conditional({WindowsCondition.class})
@Configuration
public class MainConfig2 {

5. import:[快速给容器中导入一个组件],加在配置类上
三种导入用法:
1)、直接写类型,@Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名
2)、自定义ImportSelector:返回需要导入的组件的全类名数组;
3)、自定义ImportBeanDefinitionRegistrar:手动注册bean到容器中

@Conditional({WindowsCondition.class})
@Configuration
@Import({Color.class,Red.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
//@Import导入组件,id默认是组件的全类名
public class MainConfig2 {
						。。。
						。。。
			}
@Import[快速给容器中导入一个组件]
	 * 		1)、@Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名
	 * 		2)、ImportSelector:返回需要导入的组件的全类名数组;
	 * 		3)、ImportBeanDefinitionRegistrar:手动注册bean到容器中

------------------------------------------------
//自定义逻辑返回需要导入的组件
public class MyImportSelector implements ImportSelector {

	//返回值,就是到导入到容器中的组件全类名
	//AnnotationMetadata:当前标注@Import注解的类的所有注解信息
	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		// TODO Auto-generated method stub
		//importingClassMetadata
		//方法不要返回null值

		return new String[]{"com.atguigu.bean.Blue","com.atguigu.bean.Yellow"};
	}
}
------------------------------------------------
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
	/**
	 * AnnotationMetadata:当前类的注解信息
	 * BeanDefinitionRegistry:BeanDefinition注册类;
	 * 		把所有需要添加到容器中的bean;调用
	 * 		BeanDefinitionRegistry.registerBeanDefinition手工注册进来
	 */
	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		
		boolean definition = registry.containsBeanDefinition("com.atguigu.bean.Red");
		boolean definition2 = registry.containsBeanDefinition("com.atguigu.bean.Blue");
		if(definition && definition2){
			//指定Bean定义信息;(Bean的类型,Bean。。。)
			RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
			//注册一个Bean,指定bean名
			registry.registerBeanDefinition("rainBow", beanDefinition);
		}
	}
}

6. 总结 :组件注册的几种方式:
给容器中注册组件;

  1. 包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)[自己写的类]
  2. @Bean[导入的第三方包里面的组件]
  3. @Import[快速给容器中导入一个组件]
    1. @Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名
    2. ImportSelector:返回需要导入的组件的全类名数组;
    3. ImportBeanDefinitionRegistrar:手动注册bean到容器中
  4. 使用Spring提供的 FactoryBean(工厂Bean);
    1. 默认获取到的是工厂bean调用getObject创建的对象
    2. 要获取工厂Bean本身,我们需要给id前面加一个&&colorFactoryBean

生命周期

  • Bean的生命周期

    • bean的创建—初始化—销毁的过程
    • 容器管理的bean的声明周期;
    • 我们可以自定义初始化和销毁方法;容器bean进行到当前声明周期的时候来调用我们自定义的初始化和销毁方法
  • 构造(对象创建)

    • 单实例:在容器启动的时候创建对象,
    • 多实例:在每次获取的时候创建对象。
  • 初始化:

    • 对象创建完成,并赋值好,调用初始化方法。。。
  • 销毁:

    • 单实例:容器关闭的时候
    • 多实例:容器不会管理这个bean;容器不会调用销毁方法;

几种指定初始化和销毁的方法

  1. 指定初始化和销毁方法;
    通过@Bean指定initMethod和destory-method;
public class MainConfigOfLifeCycle {
	@Bean(initMethod="init",destroyMethod="detory")
	public Car car(){
		return new Car();
	}
	--------------------------------------------
	@Component
public class Car {
	public Car(){
		System.out.println("car constructor...");
	}
	public void init(){
		System.out.println("car ... init...");
	}
	public void detory(){
		System.out.println("car ... detory...");
	}
}
  1. 通过让bean实现InitialzingBean(定义初始化逻辑),
    DisposableBean(定义销毁逻辑)。
@Component
public class Cat implements InitializingBean,DisposableBean{
	public Cat(){
		System.out.println("cat constructor...");
	}
	@Override
	public void destroy() throws Exception {
		System.out.println("cat...destroy..");		
	}
	@Override
	public void afterPropertiesSet() throws Exception {
		System.out.println("cat...afterPropertiesSet..");
	}
}

  1. 可以使用JSR250;在bean 方法上加注解
    @PostConstruct:在bean创建完成并且属性赋值完成;来执行初始化方法
    @PreDestroy:在容器销毁bean之前通知我们进行清理工作
@Component 
public class Dog {

	public Dog(){
		System.out.println("dog contructor...");
	}
	//对象创建并赋值 之后调用
	@PostConstruct
	public void init(){
		System.out.println("Dog...@PostConstruct...");
	}
	//容器移除对象之前
	@PreDestroy
	public void detory(){
		System.out.println("Dog...@@PreDestroy...");
	}
  1. BeanPostProcessor(Interface):bean的后置处理器;自定义处理器
    *在bean初始化前后进行一些处理工作;
    postProcessBeforeInitialization(Object, String):在初始化之前调用该方法
    postProcessAfterInitialization(Object, String):在初始化之后调用该方法
/**
 * 后置处理器:初始化前后进行处理工作
 * @author lenovo
 *将后置处理器加入到容器中
 */
@Component
public class MyBeanPostProcessor implements BeanPostProcessor{

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanname) throws BeansException {
		// TODO Auto-generated method stub
		System.out.println("postProcessAfterInitialization..."+beanname+"=>"+bean );
		return bean;
	}

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanname) throws BeansException {
		// TODO Auto-generated method stub

		System.out.println("postProcessBeforeInitialization..."+beanname+"=>"+bean );
		return bean;
	}
}

属性赋值:

在bean属性上添加@Value注解为属性赋值
使用@Value赋值:

  1. 基本数值
@Value("张三")
	private String name;
  1. 可以写SpEL; #{}
	@Value("#{20-2}")
	private Integer age;
  1. 可以写${};取出配置文件【properties】中的值(在运行环境变量里面的值)
    需要在配置类上使用@PropertySource读取外部配置文件
		@Value("${person.nickName}")
    	private String nickName;
    	-------------------------------
 使用读取配置文件时,需要在配置类上使用@PropertySource读取外部配置文件中的k/v保存到运行的环境变量中;加载完外部的配置文件以后使用${}取出配置文件的值
@PropertySource(value={"classpath:/person.properties"})
@Configuration
public class MainConfigOfPropertyValues {
	@Bean
	public Person person(){
		return new Person();
	}

}

自动装配:

自动装配:

  • Spring利用依赖注入(DI),完成IOC容器中各个组件的依赖关系赋值
  1. @Autowired:自动注入;

    1. 默认优先按照类型容器中找对应的组件,applicationContext.getBean(BookDao.class);找到就赋值

    2. 如果找到多个相同类型的组件,在将属性名称作为组件的id去容器中查找
      applicationContext.getBean("bookDao")

    3. @Qualifier(“bookDao”):在属性上使用@Qualifier指定需要装配的组件的id,而不是使用属性名,与@Autowired结合使用

       			@Qualifier("bookDao")
       			@Autowired()
       			private BookDao bookDao;
      
    4. 自动装配默认一定要将属性赋值好,没有就会报错;
      可以使用@Autowired(required=false默认是true);能找到就装配,找不到就放弃

       @Autowired(required=false)
      
    5. @Primary:让Spring进行自动装配的时候,默认使用首选的bean;在组件@Bean那里使用此注解也可以继续使用@Qualifier指定需要装配的bean的名字

       @Primary
       @Bean("bookDao2")
       public BookDao bookDao(){
       	BookDao bookDao = new BookDao();
       	bookDao.setLable("2");
       	return bookDao;
       }
      
  2. Spring还支持使用@Resource(JSR250)和@Inject(JSR330)[java规范的注解]

    @Resource:
    可以和@Autowired一样实现自动装配功能;默认是按照组件名称进行装配的;
    没有能支持@Primary功能没有支持@Autowired(reqiured=false);

    @Resource(name="bookDao2")
    private BookDao bookDao;
    

    @Inject:
    需要导入javax.inject的包,和Autowired的功能一样。没有required=false的功能;

    	@Inject
    	private BookDao bookDao;
    

    @Autowired:Spring定义的; @Resource、@Inject都是java规范

    AutowiredAnnotationBeanPostProcessor:解析完成自动装配功能

  3. @Autowired:构造器,参数,方法,属性;都是从容器中获取参数组件的值

    [标注在方法位置]:@Bean+方法参数;参数从容器中获取;默认不写@Autowired效果是一样的;都能自动装配

     //@Autowired 
     //标注在方法,Spring容器创建当前对象,就会调用方法,完成赋值;
     //方法使用的参数,自定义类型的值从ioc容器中获取
     public void setCar(Car car) {
     	this.car = car;
     }
    

    [标在构造器上]:如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还是可以自动从容器中获取

     //默认加在ioc容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值等操作
     @Component
     public class Boss {
     private Car car;
     //构造器要用的组件,都是从容器中获取
     //@Autowired
     public Boss(@Autowired Car car){
     	this.car = car;
     	System.out.println("Boss...有参构造器");
     }
    
  4. 自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFactory,xxx);
    实现接口,调用接口中的方法,将spring底层的组件注入到自定义的bean中
    自定义组件实现xxxAware;在创建对象的时候,会调用接口规定的方法注入相关组件;Aware;
    把Spring底层一些组件注入到自定义的Bean中;
    xxxAware:功能使用xxxProcessor;
    ApplicationContextAware==》ApplicationContextAwareProcessor;

     Component
     public class Dog implements ApplicationContextAware {
     
     //@Autowired
     private ApplicationContext applicationContext;
     
     public Dog(){
     	System.out.println("dog constructor...");
     }
    

Profile:
pring为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能;
比如连接不同的数据库。

开发环境、测试环境、生产环境;
数据源:(/A)(/B)(/C);

@Profile:指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件

  1. 加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。默认是default环境
  2. 写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
  3. 没有标注环境标识的bean在,任何环境下都是加载的;
引入外部配置文件
@PropertySource("classpath:/dbconfig.properties")
@Configuration
public class MainConfigOfProfile implements EmbeddedValueResolverAware{
	@Value("${db.user}")
	private String user;
	
	private StringValueResolver valueResolver;
	
	private String  driverClass;
	
	@Profile("test")(标识为test的组件)
	@Bean("testDataSource")
	public DataSource dataSourceTest(@Value("${db.password}")String pwd) throws Exception{
		ComboPooledDataSource dataSource = new ComboPooledDataSource();
		dataSource.setUser(user);
		dataSource.setPassword(pwd);
		dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
		dataSource.setDriverClass(driverClass);
		return dataSource;
	}

	@Profile("dev")(标识为dev的组件)
	@Bean("devDataSource")
	public DataSource dataSourceDev(@Value("${db.password}")String pwd) throws Exception{
		ComboPooledDataSource dataSource = new ComboPooledDataSource();
		dataSource.setUser(user);
		dataSource.setPassword(pwd);
		dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/ssm_crud");
		dataSource.setDriverClass(driverClass);
		return dataSource;
	}	
	@Profile("prod")(标识为prod的组件)
	@Bean("prodDataSource")
	public DataSource dataSourceProd(@Value("${db.password}")String pwd) throws Exception{
		ComboPooledDataSource dataSource = new ComboPooledDataSource();
		dataSource.setUser(user);
		dataSource.setPassword(pwd);
		dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/scw_0515");
		
		dataSource.setDriverClass(driverClass);
		return dataSource;
	}
	@Override
	public void setEmbeddedValueResolver(StringValueResolver resolver) {
		// TODO Auto-generated method stub
		this.valueResolver = resolver;
		driverClass = valueResolver.resolveStringValue("${db.driverClass}");
	}
}

  1. 使用命令行动态参数: 在虚拟机参数位置加载 -Dspring.profiles.active=test
  2. 代码的方式激活某种环境;
		/1、创建一个applicationContext
		//2、设置需要激活的环境
		applicationContext.getEnvironment().setActiveProfiles("dev");
		//3、注册主配置类
		applicationContext.register(MainConfigOfProfile.class);
		//4、启动刷新容器
		applicationContext.refresh();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值