Automatic assembly of Spring annotation-driven development

review

Component registration for Spring annotation-driven development

The life cycle of Spring annotation-driven development

Prelude: attribute assignment

Assign a value to the Person class

public class Person {

    @Value("#{2.3*10}")
    private Integer age;
    @Value("张三")
    private String name; //setter和getter方法自行脑补...}

Create configuration class

@Configuration
public class MyConfigOfPropertyValues {

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

test

@Test
public void test01(){
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfigOfPropertyValues.class);
    Person person = (Person) applicationContext.getBean("person");
    System.out.println(person);
}

The result is as follows

 Take out the value in the configuration file and assign it

① Create a configuration file person.properties

person.nickName=小六子

② In order not to affect the previous test code, a new Person1 is created

public class Person1 {

    @Value("赵六")//基本数据类型都可
    private String name;
    @Value("#{20+3}")//SpEL表达式
    private Integer age;
    @Value("${person.nickName}")//EL表达式
    private String nickName;

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    public String getNickName() {
        return nickName;
    }

    public void setNickName(String nickName) {
        this.nickName = nickName;
    }

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

Add @PropertySource annotation to the configuration file

@Configuration
@PropertySource({"classpath:person.properties"})//加载配置文件位置
public class MyConfigOfPropertyValues {

    @Bean
    public Person1 person1(){
        return new Person1();
    }
}

test

@Test
public void test01(){
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfigOfPropertyValues.class);
    Person1 person1 = (Person1) applicationContext.getBean("person1");
    System.out.println(person1);
}

 The result is as follows

Once the configuration file is loaded, it enters the Spring container environment, so it can also be obtained directly through the key in the Java code 

@Test
public void test01(){
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfigOfPropertyValues.class);
    Person1 person1 = (Person1) applicationContext.getBean("person1");
    ConfigurableEnvironment environment = applicationContext.getEnvironment();//获取环境信息
    String nickName = environment.getProperty("person.nickName");//根据key获取值
    System.out.println(person1);
    System.out.println(nickName);
}

The result is as follows

Finale: automatic assembly

Spring uses dependency injection (DI) to complete the dependency assignment of each component in the IOC container

@Autowired annotation (provided by Spring)

● By default, find the corresponding components in the container according to the type first

① Create dao / service / controller and add @Autowired annotation

Take BookService as an example

@Service
public class BookService {

    @Autowired
    private BookDao bookDao;

    @Override
    public String toString() {
        return "BookService{" +
                "bookDao=" + bookDao +
                '}';
    }
}

② Configuration class

@ComponentScan({"com.spring.annotation.controller", "com.spring.annotation.dao", "com.spring.annotation.service"})
@Configuration
public class MyConfigOfAutowired {
}

③ Test

@Test
public void test01(){
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfigOfAutowired.class);
    BookService bookService = applicationContext.getBean(BookService.class);
    BookDao bookDao = applicationContext.getBean(BookDao.class);
    System.out.println(bookService);
    System.out.println(bookDao);
}

The result is as follows

Two BookDao objects are the same

● If there are two beans of the same type, find the corresponding bean in the container according to the attribute name at the time of injection

BookDao

@Repository
public class BookDao {//默认注入容器的id是类名首字母小写
    
    private String label = "1";//添加一个标识符

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }
}

Configuration class (At this time, there are two BookDao type beans in the container)

@ComponentScan({"com.spring.annotation.controller", "com.spring.annotation.dao", "com.spring.annotation.service"})
@Configuration
public class MyConfigOfAutowired {
    
    @Bean(name = "bookDao2")//手动添加bean, 这样容器中就有两个BookDao的bean了
    public BookDao bookDao2(){
        BookDao bookDao = new BookDao();
        bookDao.setLabel("2");
        return bookDao;
    }
}

test

@Test
public void test01(){
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfigOfAutowired.class);

    BookService bookService = applicationContext.getBean(BookService.class);
    System.out.println(bookService);

    BookDao bookDao = (BookDao) applicationContext.getBean("bookDao2");
    System.out.println(bookDao);

    applicationContext.close();
}

The results are as follows: Explain that the injection result of @Autowired is to find the corresponding label=1 bean in the container according to the property name bookDao

● You can also use the @Qualifier annotation to force a specific bean to be specified

@Qualifier("bookDao2")
@Autowired
BookDao bookDao;

