Spring 注解驱动开发-IOC (精华版)

Spring 注解驱动开发-IOC

组件注册

  1. 包扫描+组件标注注解(@ComponetScan/ @Controller/@Service/@Repository/@Component)
  2. @Bean[导入的第三方包里面的组件]
  3. @Import[快速给容器中导入一个组件]
    1. @Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名。
    2. ImportSelector:返回需要导入的组件的全类名数组;
    3. ImportBeanDefinitionRegistrar:手动注册bean到容器中
  4. 使用Spring提供的FactoryBean(工厂Bean);

@Configraution,@Bean

//配置类==配置文件
@Configuration  //告诉Spring这是要给配置类
public class MainConfig {

    //给容器中注册一个Bean,类型为返回值类型,id默认是方法名作为id
    @Bean("person")
    public Person person(){
        return new Person("zzp",18);
    }
}

@ComponentScan

@Configuration
@ComponentScan(basePackages = "com.zzp",
        useDefaultFilters = false,includeFilters =
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Service.class})
//        excludeFilters =
//        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Service.class, Controller.class})
)
//@ComponentScan  value:指定要扫描的包
//excludeFilters = @Filter(..)指定扫描的时候按什么规则排除哪些组件
//includeFilters = @Filter(..) 指定扫描的时候只扫哪些组件 前提需要加上 userDefaultFiters = false
public class MainConfig {

}
自定义TypFilter

MainConfig.java

@Configuration
@ComponentScan(basePackages = "com.zzp",
        useDefaultFilters = false,includeFilters =
        @ComponentScan.Filter(type = FilterType.CUSTOM,classes = {MyFilterType.class})

)
//@ComponentScan  value:指定要扫描的包
//excludeFilters = @Filter(..)指定扫描的时候按什么规则排除哪些组件
//includeFilters = @Filter(..) 指定扫描的时候只扫哪些组件 前提需要加上 userDefaultFiters = false
//FilterType.ANNOTATION  根据注解类型来过滤
//FilterType.ASSIGNABLE_TYPE  根据类的类型来过滤
//FilterType.ASPECTJ  根据表达式来过滤
//FilterType.REGEX  根据正则来过滤
//FilterType.CUSTOM 自定义过滤规则
public class MainConfig {
}

MyFilterType.java

public class MyFilterType 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();
        if (className.contains("er")){
            return true;  //返回true  合格
        }
        return false;  //false  不合格
    }
}

@Scope

@Configuration
public class MainConfig2 {

    @Scope  //调整作用域
    //	PROTOTYPE 多实例的:ioc容器启动并不会去调用方法创建对象放在容器中。
    //	                   每次获取的时候才会创建对象
    //	SINGLETON 单例的(默认值): ioc容器启动会调用方法创建对象到ioc容器中,以后每次获取就直接从容器中拿
    //	REQUEST 同一次请求创建一个实例
    //	SESSION 同一个session创建一个实例
    @Bean
    public Person person(){
        return new Person("zzp",18);
    }
}

@Lazy

主要针对于单实例bean, 默认在容器启动时创建对象;

懒加载:启动容器不创建对象,第一次使用(获取)Bean的时候创建对象,并进行初始化。

@Configuration
public class MainConfig2 {
    @Scope
    @Lazy
    @Bean
    public Person person(){
        return new Person("zzp",18);
    }
}

@Conditional

MainConfig2.java

//满足当前条件,该配置类才会生效
@Conditional(WindowsCondition.class)
@Configuration
public class MainConfig2 {

    @Scope
    @Lazy
    @Bean
    public Person person(){
        return new Person("zzp",18);
    }

    //满足当前条件,该bean才会生效
    @Conditional(WindowsCondition.class)
    @Bean("bill")
    public Person person01(){
        return new Person("bill Gates",62);
    }

    @Conditional(LinuxCondition.class)
    @Bean("linux")
    public Person person02(){
        return new Person("linux",49);
    }
}
自定义Condition

WindowsCondition.java

public class WindowsCondition implements Condition {
    /**
     *
     * @param context   当前使用的上下文
     * @param metadata  注释信息
     * @return
     */
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {

        //获取到ioc容器使用的beanFactory
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();

        //获取到类加载器
        ClassLoader classLoader = context.getClassLoader();

        //获取到bean定义的注册类
        BeanDefinitionRegistry registry = context.getRegistry();

        //获取到当前环境信息
        Environment environment = context.getEnvironment();
        String property = environment.getProperty("os.name");

        if (property.contains("Windows")){
            return true;
        }
        return false;
    }
}

LinuxCondition.java

public class LinuxCondition implements Condition {
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {

        Environment environment = context.getEnvironment();
        String property = environment.getProperty("os.name");
        if (property.contains("linux")){
            return true;
        }
        return false;
    }
}

@Import

