springboot

SpringBoot笔记

前言

​ 教程文档:
​ 黑马:Docs
​ 苍穹外卖:苍穹外卖_吾浴西风的博客-CSDN博客

起步

初始程序

​ 在idea选择 springboot generators,在选择依赖中加入spring web 依赖 创建即可
​ spring web中包含了tomcat启动时即会启动tomcat
结构:
pom
​ 在创建好项目后,项目会依赖着springboot父工程,其下再引入springboot的其他依赖时不用指明版本,父工程内已声明了
启动类:
​ 自动生成,被@SpringBootApplication 注解修饰
资源文件:
​ static: 存放着静态页面, html css js
​ application.properties:springboot的配置, 可以改为yml文件

接收前端请求

@RestController //标识当前类是一个请求处理类
public class HelloController {
    @RequestMapping("/hello") //标识请求路径
    public String hello(String name){
        System.out.println("HelloController ... hello: " + name);
        return "Hello " + name; //返回的值在响应体内 ,若要返回文件,获取response对象调用.getOutputStream() 将文件写入流即可
    }
}

获取和响应数据

​ tomcat服务器对HTTP协议的请求数据进行解析,并进行了封装
​ springboot的获取数据会封装到tomcat提供的类中, 如请求HttpServletRequest,响应 HttpServletResponse

@RestController
public class RequestController {
    /**
     * 请求路径 http://localhost:8080/request?name=Tom&age=18
     * @param request
     * @return
     */
    @RequestMapping("/request")
    public String request(HttpServletRequest request){
        //1.获取请求参数 name, age
        String name = request.getParameter("name");
        String age = request.getParameter("age");
        System.out.println("name = " + name + ", age = " + age);

        //2.获取请求路径
        String uri = request.getRequestURI(); //  /request
        String url = request.getRequestURL().toString();// /http://localhost:8080/request
        System.out.println("uri = " + uri);
        System.out.println("url = " + url);
        
        //3.获取请求方式
        String method = request.getMethod();
        System.out.println("method = " + method);
        
        //4.获取请求头
        String header = request.getHeader("User-Agent");
        System.out.println("header = " + header);
        return "request success";
        
        //5.获取请求体
        BufferedReader reader = null;
        reader = request.getReader();
    }
}

​ 设置响应数据

@RestController
public class ResponseController {

    @RequestMapping("/response")
    public void response(HttpServletResponse response) throws IOException {
        //1.设置响应状态码
        response.setStatus(401);
        //2.设置响应头
        response.setHeader("name","itcast");
        //3.设置响应体
        response.setContentType("text/html;charset=utf-8");
        response.setCharacterEncoding("utf-8");
        response.getWriter().write("<h1>hello response</h1>");  
        //以流的形式写入  getOutputStream()  获取流
    }

    @RequestMapping("/response2")
    public ResponseEntity<String> response2(){
        return ResponseEntity  #以spring提供的ResponseEntity进行返回
                .status(401)
                .header("name","itcast")
                .body("<h1>hello response</h1>"); //泛型类型就是这里响应体的类型
    }
}

RestController注解

​ @RestController 由@Controller 和@ResponseBody 组合而成
​ Controller 注解标明该类是一个接收请求的类,
​ ResponseBody 标明该类方法内将方法返回值直接响应给浏览器,如果返回值类型是实体对象/集合,将会转换为JSON格式后在响应给浏览器

