Spring5注解编程基础组件


1 配置组件(Configure Components)

注解名称说明
@Configuration把一个类作为一个IoC容器,它的某个方法头上如果注册了@Bean,就会作为这个Spring容器中的Bean
@ComponentScan在配置类上添加 @ComponentScan 注解。该注解默认会扫描该类所在的包下所有的配置类,相当于之前的context:component-scan
@Scope用户指定scope 作用域的 ,用于类上
@Lazy表示延迟初始化
@ConditionalSpring4 开始提供的,作用:按照一定条件进行判断,满足条件则给IoC容器注册Bean
@Import导入外部资源
生命周期控制1. @PostConstruct :用于指定初始化方法(用于方法上) 2. @PreDestory :用于指定注销方法(方法上)3. @DependsOn:定义Bean初始化及注销时的顺序

@Configuration 注解

Spring3.0 ,@Configuration用于定义 配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被 @Bean 注解的方法,这些方法将会被 AnnotationConfigApplicationContextAnnotationConfigWebApplicationContext 类进行扫描,并用于构建bean定义,初始化Spring容器。

注意:@Configuration注解的配置类有如下要求:

  1. @Configuration不可以是final类型;
  2. @Configuration不可以是匿名类;
  3. 嵌套的configuration必须是静态类。
## myconfig class

import com.gupaoedu.project.entity.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyConfig {

    @Bean
    public Person person(){
        /**
         * 这里的new 并不是调用我们的构造方法创建的,
         * 而是以此作为模板通过原型模式 创建出来的
         *
         */
        return new Person("tom",19);
    }
}
## myconfig class

## mytest class
import com.gupaoedu.project.entity.Person;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MyTest {

    @Test
    public void test(){
        //Ioc,控制反转
        ApplicationContext app=new AnnotationConfigApplicationContext(MyConfig.class);
        Object bean1 = app.getBean("person");
        Object bean2 = app.getBean("person");
        // 由于 == 结果为 true
        Person bean = app.getBean(Person.class);
        System.out.println(bean1 ==bean2);
        System.out.println(bean == bean1);
    }
}

## mytest class
## myentity class
import org.springframework.stereotype.Component;

@Component
public class Person {
    private String name;
    private int age;

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person() {
    }
}
## myentity class

@ComponentScan 注解

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

相当于之前的 context:component-scan
扫描到文件中带有@Service,@Component,@Repository,@Controller等这些注解的类
在这里插入图片描述
好下面咱们就先来简单演示一下这个例子

## 在包 controller下新建一个MyController@Controller注解如下:
@Controller
public class MyController {

    @Autowired
    private MyService myService;
}

## 在包 service下新建一个MyController@Service注解如下:
@Service
public class MyService {
}

## 在包 dao下新建一个MyController@Repository注解如下:
1、myDao类:
@Repository
public class MyDao {
}
2Person 
public class Person {
}

## 新建一个配置类如下:
@Configuration
//将value中的目录下的文件扫描出来
@ComponentScan(
        value = "com.xxx.project")
public class MyConfig {

}

## 新建测试方法如下:
public class MyTest {

    @Test
    public void test(){
        //Ioc,控制反转
        ApplicationContext app=new AnnotationConfigApplicationContext(MyConfig.class);
        String [] beanNames = app.getBeanDefinitionNames();
        System.out.println(Arrays.toString(beanNames).replace("\\[|\\]","").replace(",","\n"));
      
    }
}

## 结果
 myConfig、 myController、 myDao、 myService 都会被扫描到,parson却没有,因为没有添加注解,如果想要parson也扫描到IoC容器中,只需要在类上添加一个 @Component 注解即可

