Spring |(六)注解开发依赖注入及管理第三方bean

发布于:2024-11-29 ⋅ 阅读:(9) ⋅ 点赞:(0)

学习来源黑马程序员SSM框架教程_Spring+SpringMVC+Maven高级+SpringBoot+MyBatisPlus企业实用开发技术

📚注解开发依赖注入

Spring为了使用注解简化开发,并没有提供构造函数注入setter注入对应的注解,只提供了自动装配的注解实现。

🐇环境准备

在这里插入图片描述

  • 创建一个Maven项目

  • pom.xml添加Spring的依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.10.RELEASE</version>
        </dependency>
    </dependencies>
    
  • 添加一个配置类SpringConfig

    @Configuration
    @ComponentScan("com.itheima")
    public class SpringConfig {
    }
    
  • 添加BookDao、BookDaoImpl、BookService、BookServiceImpl类

    public interface BookDao {
        public void save();
    }
    @Repository
    public class BookDaoImpl implements BookDao {
        public void save() {
            System.out.println("book dao save ..." );
        }
    }
    public interface BookService {
        public void save();
    }
    @Service
    public class BookServiceImpl implements BookService {
        private BookDao bookDao;
    	public void setBookDao(BookDao bookDao) {
            this.bookDao = bookDao;
        }
        public void save() {
            System.out.println("book service save ...");
            bookDao.save();
        }
    }
    
  • 创建运行类App

    public class App {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
            BookService bookService = ctx.getBean(BookService.class);
            bookService.save();
        }
    }
    

  • 环境准备好后,运行后会发现有问题

    在这里插入图片描述

  • 原因是,在BookServiceImpl类中添加了BookDao的属性,并提供了setter方法,但是目前是没有提供配置注入BookDao的,所以bookDao对象为Null,调用其save方法就会报控指针异常

🐇注解实现按照类型注入

  • 在BookServiceImpl类的bookDao属性上添加@Autowired注解。@Autowired可以写在属性上,也可也写在setter方法上,最简单的处理方式是写在属性上并将setter方法删除掉
    @Service
    public class BookServiceImpl implements BookService {
        @Autowired
        private BookDao bookDao;
        
    	//	  public void setBookDao(BookDao bookDao) {
    	//        this.bookDao = bookDao;
    	//    }
        public void save() {
            System.out.println("book service save ...");
            bookDao.save();
        }
    }
    
  • @Autowired是按照类型注入,那么对应BookDao接口如果有多个实现类,比如添加BookDaoImpl2,这个时候再次运行App,就会报错。此时,按照类型注入就无法区分到底注入哪个对象。
    @Repository
    public class BookDaoImpl2 implements BookDao {
        public void save() {
            System.out.println("book dao save ...2");
        }
    }
    
    在这里插入图片描述
  • 解决方案:按照名称注入。先给两个Dao类分别起个名称↓
    @Repository("bookDao")
    public class BookDaoImpl implements BookDao {
        public void save() {
            System.out.println("book dao save ..." );
        }
    }
    @Repository("bookDao2")
    public class BookDaoImpl2 implements BookDao {
        public void save() {
            System.out.println("book dao save ...2" );
        }
    }
    
    • 此时就可以注入成功,但,@Autowired是按照类型注入的,给BookDao的两个实现起了名称,还是有两个bean对象,为什么不报错?
    • 因为@Autowired默认按照类型自动装配,如果IoC容器中同类的Bean找到多个,就按照变量名和Bean的名称匹配。因为变量名叫bookDao而容器中也有一个bookDao,所以可以成功注入。
    • 对应地,下边这种情况就没法实现注入,因为按照类型会找到多个bean对象,此时会按照bookDao名称去找,而IoC容器只有名称叫bookDao1bookDao2的,所以找不到,会报NoUniqueBeanDefinitionException
      在这里插入图片描述

