Spring注解 bean基础

一、Spring注解 bean扫描与注入


		org.springframework
		spring-context
		4.3.12.RELEASE
	

1. 组件注册@Configuration和@Bean的注入

1)使用xml方式

我们一起注入一个bean使用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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="person" class="com.cuzz.bean.Person">
        <property name="name" value="cuzz">property>
        <property name="age" value="18">property>
    bean>
    
beans>

我可以使用ClassPathXmlApplicationContext来获取

/**
 * 
 * @Date: 2018/9/23 10:48
 * @Description:
 */
public class MainTest {
    public static void main(String[] args) {
        ApplicationContext  applicationContext = new ClassPathXmlApplicationContext("bean.xml");
        // 用id获取
        Person bean = (Person) applicationContext.getBean("person");
        System.out.println(bean);
    }
}

输出Person(name=cuzz, age=18)

2 ) 注解

编写一个配置类

/**
 * 
 * @Date: 2018/9/23 10:55
 * @Description: 配置类
 */
//配置类==配置文件
@Configuration // 告诉Spring这是一个配置类
public class MainConfig {
    // 给容器中注册一个Bean,类型为返回值类型,id默认用方法名
    // 也可以指定id
    @Bean(value = "person01")
    public Person person() {
        return new Person("vhsj", 16);
    }
}

可以通过AnnotationConfigApplicationContext来获取,并且获取id

/**
 * 
 * @Date: 2018/9/23 10:59
 * @Description:
 */
public class MainTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
        Person person = (Person) context.getBean(Person.class);
        System.out.println(person);

        String[] names = context.getBeanNamesForType(Person.class);
        for (String name: names) {
            System.out.println(name);
        }
    }
}

输出

Person(name=vhsj, age=16)
person01

由于给bean添加一个一个value,可以改变默认id

2. 组件注册@ComponentScan

1) 使用xml

只要标注了注解就能扫描到如:@Controller @Service @Repository @component

<context:component-scan base-package="com.cuzz">context:component-scan>

2) 注解

在配置类中添加

/**
 * 
 * @Date: 2018/9/23 10:55
 * @Description: 配置类
 */
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.cuzz") // 指定包
public class MainConfig {
    
}

添加controller、service等

测试

/**
 * 
 * @Date: 2018/9/23 13:03
 * @Description:
 */
public class IOCTest {

    @Test
    public void test01() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        // 获取所有bean定义的名字
        String[] beanNames = applicationContext.getBeanDefinitionNames();
        for (String name : beanNames) {
            System.out.println(name);
        }
    }
}

输出结果

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
mainConfig
bookController
bookDao
bookService
person01

可以看出添加@Controller @Service @Repository @component注解的都可以扫描到

还可以指定添加某些类,和排除某些类,进入ComponentScan注解中有下面两个方法

ComponentScan.Filter[] includeFilters() default {};
ComponentScan.Filter[] excludeFilters() default {};

includeFilters = Filter[] :指定扫描的时候只需要包含哪些组件
excludeFilters = Filter[] :指定扫描的时候按照什么规则排除那些组件

配置类,排除Controller

@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.cuzz", excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
})
public class MainConfig {

}

运行测试方法,可以得出没有Controller类的

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
mainConfig
bookDao
bookService
person01

3 ) 自定义TypeFilter指定过滤规则

第一和第二比较常用

@ComponentScans(
		value = {
				@ComponentScan(value="com.atguigu",includeFilters = {
/*						@Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
						@Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class}),*/
						@Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})
				},useDefaultFilters = false)	
		}
		)
//@ComponentScan  value:指定要扫描的包
//excludeFilters = Filter[] :指定扫描的时候按照什么规则排除那些组件
//includeFilters = Filter[] :指定扫描的时候只需要包含哪些组件

FilterType.ANNOTATION:按照注解
FilterType.ASSIGNABLE_TYPE:按照给定的类型;
FilterType.ASPECTJ:使用ASPECTJ表达式
FilterType.REGEX:使用正则指定
FilterType.CUSTOM:使用自定义规则

新建一个MyTypeFilte类实现TypeFilter接口

/**
 * 
 * @Date: 2018/9/23 15:03
 * @Description:
 */