@Configuration
//将Blue导入到ioc容器中,id为全类名com.zzp.domain.Blue
//ImportSelector:返回需要导入的组件的全类名数组
//@Import({Blue.class, Red.class})
//ImportBeanDefinitionRegistrar手动注册进容器中
@Import({MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
public class MainConfig2 {
}
自定义ImportSelector

MyImportSelector.java

public class MyImportSelector implements ImportSelector {
	//返回值就是导入到容器中的组件全类名
    //importingClassMetadata :当前标注@Import注解的类的所有注解信息
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.zzp.domain.Blue","com.zzp.domain.Red"};
    }
}
自定义ImportBeanDefinitionRegistrar

MyImportBeanDefinitionRegistrar.java

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    /**
     *
     * @param importingClassMetadata 当前标注@Import的注解的类的所有注解信息
     * @param registry  BeanDefinition注册类
     *                  把所有需要添加到容器的bean 调用registry.registerBeanDefinition()方法手工注册进去
     *
     */

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        //判断BeanDefinition注册类 中是否包含 id为com.zzp.domain.Blue 的bean
        boolean blue = registry.containsBeanDefinition("com.zzp.domain.Blue");
        boolean red = registry.containsBeanDefinition("com.zzp.domain.Red");
        if (blue && red){
            //手工注册进ioc容器中

            //1.实例化一个beanDefinition
            RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
            //2.并注册进 BeanDefinition注册类中
            registry.registerBeanDefinition("rainBow",beanDefinition);
        }
    }
}

工厂模式 FactoryBean 组件注册

@Configuration
public class MainConfig3 {
    
    /**
     *  使用Spring 提供的FactoryBean(工厂Bean)
     *  1.默认获取到的是工厂 bean调用getObject创建对象
     *  2.要获取工厂Bean本身,需要在id前面加上&
     *  eg:  ac.getBean("&color")  获取到的对象类型是 class com.zzp.domain.ColorFactoryBean
     *       ac.getBean("color")  获取到的对象类型是 class com.zzp.domain.Color
     */
    @Bean("color")
    public ColorFactoryBean colorFactoryBean(){
        return new ColorFactoryBean();
    }
}

ColorFactoryBean.java

public class ColorFactoryBean implements FactoryBean<Color> {
    public Color getObject() throws Exception {
        return new Color();
    }

    public Class<?> getObjectType() {
        return Color.class;
    }

    public boolean isSingleton() {
        return true;
    }
}

生命周期

bean的声明周期:

​ bean创建----> 初始化 ---->销毁的过程

销毁:

  • 单实例: 容器关闭的时候 //application.close()
  • 多实例:容器不会管理这个bean,容器不会调用销毁方法。

1.通过@Bean指定init-method和destory-method

/**
 *  指定初始化和销毁方法: 通过@Bean指定init-method和destory-method
 */
@Configuration
public class MainConfigOfLifeCycle {

    @Bean(initMethod = "init",destroyMethod = "destory")
    public Red red(){
        return new Red();
    }
}

Red.java

public class Red {
    public Red() {
        System.out.println("red constructor .....");
    }

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

    public void destory(){
        System.out.println("red destory....");
    }
}

2.通过实现InitializingBean和DisposableBean接口的方法

@Configuration
public class MainConfigOfLifeCycle {

    @Bean
    public Black black(){
        return new Black();
    }
}

Black.java

public class Black implements InitializingBean, DisposableBean {

    public Black() {
        System.out.println("Black constroctor...");
    }
	
    //destory-method
    public void destroy() throws Exception {
        System.out.println("Black destory ....");
    }

    //init-method
    public void afterPropertiesSet() throws Exception {
        System.out.println("Black afterPropertiesSet ...");
    }
}

3.通过JSR250 @PostConstruct注解和@PreDestroy注解

@Configuration
public class MainConfigOfLifeCycle {

    @Bean
    public Green green(){
        return new Green();
    }
}

Green.java

public class Green {
    public Green() {
        System.out.println("Green constroctor...");
    }

    //init-method
    @PostConstruct
    public void init(){
        System.out.println("Green init...");
    }

    //destory-method
    @PreDestroy
    public void destory(){
        System.out.println("Green destory...");
    }
}

4.BeanPostProcessor接口(bean的后置处理器)

注意:这个是围绕在init初始化前后

/**
 * BeanPostProcessor【interface】 bean的后置处理器
 * 在bean初始化前后进行一些处理工作
 * postProcessBeforeInitialization:在初始化之前
 * postProcessAfterInitialization:在初始化之后
 */
@Configuration
@ComponentScan("com.zzp.condition")
public class MainConfigOfLifeCycle {
    @Bean
    public Green green(){
        return new Green();
    }
}

MyBeanPostProcessor.java

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    /**
     * @param bean      当前bean对象
     * @param beanName  当前bean的 id名字
     * @return
     * @throws BeansException
     */
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization :beanName:"+ beanName + " " + "bean对象:" + bean);
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization:beanName:"+ beanName + " " + "bean对象:" + bean);
        return bean;
    }
}

