0


5-Spring更简单的读取和存储对象

在Spring中想要更简单地读取和存储对象的核心是使用注解。(Spring,SpringBoot,SpringMVC中的注解是通用的)

1.存储Bean对象

之前需要在spring-config.xml文件中添加bean标签注册内容,而xml的问题在于出错的概率和排错的难度都高:

  • 无法像代码一样调试,只能人工去看;
  • 有错后IDEA不会提示/即使有提示,程序也能正常运行;
  • 写法不方便。

而现在只需要一个注解就ok~

1.1.前置工作:在配置文件中设置bean扫描的根路径(重要)

要想将对象存储到Spring中,需要配置存储对象的扫描包路径,只有被配置的包下的所有类,添加了注解才能被正确地识别并保存到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 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中的bean根目录-->
    <content:component-scan base-package="com.beans"></content:component-scan>
</beans>

其中:

<content:component-scan base-package="com.beans"></content:component-scan>

content:正文;

component-scan:扫描组件;

base-package:根目录(写自己的)。

规定了需要存储到Spring中的类的根目录的扫描路径,否则要扫描当前项目下的所有的类(要加到Spring中的和不要加到Spring中的类),这样提高了效率。

base-package是当前路径当前路径下的所有子路径下的所有的bean。

1.2.添加注解存储Bean对象到Spring中

1.2.1.类注解(添加到某个类上,将当前的类存储到Spring中):@Controller,@Service,@Repository,@Component,@Configuration

使用这5大类注解中的任意一种都可以将bean存储到Spring中。

注:

  1. 即使在Spring配置文件中配置了bean的扫描路径,5大类注解依旧不能省略。
  2. 即使加了5大类注解,但类没有放在Spring配置的bean路径下,那么也是不能将类注入到Spring中的。

①@Controller(控制器存储)

@Controller //将当前对象存储到Spring中
public class UserController {
    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("userController");//通常情况下是小驼峰(将加注解的类的类名首字母小写)就可以获取到bean对象
        //3.使用bean
        userController.sayHi("张三");
    }
}

当使用id获取带注解的bean时:

关于类注解的bean的命名规则:

通常bean使用的都是标准的大驼峰命名:

若bean的命名为第一个字母大写,第二个字母小写,那么读取时将bean的名称首字母小写

若bean的命名为第一个字母和第二个字母都大写,那么读取时为原bean名

若bean的命名为第一个字母和第二个字母都小写,那么读取时为原bean名(一般不存在)。

查看源码(ctrl + 鼠标左键变成手形,点击进入):

②@Service(服务存储)

@Service //将当前对象存储到Spring中
public class UserService {
    public void sayHi(String name){
        System.out.println("Hi," + name);
    }
}

③@Repository(仓库存储)

@Repository //将当前对象存储到Spring中
public class UserRepository {
    public void sayHi(String name){
        System.out.println("Hi," + name);
    }
}

④@Component(组件存储)

@Component //将当前对象存储到Spring中
public class UserComponent {
    public void sayHi(String name){
        System.out.println("Hi," + name);
    }
}

⑤@Configuration(配置存储)

@Configuration //将当前对象存储到Spring中
public class UserConfiguration {
    public void sayHi(String name){
        System.out.println("Hi," + name);
    }
}

PS:为什么要这么多类注解?

①5大类注解之间的关系:

查看源码:

结论:@Controller,@Service,@Repository,@Configuration这4个注解都是依靠@Component注解来实现的,因此@Component注解是其他4个注解的父类。那为啥还要其他4个呢?

②程序的工程分层-调用流程:

一个项目中后端接口最少有这4个目录,方便大型项目中类的分类,查找,维护:让程序员看到类注解后,能直接了解当前类的用途,让逻辑条理更加清晰;也方便项目的人员分工,每人只针对一部分即可。而若都是@Component注解,则不会直观地了解到类的用途。

so~类注解由1个变5个,使得整个代码的语义更加直观!

  • @Controller:业务逻辑层/控制器层,和前端请求打交道,验证前端的有效性;
  • @Service:服务层,组织数据调用相应的接口,不做具体的业务;
  • @Repository:数据持久层/仓库(不同的叫法:dao/repository),和数据打交道,执行具体的CRUD操作;
  • @Configuration:配置层,存放所有的配置信息。