@RestController
public class UserController {
    @RequestMapping("/list")
    public List<User> list(){
        //1.加载并读取文件
        InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");
        ArrayList<String> lines = IoUtil.readLines(in, StandardCharsets.UTF_8, new ArrayList<>());
        //2.解析数据,封装成对象 --> 集合
        List<User> userList = lines.stream().map(line -> {
            String[] parts = line.split(",");
            Integer id = Integer.parseInt(parts[0]);
            String username = parts[1];
            String password = parts[2];
            String name = parts[3];
            Integer age = Integer.parseInt(parts[4]);
            LocalDateTime updateTime = LocalDateTime.parse(parts[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            return new User(id, username, password, name, age, updateTime);
        }).collect(Collectors.toList());
        //3.响应数据
		//也可以自己解析成字符串返回,不用通过Spring解析
        //return JSONUtil.toJsonStr(userList, JSONConfig.create().setDateFormat("yyyy-MM-dd HH:mm:ss")); 
        return userList;
    }
}

static静态资源

​ 由于spring web依赖内嵌了tomcat,在resource内的static就是tomcat提供的,在其里面放置的静态资源在外部可像发起前端请求一样,之前获取界面
​ 相当于托管了前端页面,如
​ static
​ -index.html
​ -css
​ -js
​ 在外部访问时, host:post/index.html 即可访问

分层解耦

三层架构

​ 把后端的功能分层3层结构
​ controller:
​ 控制层,负责接收前端的数据,调用服务层,再将数据返回
​ service:
​ 服务层,负责逻辑处理
​ dao:
​ 数据层,各个实体类就在这里,可以使用实体类操作数据

解耦

​ spring 2个核心概念

  • 控制反转: Inversion Of Control,简称IOC。对象的创建控制权由程序自身转移到外部(容器),这种思想称为控制反转。

    • 对象的创建权由程序员主动创建转移到容器(由容器创建、管理对象)。这个容器称为:IOC容器或Spring容器。
  • 依赖注入: Dependency Injection,简称DI。容器为应用程序提供运行时,所依赖的资源,称之为依赖注入。

    • 程序运行时需要某个资源,此时容器就为其提供这个资源。
    • 例:EmpController程序运行时需要EmpService对象,Spring容器就为其提供并注入EmpService对象。

具体实现:
​ 每个实现类都有一个接口,在外部调用实现类时,直接调用接口
​ 使用@Component 修饰实现类,将实现类交给spring容器管理
​ 在外部调用时,声明其接口类,使用@Autowired修饰该变量,spring会自动创建对应的类

//服务层接口
public interface UserService {
    public List<User> findAll();
}
//管理层进行逻辑处理,调用数据层数据
@Component
public class UserServiceImpl implements UserService {
	//依赖注入
    @Autowired
    private UserDao userDao;
    @Override
    public List<User> findAll() { 
    }
}

//由控制层调用服务处
@RestController
public class UserController {
    @Autowired
    private UserService userService;
    @RequestMapping("/list")
    public List<User> list(){
        //1.调用Service
        List<User> userList = userService.findAll();
        //2.响应数据
        return userList;
    }
}

IOC(控制反转)

衍生注解

​ 使用衍生注解可标明是哪一层的, 可以不用,直接使用 Component注解

注解 说明 位置
@Component 声明bean的基础注解 不属于以下三类时,用此注解
@Controller @Component的衍生注解 标注在控制层类上
@Service @Component的衍生注解 标注在业务层类上
@Repository @Component的衍生注解 标注在数据访问层类上(由于与mybatis整合,用的少)

组件扫描

  • 前面声明bean的四大注解,要想生效,还需要被组件扫描注解 @ComponentScan 扫描。
  • 该注解虽然没有显式配置,但是实际上已经包含在了启动类声明注解 @SpringBootApplication 中,默认扫描的范围是启动类所在包及其子包

若要扫描其他包,需要手动添加@ComponentScan注解
@ComponentScan({"com.littled","com.abc"}) //传给value的数组就是添加的包

  • 声明bean的时候,可以通过value属性指定bean的名字,如果没有指定,默认为类名首字母小写。
  • 使用以上四个注解都可以声明bean,但是声明控制器bean只能用@Controller。@RestController 包含着Controller注解

DI(依赖注入)

Autowired注入

​ @Autowired注解,默认是按照类型进行自动装配的(去IOC容器中找某个类型的对象,然后完成注入操作)
​ 若将多个实现同一个父类接口的类托管给IOC容器,则在注入时会发生报错,此时需要使用其他注解
属性注入:

@RestController
public class UserController {
    //方式一: 属性注入
    @Autowired
    private UserService userService;
  }

构造函数注入:
​ 如果只有一个构造函数,@Autowired注解可以省略。(通常来说,也只有一个构造函数)

@RestController
public class UserController {
    //方式二: 构造器注入
    private final UserService userService;
    @Autowired //如果当前类中只存在一个构造函数, @Autowired可以省略
    public UserController(UserService userService) {
        this.userService = userService;
    }
 }   

set注入:

@RestController
public class UserController {
    //方式二: 构造器注入
    private final UserService userService;
    @Autowired //如果当前类中只存在一个构造函数, @Autowired可以省略
    public UserController(UserService userService) {
        this.userService = userService;
    }
 }   

其他注入方式

​ 使用@Primary注解:当存在多个相同类型的Bean注入时,加上@Primary注解,来确定默认的实现。
​ 使用@Qualifier注解:指定当前要注入的bean对象。 在@Qualifier的value属性中,指定注入的bean的名称。
​ 使用@Resource注解:是按照bean的名称进行注入。通过name属性指定要注入的bean的名称。

  • @Autowired 是spring框架提供的注解,而@Resource是JDK提供的注解
  • @Autowired 默认是按照类型注入,而@Resource是按照名称注入
@Primary
@Service
public class ServiceA implements Service{}



@Autowired
@Qualifier("serviceA") //IOC容器内bean对象容器名称,默认为类的首字母小写,也可在声明时自己指定

@Primary
@Service
public class ServiceA implements Service{}

请求控制

请求类别

​ 前端的请求有 get,post,delete等, 在用@RequestMapping获取时,需要判断请求类别
​ 可以使用对应方法的注解进行接收
​ get: @GetMapping("URI")
​ post: @PostMapping("URI")
​ delete: @DeleteMapping("URI")

请求参数

路径参数:
​ /depts/{id}
​ 使用@PathVariable 注解 获取

    @DeleteMapping("/depts/{id}")
    public Result delete(@PathVariable Integer id) {
    }

​ /depts?name=littled&age=18
​ 使用@RequestParam 注解获取,按键名和变量名配对
​ 可设置默认值@RequestParam(defaultValue = "1")

    @DeleteMapping("/depts")
    public Result delete(@RequestParam String name,@RequestParam Integer age) {
    }

请求体参数:
​ 若请求体是json类型,可用@RequestBody 将JSON直接封装到实体类中
​ 若不封存到对象中,则使用与键名相同的变量名自动接收,若名字不同可使用@RequestParam("键名") 指定名字

    @PostMapping("/depts")
    public Result add(@RequestBody Dept dept){
    }

请求路径

​ 可在一个控制类中声明该控制类接管的路径
​ 在RestController注解的value中声明

@Slf4j
@RestController("/user")
public class DeptController {
    @PostMapping("/depts") //实际是接管 /user/depts
    public Result add(@RequestBody Dept dept){
    }
}

配置文件

​ springboot的配置文件可改为yml格式
​ 在配置文件内的配置可使用@Value 注解读取

@Component
public class AliOSSUtils {

    @Value("${qwq.awa.aaaa}")
    private String name;
 }   

qwq.awa.aaaa=littled

​ @ConfigurationProperties注解:
​ 需要引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
</dependency>

​ @ConfigurationProperties可以批量的将外部的属性配置注入到bean对象的属性中

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/*阿里云OSS相关配置*/
@Data
@Component
@ConfigurationProperties(prefix = "aliyun.oss")
public class AliOSSProperties {
    //区域
    private String endpoint;
    //身份ID
    private String accessKeyId ;
    //身份密钥
    private String accessKeySecret ;
    //存储空间
    private String bucketName;
}

​ 使用这些配置文件的注解,都需要先注入类 也就是@AutoWired

过滤器Filter

步骤:
​ 定义过滤器 :定义一个类,实现 Filter 接口,并重写其所有方法。
​ 配置过滤器:Filter类上加 @WebFilter 注解,配置拦截资源的路径。引导类上加 @ServletComponentScan 开启Servlet组件支持

定义过滤器

//定义一个类,实现一个标准的Filter过滤器的接口
@WebFilter(urlPatterns = "/*") //配置过滤器要拦截的请求路径( /* 表示拦截浏览器的所有请求 )
public class DemoFilter implements Filter {
    @Override //初始化方法, 只调用一次
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("init 初始化方法执行了");
    }

    @Override //拦截到请求之后调用, 调用多次
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("Demo 拦截到了请求...放行前逻辑");
        //放行
        chain.doFilter(request,response);
    }

