spring

107 阅读5分钟

SpringBoot实战教程

juejin.cn/post/689885…

Spring bean配置

www.cnblogs.com/xjs18747044… juejin.cn/post/684490…

@Configuration
public class AppConfig {
    @Bean(name="entitlement")
    public Entitlement entitlement() {
        Entitlement ent= new Entitlement();
        ent.setName("Entitlement");
        ent.setTime(1);
        return ent;
    }

    @Bean(name="entitlement2")
    public Entitlement entitlement2() {
        Entitlement ent= new Entitlement();
        ent.setName("Entitlement2");
        ent.setTime(2);
        return ent;
    }
}
public class JavaConfigTest {
    public static void main(String[] arg) {        
        ApplicationContext context = new AnnotationConfigApplicationContext(TestConfiguration.class); // 注解形式的spring容器加载方式,用AnnotationConfigApplicationContext替换ClassPathXmlApplicationContext("spring-context.xml");
        ctx.register(AppConfig.class); // 手动装载到容器
        ctx.refresh();
        Entitlement ent = (Entitlement)ctx.getBean("entitlement");
        Entitlement ent2 = (Entitlement)ctx.getBean("entitlement2");
        ctx.close();
    }
}

注:

  • @Configuration可理解为用spring的时候xml里面的标签

  • @Bean可理解为用spring的时候xml里面的标签

  • 使用AnnotationConfigApplicationContext可以实现基于Java的配置类加载Spring的应用上下文。避免使用application.xml进行配置。

  • @ComponentScan主要就是定义扫描的路径从中找出标识了需要装配的类自动装配到spring的bean容器中

  • @Controller,@Service,@Repository注解都有一个共同的注解@Component,而有了注解@Component如果也在@ComponentScan注解的路径下就会被扫描spring容器中自动装载

  • 若是springboot项目,默认的扫描包路径启动类所在包,其原理是,启动类标记为@SpringBootApplication,该注解又最终被@Configuration标记,于是启动时用自身的class去new AnnotationConfigApplicationContext,其中底层确定扫描包的代码为: if (basePackages.isEmpty()) { basePackages.add(ClassUtils.getPackageName(declaringClass)); }

  • 多个@Configuration会被合并,相当于定义了多个标签,然后生成一个spring容器上下文,但至少启动时要有一个@Configuration的类被加载,才可以指明扫描路径从而进一步加载注入其他@Configuration或@Component。例如配置类A被初始加载,其中标记ComponentScan路径包含配置类B,然后B被加载又可指定其他ComponentScan路径。或者直接组合多种配置 @ImportResource("classpath:configtest.xml") @Import(TestConfig.class)

  • @EnableXXX注解配合@Configuration使用

  • @PropertySource注解将properties配置文件中的值存储到Spring的 Environment

  • 常见的写法是写一个@Configuration配置类,然后将其中的方法标记为@Bean去加载需要容器托管的对象,注意和@Component标记配置类的区别:@Configuration标记时,其中@Bean的方法会被代理,多次手动调用只会真正执行一次(当然添加@Scope("prototype")标记时会执行多次),而@Component标记时多次调用@Bean的方法会执行多次

  • @Bean 支持两种属性,即 initMethod 和destroyMethod,这些属性可用于定义生命周期方法。在实例化 bean 或即将销毁它时,容器便可调用生命周期方法。生命周期方法也称为回调方法,因为它将由容器调用。使用 @Bean 注释注册的 bean 也支持 JSR-250 规定的标准 @PostConstruct 和 @PreDestroy 注解。

Spring容器上下文:

  在SpringIOC容器读取Bean配置创建Bean实例之前,必须对它进行实例化,只有在容器实例化后,才可以从IOC容器里获取Bean实例并使用。   Spring提供了两种类型的IOC容器实现。     ==BeanFactory:IOC容器基础实现,是Spring框架的基础设施,面向Spring本身;     ==ApplicationContext:提供了更多的高级特性,是BeanFactory的子接口。面向使用Spring框架的开发者,几乎所有的应用场合都直接使用ApplicationContext而非底层的BeanFactory。

ApplicationContextAware

通过它可以在代码中获取Spring容器上下文环境对象,即实现ApplicationContextAware接口中的setApplicationContext方法可以拿到ApplicationContext对象。然后就可以通过这个上下文环境对象得到Spring容器中的Bean。

注意: 可以直接使用 @Autowired 注入 ApplicationContext 到想要的地方,不再通过ApplicationContextAware接口手动获取之。

用法示例:在不方便直接注入bean到属性的地方,比如在Utils使用到dao,就可以手动获取bean。

@Component
public class SpringJobBeanFactory implements ApplicationContextAware {
    private static ApplicationContext applicationContext;
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringJobBeanFactory.applicationContext=applicationContext;
        
    }
     public static ApplicationContext getApplicationContext() {
            return applicationContext;
    }
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) throws BeansException {
            if (applicationContext == null){
                return null;
            }
            return (T)applicationContext.getBean(name);
      }
}

// 使用:
TypeSetErpService typeSetErpServ = SpringJobBeanFactory.getBean("typeSetErpServiceImpl");


自动注入@Autowired

// 源码
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
  boolean required() default true; // 定义是否必须注入,默认找不到要注入的对象时会报错
}
// 其中@Retention定义被它所注解的注解保留多久
public enum RetentionPolicy {
    SOURCE, // 注解在编译时就被忽略
    CLASS, // 默认是该策略,注解被编译器编译进class文件;但是不被VM运行时保留
    RUNTIME // 一直保留到运行时,可以通过反射获取注解信息
}

// 1、普通用法,依赖注入成员变量
@Configuration
@ComponentScan("com.mybokeyuan.springAutowiredDemo")
  public class AutowiredConfig {
}
@Component
public class MyService {
    @Autowired
    private IndexService indexService;
}
public class DemoClientTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AutowiredConfig.class);
        System.out.println(ac.getBean(MyService.class));

    }
}

// 2、依赖注入方法
@Component
public class MyService {
    private IndexService indexService;
    @Autowired
    public void getIndexInterf (IndexService index) {
        indexService = index;
    }
    @Autowired
    private ApplicationContext applicationContext;
}
// 这样的用法类似于set方法的功能,此外applicationContext也可以注入,不需要用ApplicationContextAware了。

// 3、构造方法注入参数
形参会自动注入。此处需要注意,如果只有一个构造方法加了@Autowired注解,那么就会用这个构造方法初始化;如果有两个自定义构造方法,两个都没加@Autowired注解,则会报错,因为Spring不知道你要用哪个构造方法初始化;同理,如果有多个构造方法都加了@Autowired注解,那么还是会报错。

// 4、注入Map、List等集合

@Autowired注入顺序

  • 若只有一个实现类,由于Autowired首先是按照类型去匹配的,所以属性名字怎么写都没关系,都可以注入进去;
  • 若有多个实现类,Autowired就按照属性名字去找,即找一个名字为 abc的bean注入,然而IoC容器不存在一个名字叫abc的 bean,找不到就报错
  • 若有多个实现类并且名字一样,这时需要在声明bean的地方加上不同的别名区分
@Autowired
private HelloService abc;
@Service("abc")
public class HelloServiceImpl implements HelloService
  • 若有多个实现类并且名字一样且不想用别名,这时候使用注解@Qualifier 配合@Autowired 一起使用,指定一个bean的名字
// 生成一个bean,名字为 helloController
@Controller
public class HelloController {
    @Autowired
    @Qualifier("helloServiceImpl")
    private HelloService abc;
    
    public void hello() {
        abc.sayHello();
    }
}