【Spring 学习笔记(八)】Spring IoC/DI注解开发 之 原始注解开发

296 阅读7分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第11天,点击查看活动详情

写在前面😘

大一电子信息工程新生,请多多关照,希望能在掘金记录自己的学习历程!
【Spring 学习笔记】 系列教程基于 Spring 5.2.10.RELEASE讲解 。

一、注解开发简介

有关Spring IoC的xml文件配置开发终于介绍了,但是感觉写配置文件好麻烦,没体验到Spring简化开发的过程啊。

其实,要想真正简化开发,害得用到Spring的注解开发。

接下来,就让大牛角来讲解Spring 注解开发吧!

1️⃣什么是注解开发

指的是在类或者方法上加入特定的注解(@XXX),完成特定功能的开发。

@Component
public class XXX{}

2️⃣spring注解开发历程

  1. Spirng2.x开始支持注解开发 @Component 等 目的:起初,只是为了简化XML的配置,作为XML的有益补充
  2. Spring3.x 纯注解开发@Configuration 等 目的:彻底替换XML,基于纯注解开发
  3. Spirng4.x SpringBoot 提倡使用注解进行常见开发

3️⃣注解开发优点

使用注解的形式替代 xml 配置,将繁杂的 Spring 配置文件从工程中消除掉,简化开发。

二、原始注解开发(Spring 2.x)

Spring 原始注解:主要是替代 <Bean> 的配置。

原始注解(也称基础注解),仅仅是简化XML的配置,并不能完全替代XML(如还是需要用到XML的包扫描)。

1️⃣开启注解功能

  • 开启注解功能,需要在 Spring 的配置文件中配置注解扫描
<!--base-package: 添加注解的类所在的包位置-->
<context:component-scan base-package="com.bighorn"/>

注意点:记得在<beans>标签里打开context命名空间

  • 说明
    • 在进行包所扫描时,会对配置的包及其子包中所有文件进行扫描。
    • 扫描时仅读取 spring 可识别的注解。
    • 扫描结束后会将有效注解转化为 spring 对应的资源加入 IoC 容器。
  • 注意
    • 记得在<beans>标签里打开context命名空间
    • 无论是注解开发还是 XML 开发,最终都是将资源加载到 IoC 容器中,差别就是数据读取方式不同。
    • 注解的加载效率优于 XML 配置文件。

2️⃣Bean定义注解

2.1@Component

  • 类型:类注解
  • 位置:类定义上方。
  • 作用:设置该类为 spring 管理的 bean ,替换Spring配置文件中的<bean>标签
  • 注意点:id可以不写,默认id是首字母小写的类名
示例👇
@Component("user")
public class User{
    ...
}

这相当于这样xml配置中定义bean

<bean id="user" class="com.bighorn.pojo.User"/>

2.2@Component衍生注解

项目开发时一般会将程序分层:控制层(controller/web),业务层(service),持久层(dao)。

为了更好区分每一层的作用,spring衍生了@Component注解:@Controller@Service@Repository

注解说明
@Component使用在类上用于实例化 Bean
@Controller使用在 web 层类上用于实例化 Bean
@Service使用在 service 层类上用于实例化 Bean
@Repository使用在 dao 层类上用于实例化 Bean

注意:本质上这些衍生注解就是@Component 作用,细节以及用法完全一致

目的:更加准确的表达一个类型的作用。

示例👇
/*创建web层对象**/
@Controller
public class UserController {
    ...
}
/*创建service层对象**/
@Service
public class UserServiceImpl implements UserService {
    ...
}
/*创建dao层对象**/
@Repository
public class UserDaoImpl implements UserDao {
   ...
}

3️⃣Bean作用域注解

@Scope

  • 类型:类注解
  • 位置:类定义上方。
  • 作用:设置该类作为 bean 对应的 scope 属性。
  • 注意:不加**@Scope**,默认是singleton单例模式
示例👇
@Component  //默认id:user
@Scope("prototype")//非单例模式
public class User {

}

4️⃣Bean生命周期注解

4.1@PostConstruct

  • 类型:方法注解
  • 位置:方法定义上方。
  • 作用:设置该类作为 bean 对应的初始化方法

4.2@PreDestory

  • 类型:方法注解
  • 位置:方法定义上方。
  • 作用:设置该类作为 bean 对应的销毁方法

4.3示例👇

  1. 如果你使用的JDK是9以后的,你可能需要先导入javax.annotation包,才能使用生命周期相关注解。
<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.2</version>
</dependency>
  1. 定义初始化方法和销毁方法,并在方法上方添加相关注解。
@Component
public class User {

    // 初始化方法
    @PostConstruct
    public void init(){
        System.out.println("这是初始化回调方法");
    }
    //销毁回调方法
    @PreDestory
    public void destroy(){
        System.out.println("这是销毁回调方法");
    }
}

这相当于这样配置xml

