框架: 快速开发项目的一个架子
ssh
ssm
spring --> applicationContext.xml配置文件(spring不是业务层,是管理其他框架的)
springmvc --> springmvc.xml配置文件 (对应之前servlet)
mybatis —> mybatis-config.xml配置文件(对应之前jdbc)
—> springboot优化了之前的框架配置,思想是约定大于配置
一、引言
1.1 初始化配置
为了使用SSM框架去开发,准备SSM框架的模板配置。
1.2 整合第三方框架
为了Spring整合第三方框架,单独的去编写xml文件。
1.3 后期维护
后期SSM项目后期xml文件特别多,维护xml文件的成本是很高的
1.4 部署工程
SSM工程部署也是很麻烦,依赖第三方的容器
1.5 敏捷式开发
基于Java的SSM开发方式是很笨重,而现在的python,php,NodeJS的敏捷式开发已经盖过Java一头
二、SpringBoot介绍
SpringBoot是由Pivotal团队研发的,SpringBoot并不是一门新技术,只是将之前常用的Spring,SpringMVC,data-jpa等常用的框架封装到了一起,
帮助你隐藏这些框架的整合细节
,实现敏捷开发。约定大于配置
SpringBoot就是一个工具集。
官网:https://spring.io/projects/spring-boot
SpringBoot特点:
- SpringBoot项目不需要模板化的配置。
- SpringBoot中整合第三方框架时,只需要导入相应的starter依赖包,就自动整合了。约定大于配置。
- SpringBoot默认只有一个.properties或者.yml的配置文件,不推荐使用xml,后期会采用.java的文件去编写配置信息。
- SpringBoot工程在部署时,采用的是jar包的方式,内部自动依赖Tomcat容器,提供了多环境的配置。
- 后期要学习的微服务框架SpringCloud需要建立在SpringBoot的基础上。
三、SpringBoot快速入门
3.1 快速构建SpringBoot
3.1.1 选择构建项目的类型
选择构建项目的类型 |
---|
注意:根据网络状况,可能会提示无法连接。如果不能连接,使用http://start.springboot.io(或者http://start.aliyun.com)
3.1.2 指定SpringBoot版本和需要的依赖
指定SpringBoot版本和需要的依赖 |
---|
ps: 如果2.7.13版本创建完项目会报错,提示alimaven找不到依赖的情况,就手动将pom文件中版本改成2.7.2
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.2</version>
<relativePath/>
</parent>
3.1.3 导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
3.1.4 编写了Controller
Controller就是之前的Servlet,用于接收请求做出响应
创建一个controller包,单独写controller类
@Controller
public class TestController {
@RequestMapping("/test")
public String test(){
return "ok.html";
}
}
3.1.5 编写页面
在resources/static下创建ok.html页面
ps: 默认的固定格式,只能在resources下,且只能是static
3.1.6 测试
主类点击启动项目
SpringBoot项目启动成功后,不会主动打开浏览器,需要自己手动打开浏览器测试
效果 |
---|
手动访问/tset路径,看到页面跳转 |
3.2 SpringBoot的目录结构
3.2.1 pom.xml文件
指定了一个父工程: 指定当前工程为SpringBoot,帮助我们声明了starter依赖的版本。
<!-- 没有这个的话,有dependencyManagement也行 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.2</version> <relativePath/> </parent> <!-- 有这个dependencyManagement,就不需要上面parent --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
项目的元数据:包名,项目名,版本号。
<groupId>com.qf</groupId> <artifactId>test_springboot01</artifactId> <version>0.0.1-SNAPSHOT</version> <name>TestSpringboot01</name>
指定了properties信息:指定了java的版本为1.8
<properties> <java.version>1.8</java.version> </properties>
导入依赖:按需导入(web,mysql等等)
插件:spring-boot-maven-plugin (如果有报错,也可以不要)
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.7.2</version> </plugin> </plugins> </build>
3.2.2 .gitignore文件
默认帮我们忽略了一些文件和目录,避免提交到Git仓库中
3.2.3 目录结构
-src
-main
-java
-包名
启动类.java # 需要将controller类,放在启动类的子包中或者同级包下,否则需要使用@ComponentScan 注解,并指定扫描的包即可
-resources # resources下放除了java代码之外其他资源文件
-static # 存放静态资源的,js,css,html
-templates # 存储模板页面的,Thymeleaf,jsp,freemarker
application.properties # SpringBoot提供的配置文件,后缀支持2种:1.properties 2.yml(推荐),用来修改默认配置
-test # 只是为了测试用的
3.3 补充
可以给主类加上如下代码即可启动成功后,自动打开浏览器
public static void main(String[] args) {
int port = 8080;
String portPrefix = "--server.port=";
for (String arg : args) {
if (arg.startsWith(portPrefix)) {
port = Integer.parseInt(arg.substring(portPrefix.length()));
}
}
SpringApplication.run(Day35Springboot01Application.class, args);
try {
Runtime.getRuntime().exec("cmd /c start http://localhost:" + port);
} catch (IOException e) {
e.printStackTrace();
}
}
四、Spring
介绍…balabala
spring的核心功能
Spring是一个容器,帮助管理对象生命周期(创建对象,使用对象,对象销毁等), Spring框架中主要的功能有 - IOC,DI - AOP - MVC - Dao(JDBCTemplate)等
- IOC,DI
- AOP
4.1 IOC+DI[重点]
4.1.0 引言
以前写的[登录+查询全部功能]
// 控制层
public class LoginServlet extends HttpServlet{
UserService service;
void doGet(){
service.findAdminByLogin(username,password);
}
}
// 业务层接口
public interface UserService{
Admin findUseryLogin(String username,String password);
}
// 业务层实现类
public class UserServiceImpl implements AdminService {
Admin findUseryLogin(String username,String password) {
// ...
}
}
public class UserServiceImpl2 implements AdminService {
Admin findUseryLogin(String username,String password) {
// ...
}
}
以上这样写有缺点:
1 LoginServlet类还是需要和UserService和UserServiceImpl耦合
2 扩展性不好,假如有新的实现类UserServiceImpl2,就需要改动代码
现在需要一种技术,降低耦合且还可以根据运行时状态给属性动态赋值
4.1.1 介绍
IOC是Spring框架的核心功能之一,IOC(inversion of control)控制反转
控制: 控制创建对象的能力(以前是自己需要什么对象,自己创建)
反转: 原来创建对象是自己做,反转就是将创建对象的能力交给Spring
IOC(控制反转): 将创建对象的能力反转给Spring,由Spring创建对象
DI(dependency injection) 依赖注入,即 属性赋值
spring的ioc,帮助我们创建对象,是通过注解实现的, 常用的
创建对象
的注解
- @Controller 在控制层代码上使用
- @Service 在业务层代码上使用
- @Repository 在数据层代码上使用
- @Component 在其他代码上使用
–> 以上四个注解都是创建对象用的,只不过建议是各层用对应的
属性赋值(依赖注入的注解)
- @Autowired
- @Resource
4.1.2 演示1
需求: 项目中控制层Controller中需要使用到业务层service对象来处理业务,例如AdminController中需要创建AdminService对象使用,使用IOC+DI完成
AdminService和AdminServiceImpl
public interface AdminService {
void login();
}
@Service // 加上该注解,AdminServiceImpl类就会被spring容器创建对象
public class AdminServiceImpl implements AdminService{
@Override
public void login() {
System.out.println("业务层执行..." );
}
}
AdminController
@Controller // 创建对象
public class AdminController {
// 在控制层中需要使用业务层对象
// 不再主动new对象,而是从容器中拿
// @Autowired注解就会从容器中找到该类型的对象赋值给该变量
// 即这就是属性赋值,也就是依赖注入,即DI
@Autowired // 属性赋值
private AdminService adminService;
@RequestMapping("/login")
public String login() {
adminService.login();
return "ok.html";
}
}
练习: 在AdminService中使用AdminDao对象
创建dao,和dao.impl包以及AdminDao,AdminDaoImpl
public interface AdminDao {
void login();
}
@Repository // 创建Dao对象
public class AdminDaoImpl implements AdminDao {
@Override
public void login() {
System.out.println("持久层执行" );
}
}
业务层注入Dao层对象
@Service // 加上这个注解,spring容器就会创建该类对象
public class AdminServiceImpl implements AdminService {
@Autowired // 注入Dao对象
private AdminDao dao;
@Override
public void login() {
System.out.println("业务层执行..." );
dao.login();
}
}
4.1.3 演示2-@Autowired 依赖注入(属性赋值)的原理
@Autowired给属性赋值,是如何绑定属性赋值的? 是按照类型注入的
但是如果spring容器中有多个同类型的对象时,@Autowired注入就会失败
如果spring容器中有多个同类型的对象时 , 如何解决?
此时通过对象名来确定指定注入(赋值)哪个对象.@Qualifier("as2")
对象名是什么? 默认通过@Controller,@Service等这些创建的对象,对象名默认是类名首字母小写,
也可以通过在@Controller,@Service等这些注解后设置对象名,例如@Service(“as1”)
思考,课下扩展: @Autowired和@Resource什么异同
4.1.4 演示3
演示@Component注解创建对象
假如有个类Admin,现在需要该类对象,就可以在该类上加上@Component注解,并在其他地方使用@Autowired注入
4.2 AOP
4.2.1 介绍
Spring中另外一个核心功能,AOP
AOP(Aspect Oriented Programming),即面向切面编程.
OOP(Object Oriented Programming ),即面向对象编程.
AOP面向切面编程,利用 一种称为
"横切"
的技术,剖开封装的对象内部,并将那些影响了多个类的公共行为
抽取出封装到一个可重用模块,并将其命名 为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
面向切面编程的作用:就是将项目中与核心逻辑无关的代码横向抽取成切面类,通过织入作用到目标方法,以使目标方法执行前后达到增强的效果.
原理: AOP底层使用的就是动态代理,给AOP指定哪些类型(目标类)需要增强,就会产生对应的代理对象,代理对象执行方法前后会先执行增 强的方法.
好处:减少系统的重复代码,降低模块之间的耦合度,便于维护,可以只关注核心业务
补充:
AOP底层实现原理是代理设计模式,且是动态代理
静态代理
动态代理(jdk动态代理技术 / cglib动态代理技术)
-----
jdk代理技术,要求目标类必须是接口
cglib代理技术, 目标类可以是接口也可以是类
想详细了解的:
https://www.bilibili.com/video/BV1uN411r7MB/?spm_id_from=333.999.0.0
4.2.2 AOP术语
连接点(Joinpoint):连接点是程序类中客观存在的方法,可被Spring拦截并切入内容。即每个方法在切入之前,都是连接点
切入点
(Pointcut):被Spring切入连接点。即真正会增强的目标方法通知、
增强
(Advice):可以为切入点添加额外功能,分为:前置通知、后置通知、异常通知、环绕通知等。
目标对象
(Target):被代理的目标对象织入(Weaving):把通知应用到具体的类,进而创建新的代理类的过程。
代理
(Proxy):被AOP织入通知后,产生的结代理类。
切面
(Aspect):由切点和通知组成
4.2.3 应用场景
- 事务管理
- 后续spring管理事务用的AOP原理
- 权限校验
- 后期使用Spring Security注解开发时,其实利用了AOP思想
- 日志记录
- 性能检测
- 等等
4.2.4 演示: 前置增强
需求: 实现业务层代码执行时,能出现一些增强的效果
开发步骤
创建切面类,类上加注解
@Component ,加上该注解,springboot框架就会创建该类对象
@Aspect , 加上该注解,springboot框架内部就会知道该类是一个切面类
设置切入点方法,并加注解
- @Pointcut , 用于定义要增强的目标方法路径
设置各种增强(或者叫通知)方法
注解 解释 @Around 环绕通知 @Before 前置通知 @After 后置通知 @AfterReturning 后置返回通知 @AfterThrowing 异常通知
pom.xml添加aop依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
演示给业务层代码设置前置增强
package com.qf.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/**
* --- 天道酬勤 ---
*
* @author QiuShiju
* @date 2024/7/1
* @desc 切面类,定义增强的方法
*/
@Component // 由spring创建对象
@Aspect // 让框架知道,这是一个切面
public class MyAspect {
/**
* 自己定义一个方法,用于增强
* 例如: 权限校验
* @Before注解,可以在目标方法执行前,执行这个myBefore方法
* 注解内写的就是,目标方法路径
* 第一个* ,是返回值任意
* com.qf.service.impl.* 是指impl包下的任意类
* .* 类中任意方法名
* (..) 方法参数任意
* ------------------------
* 参数 JoinPoint,就是目标对象
*/
@Before("execution(* com.qf.service.impl.*.*(..))")
public void myBefore(JoinPoint joinPoint) {
System.out.println("方法执行前: 权限校验" );
System.out.println("目标方法名:"+ joinPoint.getSignature( ).getName( ));
// 权限校验,如果通过,无事发生,正常执行
// 权限校验,如果不通过,可以抛出异常,让代码停止,目标方法不执行
// throw new RuntimeException("权限校验失败");
}
}
启动项目,测试
- 正常发请求访问控制层,控制层调用业务层代码时,就会触发AOP的增强机制
4.2.5 演示各种增强
package com.qf.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* --- 天道酬勤 ---
*
* @author QiuShiju
* @desc
*/
@Component
@Aspect
public class MyAspect {
/**
* 将目标方法路径,提取成公共的路径,直接调用
* 后续其他增强就不用每次都写
*/
@Pointcut("execution(* com.qf.service.*.*(..))")
public void myPointcut() {}
/**
* @Around 注解说明该方法是环绕通知的方法
* 注解内写的目标方法的路径模板
* execution 固定关键词
* * 返回值任意
* com.qf.day33_springboot.service 包路径
* .* 该包下所有文件
* .* 该类下所有方法
* (..) 该方法所有参数
*-------------------------
* ProceedingJoinPoint 参数代表目标方法对象
*/
@Around("myPointcut()")
public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable {
// 目标方法前:
System.out.println("开启事务/权限校验" );
// 目标方法执行
Object ret = joinPoint.proceed( );
System.out.println("目标方法返回值---> " + ret );
// 目标方法后:
System.out.println("提交事务/日志记录" );
return ret;
}
/**
JoinPoint 是目标方法对象
*/
@Before("myPointcut()")
public void myBefore(JoinPoint joinPoint) {
// 目标对象
Object target = joinPoint.getTarget( );
System.out.println("target = " + target);
// 获得目标方法签名(方法名)
Signature signature = joinPoint.getSignature( );
System.out.println("signature = " + signature);
System.out.println("前置通知--->权限校验--->OK" );
// 假设权限校验没有通过,通过抛出异常让代码停下,不再执行目标方法
// System.out.println("前置通知--->权限校验--->ERROR" );
// throw new RuntimeException("权限校验--->ERROR");
}
@After("myPointcut()")
public void myAfter() {
// 获得ip
// 获得时间
// 获得人名
// 获得日志描述信息
System.out.println("后置通知--->记录日志,释放资源" );
}
/**
* 后置返回增强,一般用于接收目标方法的返回值
* --------------------------------
* 当注解括号内参数只有一个,且参数名是value,那么可以省略value直接写值
* 当多于1个参数,所有参数都需要写k=v
* ---------------------------------
* @AfterReturning 后置返回增强,用于目标方法的返回值
* 参数value用于指定目标方法
* 参数returning用于指定返回值,该返回值需要定义在本方法的参数上
*/
@AfterReturning(value = "myPointcut()",returning = "ret")
public Object myAfterRet(Object ret) {
System.out.println("后置返回通知,接收到目标方法返回值--->" + ret);
return ret;
}
@AfterThrowing(value = "myPointcut()",throwing = "e")
public void myException(Exception e) {
System.out.println("目标方法报的错---> " + e.getMessage());
}
}
4.2.6 AOP实战-Log记录
暂时无法完成,还有部分知识没讲,等讲完mvc和mybatis后再来实战,暂时可以大概看一眼
实战需求参考若依系统首页 (ruoyi.vip)
即将用户在系统中做的任何操作都记录到数据库,供后续查看
首先设计日志表,将来存储日志信息
CREATE TABLE `log` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`log_time` datetime DEFAULT NULL,
`log` varchar(255) DEFAULT NULL,
`ip` varchar(255) DEFAULT NULL,
`name` varchar(255)