Spring-基本注解

基础环境

基本组件(Spring-Context)

  • aop
  • beans
  • context
  • core
  • expression
  • jcl

基本调用

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        Person person = context.getBean(Person.class);
        System.out.println(person);
    }
}

通过注解配置获取上下文,开启工作。

配置

@Configuration

@Configuration
public class Config {
    @Bean(value = "person")
    public Person person(){
        return new Person("seconder");
    }
}

通过@Configuration进行配置。

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class, Person.class);

获取配置信息,支持多个配置类传入。

@ComponentScan

@ComponentScan("com.seconder")
@Configuration
public class Config {
}

指定额外扫描包。

Filter

  • type:导入类型指定
    • ANNOTATION:通过注解判断
    • ASSIGNABLE_TYPE:通过类型判断
    • ASPECTJASPECTJ表达式
    • REGEX:正则表达式
    • CUSTOM:自定义规则
  • classes:指定指定类

自定义规则

public class Filter implements TypeFilter {
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        return false;
    }
}

需要实现指定接口TypeFilter

  • metadataReader:当前扫描的类的信息
  • metadataReaderFactory:可获取其他扫描的类的信息
@ComponentScan(value="com.godme",useDefaultFilters = false, excludeFilters = {
        @Filter(type = FilterType.CUSTOM, classes = {com.godme.filter.Filter.class})
})

这样就可以直接使用了。

字符判断

public class Filter implements TypeFilter {
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        String className = metadataReader.getClassMetadata().getClassName();
        if(className.contains("seconder")){
            return true;
        }
        return false;
    }
}

父子不同在

public class Filter implements TypeFilter {
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        ClassMetadata metadata = metadataReader.getClassMetadata();
        if(!metadata.hasSuperClass()){
            return true;
        }
        if(metadataReaderFactory.getMetadataReader(metadata.getSuperClassName()) == null){
            return true;
        }
        return false;
    }
}

excludeFilters

排除符合特征的导入项

@ComponentScan(value="com.seconder", excludeFilters = {
        @Filter(type=FilterType.CUSTOM, classes={
                Person.class
        })
})

可以指定多个@Filter过滤器,每个过滤器还可以指定多个过滤类。

includeFilters

excludeFilters,不过含义相反,不是排除,而是只包含

@ComponentScan(useDefaultFilters = false,includeFilters = {
        @Filter(type=FilterType.ANNOTATION, classes={
                Person.class
        })
})

默认的包含规则useDefaultFilterstrue,也就是全包含,不过滤。

excludeFilters可以生效,但是includeFilters需要把它设置为false

否则includeFilters没有包含,却被默认的过滤器给直接导入了。

ComponentScans

@ComponentScans(value = {
        @ComponentScan(value="com.godme",useDefaultFilters = false, excludeFilters = {
                @Filter(type = FilterType.CUSTOM, classes = {com.godme.filter.Filter.class})
        })
})

可以一次性指定多个导入项。

导入和注册

  • 单组件:注册
  • ComponentScan:单包扫描导入,多个组件,多过滤规则
  • ComponentScans:多包导入,ComponentScan集合

总结下来:单组件主动注册多组件类型过滤

对象

@Bean

@Configuration
public class Config {
    @Bean
    public Person person(){
        return new Person("seconder");
    }
}

配置下,@Bean标记方法

  • 返回值:类型
  • 方法名:对象名

Person如果没有无参构造会报错,不知道是不是必须要有,为什么。

value

@Configuration
public class Config {
    @Bean(value = "person")
    public Person person(){
        return new Person("seconder");
    }
}

value可指定变量名,而不采用方法名,不指定默认采用方法名。

@Scope

  • SCOPE_SINGLETONsingleton):单实例,每次获取都是同一个对象,默认
  • SCOPE_PROTOTYPEprototype):多实例,每次获取都重新创建对象
  • SCOPE_REQUESTrequest):同请求创建单一对象
  • SCOPE_SESSIONsession):同会话创建单一对象

@Lazy

  • singleton:自动填充容器,后续自动获取
  • prototype:获取时创建对象,不预先创建对象到容器
@Configuration
public class Config {
    @Lazy
    @Bean
    public Person person(){
        return new Person();
    }
}

懒加载,调用时慢慢创建对象,不获取不作为。

@Conditional

public class MyCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        if(context.getRegistry().containsBeanDefinition("seconder")){
            return false;
        }
        return true;
    }
}

实现Condition接口。

@Configuration
public class Config {
    @Conditional(MyCondition.class)
    @Bean
    public Person person(){
        return new Person();
    }
}

通过@Conditional进行条件调用,不存在seconder的变量名就导入。