上面只是简单的介绍了@ComponentScan注解检测包含指定注解的自动装配,接下来让我们来看看@ComponentScan注解的更加详细的配置,在演示详细的配置之前,让我们先看看@ComponentScan的源代码如下:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
    /**
     * 对应的包扫描路径 可以是单个路径,也可以是扫描的路径数组
     * @return
     */
    @AliasFor("basePackages")
    String[] value() default {};
    /**
     * 和value一样是对应的包扫描路径 可以是单个路径,也可以是扫描的路径数组
     * @return
     */
    @AliasFor("value")
    String[] basePackages() default {};
    /**
     * 指定具体的扫描的类
     * @return
     */
    Class<?>[] basePackageClasses() default {};
    /**
     * 对应的bean名称的生成器 默认的是BeanNameGenerator
     * @return
     */
    Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
    /**
     * 处理检测到的bean的scope范围
     */
    Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
    /**
     * 是否为检测到的组件生成代理
     * Indicates whether proxies should be generated for detected components, which may be
     * necessary when using scopes in a proxy-style fashion.
     * <p>The default is defer to the default behavior of the component scanner used to
     * execute the actual scan.
     * <p>Note that setting this attribute overrides any value set for {@link #scopeResolver}.
     * @see ClassPathBeanDefinitionScanner#setScopedProxyMode(ScopedProxyMode)
     */
    ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
    /**
     * 控制符合组件检测条件的类文件   默认是包扫描下的  **/*.class
     * @return
     */
    String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
    /**
     * 是否对带有@Component @Repository @Service @Controller注解的类开启检测,默认是开启的
     * @return
     */
    boolean useDefaultFilters() default true;
    /**
     * 指定某些定义Filter满足条件的组件 FilterType有5种类型如:
     *                                  ANNOTATION, 注解类型 默认
                                        ASSIGNABLE_TYPE,指定固定类
                                        ASPECTJ, ASPECTJ类型
                                        REGEX,正则表达式
                                        CUSTOM,自定义类型
     * @return
     */
    Filter[] includeFilters() default {};
    /**
     * 排除某些过来器扫描到的类
     * @return
     */
    Filter[] excludeFilters() default {};
    /**
     * 扫描到的类是都开启懒加载 ,默认是不开启的
     * @return
     */
    boolean lazyInit() default false;
}
a.演示basePackageClasses参数,如我们把配置文件改成如下:
### 扫包路径改为了 com.xxx.project.dao , dao层,只有myDao外加basePackageClasses指定的myService 加入到了spring容器中

@ComponentScan(value="com.xxx.project.dao",useDefaultFilters=true,basePackageClasses=myService.class)
@Configuration
public class MainScanConfig {
}

结果如下:
 myConfig
 myDao
 myService
b.演示includeFilters参数的使用如下:
在service 层下新建一个myService2类如下:注意没有带@Service注解
public class myService2{
}
##配置类改成:
@ComponentScan(value="com.zhang",useDefaultFilters=true,
   includeFilters={
       @Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
       @Filter(type=FilterType.ASSIGNABLE_TYPE,classes={myService2.class})
   })
@Configuration
public class myconfig{
}

##结果为:
myConfig
myController
myDao
myService
myService2

##myService2同样被加入到了spring容器

c.自定义的实现了TypeFilter的MyTypeFilter类如下:

public class MyTypeFilter implements TypeFilter {
   /**
    *
    * @param metadataReader 获取当前正在操作的类的信息
    * @param metadataReaderFactory 获取上下文中所有的信息
    * @return
    * @throws IOException
    */
   @Override
   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+"---------");
       //获取类名中包含er的
       if(className.contains("er")){
           System.out.println("++++++++++"+className+"++++++++++");
           return true;
       }
       return false;
   }
}

##修改主配置如下:
@Configuration
//将value中的目录下的文件扫描出来
@ComponentScan(
       value = "com.gupaoedu.project",
       includeFilters = {@Filter( type = FilterType.CUSTOM,value = {MyTypeFilter.class})},// 扫描自定义规则的类
       useDefaultFilters=false

)
public class MyConfig {

}


结果:
myConfig
myController
myService
myService2
##将类名中不带有er字符串的bean过滤掉了

@Scope注解

@Scope注解的目的是用来调节作用域
@Scope(“prototype”)//多实例,IOC容器启动创建的时候,并不会创建对象放在容器在容器当中,当你需要的时候,需要从容器当中取该对象的时候,就会创建。
@Scope(“singleton”)//单实例 IOC容器启动的时候就会调用方法创建对象,以后每次获取都是从容器当中拿同一个对象(map当中)。
@Scope(“request”)//同一个请求创建一个实例
@Scope(“session”)//同一个session创建一个实例
例如:

