通过之前的学习,我们也能够实现基本的 Spring 读取和存储对象操作,感觉有点麻烦,接下来, 更简单的存储和读取对象的核心就是使用注解
1 存储 Bean 对象
1.1 配置扫描路径(重要)
先把依赖文件导入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>
接下来写入配置文件,名字最好起的有意义
把这串类容粘贴进去:
<?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">
<!-- 配置 Spring 扫描的根路径(此根路径下的所有 Spring 存对象的注解才能生效) -->
<content:component-scan base-package="beans"></content:component-scan>
</beans>
1.2 添加注解存储 Bean 对象
有两种注解类型可以实现:
1: 使用 5 大类注解实现:
@Controller[控制器]
,@Service[服务]
,@Repository[仓库]
,
@Configuration[配置]
,Component[组件]
2: 通过方法注解:
@Bean方法
1.2.1 类注解
① @Controller(控制器存储)
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
public void sayHi(String name){
System.out.println("hello: "+name);
}
}
后面解释原因.
② @Service(服务存储)
import org.springframework.stereotype.Service;
@Service
public class UserService {
public void sayHi(String name){
System.out.println("hello: "+name);
}
}
③ @Repository(仓库存储)
import org.springframework.stereotype.Repository;
@Repository
public class UserRepository {
public void sayHi(String name){
System.out.println("hello: "+name);
}
}
④ Configuration(配置存储)
import org.springframework.context.annotation.Configuration;
@Configuration
public class UserConfiguration {
public void sayHi(String name){
System.out.println("hello: "+name);
}
}
⑤ Component(组件存储)
import org.springframework.stereotype.Component;
@Component
public class UserComponent {
public void sayHi(String name){
System.out.println("hello: "+name);
}
}
测试读取 bean 的代码
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//Controller
UserController userController = context.getBean("userController",UserController.class);
userController.sayHi("Controller");
//Service
UserService userService = context.getBean("userService",UserService.class);
userService.sayHi("Service");
//Repository
UserRepository userRepository = context.getBean("userRepository",UserRepository.class);
userRepository.sayHi("Repository");
//Configuration
UserConfiguration userConfiguration = context.getBean("userConfiguration",UserConfiguration.class);
userConfiguration.sayHi("Configuration");
//Component
UserComponent userComponent = context.getBean("userComponent",UserComponent.class);
userComponent.sayHi("Component");
}
}
注意事项
1: 读取存入对象的 id 默认情况首字母是要小写的,大写读不到
2: 当第一个字母和第二个字母都大写了,首字母小写就不能读到
这里就需要去观察源码是怎么实现的了,
双击shift
搜索beanName
它使⽤的是 JDK Introspector 中的 decapitalize ⽅法,源码如下
像这种情况大写就行了
3: 包路径要写对
为什么需要这么多类注解?
让代码的可读性更高,能够让程序员能够直观的判断当前类的用途
@Controller:表示的是业务逻辑层;
@Servie:服务层;
@Repository:持久层;
@Configuration:配置层;
程序的⼯程分层,调⽤流程如下:
类注解之间的关系
查看 @Controller/@Service/@Repository/@Configuration等注解的源码发现:
这几个注解都有一个注解@Component
,说明了它们本身就是属于@Component
的"子类"
1.2.2 @Bean(方法注解)
类注解是添加到某个类上的,⽽⽅法注解是放到某个⽅法上的,如以下代码的实现:
先创建一个User类:
public class User {
private int id;
private String name;
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
在创一个UserBean:
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class UserBean {
@Bean
public User user(){
User user = new User();
user.setId(1);
user.setName("张三");
return user;
}
}
测试读取的内容:
import beans.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
User user = context.getBean("user",User.class);
System.out.println(user.toString());
}
}
注意要有类注解,不然获取不到:
如果不加类注解:
方法重命名
可以通过设置 name 属性给 Bean 对象进⾏重命名操作,如下代码所示:
@Bean 命名规则,当没有设置 name 属性时,那么 bean 默认的名称就是方法名,当设置了name 属性之后,只能通过重命名的 name 属性对应的值来获取,也就是说重命名之后,在使用方法名就获取不到 bean 对象
他也可以重命名多个:
2. 获取 Bean 对象(对象装配)
获取 bean 对象也叫做对象装配,是把对象取出来放到某个类中,有时候也叫对象注⼊
对象注入(对象装配)[从容器中将对象读取出来]三种方式:
- 属性注入(字段注入)
- 构造方法注入
- Setter注入
2.1 属性注入
属性注入使用 @Autowired
实现的,我们来将 Service 类注入到 Controller 类中:
Service:
import org.springframework.stereotype.Service;
@Service
public class UserService {
public void sayHi(){
System.out.println("hello,Service!");
}
}
Controller:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
@Autowired
private UserService userService;
public void sayHi(){
userService.sayHi();
}
}
App启动:
import beans.UserController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App2 {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
UserController userController = context.getBean(UserController.class);
userController.sayHi();
}
}
2.2 构造方法注入
构造⽅法注⼊是在类的构造⽅法中实现注⼊
2.3 Setter注入
Setter 注⼊和属性的 Setter ⽅法实现类似,只不过在设置 set ⽅法的时候需要加上 @Autowired
注解
注:
如果当前类里构造方法只有一个, 那么@Autowired
是可以被省略的
三种注入优缺点分析
属性注入:
优点
就是简洁,使用方便;
缺点
通用性不好,只能⽤于 IoC 容器,如果是⾮ IoC 容器不可⽤,并且只有在使⽤的时候才会出现 NPE(空指针异常)
构造方法(官方推荐):
它的缺点
是如果有多个注⼊会显得⽐较臃肿,但出现这种情况你应该考虑⼀下当前类是否符合程序的单⼀职责的设计模式了,它的优点
是通⽤性好,在使⽤之前⼀定能把保证注⼊的类不为空
Setter注入:
Setter ⽅式是 Spring 前期版本推荐的注⼊⽅式,但通⽤性不如构造⽅法
,所有 Spring 现版本已经推荐使⽤构造⽅法注⼊的⽅式来进⾏类注⼊了
2.4 @Resource: 另一种注入关键字
在进⾏类注⼊时,除了可以使⽤ @Autowired
关键字之外,我们还可以使⽤ @Resource
进⾏注⼊,注意一点的是,@Resource
无法进行构造方法注入
属性注入:
Setter注入:
构造方法注入就不行:
@Autowired 和 @Resource 的区别
1:
出身不同:@Resource
来自于 JDK(Java 亲儿子),@Autowired
是 Spring 框架提供的
2:
用法不同:@Autowired
支持属性注入,构造方法注入,Setter注入,而@Resource
不支持构造方法注入
3:
支持的参数不同:@Resource
支持更多的参数设置,比如 name,type 设置,而@Autowired
只支持required
参数设置
同一个类型多个 @Bean 报错
报错的原因就是非唯一的 Bean 对象
解决办法:
1:
精确的描述 bean 的名称(将注入的名称写对)
2:
使用@Resource
设置 name 的方式 来重命名注入对象(如果已经写太多不想改了就可以用这种方式)
注:
@Atowired
不能这样设置
3:
使用@Qualifier
来筛选bean对象(@Atowired
和@Resource
都可以)