首页 > 其他分享 >7. IOC & DI注解开发

7. IOC & DI注解开发

时间:2023-06-24 13:44:31浏览次数:27  
标签:DI bookDao class BookDao 注解 save IOC public

要想真正简化开发,就需要用到 Spring 的注解开发,Spring 对注解支持的版本历程:

  • 2.0 版开始支持注解
  • 2.5 版注解功能趋于完善
  • 3.0 版支持纯注解开发

关于注解开发,我们会讲解两块内容注解开发定义bean​ 和纯注解开发​。

注解开发定义 bean 用的是 2.5 版提供的注解,纯注解开发用的是 3.0 版提供的注解。

1. 环境准备

  • 创建一个 Maven 项目

  • pom.xml 添加 Spring 的依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.10.RELEASE</version>
        </dependency>
    </dependencies>
    
  • resources 下添加 applicationContext.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 id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
    </beans>
    
  • 添加 BookDao、BookDaoImpl、BookService、BookServiceImpl 类

    public interface BookDao {
        public void save();
    }
    public class BookDaoImpl implements BookDao {
        public void save() {
            System.out.println("book dao save ..." );
        }
    }
    public interface BookService {
        public void save();
    }
    
    public class BookServiceImpl implements BookService {
        public void save() {
            System.out.println("book service save ...");
        }
    }
    
    
  • 创建运行类 App

    public class App {
        public static void main(String[] args) {
            ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
            BookDao bookDao = (BookDao) ctx.getBean("bookDao");
            bookDao.save();
        }
    }
    

2. 注解开发定义 bean

步骤 1:删除原 XML 配置

将配置文件中的<bean>​ 标签删除掉

<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>

步骤 2:Dao 上添加注解

在 BookDaoImpl 类上添加@Component​ 注解

@Component("bookDao")
public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ..." );
    }
}

注意:@Component 注解不可以添加在接口上,因为接口是无法创建对象的。

XML 与注解配置的对应关系:

image

步骤 3:配置 Spring 的注解包扫描

为了让 Spring 框架能够扫描到写在类上的注解,需要在配置文件上进行包扫描

<?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">
    <context:component-scan base-package="com.itheima"/>
</beans>

说明:

component-scan

  • component:组件,Spring 将管理的 bean 视作自己的一个组件
  • scan:扫描

base-package 指定 Spring 框架扫描的包路径,它会扫描指定包及其子包中的所有类上的注解。

  • 包路径越多[如:com.itheima.dao.impl],扫描的范围越小速度越快
  • 包路径越少[如:com.itheima],扫描的范围越大速度越慢
  • 一般扫描到项目的组织名称即 Maven 的 groupId 下[如:com.itheima]即可。

步骤 4:运行程序

运行App​ 类查看打印结果

image

步骤 5:Service 上添加注解

在 BookServiceImpl 类上也添加@Component​ 交给 Spring 框架管理

@Component
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();
    }
}

步骤 6:运行程序

在 App 类中,从 IOC 容器中获取 BookServiceImpl 对应的 bean 对象,打印

public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        System.out.println(bookDao);
        //按类型获取bean
        BookService bookService = ctx.getBean(BookService.class);
        System.out.println(bookService);
    }
}

说明:

  • BookServiceImpl 类没有起名称,所以在 App 中是按照类型来获取 bean 对象

  • @Component 注解如果不起名称,会有一个默认值就是当前类名首字母小写​,所以也可以按照名称获取,如

    BookService bookService = (BookService)ctx.getBean("bookServiceImpl");
    System.out.println(bookService);
    

对于@Component 注解,还衍生出了其他三个注解@Controller​、@Service​、@Repository

通过查看源码会发现:

image

这三个注解和@Component 注解的作用是一样的,为什么要衍生出这三个呢?

方便我们后期在编写类的时候能很好的区分出这个类是属于表现层​、业务层​ 还是数据层​ 的类。

知识点 1:@Component 等

名称 @Component/@Controller/@Service/@Repository
类型 类注解
位置 类定义上方
作用 设置该类为 spring 管理的 bean
属性 value(默认):定义 bean 的 id