public class MyTypeFilter implements TypeFilter{
    /**
     * 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);
        // 这些类名中包含er就返回true
        if(className.contains("er")){
            return true;
        }
        return false;
    }
}

使用自定义注解记得需要关闭默认过滤器useDefaultFilters = false

/**
 * 
 * @Date: 2018/9/23 10:55
 * @Description: 配置类
 */
@Configuration 
@ComponentScan(value = "com.cuzz",
        includeFilters = @ComponentScan.Filter(type = FilterType.CUSTOM,
                classes = MyTypeFilter.class),
        useDefaultFilters = false)
public class MainConfig {
    // 给容器中注册一个Bean,类型为返回值类型,id默认用方法名
    // 也可以指定id
    @Bean(value = "person01")
    public Person person() {
        return new Person("vhsj", 16);
    }
}

测试

--->com.cuzz.AppTest
--->com.cuzz.bean.MainTest
--->com.cuzz.config.IOCTest
--->com.cuzz.config.MainTest
--->com.cuzz.App
--->com.cuzz.bean.Person
--->com.cuzz.config.MyTypeFilter
--->com.cuzz.controller.BookController
--->com.cuzz.dao.BookDao
--->com.cuzz.sevice.BookService
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
mainConfig     // 不是扫描的 
person		   // 这个是在bean中
myTypeFilter   // 有er
bookController // 有er
bookService    // 有er
person01       // 这个是在bean中

3. 组件注册@Scope设置作用域

1)Spring的bean默认是单例的

@Test
    public void test02() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
        // 获取所有bean定义的名字
        String[] beanNames = applicationContext.getBeanDefinitionNames();
        for (String name : beanNames) {
            System.out.println(name);
        }
        Object bean = applicationContext.getBean("person");
        Object bean2 = applicationContext.getBean("person");
        System.out.println(bean == bean2);   // 输出true
    }

2)Scope的四个范围

ConfigurableBeanFactory#SCOPE_PROTOTYPE   // 多实例 每次获取时创建对象,不会放在ioc容器中
ConfigurableBeanFactory#SCOPE_SINGLETON   // 单实例 ioc容器启动是创建对象,以后从容器中获取
WebApplicationContext#SCOPE_REQUEST       // web同一次请求创建一个实例
WebApplicationContext#SCOPE_SESSION       // web同一个session创建一个实例

如果我们把Scope修改

/**
 * 
 * @Date: 2018/9/23 15:40
 * @Description:
 */
@Configuration
public class MainConfig2 {
	/**
	 * 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(value = "prototype")
    @Bean
    public Person person() {
        return new Person("vhuj", 25);
    }
}

则测试输出false

4. 组件注册@Lazy-bean懒加载

1)懒加载

懒加载的是针对单实例Bean,默认是在容器启动的时创建的,我们可以设置懒加载容器启动是不创建对象,在第一次使用(获取)Bean创建对象,并初始化

2 ) 测试

先给添加一个@Lazy注解

@Configuration
public class MainConfig2 {

    @Lazy
    @Bean
    public Person person() {
        System.out.println("给容器中添加Person...");
        return new Person("vhuj", 25);
    }
}

编写一个测试方法

@Test
    public void test03() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);

        System.out.println("ioc容器创建完成...");
        Object bean = applicationContext.getBean("person");
    }

输出

ioc容器创建完成...
给容器中添加Person...

添加一个@Lazy是在第一次获取时,创建对象,以后获取就不需要创建了,直接从容器中获取,因为它是单实例

5. 组件注册@Conditional按条件注册

按照一定条件进行判断,满足条件给容器中注册Bean

1 ) 编写自己的Condition类

如果系统是windows,给容器中注入"bill"

如果系统是linux,给容器中注入"linus"

编写WindowCondition类并重写matches方法

/**
   * 
   * @Date: 2018/9/23 20:30
   * @Description: 判断是否是windows
   */
  public class WindowCondition implements Condition{
  
      /**
       * @param context 判断条件
       * @param metadata 注释信息
       * @return boolean
       */
      @Override
      public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
          Environment environment = context.getEnvironment();
          String property = environment.getProperty("os.name");
          if (property.contains("Windows")) {
              return true;
          }
          return false;
      }
  }

context有以下方法