🐇注解实现按照名称注入

  • 当根据类型在容器中找到多个bean,注入参数的属性名又和容器中bean的名称不一致,这个时候就需要使用到@Qualifier来指定注入哪个名称的bean对象。

    @Service
    public class BookServiceImpl implements BookService {
        @Autowired
        @Qualifier("bookDao1")
        private BookDao bookDao;
        public void save() {
            System.out.println("book service save ...");
            bookDao.save();
        }
    }
    
  • @Qualifier注解后的值就是需要注入的bean的名称。

  • @Qualifier不能独立使用,必须和@Autowired一起使用。

🐇简单数据类型注入

  • 简单类型注入的是基本数据类型或者字符串类型,下面在BookDaoImpl类中添加一个name属性,用其进行简单类型注入,并使用@Value注解,将值写入注解的参数中。

    @Repository("bookDao")
    public class BookDaoImpl implements BookDao {
        @Value("itheima")
        private String name;
        public void save() {
            System.out.println("book dao save ..." + name);
        }
    }
    
  • 注意数据格式要匹配,如将"abc"注入给int值,这样程序就会报错。


🔥这个注解似乎跟直接赋值是一个效果,存在的意义是什么?

  • @Value一般会被用在从properties配置文件中读取内容,具体实现如下↓

  • 步骤1:resource下准备properties文件(jdbc.properties)。

    name=itheima888
    
  • 步骤2:使用注解加载properties配置文件,在配置类上添加@PropertySource注解。

    @Configuration
    @ComponentScan("com.itheima")
    @PropertySource("jdbc.properties")
    public class SpringConfig {
    }
    
  • 步骤3:使用@Value读取配置文件中的内容。

    @Repository("bookDao")
    public class BookDaoImpl implements BookDao {
        @Value("${name}")
        private String name;
        public void save() {
            System.out.println("book dao save ..." + name);
        }
    }
    
  • 步骤4:运行App类,查看运行结果,说明配置文件中的内容已经被加载到。
    在这里插入图片描述

  • 注意:

    • 如果读取的properties配置文件有多个,可以使用@PropertySource的属性来指定多个

      @PropertySource({"jdbc.properties","xxx.properties"})
      
    • @PropertySource注解属性中不支持使用通配符*,运行会报错

      @PropertySource({"*.properties"})
      
    • @PropertySource注解属性中可以把classpath:加上,代表从当前项目的根路径找文件

      @PropertySource({"classpath:jdbc.properties"})
      

📚IoC/DI注解开发管理第三方bean

之前定义bean的时候都是在自己开发的类上面写个注解就好,但如果是第三方类,这些类都是在jar包中,没办法在类上面添加注解,这个时候就用到了一个全新的注解@Bean

🐇环境准备

在这里插入图片描述

  • 创建一个Maven项目。
  • pom.xml添加Spring的依赖。
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.10.RELEASE</version>
        </dependency>
    </dependencies>
    
  • 添加一个配置类SpringConfig
    @Configuration
    public class SpringConfig {
    }
    
  • 添加BookDao、BookDaoImpl类。
    public interface BookDao {
        public void save();
    }
    @Repository
    public class BookDaoImpl implements BookDao {
        public void save() {
            System.out.println("book dao save ..." );
        }
    }
    
  • 创建运行类App。
    public class App {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        }
    }
    

🐇注解开发管理第三方bean

