DI注解开发

5 篇文章 0 订阅
3 篇文章 0 订阅


Spring的DI对应的配置开发使用起来相对来说还是比较复杂的,复杂的地方在 配置文件

Spring可以简化代码的开发,到底是如何简化代码开发的呢?

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

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

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

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

1.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="orderDao" class="com.dom.dao.impl.OrderDaoImpl"/>
    </beans>
    
  • 添加OrderDao、OrderDaoImpl、OrderService、OrderServiceImpl类

    public interface OrderDao {
        public void save();
    }
    public class OrderDaoImpl implements OrderDao {
        public void save() {
            System.out.println("order dao save ..." );
        }
    }
    public interface OrderService {
        public void save();
    }
    
    public class OrderServiceImpl implements OrderService {
        public void save() {
            System.out.println("order service save ...");
        }
    }
    
    
  • 创建运行类App

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

最终创建好的项目结构如下:
在这里插入图片描述

1.2 注解开发定义bean

在上述环境的基础上,我们来学一学Spring是如何通过注解实现bean的定义开发?

步骤1:删除原XML配置

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

<bean id="orderDao" class="com.dom.dao.impl.OrderDaoImpl"/>
步骤2:Dao上添加注解

在OrderDaoImpl类上添加@Component注解

@Component("orderDao")
public class OrderDaoImpl implements OrderDao {
    public void save() {
        System.out.println("order dao save ..." );
    }
}

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

XML与注解配置的对应关系:
在这里插入图片描述

步骤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.dom"/>
</beans>

说明:

component-scan

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

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

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

运行App类查看打印结果
在这里插入图片描述

步骤5:Service上添加注解

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

@Component
public class OrderServiceImpl implements OrderService {
    private OrderDao orderDao;

    public void setOrderDao(OrderDao orderDao) {
        this.orderDao = orderDao;
    }

    public void save() {
        System.out.println("order service save ...");
        orderDao.save();
    }
}
步骤6:运行程序

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

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

打印观察结果,两个bean对象都已经打印到控制台
在这里插入图片描述

说明:

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

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

    OrderService orderService = (OrderService)ctx.getBean("orderServiceImpl");
    System.out.println(orderService);
    

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

通过查看源码会发现:
在这里插入图片描述

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

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

知识点1:@Component等
名称@Component/@Controller/@Service/@Repository
类型类注解
位置类定义上方
作用设置该类为spring管理的bean
属性value(默认):定义bean的id
1.2 纯注解开发模式

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

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

具体如何实现?

1.2.1 思路分析

实现思路为:

  • 将配置文件applicationContext.xml删除掉,使用类来替换。
1.2.2 实现步骤
步骤1:创建配置类

创建一个配置类SpringConfig

public class SpringConfig {
}

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

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

@Configuration
public class SpringConfig {
}
步骤3:用注解替换包扫描配置

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

@Configuration
@ComponentScan("com.dom")
public class SpringConfig {
}
步骤4:创建运行类并执行

创建一个新的运行类AppForAnnotation

public class AppForAnnotation {

    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        OrderDao orderDao = (OrderDao) ctx.getBean("orderDao");
        System.out.println(orderDao);
        OrderService orderService = ctx.getBean(OrderService.class);
        System.out.println(orderService);
    }
}