// 能获取ioc使用的beanfactory
  ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
  // 能获取到类加载器
  ClassLoader classLoader = context.getClassLoader();
  // 获取到环境变量
  Environment environment = context.getEnvironment();
  // 获取到Bean定义的注册类
  BeanDefinitionRegistry registry = context.getRegistry();

2)配置类

添加Bean添加Condition条件

@Configuration
public class MainConfig2 {

    @Conditional({WindowCondition.class})
    @Bean("bill")
    public Person person01() {
        return new Person("Bill Gates", 60);
    }
    @Conditional({LinuxCondition.class})
    @Bean("linux")
    public Person person02() {
        return new Person("linus", 45);
    }

}

3 ) 测试

@Test
    public void test04() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);

        // 获取环境变量
        ConfigurableEnvironment environment = applicationContext.getEnvironment();
        String property = environment.getProperty("os.name");
        System.out.println(property);

        // 获取所有bean定义的名字
        String[] beanNames = applicationContext.getBeanDefinitionNames();
        for (String name : beanNames) {
            System.out.println(name);
        }

        // key 是id
        Map<String, Person> map = applicationContext.getBeansOfType(Person.class);
        System.out.println(map);
    }

发现只有“bill”这个Bean被注入

Windows 7
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
mainConfig2
bill
{bill=Person(name=Bill Gates, age=60)}

6. 组件注册@Improt给容器中快速导入一个组件

1 ) @Import导入

@Import可以导入第三方包,或则自己写的类,比较方便,Id默认为全类名

比如我们新建一个类

/**
 * 
 * @Date: 2018/9/23 21:08
 * @Description:
 */
public class Color {
}

我们只需要在配置类添加一个@Import把这个类导入

@Import({Color.class})
@Configuration
public class MainConfig2 {}

2 ) ImportSelector接口导入的选择器

返回导入组件需要的全类名的数组

public interface ImportSelector {

	/**
	 * Select and return the names of which class(es) should be imported based on
	 * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
	 */
	String[] selectImports(AnnotationMetadata importingClassMetadata);

}

编写一个MyImportSelector类实现ImportSelector接口

/**
 * 
 * @Date: 2018/9/23 21:15
 * @Description:
 */
public class MyImportSelector implements ImportSelector{

    // 返回值就导入容器组件的全类名
    // AnnotationMetadata:当前类标注的@Import注解类的所有注解信息
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[] {"com.cuzz.bean.Car"};
    }
}

在配置类中,通过@Import导入

/**
 * 
 * @Date: 2018/9/23 15:40
 * @Description: 配置类
 */
@Import({Color.class, MyImportSelector.class})
@Configuration
public class MainConfig2 {}

测试结果,com.cuzz.bean.Car注入了

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
mainConfig2
com.cuzz.bean.Color
com.cuzz.bean.Car

3 ) ImportBeanDefinitionRegistrar接口选择器

public interface ImportBeanDefinitionRegistrar {

	/**
	 * Register bean definitions as necessary based on the given annotation metadata of
	 * the importing {@code @Configuration} class.
	 * 

Note that {@link BeanDefinitionRegistryPostProcessor} types may not be * registered here, due to lifecycle constraints related to {@code @Configuration} * class processing. * @param importingClassMetadata annotation metadata of the importing class * @param registry current bean definition registry */ public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry); }

编写一个ImportBeanDefinitionRegistrar实现类

/**
 * 
 * @Date: 2018/9/23 21:29
 * @Description:
 */
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    /**
     * @param importingClassMetadata 当前类的注解信息
     * @param registry 注册类
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 查询容器
        boolean b = registry.containsBeanDefinition("com.cuzz.bean.Car");
        // 如果有car, 注册一个汽油类
        if (b == true) {
            // 需要添加一个bean的定义信息
            RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Petrol.class);
            // 注册一个bean, 指定bean名
            registry.registerBeanDefinition("petrol", rootBeanDefinition);
        }

    }
}

配置类

/**
 * 
 * @Date: 2018/9/23 15:40
 * @Description: 配置类
 */
