7.4BeanPostProcessor—bean的后置处理器
在bean初始化前后进行一些处理工作;
postProcessBeforeInitialization:在初始化之前工作
postProcessAfterInitialization:在初始化之后工作
创建MyBeanPostProcessor,实现BeanPostProcessor接口
/** * 后置处理器:初始化前后进行处理工作 * 将后置处理器加入到容器中 */ @Component 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; } }
运行结果:
BeanPostProcessor原理
populateBean(beanName, mbd, instanceWrapper);给bean进行属性赋值 initializeBean { applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); invokeInitMethods(beanName, wrappedBean, mbd);执行自定义初始化 applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); }
Spring底层对 BeanPostProcessor 的使用;
bean赋值,注入其他组件,@Autowired,生命周期注解功能,@Async,xxx BeanPostProcessor;
8.@Value
作用:为属性赋值
基本数值
可以写SpEL:#{}
可以写${};取出配置文件[properties]中的值(在运行环境变量中的值)
Person类
public class Person { //使用@Value赋值 //1.基本数值 //2.可以写SpEL:#{} //3.可以写${};取出配置文件[properties]中的值(在运行环境变量中的值) @Value("zhangsan") private String name; @Value("#{20+1}") private Integer age; @Value("${person.nickName}") private String nickName; public String getNickName() { return nickName; } public void setNickName(String nickName) { this.nickName = nickName; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Person(String name, Integer age) { this.name = name; this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", nickName='" + nickName + '\'' + '}'; } public Person(){ } }
MainConfig配置类:
//使用@PropertySource读取外部配置文件中的k/v保存到运行的环境变量中;加载完外部的配置文件以后使用${}取出配置文件的值 @PropertySource(value = {"classpath:person.properties"}) //@PropertySources(value = {@PropertySource({}),@PropertySource({})}) @Configuration public class MainConfigOfPropertyValues { @Bean public Person person(){ return new Person(); } }
测试
@Test public void test08(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfPropertyValues.class); Person person = (Person) applicationContext.getBean("person"); System.out.println(person); Environment environment = applicationContext.getEnvironment(); String value = environment.getProperty("person.nickName");//通过environment也可以获取配置文件的属性值. System.out.println(value); applicationContext.close(); }
person.properties文件
person.nickName=\u5C0F\u674E\u56DB
运行结果:
9.自动装配
Spring利用依赖注入(DI),完成对IOC容器中中各个组件的依赖关系赋值;
9.1@Autowired:自动注入
1)、默认优先按照类型去容器中找对应的组件:applicationContext.getBean(BookDao.class);找到就赋值
2)、如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找 applicationContext.getBean(“bookDao”)
@Controller public class BookController { @Autowired private BookService bookService; } @Service public class BookService { @Autowired private BookDao bookDao; @Override public String toString() { return "BookService{" + "bookDao=" + bookDao + '}'; } } // 默认是类名首字母小写 @Repository public class BookDao { private String Label = "1"; public String getLabel() { return Label; } public void setLabel(String label) { Label = label; } @Override public String toString() { return "BookDao{" + "Label='" + Label + '\'' + '}'; } }
@Configuration @ComponentScan({"com.rg.service","com.rg.dao","com.rg.controller"}) public class MainConfigOfAutowired { @Bean("bookDao2") public BookDao bookDao(){ BookDao bookDao = new BookDao(); bookDao.setLabel("2"); return bookDao; } }
测试:
@Test public void test01(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class); //获取BookService中注入的BookDao BookService bookService = applicationContext.getBean(BookService.class); //BookDao bean = applicationContext.getBean(BookDao.class); 当IOC容器中有多个对象时不能用类.class进行获取对象. //System.out.println(bean); System.out.println(bookService); }
运行结果:
IOC容器中有bookDao和bookDao2,因为@Autowired 下面的需要的名字为bookDao,所以里面注册的就是bookDao对象.如果改为bookDao2则里面注册的就是bookDao2对象.
3)、使用@Qualifier指定需要装配的组件的id,而不是默认使用属性名
BookService{ @Qualifier("bookDao2") @Autowired BookDao bookDao; }
这样bookDao里面注册的就是bookDao2.
4)、自动装配默认一定要将属性赋值好,没有就会报错;通过使用@Autowired(required=false); 可以当有的时候进行注册,没有的时候为null.
BookService{ @Qualifier("bookDao2") @Autowired(required = false) BookDao bookDao; }
5)、@Primary:让Spring进行自动装配的时候,默认使用首选的bean;也可以继续使用@Qualifier指定需要装配的bean的名字
BookService{ //@Qualifier("bookDao2") @Autowired(required = false) BookDao bookDao; } @Repository public class BookDao { ...... } @Configuration @ComponentScan({"com.rg.service","com.rg.dao","com.rg.controller"}) public class MainConfigOfAutowired { @Primary @Bean("bookDao2") public BookDao bookDao(){ BookDao bookDao = new BookDao(); bookDao.setLabel("2"); return bookDao; } }
当没有Qualifier时, 为BookDao加上@Primary,则其优先权会升高,会优先注册该对象. 但如果此时有Qualifier,则由里面的名称决定.
6 )、扩展:Spring还支持使用@Resource(JSR250)和@Inject(JSR330) [java规范的注解]
@Resource:
可以和@Autowired一样实现自动装配功能;默认是按照组件名称进行装配的;
没有能支持@Primary功能没有支持@Autowired(reqiured=false);
@Inject:
需要导入javax.inject的包,和Autowired的功能一样。没有required=false的功能;支持@Primary
@Autowired:Spring定义的规范; @Resource、@Inject都是java规范
小结:
@Autowired 当IOC容器中只有一个时,可以这样使用.此时由 待注册的名称决定.
@Autowired经常和@Qualifier一起使用;此时,可以打破默认,由Qualifier指定的对象注册.
@Autowired还可以和@Primary+其他组件注解(@Bean,@Service,@Controller…),从而提升其优先级,优先被使用.
9.2 扩展:@Autowired使用的位置:
@Autowired:构造器,参数,方法,属性;都是从容器中获取参数组件的值
1.放在参数位置
1)、放在属性位置或setter方法上
默认加在ioc容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值等操作
@Component public class Car { .... } @Component public class Boss { //@Autowired:属性位置. private Car car; public Car getCar() { return car; } //标注在方法,Spring容器创建当前对象,就会调用方法,完成赋值 //方法使用的参数,自定义类型从IOC容器中获取. @Autowired//方法上 public void setCar(Car car) { this.car = car; } }
2 ) 、[标在构造器上]:如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还是可以自动从容器中获取
@Component public class Car { .... } @Component public class Boss { private Car car; @Autowired//可省略 public Boss(Car car){ this.car = car; System.out.println("Boss 有参构造器..."); } public Car getCar() { return car; } public void setCar(Car car) { this.car = car; } @Override public String toString() { return "Boss{" + "car=" + car + '}'; } }
程序中:会自动调用有参构造为Boss中的Car进行赋值.IOC容器中的Car和Boss中的Car是同一个.
3)、[标注在方法位置]:@Bean+方法参数;参数从容器中获取;默认不写@Autowired效果是一样的;都能自动装配
@Bean @Autowired public Color color(Car car){ Color color = new Color(); color.setCar(car); return color; }
注:参数卸载方法上或者 参数之前,或者省略都是可以的.
9.3@Profile
Spring为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能;
如:当开发环境时我们自动在环境中加入开发的相关组件,当是测试环境时,自动添加测试的相关组件,当是生成环境时,自动添加生产的组件.
@Profile:指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件
@Configuration @PropertySource("classpath:/jdbc.properties") public class MainConfigOfProfile implements EmbeddedValueResolverAware { private String driverClass; @Value("${jdbc.user}") // @value放在属性上 private String user; @Override public void setEmbeddedValueResolver(StringValueResolver resolver) { //this.resolver = resolver; driverClass = resolver.resolveStringValue("${jdbc.driver}"); } @Bean public Yellow yellow(){ return new Yellow(); } @Bean("prodDataSource") @Profile("prod") //@Profile("default") @Value放在参数上 public DataSource dataSourceProd(@Value("${jdbc.password}")String pwd) throws Exception { ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setUser("root"); dataSource.setPassword(pwd); dataSource.setJdbcUrl("jdbc:mysql:///goods"); dataSource.setDriverClass(driverClass); return dataSource; } @Profile("test") @Bean("testDataSource") public DataSource dataSourceTest(@Value("${jdbc.password}")String pwd) throws Exception { ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setUser("root"); dataSource.setPassword(pwd); dataSource.setJdbcUrl("jdbc:mysql:///test"); dataSource.setDriverClass(driverClass); return dataSource; } @Bean("devDataSource") @Profile("dev") public DataSource dataSourceDev(@Value("${jdbc.password}")String pwd) throws Exception { ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setUser("root"); dataSource.setPassword(pwd); dataSource.setJdbcUrl("jdbc:mysql:///travel"); dataSource.setDriverClass(driverClass); return dataSource; } }
补充:
使用配置文件的两种方式:
使用@PropertySource(“classpath:/文件名称”),搭配@Value(“键”)
类实现EmbeddedValueResolverAware通过 resolver.resolveStringValue 获取键对应的值.
测试:
@Test public void test01(){ //1.使用命令行动态参数,在Edit Configurations的VM options中加入 -Dspring.profiles.active=test //2.使用代码的方式激活某种环境 //1.创建一个applicationContext AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); //2.设置需要激活的环境 //AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfProfile.class); applicationContext.getEnvironment().setActiveProfiles("dev","test"); //3.注册主配置类 applicationContext.register(MainConfigOfProfile.class); //4.启动刷新容器 applicationContext.refresh(); String[] names = applicationContext.getBeanNamesForType(DataSource.class); System.out.println(names); for (String name : names) { System.out.println(name); } applicationContext.close(); }
运行结果:
2)、写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
如在类上加@Profile(“test”),使用的是开发环境,则里面的所有配置都无效.只有一致时才生效.
3)、没有标注环境标识的bean在,任何环境下都是加载的;
根据尚硅谷 雷神的<<Spring注解开发>> 整理总结