Spring注解版学习

Spring纯注解开发

Spring纯注解和Spring的xml相比而言,注解版是非常容易的。但是论学习的话,推荐使用xml来进行入门,这样方便进行理解。掌握了spring的xml,那么纯注解就比较简单了。

注意一个小习惯:

在spring中,习惯把注册到SpirngIOC容器中的对象叫做bean组件。

首先参考下xml的格式和注解版的来进行对比:

1、XML版本

<?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 class="com.guang.User" id="user" name="user1,user2,user3" scope="singleton">
        <property name="id" value="1"/>
        <property name="name" value="guang"/>
    </bean>
</beans>

对应的类(上面配置的是使用无参构造方法以及set方法来给user来进行赋值的):

public class User {
    private Integer id;
    private String name;

    public void setId(Integer id) {
        this.id = id;
    }

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

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

测试类:

public class MainConfig {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-				beans.xml");
        User user = applicationContext.getBean("user", User.class);
        System.out.println(user);
    }
}

注意下:

applicationContext.getBean(name);// 这里的name可以是id,也可以是别名。但是在注解版的里面放弃了id,直接使用name,也就是别名来进行获取bean

2、注解版

2.1、@Configuration

使用了注解版,那么必然有一个类是用来代替xml配置文件的。使用一个注解@Configuration来进行代替。

@Configuration注解是配置在类上的。@Configuration的底层注解是一个@Component,将标注的所在类注册成一个bean放入到SpringIOC容器中去。所以配置类也是一个bean组件。

@Configuration
public class MainConfig{
    
}

使用了@Configuration注解所标注的类就相当于是xml文件中的配置约束文件中的标签:

<?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">

</beans>

在@Configration中,可以查看到spring5中引入了一个特殊的属性proxyBeanMethods,表示代理bean的方法,默认是true

boolean proxyBeanMethods() default true;

表示的是如果配置类上下文中,多个@Bean方法中,如果需要引入到另外一个使用@Bean方法来给当前@Bean所修饰的方法来注入依赖的对象的时候,根据proxyBeanMethods是true还是false来决定是否从容器中进行获取。同时,网上很多博客都说,这也决定了加载bean的速度,如果是false,那么速度会加快;如果是true,加载速度会变慢。

如果是true的时候,会从容器中获取;如果是false,会new一个新的对象来进行属性注入。

如果是true的时候,会使用cglib来实现动态代理,会从容器中检查是否有这个对象,有的获取得到对象给对象赋值上;如果是false的时候,则不会从容器中获取得到对象,而是new一个新的对象来给bean组件进行赋值。

写两个类测试下:

@ToString
@Data
public class Person {
    
    private Integer id;
    private String username;
    private Wile wile;
}

@ToString
@Data
public class Wile {
    private String name;
    private Integer id;

    public Wile(){
        System.out.println("wile被调用了");
    }
}

// 将其置为false,每次都会新new一个对象。可以看其构造函数是否执行了,来进行查看
@Configuration(proxyBeanMethods = false)
@ComponentScan(basePackages = {"com.guang.entity"})
public class MainConfig {
    @Bean("wife")
    public Wile getWile(){
        Wile wile = new Wile();
        wile.setId(1);
        wile.setName("chenyang");
        return wile;
    }

    @Bean("person")
    public Person getPerson(){
        Person person = new Person();
        person.setWile(getWile());
        return person;
    }
}

在bean容器中进行获取的时候,是false的时候,可以发现每次Wife的无参构造函数会执行两次;是true的时候,只会执行一次。

参考着上面的来进行书写,因为标签是在标签里面来写的,所以我们就可以在在配置类中来进行配置bean

2.2、@Bean

@Bean注解是使用在方法上的。方法的返回值代表着是bean的类型,方法名字代表的是bean的name。前提是如果没有在@Bean中的value属性中指定,如果在@Bean中的value属性中指定了,那么使用value的值来代替对应的值。

@Configuration
public class MainConfig{
	@Bean
	public User user(){
    	return new User();
	}
}    

这段代码就将user组件注册到SpringIOC容器中去了。

注解测试:

public class AnnotationTest {
    @Test
    public void testOne(){
        // 获取得到容器
        AnnotationConfigApplicationContext applicationContext = new 			    					   			AnnotationConfigApplicationContext(MainConfig.class);
        User user = applicationContext.getBean("user", User.class);
        System.out.println(user);
    }
}

注意下:

applicationContext.getBean(name);// 这里的name是方法名字,但是我们更多的不适用方法名字,而是给这个bean的name指定下

在这里使用注解@Bean来将bean的name修改下:

@Configuration
public class MainConfig{
	@Bean(value="user")
	public User getUser(){
    	return new User();
	}
}

测试:

public class AnnotationTest {
    @Test
    public void testOne(){
        // 获取得到容器
        AnnotationConfigApplicationContext applicationContext = new 			    					   			AnnotationConfigApplicationContext(MainConfig.class);
        User user = applicationContext.getBean("user", User.class);
        System.out.println(user);
    }
}

此时,如果再通过方法名字当做name来进行调用,会出现找到这个

org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'getUser' available