@Conditional(MyCondition.class)
@Configuration
public class Config {
    @Bean
    public Person person(){
        return new Person();
    }
}

对类使用,会自动映射到全部方法。

导入

@Import

组件导入

  • @Resource/@Service/@Component:类主动注册
  • @Bean:对象手动注册
@Configuration
@Import(Person.class)
public class Config {

}
  • 自己编写的组件,可以直接类注册,组件名为类名小写。
  • 无论是否第三方,可以手动注册对象,组件名可自定义,默认为方法名。
  • @Import可以对无源码第三方类进行类注册,组件名为全类名。

ImportSelector

public class MyImportSeletcot implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{Person.class.getName()};
    }
}

实现ImportSelector接口,返回需要导入的类名数组。

@Configuration
@Import(MyImportSeletcot.class)
public class Config {

}

当导入该类的时候,会直接导入类名数组中的全部类组件。

相当于channelInitializer,会导入内部添加的组件,本身类似于一种集合器。

  • 必须为全类名
  • 不可指定实例名称

ImportBeanDefinitionRegistrar

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        if(!registry.containsBeanDefinition("person")){
            RootBeanDefinition person = new RootBeanDefinition(Person.class);
            registry.registerBeanDefinition("person", person);
        }
    }
}

实现接口,手动注册。

@Import(MyImportBeanDefinitionRegistrar.class)

FactoryBean

public class PersonFactoryBean implements FactoryBean<Person> {
    @Override
    public Person getObject() throws Exception {
        return new Person();
    }
    @Override
    public Class<?> getObjectType() {
        return Person.class;
    }
    @Override
    public boolean isSingleton() {
        return true;
    }
}
  • 对象
  • 类型
  • 数量
@Configuration
public class Config {
    @Bean
    public PersonFactoryBean personFactoryBean(){
        return new PersonFactoryBean();
    }
}

差异

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        System.out.println(context.getBean("&personFactoryBean").getClass().getName());
        System.out.println(context.getBean("personFactoryBean").getClass().getName());
    }
}

名称直接获取会创建目标对象,并非直接导入的FactoryBean对象。

想获取FactoryBean对象,需要添加上&符号。

way
@Import
ImportSelector
ImportBeanDefinitionRegistrar
FactoryBean

生命

@Bean

@Configuration
public class Config {
    @Bean(initMethod = "init", destroyMethod = "destroy")
    public Person person(){
        return new Person();
    }
}
  • 外部指定类内部方法
  • initMethod指定初始化方法,destroyMethod指定销毁方法。
  • initconstructor之后进行执行
  • singletoncontext.close之后会destroyprototypy不会。

InitializingBean, DisposableBean

class Person implements InitializingBean, DisposableBean{
    public Person(){
        System.out.println("constructor");
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("init");
    }
    @Override
    public void destroy() throws Exception {
        System.out.println("destroy");
    }
}
  • 类内部实现接口
  • InitializingBean-afterPropertiesSet定义初始化方法
  • DisposableBean-destroy定义销毁方法
  • afterPropertiesSetconstructor之后执行
  • prototypecontext.close之后不执行

@PostConstruct,@PreDestroy

class Person{
    public Person(){
        System.out.println("constructor");
    }
    @PostConstruct
    public void afterPropertiesSet() throws Exception {
        System.out.println("init");
    }
    @PreDestroy
    public void destroy() throws Exception {
        System.out.println("destroy");
    }
}
  • 类内部标记方法
  • @PostConstructor指定初始化方法,@PreDestory指定销毁方法。
  • prototype不自动销毁

BeanPostProcessor

public class Person implements BeanPostProcessor {
    public Person(){
        System.out.println("constructor");
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("after");
        return null;
    }
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("before");
        return null;
    }
}
  • 类内部接口实现
  • 有参,可设置
  • 两者constructor后执行,无销毁
  • prototype有效,singleton无效

顺序

@Component
public class Processor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization");
        return bean;
    }
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization");
        return bean;
    }
}
public class Person implements InitializingBean,DisposableBean {
    public Person(){
        System.out.println("constructor");
    }
    @Override
    public void destroy() throws Exception {
        System.out.println("DisposableBean");
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("BeanPostProcessor");
    }
    @PostConstruct
    public void postConstruct(){
        System.out.println("PostConstruct");
    }
    @PreDestroy
    public void preDestroy(){
        System.out.println("PreDestroy");
    }
    public void init(){
        System.out.println("init");
    }
    public void destroy2(){
        System.out.println("destroy2");
    }
}

在这里插入图片描述

初始化构造器>处理器>注解>接口>方法指定

  1. constructor
  2. postProcessBeforeInitialization
  3. PostConstruct
  4. InitializingBean
  5. init
  6. postProcessAfterInitialization