3. 纯注解开发模式

上面已经可以使用注解来配置 bean,但是依然有用到配置文件,在配置文件中对包进行了扫描,Spring 在 3.0 版已经支持纯注解开发

  • Spring3.0 开启了纯注解开发模式,使用 Java 类替代配置文件,开启了 Spring 快速开发赛道

具体如何实现?

3.1 思路分析

实现思路为:

  • 将配置文件 applicationContext.xml 删除掉,使用类来替换。

3.2 实现步骤

步骤 1:创建配置类

创建一个配置类SpringConfig

public class SpringConfig {
}

步骤 2:标识该类为配置类

在配置类上添加@Configuration​ 注解,将其标识为一个配置类,替换applicationContext.xml

@Configuration
public class SpringConfig {
}

步骤 3:用注解替换包扫描配置

在配置类上添加包扫描注解@ComponentScan​ 替换<context:component-scan base-package=""/>

@Configuration
@ComponentScan("com.itheima")
public class SpringConfig {
}

步骤 4:创建运行类并执行

创建一个新的运行类AppForAnnotation

public class AppForAnnotation {

    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        System.out.println(bookDao);
        BookService bookService = ctx.getBean(BookService.class);
        System.out.println(bookService);
    }
}

运行 AppForAnnotation,可以看到两个对象依然被获取成功

image

至此,纯注解开发的方式就已经完成了,主要内容包括:

  • Java 类替换 Spring 核心配置文件

image

  • @Configuration 注解用于设定当前类为配置类

  • @ComponentScan 注解用于设定扫描路径,此注解只能添加一次,多个数据请用数组格式

    @ComponentScan({com.itheima.service","com.itheima.dao"})
    
  • 读取 Spring 核心配置文件初始化容器对象切换为读取 Java 配置类初始化容器对象

    //加载配置文件初始化容器
    ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    //加载配置类初始化容器
    ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
    

知识点 1:@Configuration

名称 @Configuration
类型 类注解
位置 类定义上方
作用 设置该类为 spring 配置类
属性 value(默认):定义 bean 的 id

知识点 2:@ComponentScan

名称 @ComponentScan
类型 类注解
位置 类定义上方
作用 设置 spring 配置类扫描路径,用于加载使用注解格式定义的 bean
属性 value(默认):扫描路径,此路径可以逐层向下扫描

小结:

这一节重点掌握的是使用注解完成 Spring 的 bean 管理,需要掌握的内容为:

  • 记住@Component、@Controller、@Service、@Repository 这四个注解
  • applicationContext.xml 中<context:component-san/>​ 的作用是指定扫描包路径,注解为@ComponentScan
  • @Configuration 标识该类为配置类,使用类替换 applicationContext.xml 文件
  • ClassPathXmlApplicationContext 是加载 XML 配置文件
  • AnnotationConfigApplicationContext 是加载配置类

4. 注解开发 bean 作用范围与生命周期管理

使用注解已经完成了 bean 的管理,接下来按照前面所学习的内容,将通过配置实现的内容都换成对应的注解实现,包含两部分内容:bean作用范围​ 和bean生命周期​。

4.1 bean 作用范围

知识点 1:@Scope

名称 @Scope
类型 类注解
位置 类定义上方
作用 设置该类创建对象的作用范围 可用于设置创建出的 bean 是否为单例对象
属性 value(默认):定义 bean 作用范围, 默认值 singleton(单例),可选值 prototype(非单例)

4.2 Bean 的生命周期

(1)在 BookDaoImpl 中添加两个方法,init​ 和destroy​,方法名可以任意

@Repository
public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ...");
    }
    public void init() {
        System.out.println("init ...");
    }
    public void destroy() {
        System.out.println("destroy ...");
    }
}

(2)如何对方法进行标识,哪个是初始化方法,哪个是销毁方法?

只需要在对应的方法上添加@PostConstruct​ 和@PreDestroy​ 注解即可。