2.3.1、属性注入

在xml中属性注入大概分为set方法注入、构造方法注入、P标签注入以及Spel表达式注入,但是最常用的就是set方法注入和Spel表达式注入。

利用@Value来给类中的属性来注入值

public class Car {
    @Value("1")
    private Integer id;
    @Value("guang")
    private String name;

    public Integer getId() {
        return id;
    }

    public String getName() {
        return name;
    }
}

使用@Value注解的同时,必须要将这个类加入到SpringIOC容器中才可以来进行使用这种便捷的方式使用。

@Autowired注解

标注在一个变量上的时候,先从容器中找到这个类型的bean,如果有多个,会存在着错误。因为这个是通过byType的方式来进行注入的;如果存在着相同类型的bean的时候,可以指定@Qulifierd来指定相同类型的name来进行注入。

2.3.2、@DependOn注解

标注了加载bean的优先级顺序,如果一个bean依赖了另外一个bean,那么被依赖的bean将会优先加载,从而使用这种方式来指定bean组件的优先级顺序。当前bean在加载的时候发现依赖了另外的一个bean的时候,先去加载另外一个bean,加载完成之后,再去创建当前的组件。

2.3、XML和注解版的异同

1、都是将bean注入SpringIOC容器中去的,但是又是有区别的。

使用xml方式注入到容器中,我们无法进行更改其创建过程。但是可以使用工厂方法来进行更改。但是使用工厂模式和使用注解相比较,工厂模式显得比较鸡肋,因为代码太多了!况且xml可以做的事情,注解也可以做到,而且注解比较简单,何乐而不为?

2、@Conponent及其衍生注解和@Bean的区别

我们经常在controller、service、dao层中加上注解,这样做的意义是最终将bean定义注册成组件放入到SpringIOC容器中去。
而使用@Bean我们可以自定义其创建过程,将创建好的bean放入到SpringIOC容器中去。
  
二者最大的区别:对对象的创建过程实现自定义。而且对于第三方类来说,我们无法获取得到第三方类的源代码,也就是说我们要将不是我们写的类注册到SpringIOC容器中来,无法使用@Conponent,但是使用@Bean注解就可以简单的将其注入进来。比如说将Druid连接池注册成一个bean的时候,@Conponent注解无法来注册,此时使用@Bean就可以来解决这个问题。

在主配置类中使用:

@Bean
public DruidDataSource druidDataSource(){
    DruidDataSource druidDataSource = new DruidDataSource();
    druidDataSource.setDriverClassName(driver);
    druidDataSource.setUrl(url);
    druidDataSource.setUsername(username);
    druidDataSource.setPassword(password);
    return druidDataSource;
}

2.4、向容器中导入组件的另外几种方式

2.4.1、@Import注解

可以在主配置类中引入其他类做来做springIOC的组件。如:在主配置类中引入User类,将User类注册成SpringIOC容器中的bean

@Import({User.class})
@Configuration
public class MainConfig {}

User类:

public class User {
    private Integer id;
    private String username;
}

测试类:

// 获取得到容器
AnnotationConfigApplicationContext applicationapplicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
for (String beanDefinitionName : applicationapplicationContext.getBeanDefinitionNames()) {
    System.out.println(beanDefinitionName);
}
// 使用@Import注解,使用类型来获取。要是想使用其name来进行获取,那么可以查看下它在容器中的name:com.guang.beans.User.User
User user1 = applicationapplicationContext.getBean( User.class);

2.4.2、Import注解扩展

@Import除了可以将类注册成springIOC的组件之外,还可以引入另外的配置类。因为对于xml文件来说,如果xml针对于controller、service、dao包都用一个xml来保存对应的信息,那么注解版的也有这样的作用。可以将另外的一个配置类引入到主配置类进行使用。

这个配置类中不需要再加上@Configration注解了。

public class SecondConfig {

    @Bean(value="user")
    public User getUser(){
        User user = new User();
        user.setId(2);
        user.setUsername("meng");
        return user;
    }
}

在主配置类中使用:

@Configuration
@Import({SecondConfig.class})
public class MainConfig {}

2.4.3、ImportSelector接口

作用于是批量注册bean组件。需要注意的是:根据类的全限定类名来进行注册的

@Component
public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        // 可以书写多个
        return new String[]{"com.guang.beans.Car"};
    }
}

在获取得到bean的时候,只能够根据类型来获取得到bean。

2.4.4、ImportBeanDefinitionRegistrar 接口

@Component
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
        GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
        genericBeanDefinition.setBeanClass(Flower.class);
        registry.registerBeanDefinition("flower",genericBeanDefinition);
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        ImportBeanDefinitionRegistrar.super.registerBeanDefinitions(importingClassMetadata, registry);
    }
}

这里可以来设置对应的bean的id。

3、总结

在spring中将一个类注册成bean的几种方式:

1、通过xml;
2、通过注解:@Conponent及其衍生注解
3、@Import注解;
4、接口:ImportBeanDefinitionRegistrar和ImportSelector
posted @ 2021-05-31 21:25  写的代码很烂  阅读(87)  评论(0编辑  收藏  举报