运行AppForAnnotation,可以看到两个对象依然被获取成功
在这里插入图片描述

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

  • Java类替换Spring核心配置文件在这里插入图片描述

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

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

    @ComponentScan({com.dom.service","com.dom.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是加载配置类
1.3 注解开发bean作用范围与生命周期管理

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

1.3.1 环境准备

老规矩,学习之前先来准备环境:

  • 创建一个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.dom")
    public class SpringConfig {
    }
    
  • 添加OrderDao、OrderDaoImpl类

    public interface OrderDao {
        public void save();
    }
    @Repository
    public class OrderDaoImpl implements OrderDao {
        public void save() {
            System.out.println("order dao save ..." );
        }
    }
    
  • 创建运行类App

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

最终创建好的项目结构如下:
在这里插入图片描述

1.3.2 Bean的作用范围

(1)先运行App类,在控制台打印两个一摸一样的地址,说明默认情况下bean是单例
在这里插入图片描述

(2)要想将OrderDaoImpl变成非单例,只需要在其类上添加@scope注解

@Repository
//@Scope设置bean的作用范围
@Scope("prototype")
public class OrderDaoImpl implements OrderDao {

    public void save() {
        System.out.println("order dao save ...");
    }
}

再次执行App类,打印结果:
在这里插入图片描述

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

(1)在OrderDaoImpl中添加两个方法,initdestroy,方法名可以任意

@Repository
public class OrderDaoImpl implements OrderDao {
    public void save() {
        System.out.println("order dao save ...");
    }
    public void init() {
        System.out.println("init ...");
    }
    public void destroy() {
        System.out.println("destroy ...");
    }
}

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

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

@Repository
public class OrderDaoImpl implements OrderDao {
    public void save() {
        System.out.println("order 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);
        OrderDao orderDao1 = ctx.getBean(OrderDao.class);
        OrderDao orderDao2 = ctx.getBean(OrderDao.class);
        System.out.println(orderDao1);
        System.out.println(orderDao2);
        ctx.close(); //关闭容器
    }
}

(4)运行App,类查看打印结果,证明init和destroy方法都被执行了。
在这里插入图片描述

注意:@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
类型方法注解
位置方法上
作用设置该方法为销毁方法
属性

小结
在这里插入图片描述

1.4 注解开发依赖注入

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

1.4.1 环境准备

在学习之前,把案例环境介绍下:

  • 创建一个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.dom")
    public class SpringConfig {
    }
    
  • 添加OrderDao、OrderDaoImpl、OrderService、OrderServiceImpl类

    public interface OrderDao {
        public void save();
    }
    @Repository
    public class OrderDaoImpl implements OrderDao {
        public void save() {
            System.out.println("order dao save ..." );
        }
    }
    public interface OrderService {
        public void save();
    }
    @Service
    public class OrderServiceImpl implements OrderService {
        private OrderDao orderDao;
    	public void setOrderDao(OrderDao orderDao) {
            this.orderDao = orderDao;
        }
        public void save() {
            System.out.println("order service save ...");
            orderDao.save();
        }
    }
    
  • 创建运行类App

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

最终创建好的项目结构如下:
在这里插入图片描述

环境准备好后,运行后会发现有问题
在这里插入图片描述

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

1.4.2 注解实现按照类型注入

对于这个问题使用注解该如何解决?

(1) 在OrderServiceImpl类的orderDao属性上添加@Autowired注解

@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    private OrderDao orderDao;
    
//	  public void setOrderDao(OrderDao orderDao) {
//        this.orderDao = orderDao;
//    }
    public void save() {
        System.out.println("order service save ...");
        orderDao.save();
    }
}

注意:

  • @Autowired可以写在属性上,也可也写在setter方法上,最简单的处理方式是写在属性上并将setter方法删除掉
  • 为什么setter方法可以删除呢?
    • 自动装配基于反射设计创建对象并通过暴力反射为私有属性进行设值
    • 普通反射只能获取public修饰的内容
    • 暴力反射除了获取public修饰的内容还可以获取private修改的内容
    • 所以此处无需提供setter方法

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

@Repository
public class OrderDaoImpl2 implements OrderDao {
    public void save() {
        System.out.println("order dao save ...2");
    }
}

这个时候再次运行App,就会报错
在这里插入图片描述

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

  • 先给两个Dao类分别起个名称

    @Repository("orderDao")
    public class OrderDaoImpl implements OrderDao {
        public void save() {
            System.out.println("order dao save ..." );
        }
    }
    @Repository("orderDao2")
    public class OrderDaoImpl2 implements OrderDao {
        public void save() {
            System.out.println("order dao save ...2" );
        }
    }
    

    此时就可以注入成功,但是得思考个问题:

    • @Autowired是按照类型注入的,给OrderDao的两个实现起了名称,它还是有两个bean对象,为什么不报错?

    • @Autowired默认按照类型自动装配,如果IOC容器中同类的Bean找到多个,就按照变量名和Bean的名称匹配。因为变量名叫orderDao而容器中也有一个booDao,所以可以成功注入。

    • 分析下面这种情况是否能完成注入呢?
      在这里插入图片描述

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

1.4.3 注解实现按照名称注入

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

@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    @Qualifier("orderDao1")
    private OrderDao orderDao;
    
    public void save() {
        System.out.println("order service save ...");
        orderDao.save();
    }
}

@Qualifier注解后的值就是需要注入的bean的名称。

注意:@Qualifier不能独立使用,必须和@Autowired一起使用

1.4.4 简单数据类型注入

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

@Repository("orderDao")
public class OrderDaoImpl implements OrderDao {
    private String name;
    public void save() {
        System.out.println("order dao save ..." + name);
    }
}

数据类型换了,对应的注解也要跟着换,这次使用@Value注解,将值写入注解的参数中就行了

@Repository("orderDao")
public class OrderDaoImpl implements OrderDao {
    @Value("dom")
    private String name;
    public void save() {
        System.out.println("order dao save ..." + name);
    }
}

注意数据格式要匹配,如将"abc"注入给int值,这样程序就会报错。

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

1.4.5 注解读取properties配置文件

@Value一般会被用在从properties配置文件中读取内容进行使用,具体如何实现?

步骤1:resource下准备properties文件

jdbc.properties

name=dom888
步骤2: 使用注解加载properties配置文件

在配置类上添加@PropertySource注解

@Configuration
@ComponentScan("com.dom")
@PropertySource("jdbc.properties")
public class SpringConfig {
}

步骤3:使用@Value读取配置文件中的内容
@Repository("orderDao")
public class OrderDaoImpl implements OrderDao {
    @Value("${name}")
    private String name;
    public void save() {
        System.out.println("order dao save ..." + name);
    }
}

步骤4:运行程序

运行App类,查看运行结果,说明配置文件中的内容已经被加载到
在这里插入图片描述

注意:

  • 如果读取的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文件对应的文件名或文件名组成的数组
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值