@Import({Color.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
@Configuration
public class MainConfig2 {}

测试结果,出现了petrol

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
mainConfig2
com.cuzz.bean.Color
com.cuzz.bean.Car 
petrol

4)总结

/**
 * 给容器中注册组件;
 * 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
 */

7. 组件注册使用FactoryBean注册组件

编写一个ColorFactoryBean类

/**
 * 
 * @Date: 2018/9/23 21:55
 * @Description: Spring定义的工厂Bean
 */
public class ColorFactoryBean implements FactoryBean<Color> {
    // 返回一个Color对象
    @Override
    public Color getObject() throws Exception {
        return new Color();
    }

    @Override
    public Class<?> getObjectType() {
        return Color.class;
    }
    // 是否为单例
    @Override
    public boolean isSingleton() {
        return true;
    }
}

注入到容器中

@Bean
    public ColorFactoryBean colorFactoryBean() {
        return new ColorFactoryBean();
    }

测试

@Test
    public void test05() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);

        Object bean = applicationContext.getBean("colorFactoryBean");
        // 工厂bean调用的是getClass()方法
        System.out.println("colorFactoryBean的类型是: " + bean.getClass());
    }

输出,发现此时的bean调用的方法是getObjectType方法

colorFactoryBean的类型是: class com.cuzz.bean.Color

如果需要获取BeanFactory本身,可以在id前面加一个“&”标识

@Test
    public void test05() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);

        Object bean = applicationContext.getBean("colorFactoryBean");
        // 工厂bean调用的是getClass()方法
        System.out.println("colorFactoryBean的类型是: " + bean.getClass());
        Object bean2 = applicationContext.getBean("&colorFactoryBean");
        // 工厂bean调用的是getClass()方法
        System.out.println("colorFactoryBean的类型是: " + bean2.getClass());
    }

此时输出

colorFactoryBean的类型是: class com.cuzz.bean.Color
colorFactoryBean的类型是: class com.cuzz.bean.ColorFactoryBean

二、Spring注解 bean生命周期

1. 声明周期@Bean指定初始化和销毁方法

1 ) Bean的生命周期

Bean的创建、初始化和销毁是由容器帮我们管理的

我们可以自定义初始化和销毁方法,容器在进行到当前生命周期的时候来调用我买自定义的初始化和销毁方法

构造(对象创建)

单实例: 在容器启动的时候创建

多实例: 在每次获取的时候创建对象

2 ) 指定初始化方法

**初始化:**对象创建完成后,并赋值化,调用初始化方法

**销毁:**单实例是在容器关闭的时候销毁,多实例容器不会管理这个Bean,容器不会调用销毁方法

编写一个Car类

/**
 * 
 * @Date: 2018/9/23 21:20
 * @Description:
 */
public class Car {

    public Car () {
        System.out.println("car constructor...");
    }

    public void init() {
        System.out.println("car...init...");
    }

    public void destroy() {
        System.out.println("car...destroy...");
    }
}

在xml中我们可以指定init-methoddestroy-method方法,如

<bean id="car" class="com.cuzz.bean.Car" init-method="init" destroy-method="destroy">bean>

使用注解我们可以

/**
 * 
 * @Date: 2018/9/24 12:49
 * @Description: 配置类
 */
@Configuration
public class MainConfigOfLifecycle {

    @Bean(initMethod = "init", destroyMethod = "destroy")
    public Car car() {
        return new Car();
    }

}

测试

/**
 * 
 * @Date: 2018/9/24 13:00
 * @Description:
 */
public class IOCTestLifeCycle {

    @Test
    public void test01() {
        // 创建ioc容器
        AnnotationConfigApplicationContext applicationContext =
                new AnnotationConfigApplicationContext(MainConfigOfLifecycle.class);
        System.out.println("容器创建完成...");
        // 关闭容器
        System.out.println("--->开始关闭容器");
        applicationContext.close();
        System.out.println("--->已经关闭容器");
    }
}

可以看出先创建car,再调用init方法,在容器关闭时销毁实例

car constructor...
car...init...
容器创建完成...
--->开始关闭容器
car...destroy...
--->已经关闭容器

在配置数据源的时候,有很多属性赋值,销毁的时候要把连接给断开

2. 生命周期InitializingBean和DisposableBean

1 ) InitializingBean

可以通过Bean实现InitializingBean来定义初始化逻辑,是设置好所有属性会调用afterPropertiesSet()方法

public interface InitializingBean {

