目录
1. 存储Bean对象
1.1 配置扫描路径
1.2 添加注解存储Bean对象
1.2.1 @Controller(控制器存储)
1.2.2 @Service(服务存储)
1.3 这么多注解???为什么??
1.3.1 类注解时间的关系
1.4 方法注解@Bean
1.4.1 @Bean重命名
1.4.2 @Bean存储多个相同对象
2.获取Bean对象(对象装配)
2.1 属性注入
2.2 Setter注入
2.3 构造方法注入(Spring官方推荐)
2.4 @Resource注解
1. 存储Bean对象
通过再配置文件中添加bean注册内容的方式来存储bean感觉是一个十分不方便的操作。想要通过更加简单的操作来实现对bean的注册该怎么办呢?Spring为使用者还提供了通过注解的方式来注册bena。这种方式也是主流的方式,工作当中也大多是通过这种方式来注册bean的,因为十分的方便,而且关联到java的标准分层。
1.1 配置扫描路径
配置扫描路径是什么意思呢??难道不是直接在需要的地方加上注解就可以了吗??
Spring作为一个成熟的框架,肯定是要追求效率的,如果是直接加上注解就可以注册的话,就必须扫描所有的代码遍历去检查有没有注册的需求。这样逻辑工作的话,效率肯定是十分低下的,为了提高效率,Spring决定只扫描在配置在base-package包以及其子包。如果不在base-package包下的地方即使加上了注解也是没有办法注册bean的,在使用的时候就会报错。只有被配置的包下的所有类,并且添加了注解才能被正确的识别并保存到 Spring 中。
怎么配置扫描路径?只需要在spring-config.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 htt
p://www.springframework.org/schema/beans/spring-beans.xsd http://www.spring
framework.org/schema/context https://www.springframework.org/schema/contex
t/spring-context.xsd">
<content:component-scan base-package="com.xxx.yyy"></content:compon
ent-scan>
</beans>
其中
添加完扫描路径以后,Spring就会在这个包下面扫面有哪些添加了注解然后将其存储到Spring中。
1.2 添加注解存储Bean对象
Spring提供了两种类型的注解来帮助使用者将对象存储到Spring当中。
- 1. 类注解:@Controller、@Service、@Repository、@Component、@Configuration。
- 2. ⽅法注解:@Bean。
下面先来介绍他们是怎么使用的,然后再去介绍他们的使用场景。
1.2.1 @Controller(控制器存储)
首先它是一个类注解,使用的时候就是在想要存储的类上加上这个注解。使用@Controller存储bean的代码如下所示:
@Controller
public class UserController {
public void sayHi(String name){
System.out.println("Hi I am " + name);
}
}
读取使用起来还是一样的:
public class App {
public static void main(String[] args) {
// 1.得到 spring 上下⽂
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
// 2.得到 bean
UserController userController = (UserController) context.getBean(
"userController");
// 3.调⽤ bean ⽅法
userController.sayHi("Zhangsan");
}
}
其实现在有一个小问题,我们使用注解的时候并没有指定id啊,这样我们在读取的时候是怎么样知道他的id并且是获取的呢?根据上面的使用情况来看即使将注解的类名的首字母改成小写就是存储的id,那么到底是不是这样的?
答案其实不是这样的,这需要搞清楚类注解的bean命名规则才能得到答案。我们可以看一下Spring的源码中对于BeanName的实现到底是什么情况?在 Idea 中使⽤搜索关键字“beanName”可以看到以下内容:
看完源代码之后就能十分确定我们在使用的时候怎么确定ID了,分为两种情况:
- 1.类名首字母和第二个字母都是大写的情况下,bean的名字就是类名;
- 2.其他情况下就是将首字母改为小写作为bean的名字。
1.2.2 @Service(服务存储)
使⽤ @Service 存储 bean 的代码如下所示,和前面的controller的使用方法一致。
@Service
public class UserService {
public void sayHi(String name) {
System.out.println("Hi," + name);
}
}
public class App {
public static void main(String[] args) {
// 1.得到 spring 上下⽂
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
// 2.得到 bean
UserController userController = (UserController) context.getBean("userService");
// 3.调⽤ bean ⽅法
userController.sayHi("Zhangsan");
}
}
剩下的集中情况不再去逐个演示,使用起来的方式都是大同小异,在类上加上对应的注解就可以了。用法并不是重点,重点是为什么提供这么多的注解??而且他们的使用方法都是一致的,这是为什么??
1.3 这么多注解???为什么??
那么为什么需要怎么多的类注解也是相同的原因,就是让程序员看到类注解之后,就能直接了解当前类 的⽤途,⽐如:
- @Controller:表示的是业务逻辑层;
- @Servie:服务层;
- @Repository:持久层;
- @Configuration:配置层。
这样就涉及到程序的工程分层,调用的流程大致如下:
1.3.1 类注解时间的关系
查看 @Controller / @Service / @Repository / @Configuration 等注解的源码发现:
其实这些注解⾥⾯都有⼀个注解 @Component,说明它们本身就是属于 @Component 的“⼦类”。
1.4 方法注解@Bean
通过学习发现类注解是加在类上面的,想必方法注解就是加在某个方法上面了。另外要注意的是,这个方法必须要有一个返回值,如果没有返回值的话,Spring存什么呢?所以必须是要有一个返回值的,然后将这个返回值存入到spring中。并且存入的时候只能存一个无参方法的返回值。
先来看一下直接在方法上加上Bean注解是否能够成功使用?
public class StudentBeans {
@Bean // 使用Bean注解讲函数的返回值也就是Student对象存储到spring中
public Student stu1(){
Student student = new Student("tom");
student.setAge(18);
student.setPassword("123456");
return student;
}
}
public class App {
public static void main(String[] args) {
// 1.得到 spring 上下⽂
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
// 2.得到 bean
Student stu1 = context.getBean("student", Student.class);
// 3.调⽤ bean ⽅法
System.out.println(stu1.getName());
}
}
运行时候发现报错,No bean named 'student' available,也就是我们spring中没有student。实际上造成上面报错的原因是代码中包含两处错误:
- 1.Bean方法注解的命名规则相较于类注解已经发生了变化,Bean方法注解存入的id就是方法名,和类名没有任何的关系。比如 public Student stu1()中存入的id就是stu1。
- 2. Bean注解必须搭配五大类注解使用才可以生效,否则也会报错。
加入类注解,根据bean命名规则修改代码后:
运行代码,观察是否能够成功获取到stu对象:
1.4.1 @Bean重命名
@Bean(name = {"student","stu"})
使用上面的代码可以给@Bean存入的对象重命名,而且可以起多个名字。name可以使用value替换。但是当@Bean重命名以后原来的默认的id就无法再获取到了。也就是通过
Student stu1 = context.getBean("stu1" , Student.class);无法在获取到存入的对象,只能通过重命名以后的id来获取到。在使用的时候要注意这个地方
1.4.2 @Bean存储多个相同对象
当使用@Bean注解存储多个相同对象的时候可以使用重命名来加以区分:
@Component
public class StudentBeans {
@Bean(name = {"student","stu"}) // 使用Bean注解讲函数的返回值也就是Student对象存储到spring中
public Student stu1(){
Student student = new Student("tom");
student.setAge(18);
student.setPassword("123456");
return student;
}
@Bean(name = {"student1","stu1"}) // 使用Bean注解讲函数的返回值也就是Student对象存储到spring中
public Student stu(){
Student student = new Student("jack");
student.setAge(18);
student.setPassword("123456");
return student;
}
}
如果现在在两个不同的类中,分别存在一个方法名和返回值都一样的方法并且都是用了@Bean注解,会不会报错??
答案是不会报错,但是每次运行的结果都是相同的结果,并不是随机的(我这里每次都是jordan)
这里可以通过@Oeder()来控制注入的顺序,值越小,越先注入,然后就会被后者注入的覆盖掉。
2.获取Bean对象(对象装配)
2.1 属性注入
缺点:
- 无法注入final修饰的变量;
- 只能适用于IoC容器,兼容性不好;
- 此恶法简单,更容易违反单一设计原则;
优点:
- 使用十分简单;
2.2 Setter注入
优点:更符合单一设计原则,每次都只传递一个对象;
缺点:
- 不可以注入一个final修饰的变量;
- 使用Setter注入的对象可以被修改 ,因为使用的载体就是一个setter方法,setter方法可以被多次调用;
2.3 构造方法注入(Spring官方推荐)
当当前类中只有一个构造方法的时候,@Autowired注解可以省略不写!
优点:
- 1.可注入final修饰的变量;
为什么构造方法可以注入final修饰的不可变对象呢??因为java中被final修饰的对象,必须满足:final修饰的对象直接被赋值 或者 final修饰的对象在构造方法中被赋值
- 2.注入的对象不会被修改,因为构造方法只能执行一次;
- 3 .构造方法注入可以保证注入对象被完全初始化;
- 4.构造方法的兼容性更好,即使脱离了IoC容器中也可以使用。
缺点:
- 可能会违反单一设计原则(使用者可控,一般来说不会违反);
2.4 @Resource注解
@Resource在属性注入和Setter注入场景下可以替换@Autowired,但是在构造方法注入场景下只能使用@Autowired注解;
@Resource支持更多的参数设置:(当同一个类型的类被注入多个在spring当中的时候,使用@Autowired可能会解决不了命名冲突的问题,这时候就只能使用@Resource来给注入的bean重命名)