文章目录
- 前言
- 一.存储 Bean 对象
- 1.1 类注解的用法
- 1.2 为什么要使用这么多类注解
- 1.2.1 为什么需要五大类注解
- 1.3 各个类注解的关系
- 1.4 Bean的命名规则
- 1.5 方法注解的使用
- 二.取出 Bean 对象
- 2.1 属性注入
- 2.2 Setter注入
- 2.3 构造方法注入
- 三.总结
前言
经过前面的学习,我们已经学会spring的存储对象和读取对象操作,我们简单的来回忆一下,上一篇文章的内容.存储操作如下:
操作容器之前,先要有容器,所以先要得到容器。
存对象
a.创建Bean(普通类)。
b.将 Bean注册(配置)到spring-confing.xml中。
取对象
a.得到Spring 上下文,并读取到Spring的配置文件。b.获取某一个 Bean对象。
c.使用Bean对象。
接下来我们会介绍比上面更加简单的方法,比如说注解的方法,大家耐心看我接下来的解释.
一.存储 Bean 对象
这里存储对象的方法大概有两种:
1.使用类注解:@Controller、@Service、@Repository、@Component、@Configuration
2.使用方法注解:@Bean
我们接下来会分别会对这些对象做出以下解释
1.各个注解的用法
2.为什么要使用这么多种类的注解
3.各个类注解之间的关系
4.存储Bean注解之间的命名关系
5.方法注解的用法.
我们在使用这些类注解之前,我们要配置扫描的路径.
配置扫描路径的意思是,我们想要将对象成功的存储到 Spring 中,我们需要配置⼀下存储对象的扫描包路径,只有被配置的包下的所有类,添加了注解才能被正确的识别并保存到 Spring 中.
1.1 类注解的用法
类注解的用法.
@Controller(控制器)注解的用法
@Controller
//将当前类存储到 spring当中
public class UserController {
public void sayHi(String name) {
System.out.println("Hi, UserController1");
}
}
我们先使⽤之前读取对象的⽅式来读取上⾯的 UserController 对象,如下代码所示:
//1.得到spring容器
ApplicationContext context=new
ClassPathXmlApplicationContext("spring-config.xml");
//2.得到bean对象
UserController userController=context.getBean("userController",UserController.class);
//使用bean对象.
userController.sayHi("张三");
@Service(服务存储)
@Service
//将当前类存储到 spring当中
public class UserController2 {
public void sayHi() {
System.out.println("Hi, UserController2");
}
}
具体使用:
//1.得到spring容器
ApplicationContext context=new
ClassPathXmlApplicationContext("spring-config.xml");
//2.得到bean对象
UserController userController=context.getBean("userController2",UserController2.class);
//使用bean对象.
userController.sayHi();
@ Resopsitory(仓库存储)
@Repository
//将当前类存储到 spring当中
public class UserController3 {
public void sayHi() {
System.out.println("Hi, UserController3");
}
}
具体使用:
//1.得到spring容器
ApplicationContext context=new
ClassPathXmlApplicationContext("spring-config.xml");
//2.得到bean对象
UserController userController=context.getBean("userController3",UserController3.class);
//使用bean对象.
userController.sayHi();
@Compentent(组件存储)使用方法
public class UserController4 {
public void sayHi() {
System.out.println("Hi, UserController4");
}
}
具体使用:
//1.得到spring容器
ApplicationContext context=new
ClassPathXmlApplicationContext("spring-config.xml");
//2.得到bean对象
UserController userController=context.getBean("userController4",UserController4.class);
//使用bean对象.
userController.sayHi();
@Configuration(配置存储)
@Configuration
//将当前类存储到 spring当中
public class UserController5 {
public void sayHi() {
System.out.println("Hi, UserController5");
}
}
具体使用:
//1.得到spring容器
ApplicationContext context=new
ClassPathXmlApplicationContext("spring-config.xml");
//2.得到bean对象
UserController userController=context.getBean("userController5",UserController5.class);
//使用bean对象.
userController.sayHi();
1.2 为什么要使用这么多类注解
经过上面的介绍,我们注意到一个问题,既然每一个类注解都能加载到spring中,那我们为什么又要设计这么多注解类呢?这完全就是说不通的呀?
spring之所以要使用这么多注解,使用这些注解,开发人员可以在代码中直接表达配置和逻辑,而无需显式地编写XML配置文件。这大大简化了项目的配置和管理,提高了开发效率。同时,Spring框架支持自定义注解,开发人员也可以根据项目需求创建自己的注解,进一步提高代码的可读性和可维护性.
把这段话放在生活的例子中去说,就是我们每个省份的车牌号管理方式是一样的.
比如陕西的车牌号就是:陕X︰XXXXXX,北京的车牌号︰京X︰XXXXXX,一样。甚至一个省不同的县区也是不同的,比如西安就是,陕A︰XXXXX,咸阳:陕B︰XXXXXX,宝鸡,陕C: XXXXXX,一样。这样做的好处除了可以节约号码之外,更重要的作用是可以直观的标识一辆车的归属地。
这样一说明,就和我们上面的说法一一对上了.但是经过上述的解释之后,我们又要引出另外一个问题,为什么需要五大类注解?
1.2.1 为什么需要五大类注解
这个问题,我们换一个方向去思考,我们使用五大类的原因是什么?
很显然的话,我们是因为,如果我们是一个程序员的话,看到具体的注解,我们就知道这段代码是干什么,或者说,是让程序更清晰,更明了.
下面就是程序的分工和分层.
一般来说javaEE的基本分层就三层:
1.控制层
2.服务层
3.数据持久层
我们继续了解一下,阿里巴巴规定的规范结构.
经过两个对比之后,发现基本大相径庭.
1.3 各个类注解的关系
查看@Controller /@Service /@Repository / @Configuration等注解的源码发现:
其实这些注解⾥⾯都有⼀个注解 @Component,说明它们本身就是属于 @Component 的“⼦类”。
1.4 Bean的命名规则
这里为什么要单独提一嘴呢?我们拿取bean的操作的时候,是有语法规定的.
我这里直接说结论,后面带大家来解释一下:
我们读取的时候,情况如下:
1.是在bean对象正常使用大驼峰命名,使用小驼峰读取
2.假如不是大驼峰命名不是小驼峰,就直接返回当前类的类名即可.
具体例子如下:
为什么会发生这样的命名规则呢?具体的我们还是要看一下源码.
这就是我们对Bean的规则说明.
1.5 方法注解的使用
类注解是添加到某个类上的,⽽⽅法注解是放到某个⽅法上的,如以下代码的实现:
@Component
public class StudentBean {
@Bean
public Student student(){
Student student=new Student();
student.setAge(10);
student.setUsername("zhangsan");
return student;
}
}
这里我们要取出来使用,代码如下:
public class App2 {
public static void main(String[] args) {
//1.得到spring容器
ApplicationContext context=new ClassPathXmlApplicationContext("spring-config.xml");
//2.获取到bean对象
Student student=context.getBean("student", Student.class);
//3使用
System.out.println(student.toString());
}
}
这里还要说一下重命名 Bean的操作:
可以通过设置name属性给Bean对象进行重命名操作,如下代码所示:
@Component
public class StudentBean2 {
@Bean("stu1")
public Student student(){
Student student=new Student();
student.setAge(10);
student.setUsername("张飞");
return student;
}
}
接下来就可以通过stu1获取Student对象了.
public class App2 {
public static void main(String[] args) {
//1.得到spring容器
ApplicationContext context=new ClassPathXmlApplicationContext("spring-config.xml");
//2.获取到bean对象
Student student=context.getBean("stu1", Student.class);
//3使用
System.out.println(student.toString());
}
}
二.取出 Bean 对象
IoC 和 DI 是 Spring 中最重要的两个概念,其中 IoC(Inversion of Control)为控制反转的思想,而 DI(Dependency Injection)依赖注入为其(IoC)具体实现。那么 DI 实现依赖注入的方式有几种?这些注入方式又有什么不同?接下来,我们一起来看。
获取 bean对象也叫做对象装配,是把对象取出来放到某个类中,有时候也叫对象注入。
我们这里取出对象的方式也有以下几种方式:
1.属性注入
2.Setter注入
3.构造方法注入
接下来我们分别来看这几种注入的形式,这几种注入的形式,都是使用的一个注解
@Autowired
2.1 属性注入
属性注入是我们最熟悉,也是日常开发中使用最多的一种注入方式,它的实现代码如下:
先装配到spring中
@Service
//装入spring
public class StudentService {
public void sayHi() {
System.out.println("do studentService sayHi()");
}
}
再通过属性注入,取出来
@Controller
public class StudentController {
//1.属性注入
@Autowired
private StudentService studentService;
public void sayHi(){
System.out.println("com.java.demo -> do UserController sayHi()");
studentService.sayHi();
}
}
具体使用:
StudentController studentController=context.getBean("studentController",StudentController.class);
studentController.sayHi();
优缺点分析:
优点:属性注入最大的优点就是实现简单、使用简单
缺点:
1.无法注入一个不可变的对象(final 修饰的对象);
2.只能适应于 IoC 容器;
3.更容易违背单一设计原则。
2.2 Setter注入
@Controller
public class StudentController {
private StudentService userService;
@Autowired
//2.setter注入
public void setStudentService(StudentService studentService) {
this.studentService = studentService;
}
}
具体使用:
StudentController studentController=context.getBean("studentController",StudentController.class);
studentController.sayHi();
优缺点分析:
优点: 要说 Setter 注入有什么优点的话,那么首当其冲的就是它完全符合单一职责的设计原则,因为每一个 Setter 只针对一个对象。
缺点:
1.不能注入不可变对象(final 修饰的对象);
2.注入的对象可被修改。
2.3 构造方法注入
@Controller
public class StudentController {
private StudentService userService;
// 3.构造方法注入
@Autowired
public StudentController( StudentService userService) {
this.userService = userService;
}
public void sayHi(){
System.out.println("com.java.demo -> do UserController sayHi()");
userService.sayHi();
}
}
具体使用:
StudentController studentController=context.getBean("studentController",StudentController.class);
studentController.sayHi();
优缺点分析:
优点:
- 可注入不可变对象;
原因:
这是Java 的规定,在 Java 中,被final对象必须满足以下两个条件中的任意一个:
a.final 修饰的对象,那么直接赋值;
b.final修饰的对象,必须在构造方法中赋值。 - 注入对象不会被修改;
因为构造方法只能只会执行一次 - 注入对象会被完全初始化;
- 通用性更好。
另外还要注意一点,这里除了使用@Autowired,还可以使用@Resource注解.但是Resource注解只能用在set方法和属性注入,构造方法是不能使用的.
代码案例如下:
public class StudentController {
@Resource
private StudentService userService;
}
具体使用:
StudentService service=context.getBean("studentService",StudentService.class);
service.sayHi();
下面继续介绍Resource的参数化设置的例子
代码如下:
现在存入两个相同的对象.
@Component
public class StudentBean {
@Bean("student1")
public Student student(){
Student student=new Student();
student.setAge(10);
student.setUsername("zhangsan");
return student;
}
@Bean
public Student getstudentbyname(){
Student student=new Student();
student.setAge(10);
student.setUsername("lisi");
return student;
}
}
取出对象的参数
@Controller
public class StudentController3 {
@Resource//更加简单的审批让容器中注入对象
private Student student;
public void sayHi(){
System.out.println("com.java.demo -> do StudentController3 sayHi");
System.out.println(student.getUsername());
}
}
如果说直接这样取的话,会报一个错误.
大家请看:
大概就是没找到,但是我们Resource和Autowire已经有了方案,请看以下代码:
第一种:Resource 搭配name参数
@Controller
public class StudentController3 {
@Resource(name = "student1")
//更加简单的审批让容器中注入对象
private Student student;
public void sayHi(){
System.out.println("com.java.demo -> do StudentController3 sayHi");
System.out.println(student.getUsername());
}
}
第二种: @Autowired搭配使用 @Qualifier 注解定义名称
@Controller
public class StudentController3 {
@Autowired
@Qualifier(value="student1")
//更加简单的审批让容器中注入对象
private Student student;
public void sayHi(){
System.out.println("com.java.demo -> do StudentController3 sayHi");
System.out.println(student.getUsername());
}
}
最后做个总结:
但是这两个注解之间是有区别的,具体的区别如下:
Autowired和@Resource 的区别
- 出身不同:@Autowired来自于Spring,而@Resource 来自于JDK的注解;
- 使用时设置的参数不同∶相比于@Autowired 来说,@Resource支持更多的参数设置,例如name设置,根据名称获取Bean。
- @Autowired可用于Setter注入、构造函数注入和属性注入,而@Resource 只能用于Setter注入和属性注入,不能用于构造函数注入。
- 查找的方式不同:@Autowired先根据类型去查找,再根据名称去查找,@Resource是先根据名称去查找,再根据类型去查找.
三.总结
- 将对象存储到Spring 中:
a.使用类注解:@Controller、@Service、@Repository、@Configuration、@Component【它们之间的关系】
b.使用方法注解:@Bean【注意事项:必须配合类注解一起使用】 - Bean 的命名规则:首字母和第二个字母都非大写,首字母小写来获取Bean,如果首字母和第二个字母都是大写,那么直接使用原Bean名来获取 Bean。
- 从 Spring中获取对象︰
a.属性注入
b.Setter注入
c.构造函数注入(推荐) - 注入的关键字有
a.@Autowiredb.@Resource - @Autowired和@Resource区别:出身不同;使用时设置参数不同@Resource支持更多的参数,比如name。
- 解决同一类型多个Bean的报错:
a.使用@Resource(name=“”)b.使用@Qualifier(“”)