销毁注解>接口>方法指定

  1. PreDestroy
  2. DisposableBean
  3. destroy2
  • 处理器只负责初始化化
  • 销毁只负责singleton
  • 处理器>注解>接口>方法指定

配置

PropertySource

@PropertySource(value = {"classpath:person.properties"})
public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        String name = context.getEnvironment().getProperty("person.name");
        System.out.println(name);
        context.close();
    }
}

通过enviroment就可以获取环境变量了,配置的变量也会导入哦。

最好标记在@Configuration配置类上,然后其他地方引入,避免扫描未执行。

属性(@Value)

赋值

public class Person {
    @Value("seconder")
    private String name;
}

SpEL

public class Person {
    @Value("#{6 + 3 + 3 + 4}")
    private int age;
}

#{}SpEL表达式,是可以直接计算的。

取配置

public class Person {
        @Value("${person.name}")
        private int age;
}

取配置需要配合@PropertySource先导入了配置,或者直接导入环境变量。

总的来说,都是""字符串,只是特殊处理之间的区别。

@ConfigurationPropertiesspring-boot中直接进行对应名称的直接区配置注入。

注入

@Configuration
public class Config {
    @Bean
    public Person father() {
        return new Person("father");
    }
    @Bean
    public Person mother(){
        return new Person("mother");
    }
    @Bean
    public Family family(){
        return new Family();
    }
}
public class Family {
    @Autowired
    public Person person;
}

@Autowired

@Autowired连线注入会是哪一个呢,结果是报错。

public class Family {
    @Autowired(required = false)
    public Person person;
}

默认情况下,属性都是非空的,也就是require=true,找不到注入的数据,就会报错。

如果不是必要的,那就设置require = false

@Qualifier

public class Family {
    @Qualifier("father")
    @Autowired
    public Person person;
}

@Bean一样

@Beanwire
default:默认为方法名default:默认为变量名
@Bean("instanceName"):指定别名@Qualifier("instance"):指定对象

默认情况下,属性变量和实例对象之间的关联,会经过

  1. 类型判断
  2. 名称比较

所以,当变量名和实例名不一致的时候,是不能够自动关联上的。

需要使用@Qualifier进行指定对象的关联。

@Primary

    @Primary
    @Bean
    public Person mother(){
        return new Person("mother");
    }

默认情况

  1. 类型一致
  2. 名称一致

手动指定

  1. 必须得知并写死对象名

两者都有些极端和弊病。

@Primary,定义优先级

  1. 类型匹配
  2. 指定对象
  3. 名称匹配
  4. 是否优先

刨除前两者的影响,当是在无法装配时,仅按照类型判断,优先装配标记@Primary的对象。

@Resource@inject

public class Family {
    @Resource(name = "father")
    public Person person;
}

@ResourceJSR250),按照名称装配

  • 无默认装配功能
  • @Primary选择功能
  • require=false,必须装配
public class Family {
    @Inject
    public Person person;
}

@InjetJSR30),可默认装配

  • 需导入javax.inject
  • required属性

@Autowired位置

  • 属性
public class Family {
    @Aurowired
    public Person person;
}
  • 方法
public class Family {
    public Person person;
    @Autowired
    public void setPerson(Person person){
        this.person = person;
    }
}
  • 参数
public class Family {
    public Person person;
    public void setPerson(@Autowired Person person){
        this.person = person;
    }
}
  • 构造器
public class Family {
    public Person person;
    @Autowired
    public Family(Person person){
        this.person = person;
    }
}
public class Family {
    public Person person;
    public Family(@Autowired Person person){
        this.person = person;
    }
}

如果只有一个有参构造器,而且是扫描注入的话

public class Family {
    public Person person;
    public Family(Person person){
        this.person = person;
    }
}

不用标注,也会自动从容器中获取并完成注入。

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

@Bean标注的方法,也是类似的原理。

当需要但是没有入参的时候,会自动从容器中进行获取并注入。

当然,也可以手动标记@Autowired入参。

    @Bean
    public Family family(@Value("family") String name){
        return new Family(name);
    }

同样的,没有回自动的从容器中获取,但是简单的类型的话,也可以手动进行指定赋值。

逆向注入

我们总是把需要的组件,向IOC中进行注入,为的是便捷的使用spring

但是,更好的使用,是能够更方便的直接操作spring的各种组件。

ApplicationContextAware

public class Person implements ApplicationContextAware {
    private ApplicationContext context;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = context;
    }
}

谁说一定得在启动类中才能获取context呢,这样一来,获取spring内部组件,更方便实现一些操作了。

EmbeddedValueResolverAware