需求:在上述环境中完成对Druid数据源的管理。

  • 步骤1:导入对应的jar包。

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.16</version>
    </dependency>
    
  • 步骤2:在配置类中添加一个方法。该方法的返回值就是要创建的Bean对象类型。

    @Configuration
    public class SpringConfig {
        public DataSource dataSource(){
            DruidDataSource ds = new DruidDataSource();
            ds.setDriverClassName("com.mysql.jdbc.Driver");
            ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
            ds.setUsername("root");
            ds.setPassword("root");
            return ds;
        }
    }
    
  • 步骤3:在方法上添加@Bean注解。@Bean注解的作用是将方法的返回值制作为Spring管理的一个bean对象。

    @Configuration
    public class SpringConfig {
    	@Bean
        public DataSource dataSource(){
            DruidDataSource ds = new DruidDataSource();
            ds.setDriverClassName("com.mysql.jdbc.Driver");
            ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
            ds.setUsername("root");
            ds.setPassword("root");
            return ds;
        }
    }
    

    不能使用DataSource ds = new DruidDataSource(),因为DataSource接口中没有对应的setter方法来设置属性。

  • 步骤4:从IoC容器中获取对象并打印。

    public class App {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
            DataSource dataSource = ctx.getBean(DataSource.class);
            System.out.println(dataSource);
        }
    }
    

    如果有多个bean要被Spring管理,直接在配置类中多些几个方法,方法上添加@Bean注解即可。

🐇引入外部配置类

  • 把所有的第三方bean都配置到Spring的配置类SpringConfig中,虽然可以但不利于代码阅读和分类管理。

  • 对于数据源的bean,我们新建一个JdbcConfig配置类,并把数据源配置到该类下。

    public class JdbcConfig {
    	@Bean
        public DataSource dataSource(){
            DruidDataSource ds = new DruidDataSource();
            ds.setDriverClassName("com.mysql.jdbc.Driver");
            ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
            ds.setUsername("root");
            ds.setPassword("root");
            return ds;
        }
    }
    
  • 【法一】使用包扫描引入

    • 步骤1:在Spring的配置类上添加包扫描。
      @Configuration
      @ComponentScan("com.itheima.config")
      public class SpringConfig {
      	
      }
      
    • 步骤2:在JdbcConfig上添加配置注解。JdbcConfig类要放入到com.itheima.config包下,需要被Spring的配置类扫描到即可。
      @Configuration
      public class JdbcConfig {
      	@Bean
          public DataSource dataSource(){
              DruidDataSource ds = new DruidDataSource();
              ds.setDriverClassName("com.mysql.jdbc.Driver");
              ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
              ds.setUsername("root");
              ds.setPassword("root");
              return ds;
          }
      }
      
    • 步骤3:运行程序。这种方式虽然能够扫描到,但是不能很快的知晓都引入了哪些配置类,所以这种方式不推荐使用。
  • 【法二】使用@Import引入(推荐)

    • 步骤1:去除JdbcConfig类上的注解。

      public class JdbcConfig {
      	@Bean
          public DataSource dataSource(){
              DruidDataSource ds = new DruidDataSource();
              ds.setDriverClassName("com.mysql.jdbc.Driver");
              ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
              ds.setUsername("root");
              ds.setPassword("root");
              return ds;
          }
      }
      
    • 步骤2:在Spring配置类中引入。

      @Configuration
      //@ComponentScan("com.itheima.config")
      @Import({JdbcConfig.class})
      public class SpringConfig {
      	
      }
      
      • 扫描注解可以移除。
      • @Import参数需要的是一个数组,可以引入多个配置类。
      • @Import注解在配置类中只能写一次,下面的方式是不允许的
        @Configuration
        //@ComponentScan("com.itheima.config")
        @Import(JdbcConfig.class)
        @Import(Xxx.class)
        public class SpringConfig {
        	
        }
        
    • 步骤3:运行程序。