The result is as follows

 ● If there is no corresponding bean in the container at all, the injection will report an error at this time. The reason is that the required attribute of @Autowired is true by default, so just set it to false

@Qualifier("bookDao2")
@Autowired(required = false)
BookDao bookDao;

● When there are multiple injections that need to use @Qualifier, it will be more troublesome. At this time, @Primary can be used (the bean specified for the annotation is preferred by default during auto- wiring )

Configuration class

@ComponentScan({"com.spring.annotation.controller", "com.spring.annotation.service", "com.spring.annotation.dao"})
@Configuration
public class MyConfigOfAutowired {

    @Primary//默认首选使用
    @Bean(name = "bookDao2")
    public BookDao bookDao(){
        BookDao bookDao = new BookDao();
        bookDao.setLabel("2");
        return bookDao;
    }
}

 The result is as follows

 If @Qualifier is used to specify explicitly at this time, it is still subject to the @Qualifier annotation, and the attribute name at this time will not work

@Service
public class BookService {

    @Qualifier("bookDao")//绝对指定使用
    @Autowired(required = false)
    BookDao bookDao2;

    @Override
    public String toString() {
        return "BookService{" +
                "bookDao2=" + bookDao2 +
                '}';
    }
}

The result is as follows

 

@Resource annotation (JSR250 specification)

● The same function of automatic injection can be realized, but the difference with @Autowired is that the @Resource annotation can only choose to inject specific beans based on the id name. If the name attribute is not given, the default is to search in the container according to the field bookDao, once Given the name attribute, search in the container according to the value of the name attribute.

@Service
public class BookService {

    @Resource(name = "bookDao2")
    BookDao bookDao;

    @Override
    public String toString() {
        return "BookService{" +
                "bookDao=" + bookDao +
                '}';
    }
}
@ComponentScan({"com.spring.annotation.controller", "com.spring.annotation.service", "com.spring.annotation.dao"})
@Configuration
public class MyConfigOfAutowired {

    @Bean(name = "bookDao2")
    public BookDao bookDao(){
        BookDao bookDao = new BookDao();
        bookDao.setLabel("2");
        return bookDao;
    }
}

 The result is as follows 

 

@Service
public class BookService {

    @Resource
    BookDao bookDao;

    @Override
    public String toString() {
        return "BookService{" +
                "bookDao=" + bookDao +
                '}';
    }
}

The result is as follows

 

@Service
public class BookService {

    @Resource
    BookDao bookDao2;

    @Override
    public String toString() {
        return "BookService{" +
                "bookDao2=" + bookDao2 +
                '}';
    }
}

The result is as follows

 ps: This annotation is not an annotation provided by Spring, so it can only be used alone. It cannot be used in conjunction with @Primary annotation / @Qualifier annotation, etc., and there is no required attribute.

@Inject annotation (JSR330 specification)

Need to import dependencies
<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>
● @Inject annotations can only be used after the dependencies are imported. The function is the same as @Autowired, the difference is that the @Inject annotation has no required attribute

@Service
public class BookService {

    @Qualifier("bookDao")//Absolutely specified use
    @Inject
    BookDao bookDao;

    @Override
    public String toString() {
        return "BookService{" +
                "bookDao=" + bookDao +
                '}';
    }
}
@ComponentScan({"com.spring.annotation.controller", "com.spring.annotation.service", "com.spring.annotation.dao"})
@Configuration
public class MyConfigOfAutowired {

    @Primary//Preferred by default
    @Bean(name = "bookDao2")
    public BookDao bookDao(){
        BookDao bookDao = new BookDao();
        bookDao.setLabel("2");
        return bookDao;
    }
}

The above configuration uses bookDao2 first by default, and uses bookDao when @Qualifier is absolutely specified

The result is as follows

 Summary: Through the above comparison, I believe that the difference between the three annotations is clear at a glance.Many instructions on the Internet do not explain the difference between the three annotations in detail.Usually, we can use @Autowired annotations for web projects, and the functions are the most comprehensive.

 Where @Autowired is located

Properties, constructors, methods, parameters

The above tests are all marked on the attributes, and then the tests are marked in other locations

● Mark in the method

Create a new Boss class

public class Boss {
    
    private Car car;

    public Car getCar() {
        return car;
    }

    public void setCar(Car car) {
        this.car = car;
    }

