Spring注解的使用之配置组件

配置组件(Configur Components)

@Configuration

把一个类作为一个IOC容器,它的某个方法头上如果注册了@Bean,就会作为这个Spring容器中的Bean
首先创建一个Bean

public class Student {
    private int age;
    private String name;

    public Student() {
    }

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

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

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

在类上注解@Configruation标志这个类是一个配置类
注意:new Student(12,“student”)在Spring中不是直接使用,而是根据方法的内容用原型模式重新new的对象,这里的声明只是告诉Spring怎么创建对象

@Configuration
public class MyConfig {
    @Bean
    public Student student(){
        return new Student(12,"student");
    }
}

使用

public class MyTest {
    @Test
    public void test(){
        ApplicationContext app=new AnnotationConfigApplicationContext(MyConfig.class);
        Object bean=app.getBean("student");
        System.out.println(bean);

    }
}

结果
在这里插入图片描述
验证Bean是否是单例的

    @Test
    public void test(){
        ApplicationContext app=new AnnotationConfigApplicationContext(MyConfig.class);
        Object bean=app.getBean("student");
        Object bean1=app.getBean("student");
        System.out.println(bean==bean1);
    }
}

结果
在这里插入图片描述
这里说明了Spring中创建的student对象时单例的
补充:

Object bean2=app.getBean(Student.class);可以根据类拿到对象
对象的名称拿的是方法名
修改方法名

    @Bean
    public Student student1(){
        return new Student(12,"student");
    }
}
Object bean1=app.getBean("student1");

结果可以得出直接使用@Bean配置的对象名使用的是其方法名,我们也可以使用@Bean"自定义名字")去自定义对象名
在这里插入图片描述

@ComponentScan

在配置类上添加@ComponentScan注解,该注解会默认扫描该类所在的包下所有的配置类,相当于XML中的<context:component-scan>
使用@ComponentScan可以自定义规则,它的默认规则是扫描使用(@Service、@Controller、@Repostory、@Component)注解的Bean,默认的就不举例了
第一种:

@Configuration
@ComponentScan(value = "com.hyg.project",includeFilters ={@ComponentScan.Filter(type = FilterType.ANNOTATION,value = Controller.class)} )
public class MyConfig1 {
}

@Controller
public class MyController {
}
public class MyTest {
    @Test
    public void test(){
        ApplicationContext app=new AnnotationConfigApplicationContext(MyConfig1.class);
        String[] beanDefinitionNames = app.getBeanDefinitionNames();
        System.out.println(Arrays.toString(beanDefinitionNames)
        .replaceAll("\\[|\\]","")
        .replaceAll(",","\n"));
    }
}

结果:
在这里插入图片描述
第二中直接指定某个类

@Configuration
@ComponentScan(value = "com.hyg.project",includeFilters ={@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = MyController.class)} )
public class MyConfig1 {
}

第三种自己定义规则
看@Filter注解里的value属性类型是一个FilterType,所以可以自己创建一个类去实现FilterType接口来实现加载的配置
在这里插入图片描述
自定义过滤类

public class MyFilter 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 有就加载 没有就返回false 表示不加载过滤掉
        if (className.contains("er")){
            return true;
        }
        return false;
    }
}

将controller、service、dao的注解去掉

//@Repository
public class MyDao {
}
//@Service
public class MyService {
}
//@Controller
public class MyController {
}

测试结果,可以看出在指定的包名下MyDao跟Student因为类名里不包含er所以被过滤掉了,这种方式就可以自己去自定义规则选择需要加载进Spring IOC容器里的Bean
在这里插入图片描述

@Scope

用于指定scope作用域的(用在类上),scope默认是单例的

@Configuration
public class MyConfig {

    /**
     * scope里可以填四个参数 :prototype:原型,多例
     *                       singleton:单例
     *                       request:主要应用于web模块,同一次请求只创建一个实例
     *                       session:主要应用于web模块,同一个session值创建一个实例
     *
     * @return
     */
    @Scope("prototype")
    @Bean
    public Student student(){
        return new Student(12,"student");
    }
}

使用prototype属性测试

public class MyTest {
    @Test
    public void test(){
        ApplicationContext app=new AnnotationConfigApplicationContext(MyConfig.class);
        Object student = app.getBean("student");
        Object student1 = app.getBean("student");
        System.out.println(student==student1);
    }
}

结果:可以得出每获取一个对象就会创建一次新对象,多例情况
在这里插入图片描述

@Lazy

配置Bean在我们使用的时候才去加载,而不是启动时就加载,默认是非延迟加载,延迟加载 只针对单例Bean起作用
验证之前