@Repository
public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ...");
    }
    @PostConstruct //在构造方法之后执行,替换 init-method
    public void init() {
        System.out.println("init ...");
    }
    @PreDestroy //在销毁方法之前执行,替换 destroy-method
    public void destroy() {
        System.out.println("destroy ...");
    }
}

(3)要想看到两个方法执行,需要注意的是destroy​ 只有在容器关闭的时候,才会执行,所以需要修改 App 的类

public class App {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookDao bookDao1 = ctx.getBean(BookDao.class);
        BookDao bookDao2 = ctx.getBean(BookDao.class);
        System.out.println(bookDao1);
        System.out.println(bookDao2);
        ctx.close(); //关闭容器
    }
}

(4)运行 App,类查看打印结果,证明 init 和 destroy 方法都被执行了。

​​image​​

注意:@PostConstruct 和@PreDestroy 注解如果找不到,需要导入下面的 jar 包

<dependency>
  <groupId>javax.annotation</groupId>
  <artifactId>javax.annotation-api</artifactId>
  <version>1.3.2</version>
</dependency>

找不到的原因是,从 JDK9 以后 jdk 中的 javax.annotation 包被移除了,这两个注解刚好就在这个包中。

知识点 1:@PostConstruct

名称 @PostConstruct
类型 方法注解
位置 方法上
作用 设置该方法为初始化方法
属性

知识点 2:@PreDestroy

名称 @PreDestroy
类型 方法注解
位置 方法上
作用 设置该方法为销毁方法
属性

小结

​​image​​

5. 注解开发依赖注入

5.1 注解实现按照类型注入

(1) 在 BookServiceImpl 类的 bookDao 属性上添加@Autowired​ 注解

@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 可以写在属性上,也可也写在 setter 方法上,最简单的处理方式是写在属性上并将setter方法删除掉

  • 为什么 setter 方法可以删除呢?

    • 自动装配基于反射设计创建对象并通过暴力反射为私有属性进行设值
    • 普通反射只能获取 public 修饰的内容
    • 暴力反射除了获取 public 修饰的内容还可以获取 private 修改的内容
    • 所以此处无需提供 setter 方法

(2)@Autowired 是按照类型注入,那么对应 BookDao 接口如果有多个实现类,比如添加 BookDaoImpl2

@Repository
public class BookDaoImpl2 implements BookDao {
    public void save() {
        System.out.println("book dao save ...2");
    }
}

这个时候再次运行 App,就会报错

image

此时,按照类型注入就无法区分到底注入哪个对象,解决方案:按照名称注入

  • 先给两个 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​ 而容器中也有一个booDao​,所以可以成功注入。
    • 分析下面这种情况是否能完成注入呢?

image

  • 不行,因为按照类型会找到多个 bean 对象,此时会按照bookDao​ 名称去找,因为 IOC 容器只有名称叫bookDao1​ 和bookDao2​,所以找不到,会报NoUniqueBeanDefinitionException

5.2 注解实现按照名称注入

当根据类型在容器中找到多个 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 一起使用

5.3 简单数据类型注入

引用类型看完,简单类型注入就比较容易懂了。简单类型注入的是基本数据类型或者字符串类型,下面在BookDaoImpl​ 类中添加一个name​ 属性,用其进行简单类型注入