	/**
	 * Invoked by a BeanFactory after it has set all bean properties supplied
	 * (and satisfied BeanFactoryAware and ApplicationContextAware).
	 * 

This method allows the bean instance to perform initialization only * possible when all bean properties have been set and to throw an * exception in the event of misconfiguration. * @throws Exception in the event of misconfiguration (such * as failure to set an essential property) or if initialization fails. */ void afterPropertiesSet() throws Exception; }

2)DisposableBean

可以通过Bean实现DisposableBean来定义销毁逻辑,会调用destroy()方法

public interface DisposableBean {

	/**
	 * Invoked by a BeanFactory on destruction of a singleton.
	 * @throws Exception in case of shutdown errors.
	 * Exceptions will get logged but not rethrown to allow
	 * other beans to release their resources too.
	 */
	void destroy() throws Exception;

}

3)例子

编写一个Cat类

/**
 * 
 * @Date: 2018/9/24 13:36
 * @Description:
 */
public class Cat implements InitializingBean, DisposableBean{

    public Cat() {
        System.out.println("cat constructor...");
    }


    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("cat...init...");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("cat...destroy...");
    }

}

测试

cat constructor...
cat...init...
容器创建完成...
--->开始关闭容器
cat...destroy...
--->已经关闭容器

3. 生命周期@PostContruct和@PreDestroy注解

@PostContruct在Bean创建完成并且属性赋值完成,来执行初始化

@PreDestroy在容器销毁Bean之前通知我们进行清理工作

编写一个Dog类,并把他注入到配置类中

/**
 * 
 * @Date: 2018/9/24 14:03
 * @Description:
 */
public class Dog {

    public Dog() {
        System.out.println("dog constructor...");
    }

    @PostConstruct
    public void postConstruct() {
        System.out.println("post construct...");
    }

    @PreDestroy
    public void preDestroy() {
        System.out.println("pre destroy...");
    }
}

测试结果

dog constructor...
post construct...
容器创建完成...
--->开始关闭容器
pre destroy...
--->已经关闭容器

4. 生命周期BeanPostProscessor后置处理器

在Bean初始化前后做一些处理

public interface BeanPostProcessor {
	// 在初始化之前工作
	Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
	// 在初始化之后工作
	Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}

编写一个MyBeanPostProcessor实现BeanPostProcessor接口

/**
 * 
 * @Date: 2018/9/24 14:21
 * @Description: 后置处理器,初始化前后进行处理工作
 */
public class MyBeanPostProcessor implements BeanPostProcessor{
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("--->postProcessBeforeInitialization..." + beanName +"==>" + bean);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("--->postProcessAfterInitialization..." + beanName +"==>" + bean);
        return bean;
    }
}

测试

--->postProcessBeforeInitialization...org.springframework.context.event.internalEventListenerProcessor==>org.springframework.context.event.EventListenerMethodProcessor@1dc67c2
--->postProcessAfterInitialization...org.springframework.context.event.internalEventListenerProcessor==>org.springframework.context.event.EventListenerMethodProcessor@1dc67c2
--->postProcessBeforeInitialization...org.springframework.context.event.internalEventListenerFactory==>org.springframework.context.event.DefaultEventListenerFactory@2bd765
--->postProcessAfterInitialization...org.springframework.context.event.internalEventListenerFactory==>org.springframework.context.event.DefaultEventListenerFactory@2bd765
cat constructor...
--->postProcessBeforeInitialization...cat==>com.cuzz.bean.Cat@1d3b207
cat...init...
--->postProcessAfterInitialization...cat==>com.cuzz.bean.Cat@1d3b207
容器创建完成...
--->开始关闭容器
cat...destroy...
--->已经关闭容器

在实例创建之前后创建之后会被执行

5. 生命周期BeanPostProcessor原理

通过debug到populateBean,先给属性赋值在执行initializeBean方法

try {
    populateBean(beanName, mbd, instanceWrapper);
    if (exposedObject != null) {
        exposedObject = initializeBean(beanName, exposedObject, mbd);
    }
}

initializeBean方法时,

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {


    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        // 执行before方法
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }
	...
    try {
        // 执行初始化
        invokeInitMethods(beanName, wrappedBean, mbd);
    }

    if (mbd == null || !mbd.isSynthetic()) {
        // 执行after方法
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }
    return wrappedBean;
}

Spring底层对BeanPostProcessor的使用