属性赋值

@Value

@Configuration
//加载配置文件到环境中
@PropertySource("classpath:person.properties")
public class MainConfigValue {

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

Person.java

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {
    /**
     *  使用Value赋值
     *1.基本数值
     *2.可以写SpEL: #{}
     *3.可以写${}:取出配置文件中的值(在运行环境变量里面的值)
     */

    @Value("zhangsan")
    private String name;
    
    @Value("#{20-2}")
    private Integer age;
    
    @Value("${person.nickname}")
    private String nickname;
}

自动装配

1.@Autowired 自动注入

/**
 * 搭配使用:
 * 1)、默认优先按照类型去容器中找相对应的组件:applicationContext.getBean(BookDao.class);
 *
 * 2)、如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找
 *
 * 3)、@Qualifier("bookDao"):使用@Qualifier指定需要装配的组件的id
 *
 * 4)、自动装配默认一定要将属性赋值好,没有就会报错
 *                            允许没有该装配的组件  @Autowired(required=false)
 *
 * 5)、@Primary: 如果有多个同类型的组件  可以让Spring进行自动装配的时候,默认首选的bean  注意:这个注解是放在需要装配的组件上的
 *
 */



2.Spring还支持使用@Resource(JSR250) 和@Inject(JSR330) 【java规范的注解】

/**
 * @Resource:
 *          可以和@Autowired一样实现自动装配功能,默认是按照组件名称进行装配的。
 *          缺点: 1.没有能支持@Primary功能  2.没有支持@Autowired(required=false)的功能
 * 
 * @Inject:
 *          需要导入javax.inject包,和@Autowired的功能一样
 *          缺点: 没有支持@Autowired(required=false)的功能
 * 
 * @Autowired是Spring定义的,   @Resource和@Inject都是Java规范
 * 
 * AutowiredAnnotationBeanPostProcessor(增强器):  解析上面这些注解以及@Autowired注解并完成自动装配功能的 
 */

3.xxxAware

自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFactory,xxx)

自定义组件实现xxxAware接口:在创建对象的时候,会调用接口规定的方法注入相关组件

把Spring底层一些组件注入到自定义的Bean中;

xxxAware的功能: 底层是使用xxxProcessor 来处理的 该xxxProcessor也是实现了BeanPostProcessor增强器

例子: 实现ApplicationContextAware 底层使用 class ApplicationContextAwareProcessor implements **BeanPostProcessor ** 增强器

Rea.java

@Component
public class Rea implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public ApplicationContext getApplicationContext() {
        return applicationContext;
    }
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

        System.out.println("context:" + applicationContext);
        this.applicationContext = applicationContext;
    }
}

4.@Profile

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

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

案例:

MyConfigOfProfile.java

/**
 *
 * @Profile:指定组件在哪个环境的情况下才能被注册到容器中,不指定则任何环境下都能注册这个组件。
 * 1)、加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。默认是default环境
 * 2)、写在配置类上,只有指定的环境的时候,整个配置类里面的所有配置才能生效
 * 3)、没有标注环境标识的bean在任何环境下都是加载的。
 */

@Profile("test")  
@PropertySource("classpath:jdbc.properties")
@Configuration
public class MyConfigOfProfile implements EmbeddedValueResolverAware {

    @Value("${jdbc.user}")
    private String user;

    @Value("${jdbc.password}")
    private String password;

    private String driverClass;

    @Profile("test")
    @Bean("dataSourceTest")
    public DataSource dataSourceTest() throws Exception {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(password);
        dataSource.setJdbcUrl("jdbc:mysql:///test");
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }

    @Profile("prod")
    @Bean("dataSourceProd")
    public DataSource dataSource1() throws Exception {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(password);
        dataSource.setJdbcUrl("jdbc:mysql:///luffy");
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }


    @Profile("dev")
    @Bean("dataSourceDev")
    public DataSource dataSourceDev() throws Exception {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(password);
        dataSource.setJdbcUrl("jdbc:mysql:///staf");
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }


    //采用实现Aware接口的方式来解析值
    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        this.driverClass = resolver.resolveStringValue("${jdbc.driver}");
    }
}

运行时怎么去切换环境呢?

1.通过代码去控制

MyTest.java

public class MyTest {

    @Test
    public void test(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        //获取环境
        ConfigurableEnvironment environment = applicationContext.getEnvironment();
        //设置环境的profile值
        environment.setActiveProfiles("test");
        //注册配置类
        applicationContext.register(MyConfigOfProfile.class);
        applicationContext.refresh();

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

2.通过命令行参数来控制

-Dspring.profiles.active=test
万般皆下品,唯有读书高!
原文地址:https://www.cnblogs.com/s686zhou/p/14530977.html