@Configuration
public class MyConfig {

    /**
     * scope注解
     * prototype 原型,多例
     * singleton 单例 默认
     * request 主要应用于webm模块,同一个请求只创建一个实例
     *  session 主要应用于web模块,同一个session值创建一个对象
     *
     * @return
     */
    @Scope("prototype")
    @Bean
    public Person person(){
        return new Person("tom",19);
    }
}

@Lazy 注解

Spring IoC (ApplicationContext) 容器一般都会在启动的时候实例化所有单实例 bean 。如果我们想要 Spring 在启动的时候延迟加载 bean,即在调用某个 bean 的时候再去初始化,那么就可以使用 @Lazy 注解。

value 取值有 true 和 false 两个 默认值为 true

true 表示使用 延迟加载, false 表示不使用,false 纯属多余,如果不使用,不标注该注解就可以了。

@Lazy注解注解的作用主要是减少springIOC容器启动的加载时间

当出现循环依赖时,也可以添加@Lazy

public class Person {
    private String name;
    private Integer age;
 
    public Person() {
    }
 
    public Person(String name, Integer age) {
        System.out.println(" 对象被创建了.............");
        this.name = name;
        this.age = age;
    }
 
  // 省略 getter setter 和 toString 方法
}

##配置类:
@Configuration
public class MyConfig {

    @Lazy
    @Bean
    public Person person(){
        System.out.println("将对象添加到IOC容器中");
        /**
         * 这里的new 并不是调用我们的构造方法创建的,
         * 而是以此作为模板通过原型模式 创建出来的
         *
         */
        return new Person("tom",19);
    }
}

## 测试类:
public class MyTest {

    @Test
    public void test(){
        //Ioc,控制反转
        ApplicationContext app=new AnnotationConfigApplicationContext(MyConfig.class);
        System.out.println("IoC容器初始化成功!!");
        Object bean1 = app.getBean("person");
    }
}
结果:
	IoC容器初始化成功!!
	将对象添加到IOC容器中

如果不接@Lazy则结果是:

	将对象添加到IOC容器中
	IoC容器初始化成功!!

@Conditional注解

@Conditional是Spring4新提供的注解,它的作用是按照一定的条件进行判断,满足条件给容器注册bean。

首先创建分别创建LinuxConditionalWinConditional ,他们都实现Condition 接口并重写matches 方法

##WinConditional 
public class WinConditional implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
        Environment environment = conditionContext.getEnvironment();
        String property = environment.getProperty("os.name");
        System.out.println(property);
        if(property.contains("Windows")){
            return true;
        }
        return false;
    }
}

##LinuxConditional 
public class LinuxConditional implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
        Environment environment = conditionContext.getEnvironment();
        String property = environment.getProperty("os.name");
        System.out.println(property);
        if(property.contains("Linux")){
            return true;
        }
        return false;
    }
}

##配置类:
@Configuration
public class MyConfig {

    @Conditional(WinConditional.class)
    @Bean
    public Person tom(){
        System.out.println("将对象tom添加到IOC容器中");

        return new Person("jack",19);
    }
    @Conditional(LinuxConditional.class)
    @Bean
    public Person james(){
        System.out.println("将对象James添加到IOC容器中");

        return new Person("James",17);
    }
}

##测试方法:
/**
 *TODO 启动增加项:
 * vm options 一栏添加:-ea -Dos.name=Linux
 */
public class MyTest {

    @Test
    public void test(){
        //Ioc,控制反转
        ApplicationContext app=new AnnotationConfigApplicationContext(MyConfig.class);
        System.out.println("IoC容器初始化成功!!");

        //假设操作系统是Windows ,就加载jack
        //如果操作系统是Linux,那就加载James
        }
}

@Import注解

import 注解,将第三方的类添加到Ioc容器中

