在本系列的上一篇文章中,我们已经了解了Spring的一些核心概念,并且还学习了Spring存取。但是我们发现在存取的过程中还是比较复杂,接下来我们将学习更为简单的Spring存取,其中涉及到的主要内容就是注解。并且在Spring家族的学习过程中,基本上注解是无处不在。
Spring项目的创建
a. 创建maven项目
b. 添加Spring框架依赖
将依赖放置于pom.xml中即可。
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
</dependencies>
c. 创建一个启动类
我创建的启动类是App类,其实本质上启动类没啥意义,就是为了测试写的代码是否正确。
Spring对象的存取
a. 配置扫描路径
想要成功的将对象存储在Spring中,我们就必须配置存储对象的扫描路径,只有扫描路径下的包,添加注解之后才可以被正确的识别并保存在Spring中。
扫描路径需要配置在resources下的xml中,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"
xmlns:content="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
</beans>
b. 注解
在Spring存取的过程中,需要用到注解,因此先来写一下需要哪几个注解,为啥要有这么多的注解,每个注解的含义是啥。
在Spring存对象的过程中,有两种注解可以实现其功能,分别是五大类注解和方法注解。
五大类注解
@Controller、@Service、@Repository、@Component、@Configuration。
@Controller
控制器,归属的是逻辑业务层,用户控制用户的行为,用来检查参数的有效性;通俗的讲,就是判断前端请求的合理性。
@Service
服务,归属的是服务层,调用持久化类来实现响应的功能,不直接和数据库交互,也不直接和前端请求交互,类似于控制中心;通俗的来说,就是中控,对前端的请求功能负责,然后来调配合适的数据库表。
@Repository
仓库,归属的是持久层,直接和数据库进行交互,通常每个表对应一个仓库;通俗的讲,就是一个库负责一个表的增删查改。
@Configuration
配置,归属的是配置层,用来配置当前项目的一些信息;通俗的说,对项目的全局配置负责。
@Component
组件,归属的是公共工具类,提供一些公共方法。
方法注解
@Bean,将当前修饰方法的方法对象存入到Spring中。
需要注意的一点就是@Bean注解必须要配合类注解来使用,否则就是无效的方法注解。
为啥要有这么多注解?
从本质上来说,五大注解的作用是一样的,都是将对象存入Spring中,那为啥还要有这么多的注解呢?从五大类注解的含义上来说,其作用就是分层,而不是一个类就将从前端接收过来的内容进行判断,改写最后给到数据库更改,而是一层一层,一层负责一件事,这样效率即提高了不少,还能增强代码的可更改性,减少“屎山代码”的出现。
对于上述注解的分层,大概是这样来区分的:首先前端接收到内容,将内容以一定的方式传给后端,后端在@Controller层接收内容,并在Controller层判断前端内容的合理性,然后将内容传给@Service层,此层作为一个中转层,接收@Controller层传过来的东西,再将东西传给@Repository,此层负责和数据库进行对接。这就是大致内容,然后另外两个注解就是负责其他的内容,等到碰见的时候做了解即可。
c. 添加注解存对象
使用五大注解
先简单的写一个内容来熟悉一下注解的作用,并且用启动类来看一下能否获取到结果。
首先就是将创建的包写入到扫描路径中,直接将下述代码置于配置文件中即可,必须要写配置文件,否则写了注解也无用。
<content:component-scan base-package="org.example.controller"></content:component-scan>
将配置文件写好之后,就可以开始写主要内容了,还是实现一个简单的hello world吧,毕竟学习一个新的编程,最先开始学的就是如何输出hell world了。
package org.example.controller;
import org.springframework.stereotype.Controller;
@Controller
public class ArticleController {
public String sayHello() {
return "hello world";
}
}
当有了配置文件并且也加了注解之后,其实内容就已经存储到Spring容器之中了,然后咋们来用启动类看一下结果吧。
package org.example;
import org.example.controller.ArticleController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
//获取Spring
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//获取Bean对象
ArticleController articleController = context.getBean("articleController", ArticleController.class);
//输出查看结果
System.out.println(articleController.sayHello());
}
}
到这里,大家已经会用注解来存储对象了。
如下图有一个问题就是我明明没写Bean对象的名称,为啥我使用了Bean对象的名称还不会报错?
这是因为在Spring中,默认Bean对象的名称就是将其类名的第一个字母小写。前提是类名书写规范的情况下,那么书写不规范呢?如果类名都是小写字母,那么就是使用类名就可以,如果第一个字母和第二个字母都是大写字母的话,那么也是使用类名获取。
使用方法注解
使用方法注解和使用类注解大致操作方法几乎一样,但是需要注意的就是使用方法注解时也必须要把类注解加上去,否则的话方法注解也没法使用。
也写一个简单的内容来看一下@Bean注解的作用吧,首先肯定是将类写入扫描路径中,但是我和类注解是在一个包下,因此不用配置,大家看一下自己的是否需要配置。
创建一个User类
在这里我使用了一个@Data注解,它的意思就是不用写set和get等各种方法,减少代码的冗余度,接下来有可能会写一个文章来专门介绍一个它,大家可以这样用,也可以写set和get方法。
package org.example.enity;
import lombok.Data;
@Data
public class User {
private Integer id;
private String username;
private String password;
}
然后的话就是使用Bean注解了。
如下代码,搭配上了Controller注解来用,这些代码没啥真正的含义,就只是表示一下Bean注解的用处。
package org.example.controller;
import org.example.enity.User;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
@Bean
public User user() {
User user = new User();
user.setId(1);
user.setUsername("王五");
user.setPassword("123");
return user;
}
}
使用一个启动类来看一下结果。
public static void main(String[] args) {
//获取Spring
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//获取Bean对象
User user = context.getBean("user", User.class);
//输出查看结果
System.out.println(user);
}
上述的Bean注解中,也可也给它一个名字,但是如果自己给了名字之后,就不能使用默认的名字了,并且好像默认的名字是和方法名一致,当然我不能太确定,只是自己做了一个简单实验验证了一小下。
@Bean(name = "user")
上述大致就是使用注解来存Bean对象的内容了。
d. 获取Bean对象
在上篇文章中,我们已经了解了先获取Spring对象再获取Bean对象的方法。接下来我们了解的不是使用注解来获取Bean对象,一般我们不会使用ApplicationContext和BeanFactory这两种方式,这虽然也能获取,但是使用注解是更好的方式,使用的注解是@Autowired和@Resource。
获取对象也叫做对象装配,意思是把对象取出来放到某个类中,有时候也叫做对象注入。
三大注入方式
属性注入、set注入、构造方法注入。
在注入方式的介绍中,要用到简单的分层,不然的话没办法演示这个注入效果。
如上图所示,三个类是咋们需要用到的类,两个类分层,然后App充当启动类,如果是在标准Web开发中,其本质就是前端,发送请求和接收请求;创建好之后,首先在spring-config.xml中将包名加上去,不然的话加上注解也毫无意义。
首先创建一个Service层。
package org.example.service;
import org.springframework.stereotype.Service;
@Service
public class CatService {
public String sayHello() {
return "hello 小猫!";
}
}
然后创建一个Controller层。
这个就是简单的基本代码,只需要加入注入方式即可。
package org.example.controller;
import org.springframework.stereotype.Controller;
@Controller
public class CatController {
public String sayHello() {
return catService.sayHello();
}
}
最后就是启动类了。
启动类的代码永远不用变,一直都是这个样子,只需要把这个controller层的代码写好即可。
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
CatController controller = context.getBean("catController", CatController.class);
System.out.println(controller.sayHello());
}
属性注入
@Autowired
private CatService catService;
属性注入的缺点:
功能性问题:无法注入一个不可变对象(final修饰的对象);
通用性问题:只能适用于IoC容器;
设计原则问题:更容易违背单一设计原则(针对对象是类)。
set注入
@Autowired
private CatService catService;
public void setCatService(CatService catService) {
this.catService = catService;
}
和第一种方法相比,只是上述代码发生了改变。
它的优点就是更加符合单一设计原则(针对对象是方法级别);但是它的缺点也很明显:
不能注入不可变对象(即final修饰的对象);
注入的对象可被修改(由于setStudentService是普通方法,因此可以被重复调用,在重复调用的时候就存在被修改的风险)。
构造方法注入
@Autowired
private CatService catService;
public CatController(CatService catService) {
this.catService = catService;
}
Spring推荐构造方法注入,使用构造方法注入@Autowired可以省略;适用于一个构造方法,如果有多个构造方法,那么不能省略。
其优点是:
可以注入不可变对象;
注入对象不会被修改(可以加上final修饰符;构造方法只是在类加载时执行一次,不像set,调用一次就执行一次,存在被修改的风险);
注入的对象会被完全初始化(使用构造方法带来的优点);
通用性更好。
两大注入注解
@Autowired、@Resource
相同点:都是用来进行获取Bean对象的,或者称为对象注入、或者也可也称为为依赖注入。
不同点:
Autowired是Spring提供的,而Resource则是JDK提供的;
Autowired可以实现三种注入方法,而Resource则不可以实现构造方法注入;
Autowired先根据名称获取,如果名称获取不到,才根据类型获取;Resource则是先根据类型获取,如果获取不到再根据名称获取;
Autowired支持required参数,而Resource则支持更多的参数,例如在一个典型的问题中:多个Bean对象类型相同获取报错,由于其没有按照规范写名称所致,如果使用Autowired注解的话,则需要多写一个@Qualifier(value = "")注解解决,但是在Resource注解中只需要加一个括号写入name即可。
总结
五大类注解和方法注解的了解;
三大注入方式和两大注入注解的了解。
更到这里,对于Spring的存取也就差不多了,最重要的也就是注解了,以后Spring中会一直使用,需要真正的理解其的作用接下来更的应该就是Bean对象的一些内容,然后就是SpringBoot等等。