public class Person implements EmbeddedValueResolverAware {
    private StringValueResolver resolver;
    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        this.resolver = resolver;
        String result = this.resolver.resolveStringValue("#{1+1}");
    }
}

怎么解析乱七八糟的表达式?这不就获取解析器了么。

BeanNameAware

public class Person implements BeanNameAware {
    private String name;
    @Override
    public void setBeanName(String name) {
        this.name = name;
    }
}

组件注册名称?轻松获取。

EnvironmentAware

public class Person implements EnvironmentAware {
    private Environment environment;
    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }
}

环境变量,轻松获取。

更多

在这里插入图片描述
多种口味任你选择。spring不仅好管理你注册的组件,其中也有很多你想要的组件。

顺序

	private void invokeAwareInterfaces(Object bean) {
		if (bean instanceof Aware) {
			if (bean instanceof EnvironmentAware) {
				((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
			}
			if (bean instanceof EmbeddedValueResolverAware) {
				((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
			}
			if (bean instanceof ResourceLoaderAware) {
				((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
			}
			if (bean instanceof ApplicationEventPublisherAware) {
				((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
			}
			if (bean instanceof MessageSourceAware) {
				((MessageSourceAware) bean).setMessageSource(this.applicationContext);
			}
			if (bean instanceof ApplicationContextAware) {
				((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
			}
		}
	}

你只需要知道入口是refresh

	public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
		this();
		register(annotatedClasses);
		refresh();
	}

调用栈里面还能找到getBean,也就是说,在进行注册的时候,也就是在你的操作开始前。

对象创建时,就会自动注入spring组件到你的对象中了,简直舒服。

@Profile

指定profile

  • 虚拟机参数
-Dspring.profiles.active=dev
  • 代码指定
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.getEnvironment().setActiveProfiles("dev");
        context.register(Config.class);
        context.refresh();
    }

对比一下直接创建的

	public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
		this();
		register(annotatedClasses);
		refresh();
	}

就是提前注入了一些参数,然后才开始的注册和刷新,机智。

选用

@Configuration
public class Config {
    @Profile("test")
    @Bean
    public Person father() {
        return new Person();
    }
    @Profile("default")
    @Bean
    public Person mother(){
        return new Person();
    }
}
  • others:名称随便定义,和设置丁profile配置参数一致即可
  • default:固定名称,当没有其他配置被选用,默认使用,如果有配置被选用,则不生效

未进行@Profile标记的方法,无论如何设置,都会进行注入。

类标记

@Profile("dev")
@Configuration
public class Config {
    @Bean
    public Person father() {
        return new Person();
    }
    @Bean
    public Person mother(){
        return new Person();
    }
}

类标记的,相当于标记给了全部方法,如果只有个别对象需要切换,不推荐如此做法。

手写判断

public class DevProfile implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        String[] profiles = context.getEnvironment().getActiveProfiles();
        if(profiles == null || profiles.length == 0){
            return false;
        }
        for(String profile:profiles){
            if("dev".equals(profile)){
                return true;
            }
        }
        return false;
    }
}

Coditional没个传参,只能这样了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
spring-boot-starter-jpa和spring-boot-starter-jdbc是Spring Boot框架中用于数据库访问的两个常用依赖库。它们之间的主要区别如下: 1. 功能:spring-boot-starter-jpa是用于支持Java持久化API(JPA)的依赖库,它提供了对关系型数据库的对象关系映射(ORM)支持。而spring-boot-starter-jdbc是用于支持Java数据库连接(JDBC)的依赖库,它提供了对关系型数据库的基本操作支持。 2. 抽象层级:spring-boot-starter-jpa在JPA之上提供了更高级别的抽象,通过使用实体类和注解来映射数据库表和实体之间的关系。它隐藏了底层数据库的细节,使开发者可以更专注于业务逻辑的实现。而spring-boot-starter-jdbc则直接使用JDBC API进行数据库操作,需要开发者手动编写SQL语句。 3. 配置方式:spring-boot-starter-jpa需要配置数据源和JPA相关的属性,如数据库连接信息、实体类扫描路径、事务管理等。而spring-boot-starter-jdbc只需要配置数据源相关的属性,如数据库连接信息即可。 4. 使用场景:如果你的应用程序需要进行复杂的对象关系映射和查询操作,或者需要使用JPA提供的高级特性(如缓存、延迟加载等),那么可以选择使用spring-boot-starter-jpa。而如果你只需要进行简单的数据库操作,或者对数据库的操作更加灵活,可以选择使用spring-boot-starter-jdbc。 总结来说,spring-boot-starter-jpa适用于需要进行对象关系映射和高级查询的场景,而spring-boot-starter-jdbc适用于简单的数据库操作和对数据库的直接控制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值