还可能会有更多分层,如有实体层model,工具层utils,数据持久层(MyBatis中)mapper等。

1.2.2.方法注解(添加到某个方法上,将当前方法返回的对象存储到Spring中):@Bean

①方法注解要配合类注解一起使用,才能将对象正常存储到Spring中。(主要是为了考虑性能的问题)** 默认读取bean的名称为方法名。**

@Component
public class UserBeans {
    @Bean
    public User getUser() {
        User user = new User();
        user.setId(1);
        user.setName("张三");
        user.setPassword("123");
        return user;
    }
}

②重命名Bean

方法名主要体现业务性,多为动词;读取bean名称多为名词。若默认二者相等会比较奇怪。

那么可以通过在@Bean注解中设置name属性给Bean对象进行重命名操作:

@Component
public class UserBeans {
    @Bean(name = {"user1"})
    public User getUser() {
        User user = new User();
        user.setId(1);
        user.setName("张三");
        user.setPassword("123");
        return user;
    }
}

注:

1).这个重命名的name其实是一个数组,一个bean可以有多个名字。

含有"{ }"的写法表示给当前对象起一个或多个别名。

@Bean(name = {"user1"})
@Bean(name = {"user1", "user2"})

并且name = { } 可以省略。

@Bean("user1", "user2")

不含有"{ }"的写法表示给当前对象起一个别名。

@Bean(name = "user1")

2).当@Bean注解重命名之后就不能使用方法名来读取bean了。

2.获取Bean对象(对象装配/对象注入)

获取bean对象是把对象取出来放到某个类中。实现方法有3种。5大类注解不能省略(存了才能取)。

2.1.属性注入:是使用@Autowired(自动写入)注解实现的

例:将Service类注入到Controller类中:

Service类的实现代码如下:

import com.model.User;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    /**
     * 根据id获取用户数据
     * @param id
     * @return
     */
    public User getUserById(Integer id) {
        //伪代码,不连接数据库
        User user = new User();
        user.setId(id);
        user.setName("唐僧");
        user.setPassword("123456");
        return user;
    }
}

Controller类的实现代码如下:

import com.beans.service.UserService;
import com.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {
    //1.属性注入:先定义一个属性,再把对象注入给属性
    @Autowired
    private UserService userService;

    public User getUserById(Integer id) {
        return userService.getUserById(id);
    }
}

获取Controller中的getUserById方法:

import com.beans.controller.UserController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    public static void main(String[] args) {
        //1.获取Spring上下文
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        //2.得到bean
        UserController userController = context.getBean(UserController.class);
        //3.使用bean
        System.out.println(userController.getUserById(10));
    }
}

2.2.构造方法注入:是在类的构造方法中实现注入

如果当前类只有一个构造方法,则@Autowired注解可以省略;如果当前类有多个构造方法,则@Autowired注解不可以省略。

import com.beans.service.UserService;
import com.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller 
public class UserController2 {
    private UserService userService;

    //2.构造方法注入
    @Autowired
    public UserController2(UserService userService) {
        this.userService = userService;
    }

    public UserController2(UserService userService, Integer id) {
        this.userService = userService;
    }

    public User getUserById(Integer id) {
        return userService.getUserById(id);
    }
}

2.3.Setter注入:和属性的Setter方法实现类似,只不过在设置set方法时需要加上@Autowired注解

import com.beans.service.UserService;
import com.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController3 {
    private UserService userService;

    //3.Setter注入
    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    public User getUserById(Integer id) {
        return userService.getUserById(id);
    }
}

PS:(经典面试题)属性注入,构造方法注入和Setter注入的异同分析?

都可以将一个对象注入到当前的类当中。

属性注入(差):

优点:简洁,使用方便。官方实际还是用属性注入多些。

缺点:不通用,只能用于IoC容器,非IoC容器不可用(使用的时候会出现NPE(空指针异常))。

构造方法注入(好):

优点:通用性强;且在使用之前一定能保证注入的类不为空(能保证在调用对象之前,此对象一定是存在的)是Spring后期推荐的注入方式。

缺点:可能存在传递多个参数来实现构造方法的初始化,如果有多个注入会显得代码比较臃肿,但出现这种情况是程序员自身的问题,他需要反思当前类是否符合程序的单一职责的设计模式。

