Spring注解驱动开发

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、Service
  • FilterType.ASSIGNABLE_TYPE 指定某个类
  • FilterType.ASPECTJ,
  • FilterType.REGEX,//正则表达式
  • FilterType.CUSTOM;//自定义

使用自定义TypeFilter:

  1. 定义实现TypeFilter接口的类实现match()方法
  2. 需要在类上加上@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
  1. 快速导入一个组件
  • 自己写的类 注册使用@Controller @Service @Repository @Component 配置@Configuration使用
  • 使用第三方jar包的话 用@Bean
  • 快速导入组件 @Import(Color.class)
  1. 使用ImportSelector 自定义ImportSelector接口的实现类 然后在Improt(ImportSelector.class)
  2. 使用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注册组件
  1. 利用工厂bean调用getObject()方法获取bean
  2. 利用isSingleton()配置单实例和多实例
  3. 如果想要获取工厂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
  1. 基本数值
  2. SPEL:#{}
  3. ${} 取出配置文件【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();

注意:

  1. 加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中,默认是default环境;
  2. 写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效;
  3. 没有标注环境标识的bean,在任何环境下都是加载的;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值