    @Override //销毁方法, 只调用一次
    public void destroy() {
        System.out.println("destroy 销毁方法执行了");
    }
}

​ Filter是servlet提供的,在springboot中要使用,还需要在启动类中开启,加上@ServletComponentScan注解 即可

@ServletComponentScan
@SpringBootApplication
public class TliasWebManagementApplication {

    public static void main(String[] args) {
        SpringApplication.run(TliasWebManagementApplication.class, args);
    }

}

过滤器链

​ 过滤器链指的是在一个web应用程序当中,可以配置多个过滤器,多个过滤器就形成了一个过滤器链
​ 以注解方式配置的Filter过滤器,它的执行优先级是按时过滤器类名的自动排序确定的,类名排名越靠前,优先级越高

拦截器Interceptor

  • 是一种动态拦截方法调用的机制,类似于过滤器。
  • 拦截器是Spring框架中提供的,用来动态拦截控制器方法的执行。

步骤:
​ 定义拦截器
​ 注册配置拦截器

自定义拦截器:实现HandlerInterceptor接口,并重写其所有方法

//自定义拦截器
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
    //目标资源方法执行前执行。 返回true:放行    返回false:不放行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle .... ");
        
        return true; //true表示放行
    }

    //目标资源方法执行后执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle ... ");
    }

    //视图渲染完毕后执行,最后执行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion .... ");
    }
}