    @Override
    public String toString() {
        return "Boss{" +
                "car=" + car +
                '}';
    }
}

Car class

public class Car {

    public Car() {
        System.out.println("Car object is created...");
    }

    /**
     * Customize an initialization object method
     */
    public void init(){
        System.out.println("Car object is initialized...");
    }

    /**
     * Customize a method of destroying objects
     */
    public void destroy(){
        System.out.println("Car object is destroyed...");
    }
}

Configuration class

@ComponentScan({"com.spring.annotation.controller", "com.spring.annotation.service", "com.spring.annotation.dao"})
@Configuration
public class MyConfigOfAutowired {

    @Primary//Preferred by default
    @Bean(name = "bookDao2")
    public BookDao bookDao(){
        BookDao bookDao = new BookDao();
        bookDao.setLabel("2");
        return bookDao;
    }

    @Bean
    public Boss boss(){
        return new Boss();
    }

    @Bean
    public Car car(){
        return new Car();
    }
}

  test

@Test
    public void test01(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfigOfAutowired.class);

        BookService bookService = applicationContext.getBean(BookService.class);
        System.out.println(bookService);

        Boss boss = applicationContext.getBean(Boss.class);
        Car car = applicationContext.getBean(Car.class);
        System.out.println(boss+" <==Is it equal ==> "+car);
    }

The result is as follows

 

 Summary: When the annotation is marked on the setter method, the method will be called when the container creates the current object, and the assignment is completed. The value is obtained from the ioc container, that is, the two objects displayed in the result are equal.

ps: After using @Bean to add the component to the container, if you want to get Car, you can get it directly from the parameters (at this time, you can add @Autowired or not), as follows

@Bean
public Boss boss(@Autowired Car car){
  
  //At this time, if @Autowired is added or not, the current car object will be automatically obtained from the ioc container
    Boss boss = new Boss();
    boss.setCar(car);
    return boss;
}

● Marked on the parameterized constructor

ps: When marked on the setter method, by default, the parameterless constructor will be called to create the object when the container starts, and then the initialization operation will be performed

Configuration class

@ComponentScan({"com.spring.annotation.controller", "com.spring.annotation.service", "com.spring.annotation.dao"})
@Configuration
public class MyConfigOfAutowired {

    @Autowired
    private Car car;

    @Primary//Preferred by default
    @Bean(name = "bookDao2")
    public BookDao bookDao(){
        BookDao bookDao = new BookDao();
        bookDao.setLabel("2");
        return bookDao;
    }

    @Bean
    public Boss boss(){
        return new Boss(car);
    }

    @Bean
    public Car car(){
        return new Car();
    }
}

Boss class, marked on the parameterized constructor

public class Boss {

    private Car car;

    @Autowired
    public Boss(Car car) {
        this.car = car;
        System.out.println("Boss's parameterized constructor is called...");
    }

    public Car getCar() {
        return car;
    }

    public void setCar(Car car) {
        this.car = car;
    }

    @Override
    public String toString() {
        return "Boss{" +
                "car=" + car +
                '}';
    }
}

The result is as follows

ps: In addition, if there is only one parameterized constructor in the Boss class, the @Autowired annotation at this time can be omitted, and the container will find it for injection by default  

● The effect of putting on the parameters (parameters of the parameter constructor/parameters of the setter method) is the same, so it will not be posted anymore

@Profile annotation (emphasis)

● Spring provides functions that can dynamically activate and switch a series of components according to the current environment

Development environment / test environment / production environment...

For example: Data source switching: A database (development environment) / B database (test environment) / C database (production environment)

Configuration steps

① Introduce c3p0 data source dependency
<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.5.2</version>
</dependency>
② Introduce database driver dependency
<dependency>
    < groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.44</version>
</dependency>
③ Install the database (I used the docker container to create the database in the virtual machine )

④ Configuration file, stored in the classpath directory

db.user=root
db.password=root
db.driverClass=com.mysql.jdbc.Driver
db.testUrl=jdbc:mysql://192.168.5.134:3306/test
db.devUrl=jdbc:mysql://192.168.5.134:3306/dev
db.prodUrl=jdbc:mysql://192.168.5.134:3306/prod

⑤ Configuration class MyConfigOfProfile

@PropertySource("classpath:/dbconfig.properties")
@Configuration
public class MyConfigOfProfile implements EmbeddedValueResolverAware {