Bean赋值、注入其他组件、@Autowired、生命周期注解功能、@Async等等都使用到了BeanPostProcessor这个接口的实现类,很重要

6 ApplicationContextAware 能获取上下文信息

ApplicationContextAwareProcesser原理:

在创建对象(如dog)之后,检查是否实现了ApplicationContextAware,如果实现了,就注入applicationContext。这就是容器的强大,对你写的那些bean,如果都没实现ApplicationContextAware接口,容器也没必要给你干这个事。

package com.atguigu.bean;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class Dog implements ApplicationContextAware {
	
	//@Autowired
	private ApplicationContext applicationContext;

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		// TODO Auto-generated method stub
		this.applicationContext = applicationContext;
	}
}

7 总结

/**
 * bean的生命周期:
 * 		bean创建---初始化----销毁的过程
 * 容器管理bean的生命周期;
 * 我们可以自定义初始化和销毁方法;容器在bean进行到当前生命周期的时候来调用我们自定义的初始化和销毁方法
 * 
 * 构造(对象创建)
 * 		单实例:在容器启动的时候创建对象
 * 		多实例:在每次获取的时候创建对象\
 * 
 * BeanPostProcessor.postProcessBeforeInitialization
 * 初始化:
 * 		对象创建完成,并赋值好,调用初始化方法。。。
 * BeanPostProcessor.postProcessAfterInitialization
 * 销毁:
 * 		单实例:容器关闭的时候
 * 		多实例:容器不会管理这个bean;容器不会调用销毁方法;
 * 
 * 
 * 遍历得到容器中所有的BeanPostProcessor;挨个执行beforeInitialization,
 * 一但返回null,跳出for循环,不会执行后面的BeanPostProcessor.postProcessorsBeforeInitialization
 * 
 * BeanPostProcessor原理
 * populateBean(beanName, mbd, instanceWrapper);给bean进行属性赋值
 * initializeBean
 * {
 * applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
 * invokeInitMethods(beanName, wrappedBean, mbd);执行自定义初始化
 * applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
 *}
 * 
 * 
 * 
 * 1)、指定初始化和销毁方法;
 * 		通过@Bean指定init-method和destroy-method;
 * 2)、通过让Bean实现InitializingBean(定义初始化逻辑),
 * 				DisposableBean(定义销毁逻辑);
 * 3)、可以使用JSR250;
 * 		@PostConstruct:在bean创建完成并且属性赋值完成;来执行初始化方法
 * 		@PreDestroy:在容器销毁bean之前通知我们进行清理工作
 * 4)、BeanPostProcessor【interface】:bean的后置处理器;
 * 		在bean初始化前后进行一些处理工作;
 * 		postProcessBeforeInitialization:在初始化之前工作
 * 		postProcessAfterInitialization:在初始化之后工作
 * 
 * Spring底层对 BeanPostProcessor 的使用;
 * 		bean赋值,注入其他组件,@Autowired,生命周期注解功能,@Async,xxx BeanPostProcessor;
 * 
 * todo: 可以看看BeanPostProcessor的实现类
 *
 */

看看执行顺序

dog constructor...
postProcessBeforeInitialization...dog=>com.atguigu.bean.Dog@5d740a0f
Dog...ApplicationContextAware.@PostConstruct...
postProcessAfterInitialization...dog=>com.atguigu.bean.Dog@5d740a0f
Dog...ApplicationContextAware.@PreDestroy...

cat constructor...
postProcessBeforeInitialization...cat=>com.atguigu.bean.Cat@7d68ef40
cat...InitializingBean.afterPropertiesSet...
postProcessAfterInitialization...cat=>com.atguigu.bean.Cat@7d68ef40
cat...DisposableBean.destroy...

三、Spring注解 属性赋值 自动注入

1. 属性赋值@value赋值

使用@Value赋值

  • 基本数值
  • 可以写SPEL表达式 #{}
  • 可以${}获取配置文件信息(在运行的环境变量中的值)

使用xml时候导入配置文件是

<context:property-placeholder location="classpath:person.properties"/>

使用注解可以在配置类添加一个@PropertySource注解把配置文件中k/v保存到运行的环境中

使用${key}来获取

/**
 * 
 * @Date: 2018/9/24 18:43
 * @Description:
 */