@Configuration
public class MyConfig {
    @Bean
    public Student student(){
        System.out.println("将对象添加到IOC容器中");
        return new Student(12,"student");
    }
}
public class MyTest {
    @Test
    public void test(){
        ApplicationContext app=new AnnotationConfigApplicationContext(MyConfig.class);
        System.out.println("IOC容器初始化完成");
        Object student = app.getBean("student");
        System.out.println(student);
    }
}

结果:可以看到Spring是先初始化Bean才完成容器的初始化
在这里插入图片描述
在@Bean上使用@Lazy
结果:当调用getBean方法的时候才初始化对象
在这里插入图片描述

@Conditional

作用是按照一定的条件判断,满足条件才给容器注册Bean
假设当系统的运行时Windows,就加载条件满足的Bean,当操作系统是Linux的就加载符合条件的Bean
首先@Conditional注解的value属性是一个继承Condition的类,所以我们需要自定义一个类去实现Condition

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
    Class<? extends Condition>[] value();
}

判断符合Windows系统的类

public class WinCondition  implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
       //拿到Bean的工厂
         ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
        //拿到环境变量
         Environment environment = conditionContext.getEnvironment();
         //拿到系统的名字
        String property = environment.getProperty("os.name");
        //如果操作系统包含windows系统的返回true
        if (property.contains("Windows")){
            return true;
        }
        return false;
    }
}

判断符合Linux系统的类

public class LinuxCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
        Environment environment = conditionContext.getEnvironment();
        String property = environment.getProperty("os.name");
        //如果操作系统包含Linux系统的返回true
        if (property.contains("Linux")) {
            return true;
        }
            return false;
        }
}

配置类,@Conditional的使用是在需要做条件判断的Bean上注解,下面的例子我给前面两个Bean判断是windows系统的就注册Bean,第三个符合Linux系统的才祖册

@Configuration
public class MyConfig {
    @Conditional(WinCondition.class)
    @Bean
    public Student student(){
        System.out.println("将对象添加到IOC容器中");
        return new Student(12,"student");
    }
    @Conditional(WinCondition.class)
    @Bean
    public Student studentOne(){
        System.out.println("将对象1添加到IOC容器中");
        return new Student(12,"student");
    }
    @Conditional(LinuxCondition.class)
    @Bean
    public Student studentTwo(){
        System.out.println("将对象2添加到IOC容器中");
        return new Student(12,"student");
    }
}
    @Test
    public void test(){
        ApplicationContext app=new AnnotationConfigApplicationContext(MyConfig.class);
        System.out.println("IOC容器初始化完成");
        String[] beanDefinitionNames = app.getBeanDefinitionNames();
        System.out.println(Arrays.toString(beanDefinitionNames)
        .replaceAll("\\[|\\]","")
        .replaceAll(",","\n"));
    }
}

结果,当前系统的windows的所以可以看到满足条件的Bean注册成功,第三个Bean不满足,因此没有注册
在这里插入图片描述
下面配置环境切到Linux,测试
在这里插入图片描述
测试结果
在这里插入图片描述
从上面的演示可以知道@Conditional可以动态的去判断这个Bean是否符合注册的条件,在开发中可以根据业务场景设置想祖册谁就注册谁。

@Import

导入外部资源,可以手动指定加载第三方资源
简单使用,创建一个新的类不需要做任何事情

public class Teacher {
}

使用@Import在value指定类进行加载

@Configuration
@Import(value = Teacher.class)
public class Config {

}

直接看测试结果,Teacher这个类被成功的加载到容器中,这就是@Import的简单使用,只需要简单的配置就可以将Bean加载到容器,不需要我们的Bean做任何事情
在这里插入图片描述
实现ImportSelector 自定义规则,在String数组中指定类路径

public class MyImport implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"com.hyg.project.entity.Teacher",
                             "com.hyg.project.entity.Student"};
    }
}

使用

@Configuration
@Import(value = MyImport.class)
public class Config {

}

结果,成功注册
在这里插入图片描述
实现ImportBeanDefinitionRegistrar,灵活的根据自己的逻辑,去注册需要的Bean

public class MyImport implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        //逻辑自定义判断  假设 判断容器是否有Student类和Teacher 这两个类,才可以把School对象注册到容器中
        boolean b =beanDefinitionRegistry.containsBeanDefinition("com.hyg.project.entity.Teacher");
        boolean b1 = beanDefinitionRegistry.containsBeanDefinition("com.hyg.project.entity.Student");
        //判断是否存在,都存在就注册School类
        if (b&&b1){
            //new beanDefinition 传入需要注册的类
            BeanDefinition beanDefinition=new RootBeanDefinition(School.class);
            //自定义类名 并传入beanDefinition
            beanDefinitionRegistry.registerBeanDefinition("school",beanDefinition);
        }

    }
}
@Configuration
@Import(value = {MyImport.class, com.hyg.demo.MyImport.class})
public class Config {
}