给Ioc容器中注册Bean的方式

  1. @Bean 直接导入
  2. @ComponentScan 默认扫描(@Controller,@Dao,@service,@Repostory,@Component)
  3. Import 快速给容器导入Bean的方式
    a. @Import 直接导入
    b. 实现ImportSelector接口 自定义规则实现
    c. 实现ImportBeanDefinitionRegistrar接口,获得BeanDefinitionRegistry对象,可以直接手动向IoC注入类
  4. FactoryBean 把需要注册的对象封装为FactoryBean
    a. FactoryBean 负责把Bean对象注册到Iocd的Bean
    b. BeanFactory 从Ioc中获取Bean对象
第一种用法:直接填class数组
@Configuration
@Import(value = {Cat.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
public class MyConfig {


    @Bean
    public Person person(){
        System.out.println("将对象添加到IOC容器中");
        /**
         * 这里的new 并不是调用我们的构造方法创建的,
         * 而是以此作为模板通过原型模式 创建出来的
         *
         */
        return new Person("tom",19);
    }
}
第二种用法:实现ImportSelector接口 自定义规则实现
创建MyImportSelector  并实现 ImportSelector 接口,重写selectImports 方法,在返回的 字符串数组中添加需要注入的类包名即可。
public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {

        return new String[]{"com.XXX.project.entity.Company","com.XXX.project.entity.Member"};
    }
}

##配置类:
需要将 MyImportSelector 输入到IoC容器中
@Configuration
@Import(value = {Cat.class,MyImportSelector.class})
public class MyConfig {


    @Bean
    public Person person(){
        System.out.println("将对象添加到IOC容器中");
        /**
         * 这里的new 并不是调用我们的构造方法创建的,
         * 而是以此作为模板通过原型模式 创建出来的
         *
         */
        return new Person("tom",19);
    }
}
第三种用法:实现ImportBeanDefinitionRegistrar接口,获得BeanDefinitionRegistry对象,可以直接手动向IoC注入类

##创建MyImportBeanDefinitionRegistrar 并实现 ImportBeanDefinitionRegistrar 接口,重写registerBeanDefinitions方法.

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    /**
     *
     * @param importingClassMetadata
     * @param registry 向容器注入对象的方法
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //假设 包中如果声明了Company 和 Mamber 这两个类 ,我才把User类注入到Ioc容器中
        boolean company = registry.containsBeanDefinition("com.XXX.project.entity.Company");
        boolean member = registry.containsBeanDefinition("com.XXX.project.entity.Member");
        if(company  && member){

            BeanDefinition beanDefinition = new RootBeanDefinition(User.class);
            //方法说明: registerBeanDefinition 方法只是向beanDefinitionMap中put 值
            registry.registerBeanDefinition("user",beanDefinition);

        }
    }
}

##配置类:
需要将 MyImportSelector 输入到IoC容器中
@Configuration
@Import(value = {Cat.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
public class MyConfig {


    @Bean
    public Person person(){
        System.out.println("将对象添加到IOC容器中");
        /**
         * 这里的new 并不是调用我们的构造方法创建的,
         * 而是以此作为模板通过原型模式 创建出来的
         *
         */
        return new Person("tom",19);
    }
}
4.FactoryBean 把需要注册的对象封装为FactoryBean
##创建 MyFactoryBean  实现FactoryBean<T> 接口,并实现getObject()getObjectType()isSingleton() 方法
public class MyFactoryBean implements FactoryBean<Monkey> {
    @Override
    public Monkey getObject() throws Exception {
        return new Monkey();
    }

    @Override
    public Class<?> getObjectType() {
        return Monkey.class;
    }
	/**
	*是否是单例模式
	**/
    @Override
    public boolean isSingleton() {
        return true;
    }
}