@PropertySource(value = {"classpath:/person.properties"})
@Configuration
public class MainConfigOfPropertyValue {

    @Bean
    public Person person() {
        return new Person();
    }
}
@Data
public class Person {

    @Value("vhuj")
    private String name;

    @Value("#{20-2}")
    private Integer age;

    @Value("${person.nickName}")
    private String nickName;
}

测试

@Test
    public void test01() {
        printBean(applicationContext);
        System.out.println("---------------------------");

        Person person = (Person) applicationContext.getBean("person");
        System.out.println(person);

        System.out.println("---------------------------");

    }

输出

---------------------------
Person(name=vhuj, age=18, nickName=三三)
---------------------------

此外,也可以代码获得值

ConfigurableEnvironment environment = applicationContext.getEnvironment();
		String property = environment.getProperty("person.nickName");
		System.out.println(property);
		applicationContext.close();

2. 自动装配@Autowired@Qualifier@Primary

自动转配:

Spring利用依赖注入(DI),完成对IOC容器中各个组件的依赖关系赋值

@Autowired自动注入:

a. 默认优先按照类型去容器中寻找对应的组件,如果找到去赋值

b. 如果找到到相同类型的组件,再将属性名(`BookDao bookdao`)作为组件的id去容器中查找

c. 接下来还可以使用`@Qualifier("bookdao")`明确指定需要装配的id

d. 默认是必须的,我们可以指定    `@Autowired(required=false)`,指定非必须

@Primary让Spring自动装配时首先装配

/**
 * 自动装配;
 * 		Spring利用依赖注入(DI),完成对IOC容器中中各个组件的依赖关系赋值;
 * 
 * 1)、@Autowired:自动注入:
 * 		1)、默认优先按照类型去容器中找对应的组件:applicationContext.getBean(BookDao.class);找到就赋值
 * 		2)、如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找
 * 							applicationContext.getBean("bookDao")
 * 		3)、@Qualifier("bookDao"):使用@Qualifier指定需要装配的组件的id,而不是使用属性名
 * 		4)、自动装配默认一定要将属性赋值好,没有就会报错;
 * 			可以使用@Autowired(required=false);
 * 		5)、@Primary:让Spring进行自动装配的时候,默认使用首选的bean;
 * 				也可以继续使用@Qualifier指定需要装配的bean的名字
 * 		BookService{
 * 			@Autowired
 * 			BookDao  bookDao;
 * 		}
 * 
 * 2)、Spring还支持使用@Resource(JSR250)和@Inject(JSR330)[java规范的注解]
 * 		@Resource:
 * 			可以和@Autowired一样实现自动装配功能;默认是按照组件名称进行装配的;
 * 			没有能支持@Primary功能没有支持@Autowired(reqiured=false);
 * 		@Inject:
 * 			需要导入javax.inject的包,和Autowired的功能一样。没有required=false的功能;
 *  @Autowired:Spring定义的; @Resource、@Inject都是java规范
 * 	
 * AutowiredAnnotationBeanPostProcessor:解析完成自动装配功能;		
 * 
 * 3)、 @Autowired:构造器,参数,方法,属性;都是从容器中获取参数组件的值
 * 		1)、[标注在方法位置]:@Bean+方法参数;参数从容器中获取;默认不写@Autowired效果是一样的;都能自动装配
 * 		2)、[标在构造器上]:如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还是可以自动从容器中获取
 * 		3)、放在参数位置:
 * 
 * 4)、自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFactory,xxx);
 * 		自定义组件实现xxxAware;在创建对象的时候,会调用接口规定的方法注入相关组件;Aware;
 * 		把Spring底层一些组件注入到自定义的Bean中;
 * 		xxxAware:功能使用xxxProcessor;
 * 			ApplicationContextAware==》ApplicationContextAwareProcessor;
 * 	
 * 		
 * @author lfy
 *
 */

3. 自动装配@Resource和@Inject

Spring还支持使用@Resource (JSR250) 和@Inject (JSR330) 注解,这两个是java规范

@Resource和@Autowired一样实现自动装配功能,默认是按组件名称进行装配的

没有支持@Primary和@Autowird(required=false)的功能

4. 自动装配其他地方的自动装配

@Autowired:构造器、参数、方法属性等

