之前学习的spring的存储和读取对象,借助1.存储 Bean 对象 1.1 前置?作:配置扫描路径(重要) 虽然通过注解能够方便存储对象到spring中,但是在存储之前要设置spring扫描的地方即包的路径,只有你的类对象在设置的相应的地方时候spring才能通过注解获取并存储对象,还是先在spring-config.xml中配置以下文件来将类的对象注册到spring框架中去(如图),但是我们每注册一个对象还要加一个bean过于繁琐和浪费时间,因此在spring中加入了注解来简化注册对象到spring中的过程
文章图片
文章图片
其中扫描的路径就是最后一行进行配置的路径,也就是所需存储的类的位置 。
也就是说,即使添加了注解,如果不是在配置的扫描包下的类对象,也是不能被存储到 Spring 中的
1.2 添加注解存储 Bean 对象 想要将对象存储在 Spring 中,有两种注解类型可以实现:
1. 类注解:@Controller、@Service、@Repository、@Component、@Configuration。
【spring|Spring中更简单地读取和存储对象(使用注解)】2. ?法注解:@Bean。
1.2.1 @Controller(控制器存储)
@Controller // 将对象存储到 Spring 中
public class UserController {
public void sayHi(String name) {
System.out.println("Hi," + name);
}
}
此时我们先使?之前读取对象的?式来读取上?的 UserController 对象,如下代码所示
public class Application {
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("Bit");
}
}
1.2.2 @Service(服务存储)
@Service
public class UserService {
public void sayHi(String name) {
System.out.println("Hi," + name);
}
}
读取方式和controller相同这里和下方都省略。。。
1.2.3 @Repository(仓库存储)
@Repository
public class UserRepository {
public void sayHi(String name) {
System.out.println("Hi," + name);
}
}
1.2.4 @Component(组件存储)
@Component
public class UserComponent {
public void sayHi(String name) {
System.out.println("Hi," + name);
}
}
1.2.5 @Configuration(配置存储)
@Configuration
public class UserConfiguration {
public void sayHi(String name) {
System.out.println("Hi," + name);
}
}
1.3 为什么要这么多类注解?在我们写一个大型程序的时候,往往需要不同的人完成不同的模块,就是为了让程序员看到不同的注解明白当前的代码 具体完成的是哪一个模块,上述我们展示的看起来每个注解好像都可以,其实功能就是一样的,实际上就是为了区分模块,举一个例子如下:
这和为什么每个省/市都有??的?牌号是?样的??如陕?的?牌号就是:陕X:XXXXXX,北京的? 牌号:京X:XXXXXX,?样。甚??个省不同的县区也是不同的,?如?安就是,陕A:XXXXX,咸 阳:陕B:XXXXXX,宝鸡,陕C:XXXXXX,?样。这样做的好处除了可以节约号码之外,更重要的作 ?是可以直观的标识?辆?的归属地@Controller:表示的是业务逻辑层;
不同的项目有不同的功能,不同的功能需要不同的实现,实现这些核心功能的代码就叫业务逻辑
比如让你实现一个功能,给你两个数,让你获取它的和,你所写的如何才能获得任意给定的两个数的和,这个程序实现过程即可成为业务逻辑处理。
@Servie:服务层;
服务层实际上不执行任何具体的工作,其功能在于组织各个业务对象、应用程序专有的服务、工作流以及其他任何出现在业务逻辑中的特殊组件。
@Repository:持久层;
可以理解成数据 保存在 数据库或者 硬盘一类可以保存很长时间的设备里面,不像放在内存中那样断电就消失了,也就是把数据存在持久化设备上
@Configuration:配置层
执行流程如图
文章图片
1.3.1 类注解之间的关系
查看 @Controller / @Service / @Repository / @Configuration 等注解的源码发现:
文章图片
其实这些注解??都有?个注解 @Component,说明它们本身就是属于 @Component 的“?类”
1.3.2 注意 Bean 命名
通过上?示例,我们可以看出,通常我们 bean 使?的都是标准的?驼峰命名,?读取的时候?字?? 写就可以获取到 bean 了,如下图所示
文章图片
然?,当我们?字?和第?个字?都是?写时,就不能正常读取到 bean 了,如下图所示:
文章图片
我们可以在 Idea 中使?搜索关键字“beanName”可以看到以下内容:
文章图片
它使?的是 JDK Introspector 中的 decapitalize ?法,源码如下
public static String decapitalize(String name) {
if (name == null || name.length() == 0) {
return name;
}
// 如果第?个字?和第?个字?都为?写的情况,是把 bean 的?字?也?写存储了
if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
Character.isUpperCase(name.charAt(0))){
return name;
}
// 否则就将?字??写
char chars[] = name.toCharArray();
chars[0] = Character.toLowerCase(chars[0]);
return new String(chars);
}
所以对于上?报错的代码,我们只要改为以下代码就可以正常运?了
文章图片
1.4 ?法注解 @Bean 类注解是添加到某个类上的,??法注解是放到某个?法上的,如以下代码的实现
public class Users {
@Bean
public User user1() {
User user = new User();
user.setId(1);
user.setName("Java");
return user;
}
}
然?,当我们写完以上代码,尝试获取 bean 对象中的 user1 时却发现,根本获取不到
public class Application {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
User user = (User) context.getBean("user1");
System.out.println(user.toString());
}
}
执行结果如图
文章图片
1.4.1 ?法注解要配合类注解使?
在 Spring 框架的设计中,?法注解 @Bean 要配合类注解才能将对象正常的存储到 Spring 容器中,因为一个类可能有多个方法,spring扫描所有的方法过于繁琐,因此spring设计的是按照类进行扫描如 下代码所示:
@Component
public class Users {
@Bean
public User user1() {
User user = new User();
user.setId(1);
user.setName("Java");
return user;
}
}
再次执?以上代码,运?结果如下
文章图片
1.4.2 重命名 Bean
可以通过设置 name 属性给 Bean 对象进?重命名操作,如下代码所示
@Component
public class Users {
@Bean(name = {"u1"})
public User user1() {
User user = new User();
user.setId(1);
user.setName("Java");
return user;
}
}
此时我们使? u1 就可以获取到 User 对象了,如下代码所示
class App {
public static void main(String[] args) {
// 1.得到 spring 上下?
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
// 2.得到某个 bean
User user = (User) context.getBean("u1");
// 3.调? bean ?法
System.out.println(user);
}
}
2.获取 Bean 对象(对象装配) 获取 bean 对象也叫做对象装配,是把对象取出来放到某个类中,有时候也叫对象注?
对象装配(对象注?)的实现?法以下 3 种:
1. 属性注?
2. 构造?法注?
3. Setter 注?
2.1 属性注? 属性注?是使? @Autowired 实现的,将 Service 类注?到 Controller 类中
Service 类的实现代码如下:
import org.springframework.stereotype.Service;
@Service
public class UserService {
/**
* 根据 ID 获取?户数据
*
* @param id
* @return
*/
public User getUser(Integer id) {
// 伪代码,不连接数据库
User user = new User();
user.setId(id);
user.setName("Java-" + id);
return user;
}
}
Controller 类的实现代码如下
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
// 注??法1:属性注?
@Autowired
private UserService userService;
public User getUser(Integer id) {
return userService.getUser(id);
}
}
获取 Controller 中的 getUser ?法:
import org.springframework.context.ApplicationContext;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
public class UserControllerTest {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
UserController userController =
context.getBean(UserController.class);
System.out.println(userController.getUser(1).toString());
}
}
最终结果如下:
文章图片
属性注?的核?实现如下:
文章图片
2.2 构造?法注? 构造?法注?是在类的构造?法中实现注?,如下代码所示
@Controller
public class UserController2 {
// 注??法2:构造?法注?
private UserService userService;
@Autowired
public UserController2(UserService userService) {
this.userService = userService;
}
public User getUser(Integer id) {
return userService.getUser(id);
}
}
2.3 Setter 注? Setter 注?和属性的 Setter ?法实现类似,只不过在设置 set ?法的时候需要加上 @Autowired 注 解,如下代码所示:
@Controller
public class UserController3 {
// 注??法3:Setter注?
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
public User getUser(Integer id) {
return userService.getUser(id);
}
}
2.4 三种注?优缺点分析 属性注?的优点是简洁,使??便;缺点是只能?于 IoC 容器,如果是? IoC 容器不可?,并且只 有在使?的时候才会出现 NPE(空指针异常),但是大多数情况下我们为了偷懒,并且多数使用也就是在spring中,因此这种方法应用较多
构造?法注?是 Spring 推荐的注??式,它的缺点是如果有多个注?会显得?较臃肿,但出现这种 情况应该考虑?下当前类是否符合程序的单?职责的设计模式了,它的优点是通?性,就是每种语言的set方法可能不尽相同,但是构造方法基本一致,还要在使?之 前?定能把保证注?的类不为空。
Setter ?式是 Spring 前期版本推荐的注??式,但通?性不如构造?法,所有 Spring 现版本已经 推荐使?构造?法注?的?式来进?类注?了。
2.5 @Resource:另?种注?关键字 在进?类注?时,除了可以使? @Autowired 关键字之外,我们还可以使? @Resource 进?注?,如 下代码所示:
@Controller
public class UserController {
// 注?
@Resource
private UserService userService;
public User getUser(Integer id) {
return userService.getUser(id);
}
}
@Autowired 和 @Resource 的区别:2.6 同?类型多个 @Bean 报错 当出现以下多个 Bean,返回同?对象类型时程序会报错,如下代码所示
1.其出身不一样,前者是来自于spring框架,后者来自于jdk中自带的jar包
2.使?时设置的参数不同:相?于 @Autowired 来说,@Resource ?持更多的参数设置,例如 name 设置,根据名称获取 Bean,当我们spring中有一个类的多个对象时候,并且在树形注入时候并没有按照对象的名称进行注入,那么当我们获取对象时候Spring会按照先根据名称查找,差找不到,因为我们没有使用对应的名称,后按照类查找的方式进行但是其查找结果,发现结果不止一个,spring 不知道获取哪一个就会报错
@Component
public class Users {
@Bean
public User user1() {
User user = new User();
user.setId(1);
user.setName("Java");
return user;
}
@Bean
public User user2() {
User user = new User();
user.setId(2);
user.setName("MySQL");
return user;
}
}
在另?个类中获取 User 对象,如下代码如下
@Controller
public class UserController4 {
// 注?
@Resource
private User user;
public User getUser() {
return user;
}
}
以上程序的执?结果如下:
文章图片
同?类型多个 Bean 报错处理
解决同?个类型,多个 bean 的解决?案有以下两个
使? @Resource(name="user1") 定义
使? @Qualifier 注解定义名称
① 使? @Resource(name="XXX")
@Controller
class UserController4 {
// 注?
@Resource(name = "user1")
private User user;
public User getUser() {
return user;
}
}
② 使? @Qualifier
@Controller
public class UserController5 {
// 注?
@Autowired
@Qualifier(value = "https://www.it610.com/article/user2")
private User user;
public User getUser() {
return user;
}
}
推荐阅读
- java学习分享|java入门笔记1
- java|java相关优秀开源项目
- JAVA|反射机制和类加载机制
- JAVA|JAVA中的构造器和this关键字
- 数据结构|Map和Set
- Java|redis 发布和订阅 持久化 事务 缓存问题
- Java|使用两个注解,三步完成SpringBoot事件监听(反射,切面实现)
- Java|基于SpringBoot和Redis(Redisson的分布式锁的使用)
- redis|SpringBoot缓存使用Redis与@CaChe注解整合 简洁使用