##配置类
@Configuration
@Import(value = {Cat.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
public class MyConfig {


    @Bean
    public Person person(){
        System.out.println("将对象添加到IOC容器中");
        /**
         * 这里的new 并不是调用我们的构造方法创建的,
         * 而是以此作为模板通过原型模式 创建出来的
         *
         */
        return new Person("tom",19);
    }

    @Bean
    public MyFactoryBean monkey(){
        return new MyFactoryBean();
    }
}

生命周期控制

对Bean生命周期的监控

  1. 配置Bean注解参数
  2. 分别实现InitialzingBean和DisposableBean接口//TODO [2]实现接口
  3. 使用注解@PostConstruct和@PreDestroy注解 //TODO [3] 使用注解
  4. 自己写一个类,去实现BeanPostProcessor接口 //TODO [4] 实现BeanPostProcessor接口
1. 配置Bean注解参数
## 实体类
public class Car {

    public Car(){
        System.out.println("调用Car的构造方法");
    }
    public void addOil(){
        System.out.println("在行驶前加油!!");
    }

    public void close(){
        System.out.println("停车熄火!!");
    }
    public void run(){
        System.out.println("正在飙车");
    }
}

## 配置类
@Configuration
@ComponentScan("com.gupaoedu.project.entity")
public class MyConfig {

    @Lazy
    @Bean(initMethod = "addOil",destroyMethod = "close")
    public Car car(){
        return new Car();
    }
}
## 测试类
/**
 * bean对象的生命周期
 *结果:
 * 调用Car的构造方法
 * 在行驶前加油!!
 * IoC容器初始化成功!!
 * com.gupaoedu.project.entity.Car@33afa13b
 * 停车熄火!!
 */
public class MyTest {

    /**
     * 对Bean生命周期的监控
     * 1、 配置Bean注解参数
     * 2、 分别实现InitialzingBean和DisposableBean接口//TODO [2]实现接口
     * 3、 使用注解@PostConstruct和@PreDestroy注解 //TODO [3] 使用注解
     * 4、 自己写一个类,去实现BeanPostProcessor接口 //TODO [4] 实现BeanPostProcessor接口
     */
    @Test
    public void test(){
        //Ioc,控制反转
        AnnotationConfigApplicationContext app=new AnnotationConfigApplicationContext(MyConfig.class);
        System.out.println("IoC容器初始化成功!!");
        Object bean1 = app.getBean("car");

        System.out.println(bean1 );
        app.close();
    }
}
2、分别实现InitialzingBean和DisposableBean接口 并重写destroy、afterPropertiesSet方法
## entity 
//TODO [2]实现接口
@Component
public class Train  implements InitializingBean, DisposableBean {

    @Override
    public void destroy() throws Exception {
        System.out.println("火车对象销毁");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("火车对象初始化");
    }
}

## 配置类
@Configuration
@ComponentScan("com.gupaoedu.project.entity")//TODO [2]实现接口
public class MyConfig {

    @Lazy
    @Bean(initMethod = "addOil",destroyMethod = "close")
    public Car car(){
        return new Car();
    }
}

## 测试类
/**
 * bean对象的生命周期
 *结果:
 * 调用Car的构造方法
 * 在行驶前加油!!
 * IoC容器初始化成功!!
 * com.gupaoedu.project.entity.Car@33afa13b
 * 停车熄火!!
 */
public class MyTest {

    /**
     * 对Bean生命周期的监控
     * 1、 配置Bean注解参数
     * 2、 分别实现InitialzingBean和DisposableBean接口//TODO [2]实现接口
     * 3、 使用注解@PostConstruct和@PreDestroy注解 //TODO [3] 使用注解
     * 4、 自己写一个类,去实现BeanPostProcessor接口 //TODO [4] 实现BeanPostProcessor接口
     */
    @Test
    public void test(){
        //Ioc,控制反转
        AnnotationConfigApplicationContext app=new AnnotationConfigApplicationContext(MyConfig.class);
        System.out.println("IoC容器初始化成功!!");
        Object bean1 = app.getBean("car");

        System.out.println(bean1 );
        app.close();
    }
}

2 赋值组件又名自动装配组件(Injection Components)

3 织入组件(Weave Components)

注解名称说明
ApplicationContextAware可以通过这个上下文环境得到Spring容器中的Bean
BeanDefinitionRegistryPostProcessorBeanDefinitionRegistryPostProcessor实现了BeanFactoryPostProcessor接口,是Spring框架的BeanDefinittionRegistry的后处理器,用来注册额外的BeanDefinition

4 切面组件(Aspect Components)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值