Setter注入(中):

优点:是Spring早期版本推荐的注入方式,比属性注入适用性好,无论是IoC容器还是非IoC容器都可以使用。

缺点:通用性不如构造方法注入,所有Spring版本已经推荐使用构造方法注入的方式来进行类注入了。

2.4.另一种注入关键字:@Resource

在进行对象注入时,除了可以使用@Autowired关键字之外,还可以使用@Resource进行注入。

2.4.1.属性注入:

import com.beans.service.UserService;
import com.model.User;
import org.springframework.stereotype.Controller;
import javax.annotation.Resource;

@Controller
public class UserController4 {
    @Resource //引入一个资源到当前类
    private UserService userService;

    public User getUserById(Integer id){
        return userService.getUserById(id);
    }
}

2.4.2.Setter注入:

import com.beans.service.UserService;
import com.model.User;
import org.springframework.stereotype.Controller;
import javax.annotation.Resource;

@Controller 
public class UserController5 {
    private UserService userService;

    @Resource
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    public User getUserById(Integer id){
        return userService.getUserById(id);
    }
}

PS:(经典面试题)@Autowired和@Resource的区别?

①出身不同:

@Autowired是Spring框架提供的实现;

而@Resource是JDK提供的实现。

②支持的参数设置不同:

@Autowired只支持required设置;

@Autowired注解源码实现

而@Resource支持更多的参数设置,如name设置,根据名称获取bean。

@Resource注解源码实现

③支持的注入类型不同:

@Autowired支持属性注入,构造方法注入和Setter注入;

而@Resource只支持属性注入和Setter注入。

④获取注入一个对象时的机制不同:

@Autowired默认先按Type进行匹配,如果找到多个bean,则又会按照组件id方式进行匹配(需要@Qualifier("name")配合);

而@Resource默认按照组件id自动注入,如果按照默认组件id找不到bean时,再按照类型去匹配。

2.5.同一类型多个@Bean报错

import com.model.User;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public class UserBeans {
    @Bean(name = {"user1"})
    public User getUser1() {
        User user = new User();
        user.setId(1);
        user.setName("张三");
        user.setPassword("123");
        return user;
    }

    @Bean(name = "user2")
    public User getUser2() {
        User user = new User();
        user.setId(2);
        user.setName("李四");
        user.setPassword("456");
        return user;
    }
}
import com.model.User;
import org.springframework.stereotype.Controller;
import javax.annotation.Resource;

@Controller
public class UserController6 {
    @Resource
    private User user;

    public void sayHi(){
        System.out.println(user);
    }
}
import com.beans.controller.UserController6;
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");
        UserController6 controller6 = context.getBean(UserController6.class);
        controller6.sayHi();
    }
}

一个类型被注册到Spring多次时,程序运行会出现异常报错:非唯一的Bean对象。

处理方法:

2.5.1.使用正确的bean name来获取(有限制,不用于大规模使用)

import com.model.User;
import org.springframework.stereotype.Controller;
import javax.annotation.Resource;

@Controller
public class UserController6 {
    @Resource
    private User user2;

    public void sayHi(){
        System.out.println(user2);
    }
}

2.5.2.使用@Resource注解设置name属性(最简单)(@Autowired只有一个参数,没有name属性)

import com.model.User;
import org.springframework.stereotype.Controller;
import javax.annotation.Resource;

@Controller
public class UserController6 {
    @Resource(name = "user1")
    private User user;

    public void sayHi(){
        System.out.println(user);
    }
}

2.5.3.使用@Autowired + @Qualifier

@Qualifier只有一个属性,做筛选功能。

import com.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;

@Controller
public class UserController6 {
    @Autowired
    @Qualifier(value = "user2")
    private User user;

    public void sayHi(){
        System.out.println(user);
    }
}

其中:

@Qualifier(value = "user2")

也可以省略"value =",写为:

@Qualifier("user2")
标签: spring java

本文转载自: https://blog.csdn.net/WWXDwrn/article/details/128736317
版权归原作者 hyperNZ 所有, 如有侵权,请联系我们删除。

“5-Spring更简单的读取和存储对象”的评论:

还没有评论