注册配置拦截器:实现WebMvcConfigurer接口,并重写addInterceptors方法,加上@Configuration注解

@Configuration  
public class WebConfig implements WebMvcConfigurer {

    //自定义的拦截器对象
    @Autowired
    private LoginCheckInterceptor loginCheckInterceptor;

    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
       //注册自定义拦截器对象
        registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**");//设置拦截器拦截的请求路径( /** 表示拦截所有请求)
        
        registry.addInterceptor(loginCheckInterceptor)
                .addPathPatterns("/**")//设置拦截器拦截的请求路径( /** 表示拦截所有请求)
                .excludePathPatterns("/login");//设置不拦截的请求路径
    }
}
拦截路径 含义 举例
/* 一级路径 能匹配/depts,/emps,/login,不能匹配 /depts/1
/** 任意级路径 能匹配/depts,/depts/1,/depts/1/2
/depts/* /depts下的一级路径 能匹配/depts/1,不能匹配/depts/1/2,/depts
/depts/** /depts下的任意级路径 能匹配/depts,/depts/1,/depts/1/2,不能匹配/emps/1

注:
​ 当拦截器和过滤器都存在时,先走过滤器再走拦截器

- 当我们打开浏览器来访问部署在web服务器当中的web应用时,此时我们所定义的过滤器会拦截到这次请求。拦截到这次请求之后,它会先执行放行前的逻辑,然后再执行放行操作。而由于我们当前是基于springboot开发的,所以放行之后是进入到了spring的环境当中,也就是要来访问我们所定义的controller当中的接口方法。

- Tomcat并不识别所编写的Controller程序,但是它识别Servlet程序,所以在Spring的Web环境中提供了一个非常核心的Servlet:DispatcherServlet(前端控制器),所有请求都会先进行到DispatcherServlet,再将请求转给Controller。

- 当我们定义了拦截器后,会在执行Controller的方法之前,请求被拦截器拦截住。执行`preHandle()`方法,这个方法执行完成后需要返回一个布尔类型的值,如果返回true,就表示放行本次操作,才会继续访问controller中的方法;如果返回false,则不会放行(controller中的方法也不会执行)。

- 在controller当中的方法执行完毕之后,再回过来执行`postHandle()`这个方法以及`afterCompletion()` 方法,然后再返回给DispatcherServlet,最终再来执行过滤器当中放行后的这一部分逻辑的逻辑。执行完毕之后,最终给浏览器响应数据。

事务

Transactional注解

@Transactional注解书写位置:

  • 方法
    • 当前方法交给spring进行事务管理
    • 当前类中所有的方法都交由spring进行事务管理
  • 接口
    • 接口下所有的实现类当中所有的方法都交给spring 进行事务管理
@Service
public class DeptServiceImpl implements DeptService {
    @Autowired
    private DeptMapper deptMapper;
    @Autowired
    private EmpMapper empMapper;
    @Override
    @Transactional  //当前方法添加了事务管理
    public void delete(Integer id){
        //根据部门id删除部门信息
        deptMapper.deleteById(id);
        //模拟:异常发生
        int i = 1/0;  //运行时异常
        //删除部门下的所有员工信息
        empMapper.deleteByDeptId(id);   
    }
}

​ 以上业务功能delete()方法在运行时,会引发除0的算数运算异常(运行时异常),出现异常之后,
​ 由于我们在方法上加了@Transactional注解进行事务管理,所以发生异常会执行rollback回滚操作,从而保证事务操作前后数据是一致的。
​ 默认情况下,只有出现RuntimeException(运行时异常)才会回滚事务
​ 配置@Transactional注解当中的rollbackFor属性,通过rollbackFor这个属性可以指定出现何种异常类型回滚事务

@Transactional(rollbackFor=Exception.class)

propagatrion

​ @Transactional注解当中的第二个属性propagation,这个属性是用来配置事务的传播行为的

属性值 含义
REQUIRED 【默认值】需要事务,有则加入,无则创建新事务
REQUIRES_NEW 需要新事务,无论有无,总是创建新事务
SUPPORTS 支持事务,有则加入,无则在无事务状态中运行
NOT_SUPPORTED 不支持事务,在无事务状态下运行,如果当前存在已有事务,则挂起当前事务
MANDATORY 必须有事务,否则抛异常
NEVER 必须没事务,否则抛异常

AOP

起步

​ Aspect Oriented Programming(面相切面编程)
​ AOP就是可以将代码插入到指定方法的 前面和后面

引入依赖pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

切面类:

@Component
@Aspect //当前类为切面类
public class TimeAspect {
    @Around("execution(* com.itheima.service.*.*(..))")  //切入点表达式,用于匹配被切入的类的方法
    public Object recordTime(ProceedingJoinPoint pjp) throws Throwable {
        //记录方法执行开始时间
        long begin = System.currentTimeMillis();
        //执行原始方法
        Object result = pjp.proceed();
        //记录方法执行结束时间
        long end = System.currentTimeMillis();
        //计算方法执行耗时
        log.info(pjp.getSignature()+"执行耗时: {}毫秒",end-begin);
        return result;
    }
}

通知类型

Spring中AOP的通知类型:

  • @Around:环绕通知,此注解标注的通知方法在目标方法前、后都被执行
  • @Before:前置通知,此注解标注的通知方法在目标方法前被执行
  • @After :后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行
  • @AfterReturning : 返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行
  • @AfterThrowing : 异常后通知,此注解标注的通知方法发生异常后执行

通知顺序

默认按照切面类的类名字母排序:

  • 目标方法前的通知方法:字母排名靠前的先执行
  • 目标方法后的通知方法:字母排名靠前的后执行

如果我们想控制通知的执行顺序有两种方式:

  1. 修改切面类的类名(这种方式非常繁琐、而且不便管理)
  2. 使用Spring提供的@Order注解
@Order(2)  //切面类的执行顺序(前置通知:数字越小先执行; 后置通知:数字越小越后执行)

切入点表达式

execution

execution主要根据方法的返回值、包名、类名、方法名、方法参数等信息来匹配,语法为:

execution(访问修饰符?  返回值  包名.类名.?方法名(方法参数) throws 异常?)

其中带?的表示可以省略的部分

  • 访问修饰符:可省略(比如: public、protected)

  • 包名.类名: 可省略

  • throws 异常:可省略(注意是方法上声明抛出的异常,不是实际抛出的异常)

@Before("execution(void com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))")

可以使用通配符描述切入点

  • * :单个独立的任意符号,可以通配任意返回值、包名、类名、方法名、任意类型的一个参数,也可以通配包、类、方法名的一部分

  • .. :多个连续的任意符号,可以通配任意层级的包,或任意类型、任意个数的参数

annotation

​ 我们可以借助于另一种切入点表达式annotation来描述这一类的切入点,从而来简化切入点表达式的书写

​ 实现步骤:

  1. 编写自定义注解

  2. 在业务类要做为连接点的方法上添加自定义注解

自定义注解:MyLog

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
}

切面类

@Component
@Aspect
public class MyAspect6 {
    //前置通知
    @Before("@annotation(com.itheima.anno.MyLog)")
    public void before(){
        log.info("MyAspect6 -> before ...");
    }
}

使用:

@MyLog //自定义注解(表示:当前方法属于目标方法)
    public List<Dept> list() {
        return deptList;
    }

Bean

获取Bean

Spring容器中提供了一些方法,可以主动从IOC容器中获取到bean对象,下面介绍3种常用方式:

  1. 根据name获取bean

    Object getBean(String name)
    
  2. 根据类型获取bean

    <T> T getBean(Class<T> requiredType)
    
  3. 根据name获取bean(带类型转换)

    <T> T getBean(String name, Class<T> requiredType)
    

获取IOC对象:
​ 直接注入

@SpringBootTest
class SpringbootWebConfig2ApplicationTests {

    @Autowired
    private ApplicationContext applicationContext; //IOC容器对象

    //获取bean对象
    @Test
    public void testGetBean(){
        //根据bean的名称获取
        DeptController bean1 = (DeptController) applicationContext.getBean("deptController");
        System.out.println(bean1);

        //根据bean的类型获取
        DeptController bean2 = applicationContext.getBean(DeptController.class);
        System.out.println(bean2);

        //根据bean的名称 及 类型获取
        DeptController bean3 = applicationContext.getBean("deptController", DeptController.class);
        System.out.println(bean3);
    }
}

Bean的作用域

作用域 说明
singleton 容器内同名称的bean只有一个实例(单例)(默认)
prototype 每次使用该bean时会创建新的实例(非单例)
request 每个请求范围内会创建新的实例(web环境中,了解)
session 每个会话范围内会创建新的实例(web环境中,了解)
application 每个应用范围内会创建新的实例(web环境中,了解)

​ 可以借助Spring中的@Scope注解来进行配置作用域

@Scope("prototype") //bean作用域为非单例
@Lazy //延迟加载(第一次使用bean对象时,才会创建bean对象并交给ioc容器管理)
@RestController
@RequestMapping("/depts")
public class DeptController {

    @Autowired
    private DeptService deptService;

    public DeptController(){
        System.out.println("DeptController constructor ....");
    }
}
  • IOC容器中的bean默认使用的作用域:singleton (单例)

  • 默认singleton的bean,在容器启动时被创建,可以使用@Lazy注解来延迟初始化(延迟到第一次使用时)

第三方Bean

​ 引入的其他依赖中的Bean即为第三方Bean
​ 若要使用IOC容器管理依赖中的类,需要 @Bean 注解
在配置类中定义@Bean标识的方法:

@Configuration //配置类  (在配置类当中对第三方bean进行集中的配置管理)
public class CommonConfig {
    //声明第三方bean
    @Bean //将当前方法的返回值对象交给IOC容器管理, 成为IOC容器bean
          //通过@Bean注解的name/value属性指定bean名称, 如果未指定, 默认是方法名
    public SAXReader reader(DeptService deptService){
        System.out.println(deptService);
        return new SAXReader();
    }
}
  • 通过@Bean注解的name或value属性可以声明bean的名称,如果不指定,默认bean的名称就是方法名。

  • 如果第三方bean需要依赖其它bean对象,直接在bean定义方法中设置形参即可,容器会根据类型自动装配。

Mybatis

​ Mybatis可用于减少JDBC操作

起步

引入Mybatis

​ 在springboot创建时,在sql中选中 Mybatis Framework 和mysql driver即可,其会自动为我们引入对应依赖的配置

<dependencies>
        <!-- mybatis起步依赖 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.3.0</version>
        </dependency>
        <!-- mysql驱动包依赖 -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
</dependencies>

数据准备

​ 实体类:实体类的属性名与表中的字段名一一对应
​ 例:
​ 用户表 user

create table user(
    id int unsigned primary key auto_increment comment 'ID,主键',
    username varchar(20) comment '用户名',
    password varchar(32) comment '密码',
    name varchar(10) comment '姓名',
    age tinyint unsigned comment '年龄'
) comment '用户表';

insert into user(id, username, password, name, age) values (1, 'daqiao', '123456', '大乔', 22),
                                                           (2, 'xiaoqiao', '123456', '小乔', 18),

​ 实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Integer id; //ID
    private String username; //用户名
    private String password; //密码
    private String name; //姓名
    private Integer age; //年龄
}

配置

​ 在 application.properties 中配置数据库的连接信息

#数据库访问的url地址
spring.datasource.url=jdbc:mysql://localhost:3306/web
#数据库驱动类类名
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#访问数据库-用户名
spring.datasource.username=root
#访问数据库-密码
spring.datasource.password=root@1234

Mapper接口

​ 在该接口使用@Mapper注解修饰,方法用对应操作数据库注解修饰,value值为sql语句
​ 方法的返回值为查询到结果后封装数据的对象
​ 如下列

import com.itheima.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;

@Mapper
public interface UserMapper {
    /**
     * 查询全部
     */
    @Select("select * from user")
    public List<User> findAll(); //查到的数据按属性和字段一一对应封装进User对象然后进入list列表
}
  • @Mapper注解:表示是mybatis中的Mapper接口