标注到方法位子上@Bean+方法参数,参数从容器中获取

/**
 * 
 * @Date: 2018/9/24 20:57
 * @Description:
 */
public class Boss {
    // 属性
    @Autowired
    private Car car;
	
    // 构造器 如果构造器只有一个有参构造器可以省略
    @Autowired
    public Boss(@Autowired ar car) {
    }

    public Car getCar() {
        return car;
    }
	
    // set方法
    @Autowired		 // 参数
    public void setCar(@Autowired Car car) {
        this.car = car;
    }
}

5. 自动装配Aware注入Spring底层注解

Spring注解 bean基础_第1张图片

自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFactory 等等),自定义组件实现xxxAware,在创建对象的时候会调用接口规定的方法注入相关的组件

/**
 * Marker superinterface indicating that a bean is eligible to be
 * notified by the Spring container of a particular framework object
 * through a callback-style method. Actual method signature is
 * determined by individual subinterfaces, but should typically
 * consist of just one void-returning method that accepts a single
 * argument.
 */
public interface Aware {

}

我们实现几个常见的Aware接口

package com.atguigu.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.stereotype.Component;
import org.springframework.util.StringValueResolver;

@Component
public class Red implements ApplicationContextAware,BeanNameAware,EmbeddedValueResolverAware {
	
	private ApplicationContext applicationContext;

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		// TODO Auto-generated method stub
		System.out.println("传入的ioc:"+applicationContext);
		this.applicationContext = applicationContext;
	}

	@Override
	public void setBeanName(String name) {
		// TODO Auto-generated method stub
		System.out.println("当前bean的名字:"+name);
	}

	@Override
	public void setEmbeddedValueResolver(StringValueResolver resolver) {
		// TODO Auto-generated method stub
		String resolveStringValue = resolver.resolveStringValue("你好 ${os.name} 我是 #{20*18}");
		System.out.println("解析的字符串:"+resolveStringValue);
	}
}

注入到配置中测试

/**
 * 
 * @Date: 2018/9/25 10:28
 * @Description:
 */
public class IOCTestAware {

    @Test
    public void test01() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAware.class);

    }
}

测试结果

当前Bean的名字: red
当前的BeanFactory: org.springframework.beans.factory.support.DefaultListableBeanFactory@159c4b8: defining beans [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,mainConfigOfAware,red]; root of factory hierarchy
传入的ioc: org.springframework.context.annotation.AnnotationConfigApplicationContext@1e89d68: startup date [Tue Sep 25 10:29:17 CST 2018]; root of context hierarchy

把Spring自定义组件注入到容器中

原理:

public interface ApplicationContextAware extends Aware {}

xxxAware都是通过xxxProcessor来处理的

比如:ApplicationContextAware 对应ApplicationContextAwareProcessor

6. 自动装配@Profile环境搭建

Profile是Spring为我们提供可以根据当前环境,动态的激活和切换一系组件的功能

a. 使用命令动态参数激活:虚拟机参数位子加载 `-Dspring.profiles.active=test

b. 使用代码激活环境

public class IOCTest_Profile {
	
	//1、使用命令行动态参数: 在虚拟机参数位置加载 -Dspring.profiles.active=test
	//2、代码的方式激活某种环境;
	@Test
	public void test01(){
		AnnotationConfigApplicationContext applicationContext = 
				new AnnotationConfigApplicationContext();
		//1、创建一个applicationContext
		//2、设置需要激活的环境
		applicationContext.getEnvironment().setActiveProfiles("dev");
		//3、注册主配置类
		applicationContext.register(MainConfigOfProfile.class);
		//4、启动刷新容器
		applicationContext.refresh();
		
		
		String[] namesForType = applicationContext.getBeanNamesForType(DataSource.class);
		for (String string : namesForType) {
			System.out.println(string);
		}
		
		Yellow bean = applicationContext.getBean(Yellow.class);
		System.out.println(bean);
		applicationContext.close();
	}

}
/**
 * Profile:
 * 		Spring为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能;
 * 
 * 开发环境、测试环境、生产环境;
 * 数据源:(/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;
	
	
	@Bean
	public Yellow yellow(){
		return new Yellow();
	}
	
	@Profile("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")
	@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")
	@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}");
	}

}

你可能感兴趣的:(spring,java,后端)