小白学习后端开发之Spring框架注解大全(一)

一、Spring 初次尝试

1、通过xml形式配置IOC容器Bean

在Project下面找到一个resources 文件夹创建一个beans.xml (xml的文件名随意)。如下,把你需要加载的bean 插入到bean节点下面。定义id 和 class类。当application被创建的时候,他就会去扫对应的xml文件,将这里定义的bean加载到IOC容器。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="person" class="lsn01.cap01.Person">
        <property name="name" value="jack"/>
        <property name="age" value="19"/>
    </bean>
</beans>

测试方式:(xml形式 需要使用ClassPathXmlApplicationContext)

ApplicationContext appxml = new ClassPathXmlApplicationContext("beans.xml");
Person person = appxml.getBean("person", Person.class);
System.out.println(person.toString());

2、通过注解的形式加载bean

注解1:@Configuration 作用于类,被这个注解标注的类可以理解成beans.xml
注解2:@Bean 作用于方法,主要作用往IOC容器中注册一个Bean, 值是id 返回值是Bean的类型

@Configuration
public class MainConfig {

    @Bean("springPerson")
    public Person getPerson(){
        return new Person("spring",17);
    }
}

测试方式:

ApplicationContext app = new AnnotationConfigApplicationContext(MainConfig.class);
Person springPerson = (Person) app.getBean("springPerson");
System.out.println(springPerson.toString());
String[] beanNamesForType = app.getBeanNamesForType(Person.class);
for (String name : beanNamesForType) {
    System.out.println(name);
}

作为一个小白,第一次看到这样的操作注解,并不知道这里有什么作用。

Spring Bean和Configuration 升级

1、ComponentScan 定义扫描组件规则

通过注解我们可以定义配置类也就是Configuration可以扫描到那些类。以MVC为例我一般会定义 Controller、Service、Dao。 所以我们会定义很多这些相关的类,但是并不是所有类都需要加载到IOC容器。所谓Component 其实Controller、Service、Dao(Repository) 都是Component没有本质上的区别,就是一个类,注解被标记的时候,表明它的作用,起到一个提示作用。
如下目录结构:

-test2
-controller
OrderControlller.java
-dao
OrderDao.java
-service
OrderService.java

@Controller
public class OrderControlller {
}

@Repository
public class OrderDao {
}

@Service
public class OrderService {
}

接下来定义配置类

@Configuration
@ComponentScan(value = {"lsn01.cap02"}, includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
}, useDefaultFilters = false)
public class Cap01MainConfig {
}

定义测试方式:

@Test
public void testComponentScan(){
    ApplicationContext app = new AnnotationConfigApplicationContext(Cap02MainConfig.class);
    String[] beanDefinitionNames = app.getBeanDefinitionNames();
    for (String beanDefinitionName : beanDefinitionNames) {
        System.out.println("beanDefinitionName = "+beanDefinitionName);
    }
}

ComponentScan注解主要属性,

1、value 配置扫码的包路径String[] value() default {}

2、includeFilters 包含哪些Filter。 可以通过定义type 和classs 指定哪些可以被扫描到。
如:@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
打印结果:

beanDefinitionName = org.springframework.context.annotation.internalConfigurationAnnotationProcessor
beanDefinitionName = org.springframework.context.annotation.internalAutowiredAnnotationProcessor
beanDefinitionName = org.springframework.context.annotation.internalCommonAnnotationProcessor
beanDefinitionName = org.springframework.context.event.internalEventListenerProcessor
beanDefinitionName = org.springframework.context.event.internalEventListenerFactory
beanDefinitionName = cap02MainConfig
beanDefinitionName = orderControlller
beanDefinitionName = orderDao
beanDefinitionName = orderService

显然并没有起到一个过滤效果,依然把所有的组件全部扫描进来,而我们需要的只是包含Controller注解的类。
原因在于 useDefaultFilters = false 设置,关闭到默认的Filters

beanDefinitionName = org.springframework.context.annotation.internalConfigurationAnnotationProcessor
beanDefinitionName = org.springframework.context.annotation.internalAutowiredAnnotationProcessor
beanDefinitionName = org.springframework.context.annotation.internalCommonAnnotationProcessor
beanDefinitionName = org.springframework.context.event.internalEventListenerProcessor
beanDefinitionName = org.springframework.context.event.internalEventListenerFactory
beanDefinitionName = cap02MainConfig
beanDefinitionName = orderControlller

3、excludeFilters 不包含哪些Filters 注意:如果使用这个注解就不需要将设置成true 否则将不会扫描任何组件
如:

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

打印结果:

beanDefinitionName = org.springframework.context.annotation.internalConfigurationAnnotationProcessor
beanDefinitionName = org.springframework.context.annotation.internalAutowiredAnnotationProcessor
beanDefinitionName = org.springframework.context.annotation.internalCommonAnnotationProcessor
beanDefinitionName = org.springframework.context.event.internalEventListenerProcessor
beanDefinitionName = org.springframework.context.event.internalEventListenerFactory
beanDefinitionName = cap02MainConfig

4、自定义Filters

public class CustomFilter implements TypeFilter {

    public boolean match(MetadataReader metadataReader,
                         MetadataReaderFactory metadataReaderFactory) throws IOException {
        //当前扫描类的注解信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        Set<String> annotationTypes = annotationMetadata.getAnnotationTypes();
        for (String annotationType : annotationTypes) {
            System.out.println("annotationType : "+annotationType);
        }
        //当前扫描类的类的信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        String className = classMetadata.getClassName();
        System.out.println("classMetadata className = "+className);
        //当前扫描类的资源
        Resource resource = metadataReader.getResource();
        String filename = resource.getFilename();
        System.out.println("filename = "+filename);

        //返回true就是包含,false就是不包含
        return false;
    }
}

相关的自定义可以根据上面的三种信息定义属于的结果逻辑返回。

打印结果

classMetadata className = lsn01.cap02.config.CustomFilter
filename = CustomFilter.class
annotationType : org.springframework.stereotype.Controller
classMetadata className = lsn01.cap02.controller.OrderControlller
filename = OrderControlller.class
annotationType : org.springframework.stereotype.Repository
classMetadata className = lsn01.cap02.dao.OrderDao
filename = OrderDao.class
annotationType : org.springframework.stereotype.Service
classMetadata className = lsn01.cap02.service.OrderService
filename = OrderService.class
beanDefinitionName = org.springframework.context.annotation.internalConfigurationAnnotationProcessor
beanDefinitionName = org.springframework.context.annotation.internalAutowiredAnnotationProcessor
beanDefinitionName = org.springframework.context.annotation.internalCommonAnnotationProcessor
beanDefinitionName = org.springframework.context.event.internalEventListenerProcessor
beanDefinitionName = org.springframework.context.event.internalEventListenerFactory
beanDefinitionName = cap02MainConfig

1、从打印结果上看我们可以看到 最先扫描的是CustomFilter
2、FilterType.ANNOTATION:按照注解
FilterType.ASSIGNABLE_TYPE:按照给定的类型;比如按BookService类型
FilterType.ASPECTJ:使用ASPECTJ表达式
FilterType.REGEX:使用正则指定
FilterType.CUSTOM:使用自定义规则,自已写类,实现TypeFilter接口>

2、scope 定义扫描规则

scope 主要作用是定义Bean在IOC容器中的创建规则
prototype: 多实例:IOC容器启动并不会去调用方法创建对象放在容器中,而是每次获取的时候才会调用方法创建对象
singleton: 单实例(默认),IOC容器启动会调用方法创建对象放到IOC容器中以后每交获取就是直接从容器(理解成从map.get对象)中拿
request: 主要针对WEB应用,同一次请求创建一个实例
session: 同一个session创建一个实例

@Configuration
public class Cap02MainConfig {
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    @Bean
    public Person person(){
        System.out.println("创建了一个person对象");
        return new Person();
    }
}

@Test
public void testScope(){
    ApplicationContext app = new AnnotationConfigApplicationContext(Cap02MainConfig.class);
    System.out.println("IOC容器初始化完成了");
    Person person1 = app.getBean("person", Person.class);
    
    Person person2 = app.getBean("person", Person.class);
    System.out.println("person1 == person2 is "+(person2 == person1));
}

打印结果:
如果不加,@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

创建了一个person对象
IOC容器初始化完成了
person1 == person2 is true

加@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

IOC容器初始化完成了
创建了一个person对象
创建了一个person对象
person1 == person2 is false

得出结论默认情况在AnnotationConfigApplicationContext创建的时候,Spring自动就完成了所有相关bean的创建,并且是单例类;如果加入Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) 是多实例类,并且不是随着ApplicationContext的创建而创建。

3、lazy懒加载

@Configuration
public class Cap02MainConfig {
    //@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    @Lazy
    @Bean
    public Person person(){
        System.out.println("创建了一个person对象");
        return new Person();
    }
}

@Test
public void testScope(){
    ApplicationContext app = new AnnotationConfigApplicationContext(Cap02MainConfig.class);
    System.out.println("IOC容器初始化完成了");
    Person person1 = app.getBean("person", Person.class);
    
    Person person2 = app.getBean("person", Person.class);
    System.out.println("person1 == person2 is "+(person2 == person1));
}

打印结果:

IOC容器初始化完成了
创建了一个person对象
person1 == person2 is true
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章