程序运行时,框架会自动生成接口的实现类对象(代理对象),并给交Spring的IOC容器管理

  • @Select注解:代表的就是select查询,用于书写select查询语句

​ 查询:
​ 最终在需要操作数据库的地方,注入Mapper接口,直接调用方法

@SpringBootTest
class SpringbootMybatisQuickstartApplicationTests {

    @Autowired
    private UserMapper userMapper; 

    @Test
    public void testFindAll(){
        List<User> userList = userMapper.findAll();
        for (User user : userList) {
            System.out.println(user);
        }
    }
}

数据库操作

删除

Mapper接口方法:

/**
 * 根据id删除
 */
@Delete("delete from user where id = #{id}")
public void deleteById(Integer id);

在Mybatis中,我们可以通过参数占位符号 #{...} 来占位,在调用deleteById方法时,传递的参数值,最终会替换占位符。

​ DML语句执行完毕,是有返回值的,我们可以为Mapper接口方法定义返回值来接收,如下:

/**
 * 根据id删除
 */
@Delete("delete from user where id = #{id}")
public Integer deleteById(Integer id);

​ Integer类型的返回值,表示DML语句执行完毕影响的记录数

符号 说明 场景
# 占位符。执行时,会将#{…}替换为?,生成预编译SQL 参数值传递
$ 拼接符。直接将参数拼接在SQL语句中,存在SQL注入问题 表名、字段名动态设置时使用