@Repository("bookDao")
public class BookDaoImpl implements BookDao {
    private String name;
    public void save() {
        System.out.println("book dao save ..." + 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 值,这样程序就会报错。

介绍完后,会有一种感觉就是这个注解好像没什么用,跟直接赋值是一个效果,还没有直接赋值简单,所以这个注解存在的意义是什么?

注解读取 properties 配置文件

@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 类,查看运行结果,说明配置文件中的内容已经被加载到

image

注意:

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

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

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

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

知识点 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 文件对应的文件名或文件名组成的数组

标签:DI,bookDao,class,BookDao,注解,save,IOC,public
From: https://www.cnblogs.com/NorthPoet/p/17500969.html

相关文章

  • [C/C++] Visual Stdio Code中多线程多源码文件编译、运行和调试
    搞了很久,记录一下:一.环境OS:Ubuntu20.04VSCode:1.77.0g++:g++(Ubuntu9.4.0-1ubuntu1~20.04.1)9.4.0二.配置文件下面两个文件先不要手动创建,下面第三章会讲到:task.json:编译程序的配置文件;launch.json:运行程序的配置文件.三.编译&运行1.打开main函数所在的cpp文......
  • 什么是 SAP Commerce Cloud SmartEdit 的 preview API
    PreviewAPI使得SmartEdit能够将商户网站加载到请求的体验环境上的iframe中。体验环境是指特定站点、目录和目录版本的商户网站,并且还可以是指定的语言、日期和时间。为了以指定的体验环境呈现商户网站,SmartEdit将请求的商户网站加载到请求的体验环境的iframe中。为了在请求的体验......
  • SAP Commerce Cloud 通过 SmartEdit 添加 Component 的一个例子
    如下图所示:位于ProductListLot内:url:http://localhost:4200/electronics-spa/en/USD/Open-Catalogue/Cameras/Digital-Cameras/Digital-Compacts/c/576观察到的OCC请求:https://localhost:9002/occ/v2/electronics-spa/cms/pages?pageType=CategoryPage&code=576&lang=en&......
  • Codeforces Round 781 (Div. 2) E. MinimizOR (可持久化字典树)
    传送门题目大意:  T组测试数据每组测试数据先输入一个n表示有一个长度为n的一维数组,然后输入n个数字表示这个一维数组。紧接着输入一个k表示有k个询问,对于每个询问会输入一个l和一个r表示询问数组中[l,r]这个区间里面任意两个下标不重复的元素最小的或(|)是多少。解题思路: ......
  • Codeforces Round 881 (Div
    E.TrackingSegments给定初始长度为n,且全为0的序列a,然后给出m个线段,如果一个线段中1的个数严格大于0的个数,那么该线段称为一个漂亮线段,现在给出q次操作,每次操作使得序列a中位置x上的0变为1,请你求出第一次使得所有线段中出现漂亮线段的询问题解:二分答案容易发现答案具有单......
  • 2023-06-23:redis中什么是缓存击穿?该如何解决?
    2023-06-23:redis中什么是缓存击穿?该如何解决?答案2023-06-23:缓存击穿是指一个缓存中的热点数据非常频繁地被大量并发请求访问,当该热点数据失效的瞬间,持续的大并发请求无法通过缓存获取到数据,而直接访问数据库,这就好像在一个稳固完好的容器上打开了一个洞。解决缓存击穿问题的方......
  • 5. IOC DI配置管理第三方bean
    1.1案例:数据源对象管理在这一节中,我们将通过一个案例来学习下对于第三方bean该如何进行配置管理。以后我们会用到很多第三方的bean,本次案例将使用咱们前面提到过的数据源​Druid(德鲁伊)​和C3P0​来配置学习下。1.1.1环境准备学习之前,先来准备下案例环境:创建一......
  • jackson 自定义注解 JacksonAnnotation
    packagecom.heima.model.common.annotation;importcom.fasterxml.jackson.annotation.JacksonAnnotation;importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotatio......
  • WordPress开启Nginx Redis Cache缓存 解决FastCGI Cache内网穿透兼容问题
    本文转载自:WordPress开启NginxRedisCache缓存解决FastCGICache内网穿透兼容问题更多内容请访问钻芒博客:https://www.zuanmang.net上回说到,Wordpress配合NginxFastCGICache缓存可以极大提升速度体验,但钻芒博客由于是通过Nginx反向代理所以使用起来纯在一定兼容问题,比如缓......
  • Could NOT find ZLIB (missing: ZLIB_LIBRARY ZLIB_INCLUDE_DIR)
     001、问题cmake报错[root@PC1build]#cmake.. 002、解决方法,下载zlib并安装官网:http://www.zlib.net/a、下载 b、解压并安装[root@PC1software]#tar-xzvfzlib-1.2.13.tar.gz[root@PC1software]#cdzlib-1.2.13/[[email protected]]#./configur......