Spring注解驱动开发
一、组件注册
1.1 @Configuration&@Bean给容器装配组件
- 通过xml配置文件的方式
<bean id="person" class="com.learn.bean.Person">
<property name="age" value="18"></property>
<property name="name" value="张三"></property>
</bean
- 在类上配置
@Configuration
注解 方法或类上加上@Bean
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class);
appplicationContext.getBean(Person.class)
1.2 @ComponentScan扫描组件&自定义扫描规则
- xml配置文件的方式添加
<context:component-scan base-package="com.learn" use-default-filters="false"></context:component-scan>
- 通过ComponentScan类扫描添加
// 配置类就相当于配置文件
@Configuration
@ComponentScan(value = "com.learn", includeFilters = {
// @Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class}),
// @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {BookDao.class}),
@Filter(type = FilterType.CUSTOM, classes = {MyFilter.class})
}, useDefaultFilters = false)
//@ComponentScan value可以指定要扫描的包
//excludeFilters = filter[] 扫描时指定要排除的组件
//incledeFilters = filter[] 扫描时指定只包含哪些组件
// ANNOTATION,按照类型
// ASSIGNABLE_TYPE,按照类型 子类父类
// ASPECTJ,
// REGEX,//正则表达式
// CUSTOM;//自定义
1.3 自定义@FilterType指定过滤规则
FilterType.ANNOTATION
泛型 类似Controller、ServiceFilterType.ASSIGNABLE_TYPE
指定某个类FilterType.ASPECTJ
,FilterType.REGEX
,//正则表达式FilterType.CUSTOM
;//自定义
使用自定义TypeFilter:
- 定义实现TypeFilter接口的类实现match()方法
- 需要在类上加上
@EnableAspectJAutoProxy
注解
@EnableAspectJAutoProxy
public class MyFilter implements TypeFilter {
/**
*
* @param metadataReader 读取到的当前正在扫描的类的信息
* @param metadataReaderFactory 可以获取到其他类信息的
* @return
* @throws IOException
*/
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;
}
}
1.4 @Scope实现组件作用域
prototype
:多实例 ioc容器启动时不回创建对象,每次获取的时候才会创建singleton
:单实例 默认 ioc容器启动会创建对象到容器中,以后用的话就直接在容器中取request
:同一次web请求 创建一个实例 在request内有效session
:同一次web请求长创建一个实例 在同一个session里面有效
1.5 @Lazy-bean懒加载
单实例bean在默认容器启动时创建bean
懒加载:启动容器时不创建bean,在使用获取bean的时候才创建并初始化
1.6 @Conditional按照条件注册bean
按照一定的条件进行判断,满足条件给容器中注册bean,可以放在类上,也可以放在方法上;
条件类需实现Condition接口
实现matches()方法
public class LinuxCondition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//TODO 判断是否是linux系统
//1、获取到ioc容器使用的beanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//2、获取到类加载器
ClassLoader classLoader = context.getClassLoader();
//3、获取到环境信息
Environment environment = context.getEnvironment();
//4、获取到注册类信息
BeanDefinitionRegistry registry = context.getRegistry();
String property = environment.getProperty("os.name");
if(property.contains("Mac")){
return true;
}
return false;
}
}
1.7 @Import
- 快速导入一个组件
- 自己写的类 注册使用
@Controller @Service @Repository @Component
配置@Configuration
使用 - 使用第三方jar包的话 用
@Bean
- 快速导入组件
@Import
(Color.class)
- 使用
ImportSelector
自定义ImportSelector接口的实现类 然后在Improt(ImportSelector.class) - 使用
ImportBeanDefinitionRegistrar
手动注册bean到容器中 实现ImportBeanDefinitionRegistrar接口手动注册
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
*
* @param importingClassMetadata 当前类的注释信息
* @param registry BeanDefinitionRegistry注册类 把需要添加的类通过registerBeanDefinition手动注册
*/
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean color = registry.containsBeanDefinition("com.learn.bean.Color");
boolean green = registry.containsBeanDefinition("com.learn.bean.Green");
if(color && green){
//BeanDefinition rainBow = registry.getBeanDefinition("RainBow");
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(RainBow.class);
registry.registerBeanDefinition("RainBow", rootBeanDefinition);
}
}
}
1.8 使用FactoryBean注册组件
- 利用工厂bean调用getObject()方法获取bean
- 利用isSingleton()配置单实例和多实例
- 如果想要获取工厂bean本身,前面加一个&符
public class ColorFactoryBean implements FactoryBean<Color> {
//获取对象
public Color getObject() throws Exception {
return new Color();
}
//获取对象的类型
public Class<?> getObjectType() {
return Color.class;
}
//true 单实例 在容器中只创建一次对象
//false 多实例 每次获取都会创建bean
public boolean isSingleton() {
return false;
}
}
二、生命周期
bean的生命周期是指 创建–>初始化–>销毁的过程
2.1 @Bean指定初始化和销毁方法
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...");
}
}
@Configuration
public class MainConfigBeanLifeCycle {
@Bean(initMethod = "init", destroyMethod = "destroy")
public Car car(){
return new Car();
}
}
2.2 InitializingBean和DisposableBean
通过实现InitializingBean定义初始化逻辑和DisposableBean定义销毁逻辑
public class Cat implements InitializingBean, DisposableBean {
public void destroy() throws Exception {
System.out.println("cat ... destroy...");
}
public void afterPropertiesSet() throws Exception {
System.out.println("cat ... afterPropertiesSet...");
}
}
2.3 @PostConstruct和@PreDestroy
@PostConstruct
在bean创建并完成属性赋值来完成初始化方法@PreDestroy
在bean销毁之前来通知我们清理
@Component
public class Dog implements ApplicationContextAware {
public Dog(){
System.out.println("dog constructor...");
}
//对象创建并赋值之后调用
@PostConstruct
public void init(){
System.out.println("Dog....@PostConstruct...");
}
//容器移除对象之前
@PreDestroy
public void detory(){
System.out.println("Dog....@PreDestroy...");
}
}
- 三种初始化方法调用的顺序为:@PostConstruct > InitializingBean接口 > @Bean(initMethod)
- 三种销毁方法调用的顺序为:@PreDestroy > DisposableBean接口 > @Bean(destroyMethod)
2.4 BeanPostProcessor(后置处理器)
在bean初始化前后进行
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization..." + beanName + "=>" +bean);
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization..." + beanName + "=>" + bean);
return bean;
}
}
- postProcessBeforeInitialization 在初始化之前执行
- postProcessAfterInitialization 在初始化之后执行
2.4.1 BeanPostProcessor原理
刷新容器的时候refresh()–>finishBeanFactoryInitialization(beanFactory)–>beanFactory.preInstantiateSingletons()–>;
遍历得到所有的BeanPostProcessor,挨个执行beforeInitialization()
一旦返回null就跳出for循环,不会执行beanProcessor.postProcessBeforeInitialization()
populateBean(beanName, mbd, instanceWrapper);//给bean的属性赋值
{
pplyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
invokeInitMethods(beanName, wrappedBean, mbd); //执行自定义初始化
applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
2.4.2 spring底层对BeanPostProcessor的使用
bean的赋值、注入其它组件、@Autowired、生命周期注解功能、@Async等功能都是通过BeanPostProcessor实现。
三、属性赋值
3.1 @Value
- 基本数值
- SPEL:#{}
- ${} 取出配置文件【properties】中的值(在运行环境变量里面的值)
3.2 @PropertySource加载外部配置文件
//使用ProperySource读取外部配置文件中的k/v保存到环境变量中,加载外部配置文件使用${}取出
@PropertySource(value = {"classpath:/person.properties"})
@Configuration
public class MainConfigOfPropertyValues {
@Bean
public Person person(){
return new Person();
}
}
四、自定装配
4.1 @Autowired & @Qualifier & @Primary
1)默认优先按照类型去容器中找对应的组件:applicationContext.getBean(BookDao.class)
2)如果找到多个相同的组件,再将属性的名称作为组件的id去容器中查找
applicationContext.getBean(“bookDao”)
3) @Autowired
自动注入,可以放在构造器,参数,方法,属性上 自动装配默认一定要将属性赋值好,没有就报错
4)使用@Qualifier
指定需要装配的组件的id,而不是使用属性名
5)使用@Primary
让Spring进行自动装配的时候 默认使用首选的bean
4.2 JSR250-@Resource、JSR330-@Inject
@Resource
:可以和@Autowired
一样实现自动装配功能,默认是按照组件名称进行装配的;但没有能支持@Primary
功能,也没有支持@Autowired(reqiured=false)
;
@Inject
:需要导入javax.inject的包,和@Autowired
的功能一样,但没有required=false
的功能
4.3 方法、构造器位置的自动装配
通过@Autowired
自动注入,可以放在构造器方法,构造器上
4.4 Aware注入Spring底层组件 & 原理
自定义组件想要使用spring底层的一些组件(ApplicationContext、BeanFactory.xxx)
自定一组件实现xxxAware:在创建对象的时候,会调用接口规定的方法注入相关组件
xxxAware:功能使用xxxProcessor 例如ApplicationContextAware ->ApplicationContextAwareProcessor
4.5 @Profile 根据环境注册Bean
Spring为我们提供的根据当前环境、动态激活和切换环境的功能
指定组件在哪个环境的情况下才能被注册到容器中,若不指定,任何环境下都能注册这个组件。
环境准备
@PropertySource("classpath:/dbconfig.properties")
@Configuration
public class MainConfigOfProfile implements EmbeddedValueResolverAware {
@Value("${db.user}")
private String user;
private String driverClass;
@Profile("test")
@Bean("testDataSource")
public DataSource dataSource(@Value("${db.password}")String pwd) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Profile("test")
@Bean("devDataSource")
public DataSource dataSourceDev(@Value("${db.password}")String pwd) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Profile("prod")
@Bean("prodDataSource")
public DataSource dataSourceProd(@Value("${db.password}")String pwd) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
dataSource.setDriverClass(driverClass);
return dataSource;
}
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.driverClass = resolver.resolveStringValue("${db.driverClass}");
}
一: 通过配置虚拟机运行参数:-Dspring.profiles.active=test
二:通过代码的方式
// 1、创建一个applicationContext
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 2、设置需要激活的环境
applicationContext.getEnvironment().setActiveProfiles("test", "dev");
// 3、注册主配置类
applicationContext.register(MainConfigOfProfile.class);
// 4、启动刷新容器
applicationContext.refresh();
注意:
- 加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中,默认是default环境;
- 写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效;
- 没有标注环境标识的bean,在任何环境下都是加载的;