🐇注解开发实现为第三方bean注入资源

  • 资源类型一:简单数据类型

    • 对应需求:对于下面代码关于数据库的四要素不应该写死在代码中,应该是从properties配置文件中读取。

      public class JdbcConfig {
      	@Bean
          public DataSource dataSource(){
              DruidDataSource ds = new DruidDataSource();
              ds.setDriverClassName("com.mysql.jdbc.Driver");
              ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
              ds.setUsername("root");
              ds.setPassword("root");
              return ds;
          }
      }
      
    • 步骤1:类中提供四个属性。

      public class JdbcConfig {
          private String driver;
          private String url;
          private String userName;
          private String password;
      	@Bean
          public DataSource dataSource(){
              DruidDataSource ds = new DruidDataSource();
              ds.setDriverClassName("com.mysql.jdbc.Driver");
              ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
              ds.setUsername("root");
              ds.setPassword("root");
              return ds;
          }
      }
      
    • 步骤2:使用@Value注解引入值。

      public class JdbcConfig {
          @Value("com.mysql.jdbc.Driver")
          private String driver;
          @Value("jdbc:mysql://localhost:3306/spring_db")
          private String url;
          @Value("root")
          private String userName;
          @Value("password")
          private String password;
      	@Bean
          public DataSource dataSource(){
              DruidDataSource ds = new DruidDataSource();
              ds.setDriverClassName(driver);
              ds.setUrl(url);
              ds.setUsername(userName);
              ds.setPassword(password);
              return ds;
          }
      }
      
  • 资源类型二:引用数据类型

    • 对应需求:假设在构建DataSource对象的时候,需要用到BookDao对象,该如何把BookDao对象注入进方法内让其使用呢?

      public class JdbcConfig {
      	@Bean
          public DataSource dataSource(){
              DruidDataSource ds = new DruidDataSource();
              ds.setDriverClassName("com.mysql.jdbc.Driver");
              ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
              ds.setUsername("root");
              ds.setPassword("root");
              return ds;
          }
      }
      
    • 步骤1:在SpringConfig中扫描BookDao,扫描的目的是让Spring能管理到BookDao,也就是说要让IoC容器中有一个bookDao对象。

      @Configuration
      @ComponentScan("com.itheima.dao")
      @Import({JdbcConfig.class})
      public class SpringConfig {
      }
      
    • 步骤2:在JdbcConfig类的方法上添加参数。引用类型注入只需要为bean定义方法设置形参即可,容器会根据类型自动装配对象。

      @Bean
      public DataSource dataSource(BookDao bookDao){
          System.out.println(bookDao);
          DruidDataSource ds = new DruidDataSource();
          ds.setDriverClassName(driver);
          ds.setUrl(url);
          ds.setUsername(userName);
          ds.setPassword(password);
          return ds;
      }
      
    • 步骤3:运行程序。

📚小结

在这里插入图片描述

  • 知识点1:@Autowired

    名称 @Autowired
    类型 属性注解 或 方法注解(了解) 或 方法形参注解(了解)
    位置 属性定义上方 或 标准set方法上方 或 类set方法上方 或 方法形参前面
    作用 为引用类型属性设置值
    属性 required:true/false,定义该属性是否允许为null
  • 知识点2:@Qualifier

    名称 @Qualifier
    类型 属性注解 或 方法注解(了解)
    位置 属性定义上方 或 标准set方法上方 或 类set方法上方
    作用 为引用类型属性指定注入的beanId
    属性 value(默认):设置注入的beanId
  • 知识点3:@Value

    名称 @Value
    类型 属性注解 或 方法注解(了解)
    位置 属性定义上方 或 标准set方法上方 或 类set方法上方
    作用 为 基本数据类型 或 字符串类型 属性设置值
    属性 value(默认):要注入的属性值
  • 知识点4:@PropertySource

    名称 @PropertySource
    类型 类注解
    位置 类定义上方
    作用 加载properties文件中的属性值
    属性 value(默认):设置加载的properties文件对应的文件名或文件名组成的数组
  • 知识点5:@Bean

    名称 @Bean
    类型 方法注解
    位置 方法定义上方
    作用 设置该方法的返回值作为spring管理的bean
    属性 value(默认):定义bean的id
  • 知识点2:@Import

    名称 @Import
    类型 类注解
    位置 类定义上方
    作用 导入配置类
    属性 value(默认):定义导入的配置类类名,
    当配置类有多个时使用数组格式一次性导入多个配置类