结果
在这里插入图片描述
验证没有Student对象看看是否注册School对象,从结果可以看出条件不成立时没有注册School对象
在这里插入图片描述
注意:用@Import导入的类名默认是全类名,而不是默认类名首字母小写

把需要注册的对象封装为FactoryBean

public class MyFactoryBean implements  FactoryBean<School> {
    @Override
    public School getObject() throws Exception {
        return new School();
    }

    @Override
    public Class<?> getObjectType() {
        return School.class;
    }
        //设置是否是单例,默认是true  可以设置false为多例
    @Override
    public boolean isSingleton() {
        return true;
    }
}
@Configuration
public class Config {
    @Bean
    public MyFactoryBean school(){
        return new MyFactoryBean();
    }
}

结果 school对象成功注入容器,并且可以发现我们在配置类中配置的Bean是MyFactoryBean ,而通过getBean可以拿到school,并且实际拿到的类是School,
在这里插入图片描述
以下方法可以拿到school对象对应的factoryBean对象,在对象名前加&

Object factoryBean=app.getBean("&school")

给Ioc容器注册Bean的总结

  1. @Bean 直接导入单个类
  2. @ComponentScan 默认扫描(@Controller、@Service、@Repostory、@Component)
  3. @Import 快速给容器导入Bean
    1. @Import 直接参数导入
    2. 实现ImportSelector 自定义规则实现
    3. 实现ImportBeanDefinitionRegistrar,获得BeanDefinitionRegistry可以手动直接往Ioc容器中注入
  4. FactoryBean 把需要注册的对象封装为FactoryBean
    1. FactoryBean 负责将Bean注册到Ioc容器的Bean
    2. BeanFactory 从Ioc容器中获取的Bean对象
    3. 使用注解@PostConstruct和@PreDestroy
    4. 实现BeanPostProcessor接口直接重写BeanPostProcessor 里面的默认方法

Bean生命周期的注解

有两种写法

  1. 配置@Bean的参数
  2. 分别实现 InitializingBean和 DisposableBean
    作用就是在Bean初始化或者销毁时做一些事情
    先创建一个Car类
public class Car {
    public Car() {
        System.out.println("调用构造方法");
    }
    public void addOil(){
        System.out.println("行驶前加油");
    }
    public void run(){
        System.out.println("正在行驶");
    }
    public void stop(){
        System.out.println("停车");
    }
}
```java
@Configuration
public class Config {
    @Bean(initMethod = "addOil",destroyMethod = "stop")
    public Car car(){
        return new Car();
    }
}

结果中看到在car对象初始化时方法addOil被调用了
在这里插入图片描述
关闭容器

    ((AnnotationConfigApplicationContext) app).close();

可以看到对象被销毁时调用了stop方法
在这里插入图片描述
第二种方法

@Component
public class Train implements InitializingBean, DisposableBean {
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("行驶前加油");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("停车");
    }
}
@Configuration
@ComponentScan("com.hyg.project.entity")
public class Config {
    @Lazy
    @Bean(initMethod = "addOil",destroyMethod = "stop")
    public Car car(){
        return new Car();
    }
}

结果
在这里插入图片描述
在这里插入图片描述
第三种,使用注解

@Component
public class Plane {
    public Plane() {
        System.out.println("调用构造方法");
    }

    @PostConstruct
    public void before(){
        System.out.println("飞机起飞前");
    }
    public void fly(){
        System.out.println("飞机在飞行");
    }
    @PreDestroy
    public void landing(){
        System.out.println("飞机降落");
    }
}

结果
在这里插入图片描述
在这里插入图片描述
第四种,实现BeanPostProcessor接口
直接重写BeanPostProcessor 里面的默认方法,将默认修饰符default改为public

@Component
public class BeanPost implements BeanPostProcessor {
    @Override
    @Nullable
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        //这里可以拿到Bean的信息
        System.out.println("Bean调用构造方法后"+beanName+"---"+bean);
        return bean;
    }

    @Override
    @Nullable
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("Bean被销毁后后"+beanName+"---"+bean);
        return bean;
    }
}

启动容器,可以看到上面的方法可以拿到各个Bean的信息,可以自己判断哪些Bean需要初始化或者销毁时处理事情,
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值