增加

/**
 * 添加用户
 */
@Insert("insert into user(username,password,name,age) values(#{username},#{password},#{name},#{age})")
public void insert(User user);

​ 如果在SQL语句中,我们需要传递多个参数,我们可以把多个参数封装到一个对象中。
​ 然后在SQL语句中,我们可以通过#{对象属性名}的方式,获取到对象中封装的属性值。

修改

/**
 * 根据id更新用户信息
 */
@Update("update user set username = #{username},password = #{password},name = #{name},age = #{age} where id = #{id}")
public void update(User user);

查询

/**
 * 根据用户名和密码查询用户信息
 */
@Select("select * from user where username = #{username} and password = #{password}")
public User findByUsernameAndPassword(@Param("username") String username, @Param("password") String password);

​ @param注解的作用是为接口的方法形参起名字的。
​ (由于用户名唯一的,所以查询返回的结果最多只有一个,可以直接封装到一个对象中)

XML映射配置

​ 实现复杂的SQL功能,使用XML来配置映射语句,也就是将SQL语句写在XML配置文件中

规范:

  1. XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放置在相同包下(同包同名)
  2. XML映射文件的namespace属性为Mapper接口全限定名一致
  3. XML映射文件中sql语句的id与Mapper接口中的方法名一致,并保持返回类型一致XML映射

posted @ 2025-06-14 00:06  LittleD-  阅读(14)  评论(0)    收藏  举报