    @Value("${db.user}")//The first way
    private String user;

    private StringValueResolver valueResolver;//The second way

    private String driverClass;

    @Bean(name = "testDataSource")
    public DataSource dataSourceTest(/*第三种方式*/@Value("${db.password}") String pwd, @Value("${db.testUrl}") String url) throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl(url);
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        return dataSource;
    }

    @Bean(name = "devDataSource")
    public DataSource dataSourceDev(@Value("${db.password}") String pwd, @Value("${db.devUrl}") String url) throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl(url);
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        return dataSource;
    }

    @Bean(name = "prodDataSource")
    public DataSource dataSourceProd(@Value("${db.password}") String pwd, @Value("${db.prodUrl}") String url) throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl(url);
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        this.valueResolver = resolver;
        driverClass = valueResolver.resolveStringValue("${db.driverClass}");
    }
}

⑥ Test

public class IOCTest_Profile {

    @Test
    public void test01(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfigOfProfile.class);
        String[] names = applicationContext.getBeanNamesForType(DataSource.class);
        for (String name: names){
            System.out.println(name);
        }
        applicationContext.close();
    }
}

 ⑦ The results are as follows

 Activate the data source (combined with @Profile annotation)

ps: If not specified, the component can be registered in any environment; @Profile dynamically specifies that the component can be registered in the container in a specific environment; beans with the @Profile logo can only be injected into the container when they are activated.

1. @Profile annotation on the bean

Configuration class

@PropertySource("classpath:/dbconfig.properties")
@Configuration
public class MyConfigOfProfile implements EmbeddedValueResolverAware {

    @Value("${db.user}")//The first way
    private String user;

    private StringValueResolver valueResolver;//The second way

    private String driverClass;

    @Bean
    public Blue blue(){
        return new Blue();
    }

    @Profile("test")//Mark as test
    @Bean(name = "testDataSource")
    public DataSource dataSourceTest(/*第三种方式*/@Value("${db.password}") String pwd, @Value("${db.testUrl}") String url) throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl(url);
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        return dataSource;
    }

    @Profile("dev")//The mark is dev
    @Bean(name = "devDataSource")
    public DataSource dataSourceDev(@Value("${db.password}") String pwd, @Value("${db.devUrl}") String url) throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl(url);
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        return dataSource;
    }

    @Profile("prod")//The mark is prod
    @Bean(name = "prodDataSource")
    public DataSource dataSourceProd(@Value("${db.password}") String pwd, @Value("${db.prodUrl}") String url) throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl(url);
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        this.valueResolver = resolver;
        driverClass = valueResolver.resolveStringValue("${db.driverClass}");
    }
}

1) Method 1: Specify the VM startup parameters of the IDE (I use idea, eclipse is also similar)

Parameters:  -Dspring.profiles.active=<profile value>

test

@Test
public void test01(){
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfigOfProfile.class);
    String[] names = applicationContext.getBeanNamesForType(DataSource.class);
    for (String name: names){
        System.out.println(name);
    }
    applicationContext.close();
}

The result is as follows

2) Method 2: Code specification

@Test
public void test01(){
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();//Use no parameter method
    applicationContext.getEnvironment().setActiveProfiles("dev", "prod");//指定环境
    applicationContext.register(MyConfigOfProfile.class);//Register configuration class
    applicationContext.refresh();//Refresh container start
    String[] names = applicationContext.getBeanNamesForType(DataSource.class);
    for (String name: names){
        System.out.println(name);
    }
    applicationContext.close();
}

The result is as follows

ps: You can specify @Profile("default") to specify the default environment, and inject the default bean without any configuration

 2. @Profile annotation on the configuration class

Configuration class

@Profile("test")
@PropertySource("classpath:/dbconfig.properties")
@Configuration
public class MyConfigOfProfile implements EmbeddedValueResolverAware {...}

The configuration environment at this time is the same as on the bean, the difference is that once the configured environment does not match the environment specified by the annotation on the configuration class, the configuration class will not be loaded

3. Beans without an environment flag are loaded in any environment (unless the configuration class is not loaded)

@Bean
public Blue blue(){
    return new Blue();
}

Add two more lines of code to the test class

Blue blue = applicationContext.getBean(Blue.class);
System.out.println(blue.getClass());

 The result is as follows

Guess you like

Origin blog.csdn.net/ip_JL/article/details/85450926