<!--通过XML配置指定回调方法-->
<bean id="user" class="com.bighorn.pojo.User" init-method="init" destroy-method="destroy">
</bean>
  1. 编写运行程序
public static void main(String[] args) {
    //获取IoC容器
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    //从容器中获取对象
    User user = context.getBean(User.class);
    System.out.println(user);
    //手动关闭容器
    context.close();
}
  1. 运行结果如下

image-20220606195926496

5️⃣Bean属性注入注解

5.1@Autowired、@Qualifier

  • 类型:属性注解、方法注解
  • 位置:属性定义上方,也可以是方法定义上方。
  • 作用:引用类型注入
  • 说明:@Autowired 默认按类型装配指定 @Qualifier 后则可以指定装配的 bean 的 id
  • 相关属性:
    • required:定义该属性是否允许为 null 。
  • 注意:@Qualifier不能独立使用,必须和@Autowired一起使用
案例1:按照Bean类型注入
  1. 编写dao层实现类
@Repository
public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("这是UserDaoImpl的save方法");
    }
}
  1. 编写service层实现类,在成员属性userDao上面添加@Autowired注解,spring就会自动根据UserDao这个类型去找相应的bean注入。
  • 注意:@Autowired可以写在属性上,也可也写在setter方法上,最简单的处理方式是写在属性上并将setter方法删除掉
@Service
public class UserServiceImpl implements UserService {
    //@Autowired 默认按类型装配
    @Autowired
    private UserDao userDao;

    @Override
    public void save() {
        userDao.save();
        System.out.println("这是UserServiceImpl的save方法");
    }
}
  1. 编写运行程序
public static void main(String[] args) {
    //获取IoC容器
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    //从容器中获取对象
    UserServiceImpl userService = context.getBean(UserServiceImpl.class);
    //调用save方法
    userService.save();
}
  1. 运行结果如下👇

image-20220606222255517

案例2:按照Bean名称注入

案例1已经验证了@Autowired是按照类型注入,但如果UserDao接口有多个实现类:UserDaoImpl2、UserDaoImpl3、UserDaoImpl4。。。

此时,按照类型注入就无法区分到底注入哪个对象,解决方案:按照名称注入,指定 @Qualifier 后则可以指定装配的 bean 的 id。

  1. 编写多个dao层的实现类,并分别为它们设置bean的id(关键)
//第一个实现类
@Repository("UserDao1")
public class UserDaoImpl1 implements UserDao {

    @Override
    public void save() {
        System.out.println("这是第一个实现类的save方法");
    }
}
//第二个实现类
@Repository("UserDao2")
public class UserDaoImpl2 implements UserDao {

    @Override
    public void save() {
        System.out.println("这是第二个实现类的save方法");
    }
}
//第三个实现类
@Repository("UserDao3")
public class UserDaoImpl3 implements UserDao {

    @Override
    public void save() {
        System.out.println("这是第三个实现类的save方法");
    }
}
  1. 编写service层实现类,在成员属性userDao上面添加@Autowired注解,并使用@Qualifier(" ")指定要注入的bean。

注意点

  • AutoWired注解基于类型进行注入注入对象的类型,必须与目标成员变量类型相同或者是其子类(实现类)
  • AutoWired 配合Qualifier注解基于名称进行注入,注入对象的id值必须与Qualifier注解中设置的名字相同
@Service
public class UserServiceImpl implements UserService {
    //@Autowired 默认按类型装配
    @Autowired
    @Qualifier("UserDao2")
    private UserDao userDao;

    @Override
    public void save() {
        userDao.save();
        System.out.println("这是UserServiceImpl的save方法");
    }
}
  1. 运行程序和案例1一样,直接放运行结果的截图。发现打印的是第二个实现类的方法

image-20220606224015510

5.2@Resource

@Resource 注解是 Java EE 规范中提供的注解,它和 @Autowired 作用一样, 但@Resource 是按照名称注入引用类型,相当于 @Autowired + @Qualifier。

@Resource 相关属性:

  • name:设置注入的 bean 的 id 。
  • type:设置注入的 bean 的类型,接收的参数为 Class 类型。
案例
//@Resource 按照名称装配
@Resource(name = "UserDao1", type = UserDaoImpl1.class)
private UserDao userDao;

5.3@Value

  • 类型:属性注解、方法注解
  • 位置:属性定义上方,方法定义上方。
  • 作用:注入普通类型的属性。
  • 说明:
    • value 值仅支持非引用类型数据,赋值时对方法的所有参数全部赋值。
    • value 值支持读取 properties 文件中的属性值,通过类属性将 properties 中数据传入类中,而且value 值支持 SpEL (先插个眼在这,明天的文章就会讲如何用注解读取properties配置文件)
    • @value 注解如果添加在属性上方,可以省略 setter方法(和@Autowired一样)
案例
@Component("user")
public class User{
    @Value("bighorn")
    private String name;
}

写在后面🍻

感谢观看啦✨
有什么不足,欢迎指出哦💖
掘金的运营同学审核辛苦了💗