Web后端开发---SpringBootWeb AOP&SpringBoot原理篇【6】
一、SpringBootWeb AOP
1 事务管理
1.1 事务回顾
1.2 Spring事务管理
@Transactional // spring事务管理
@Override
public void delete(Integer id) {
deptMapper.deleteById(id); //根据ID删除部门数据
empMapper.deleteByDeptId(id); //根据部门的ID删除该部门下的员工数据
}
/**
* 根据部门的ID删除该部门下的员工数据
* @param deptId
*/
@Delete("delete from emp where dept_id = #{deptId}")
void deleteByDeptId(Integer deptId);
#spring事务管理日志
logging:
level:
org.springframework.jdbc.support.JdbcTransactionManager: debug
1.3 事务进阶
rollbackFor 异常回滚的属性
//所有的异常都会进行事务的回滚操作,默认只有出现运行时异常才回滚事务
@Transactional(rollbackFor = Exception.class) // spring事务管理
@Override
public void delete(Integer id) {
deptMapper.deleteById(id); //根据ID删除部门数据
empMapper.deleteByDeptId(id); //根据部门的ID删除该部门下的员工数据
}
propagation 事物的传播行为
- 数据库
create table dept_log
(
id int auto_increment comment '主键ID'
primary key,
create_time datetime not null comment '操作时间',
description varchar(300) null comment '操作日志的描述'
);
- DeptLog.java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
* 部门操作日志类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DeptLog {
private Integer id; //操作日志ID
private LocalDateTime createTime; //创建时间
private String description; //操作描述
}
- DeptLogService.java
/**
* 部门操作日志管理
*/
public interface DeptLogService {
void insert(DeptLog deptLog);
}
- DeptLogServiceImpl.java
@Service
public class DeptLogServiceImpl implements DeptLogService {
@Autowired
private DeptLogMapper deptLogMapper;
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void insert(DeptLog deptLog) {
deptLogMapper.insert(deptLog);
}
}
- DeptLogMapper.java
/**
* 部门操作日志管理
*/
@Mapper
public interface DeptLogMapper {
@Insert("insert into dept_log(create_time,description)values (#{createTime}, #{description})")
void insert(DeptLog deptLog);
}
- DeptServiceImpl.java
//所有的异常都会进行事务的回滚操作,默认只有出现运行时异常才回滚事务
@Transactional(rollbackFor = Exception.class) // spring事务管理
@Override
public void delete(Integer id) {
try {
deptMapper.deleteById(id); //根据ID删除部门数据
int i = 1/0;
//if(true) {throw new Exception("出错了....");}
empMapper.deleteByDeptId(id); //根据部门的ID删除该部门下的员工数据
} finally {
DeptLog deptLog = new DeptLog();
deptLog.setCreateTime(LocalDateTime.now());
deptLog.setDescription("执行了解散部门的操作,此次解散的是" + id + "号部门");
deptLogService.insert(deptLog);
}
}
2 AOP基础
2.1 AOP概述
2.2 AOP快速入门 : 统计各个业务层方法执行耗时
<!--AOP-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- TimeAspect.java
package com.itcast.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Slf4j
@Component // 标注为bean对象
@Aspect //AOP类
public class TimeAspect {
@Around("execution(* com.itcast.service.*.*(..))") //返回值 包名.类名.方法名.. 切入点表达式
public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {
//1.记录开始时间
long begin = System.currentTimeMillis();
//2.调用原始方法运行
Object result = joinPoint.proceed();
//3.记录结束时间,计算方法执行耗时
long end = System.currentTimeMillis();
log.info(joinPoint.getSignature() + "方法执行耗时:{}ms", end - begin); //哪个方法执行耗时
return result; // 原始方法的返回值,需要返回回去
}
}
2.3 AOP核心概念
2.4 AOP进阶
通知类型
@Slf4j
@Component
@Aspect
public class MyAsperct1 {
@Before("execution(* com.itcast.service.impl.DeptServiceImpl.*(..))")
public void before() {
log.info("before......");
}
@Around("execution(* com.itcast.service.impl.DeptServiceImpl.*(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("around before......");
//调用目标对象的原始方法执行
Object result = joinPoint.proceed();
System.out.println(joinPoint.getSignature());
log.info("around after......");
return result;
}
@After("execution(* com.itcast.service.impl.DeptServiceImpl.*(..))")
public void after() {
log.info("after......");
}
@AfterReturning("execution(* com.itcast.service.impl.DeptServiceImpl.*(..))")
public void afterReturning() {
log.info("afterReturning......");
}
@AfterThrowing("execution(* com.itcast.service.impl.DeptServiceImpl.*(..))")
public void afterThrowing() {
log.info("afterThrowing......");
}
}
多个切面类通知顺序
@SpringBootTest
public class TestAop {
@Autowired
private DeptService deptService;
@Test
public void testDelete() {
deptService.delete(10);
}
@Test
public void testAopList() {
List<Dept> list = deptService.list();
for (Dept dept : list) {
System.out.println(dept);
}
}
@Test
public void testAopGetById() {
Dept dept = deptService.getById(1);
System.out.println(dept);
}
}
@Order(3)
@Slf4j
@Component
@Aspect
public class MyAspect2 {
@Before("com.itcast.aop.MyAspect1.pt()")
public void before() {
log.info("before ... 2");
}
@After("com.itcast.aop.MyAsperct1.pt()")
public void after() {
log.info("after ... 2");
}
}
@Order(2)
@Slf4j
@Component
@Aspect
public class MyAspect3 {
@Before("com.itcast.aop.MyAspect1.pt()")
public void before() {
log.info("before ... 3");
}
@After("execution(* com.itcast.service.impl.DeptServiceImpl.*(..))")
public void after() {
log.info("after ... 3");
}
}
切入点表达式–execution
//切面类
@Slf4j
@Component
@Aspect
public class MyAspect6 {
//execution(访问修饰符? 返回值 包名.类名.?方法名(方法参数)throws 异常?)
//@Pointcut("execution(public void com.itcast.service.impl.DeptServiceImpl.delete(java.lang.Integer))")
//@Pointcut("execution(void com.itcast.service.impl.DeptServiceImpl.delete(java.lang.Integer))")
//@Pointcut("execution(void delete(java.lang.Integer))") //包名.类名不建议省略
//@Pointcut("execution(void com.itcast.service.DeptService.delete(java.lang.Integer))")
//@Pointcut("execution(void com.itcast.service.DeptService.*(java.lang.Integer))")
//@Pointcut("execution(* com.*.service.DeptService.*(*))")
//@Pointcut("execution(* com.*.service.*Service.delete*(*))")
//@Pointcut("execution(* com.*.service.DeptService.*(..))")
//@Pointcut("execution(* com..DeptService.*(..))")
//@Pointcut("execution(* com..*.*(..))")
//@Pointcut("execution(* *(..))") //慎用
@Pointcut("execution(* com.itcast.service.DeptService.list()) ||" +
"execution(* com.itcast.service.DeptService.delete(java.lang.Integer))")
public void pt() {
}
@Before("pt()")
public void before() {
log.info("MyAspect6 ... before ....");
}
}
切入点表达式–@annotation
- MyLog.java
@Retention(RetentionPolicy.RUNTIME) //什么时候生效,运行时生效
@Target(ElementType.METHOD) //注解标识方法
public @interface MyLog {
}
@Slf4j
@Component
@Aspect
public class MyAspect7 {
//匹配DeptServiceImpl中的list()和delete(Integer id)方法
//@Pointcut("execution(* com.itcast.service.DeptService.list()) || execution(* com.itcast.service.DeptService.delete(java.lang.Integer))")
@Pointcut("@annotation(com.itcast.aop.MyLog)")
public void pt() {
}
@Before("pt()")
public void before() {
log.info("MyAspect7 ... before ....");
}
}
@Service
@Slf4j
public class DeptServiceImpl implements DeptService {
@Autowired
private DeptMapper deptMapper;
@MyLog
@Override
public List<Dept> list() {
List<Dept> deptList = deptMapper.list();
return deptList;
}
@MyLog
@Override
public void delete(Integer id) {
//1. 删除部门
deptMapper.delete(id);
}
}
连接点
@Slf4j
@Component
@Aspect
public class MyAspect8 {
@Pointcut("execution(* com.itcast.service.DeptService.*(..))")
private void pt() {
}
@Before("pt()")
public void before(JoinPoint joinPoint) {
log.info("MyAspect8 ... before ....");
}
@Around("pt()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("MyAspect8 around before ...");
//1.获取 目标对象的类名
String className = joinPoint.getTarget().getClass().getName();
log.info("目标对象的类名:{}", className);
//2.获取 目标方法的方法名
String methodName = joinPoint.getSignature().getName();
log.info("目标方法的方法名:{}", methodName);
//3.获取 目标方法运行时传入的参数
Object[] args = joinPoint.getArgs();
log.info("目标方法运行时传入的参数:{}", Arrays.toString(args));
//4.获取 目标方法执行
Object result = joinPoint.proceed();
//5.获取 目标方法运行时的返回值
log.info("目标方法运行时的返回值:{}", result);
log.info("MyAspect8 around after ...");
return result;
}
}
2.5 AOP案例
引入AOP的起步依赖
<!--AOP-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
导入资料中准备好的数据表结构,并引入对应的实体类
-- 操作日志表
create table operate_log(
id int unsigned primary key auto_increment comment 'ID',
operate_user int unsigned comment '操作人ID',
operate_time datetime comment '操作时间',
class_name varchar(100) comment '操作的类名',
method_name varchar(100) comment '操作的方法名',
method_params varchar(1000) comment '方法参数',
return_value varchar(2000) comment '返回值',
cost_time bigint comment '方法执行耗时, 单位:ms'
) comment '操作日志表';
- OperateLog.java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OperateLog {
private Integer id; //ID
private Integer operateUser; //操作人ID
private LocalDateTime operateTime; //操作时间
private String className; //操作类名
private String methodName; //操作方法名
private String methodParams; //操作方法参数
private String returnValue; //操作方法返回值
private Long costTime; //操作耗时
}
- OperateLogMapper.java
@Mapper
public interface OperateLogMapper {
//插入日志数据
@Insert("insert into operate_log (operate_user, operate_time, class_name, method_name, method_params, return_value, cost_time) " +
"values (#{operateUser}, #{operateTime}, #{className}, #{methodName}, #{methodParams}, #{returnValue}, #{costTime});")
public void insert(OperateLog log);
}
自定义注解@Log
- LogAspect.java
//元注解
@Retention(RetentionPolicy.RUNTIME) //运行时生效
@Target(ElementType.METHOD) //目标对象:方法上
public @interface Log {
}
定义切面类,完成记录操作日志的逻辑
- LogAspect.java
@Slf4j
@Component
@Aspect //切面类
public class LogAspect {
@Autowired
private HttpServletRequest request;
@Autowired
private OperateLogMapper operateLogMapper;
@Around("@annotation(com.itcast.anno.Log)")
public Object recordLog(ProceedingJoinPoint joinPoint) throws Throwable {
//操作人ID - 当前登录员工ID 从令牌中获取ID
//获取请求头中的JWT令牌,解析令牌
String jwt = request.getHeader("token");
Claims claims = JwtUtils.parseJWT(jwt);
Integer operateUser = (Integer) claims.get("id");
//操作时间
LocalDateTime operateTime = LocalDateTime.now();
//操作类名
String className = joinPoint.getTarget().getClass().getName();
//操作方法名
String methodName = joinPoint.getSignature().getName();
//操作方法参数
Object[] args = joinPoint.getArgs();
String methodParams = Arrays.toString(args);
long start = System.currentTimeMillis();
//调用原始目标方法运行
Object result = joinPoint.proceed();
long end = System.currentTimeMillis();
//方法返回值
String returnValue = JSONObject.toJSONString(result);
//操作耗时
long costTime = end - start;
//日志信息包含:操作人、操作时间、执行方法的全类名、执行方法名、方法运行时参数、返回值、方法执行时长
//记录操作日志
OperateLog operateLog = new OperateLog(null, operateUser, operateTime, className, methodName, methodParams, returnValue, costTime);
operateLogMapper.insert(operateLog);
log.info("AOP记录操作日志:{}", operateLog);
return result;
}
}
/**
* 员工管理Controller
*/
@Slf4j
@RestController
@RequestMapping("/emps")
@Api(tags = "员工管理")
public class EmpController {
@Autowired
private EmpService empService;
/**
* 分页查询
*
* @param page
* @param pageSize
* @return
*/
@GetMapping
public Result page(@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize,
String name, Short gender,
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) {
log.info("分页查询,参数:{},{},{},{},{},{}", page, pageSize, name, gender, begin, end);
//调用service分页查询
PageBean pageBean = empService.page(page, pageSize, name, gender, begin, end);
return Result.success(pageBean);
}
@Log
@DeleteMapping("/{ids}")
public Result delete(@PathVariable List<Integer> ids) { //PathVariable:路径参数
log.info("批量删除员工,参数ids:{}", ids);
//调用service删除员工
empService.delete(ids);
return Result.success();
}
@Log
@PostMapping
public Result save(@RequestBody Emp emp) {
log.info("新增员工,emp:{}", emp);
empService.save(emp);
return Result.success();
}
@GetMapping("/{id}")
public Result getById(@PathVariable Integer id) {
log.info("根据id查询员工信息:{}", id);
Emp emp = empService.getById(id);
return Result.success(emp);
}
@Log
@PutMapping
public Result update(@RequestBody Emp emp) {
log.info("更新员工信息:{}", emp);
empService.update(emp);
return Result.success();
}
}
/**
* 部门管理Controller
*/
@Slf4j
@RestController
@RequestMapping("/depts")
@Api(tags = "部门管理")
public class DeptController {
//获取日志记录对象
//private static Logger logger = LoggerFactory.getLogger(DeptController.class);
@Autowired //注入
private DeptService deptService;
/**
* 部门列表数据查询
*
* @return 返回所有部门数据
*/
//@RequestMapping(value = "/depts",method = RequestMethod.GET) //指定请求方式为get
@GetMapping
@ApiOperation(value = "查询全部部门")
public Result list() {
//logger.info("查询部门全部数据");
log.info("查询部门全部数据");
//调用service查询部门数据
List<Dept> deptList = deptService.list();
return Result.success(deptList);
}
/**
* 删除部门
*
* @return
*/
@Log
@ApiOperation(value = "根据id删除部门")
@DeleteMapping("/{id}")
public Result delete(@ApiParam(value = "部门id", required = true) @PathVariable Integer id) {
log.info("根据id删除部门:{}", id);
//调用service根据id删除部门
deptService.delete(id);
return Result.success();
}
/**
* 新增部门
*
* @param dept 部门接收JSON数据
* @return
*/
@Log
@PostMapping
@ApiOperation(value = "新增部门")
public Result add(@ApiParam(value = "部门", required = true) @RequestBody Dept dept) {
log.info("新增部门:{}", dept);
//调用service新增部门
deptService.add(dept);
return Result.success();
}
/**
* 根据id查询部门数据
*
* @param id
* @return
*/
@GetMapping("/{id}")
@ApiOperation(value = "根据id查询部门数据")
public Result getDeptById(@ApiParam(value = "部门id", required = true) @PathVariable Integer id) {
log.info("根据id查询部门数据:{}", id);
//调用service根据id查询部门
Dept dept = deptService.getDeptById(id);
return Result.success(dept);
}
/**
* 修改部门
*
* @param dept
* @return
*/
@Log
@PutMapping
@ApiOperation(value = "修改部门")
public Result update(@ApiParam(value = "部门", required = true) @RequestBody Dept dept) {
log.info("根据id修改部门:{}", dept);
//调用service的更新部门
deptService.updateDeptById(dept);
return Result.success();
}
}
二、SpringBoot原理篇
1 配置优先级
2 Bean管理
环境准备
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--mybatis起步依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!--PageHelper分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.2</version>
</dependency>
<!--阿里云OSS-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.15.1</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
<!-- no more than 2.3.3-->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--JWT令牌-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!--fastJSON-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
- application.yml
spring:
#数据库连接信息
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/tlias
username: root
password: 1234
#Mybatis配置
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true
- Dept.java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Dept {
private Integer id;
private String name;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
- Result.java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {
private Integer code;//响应码,1 代表成功; 0 代表失败
private String msg; //响应码 描述字符串
private Object data; //返回的数据
//增删改 成功响应
public static Result success(){
return new Result(1,"success",null);
}
//查询 成功响应
public static Result success(Object data){
return new Result(1,"success",data);
}
//失败响应
public static Result error(String msg){
return new Result(0,msg,null);
}
}
- DeptMapper.java
@Mapper
public interface DeptMapper {
//查询全部部门数据
@Select("select * from dept")
List<Dept> list();
//删除部门
@Delete("delete from dept where id = #{id}")
void delete(Integer id);
//新增部门
@Insert("insert into dept(name, create_time, update_time) values (#{name},#{createTime},#{updateTime})")
void save(Dept dept);
}
- DeptService.java
public interface DeptService {
/**
* 查询所有的部门数据
* @return
*/
List<Dept> list();
/**
* 删除部门
* @param id
*/
void delete(Integer id);
/**
* 新增部门
* @param dept
*/
void save(Dept dept);
}
- DeptServiceImpl.java
@Slf4j
@Service
public class DeptServiceImpl implements DeptService {
@Autowired
private DeptMapper deptMapper;
@Override
public List<Dept> list() {
List<Dept> deptList = deptMapper.list();
return deptList;
}
@Override
public void delete(Integer id) {
deptMapper.delete(id);
}
@Override
public void save(Dept dept) {
dept.setCreateTime(LocalDateTime.now());
dept.setUpdateTime(LocalDateTime.now());
deptMapper.save(dept);
}
}
- DeptController.java
@RestController
@RequestMapping("/depts")
public class DeptController {
@Autowired
private DeptService deptService;
@GetMapping
public Result list(){
List<Dept> deptList = deptService.list();
return Result.success(deptList);
}
@DeleteMapping("/{id}")
public Result delete(@PathVariable Integer id) {
deptService.delete(id);
return Result.success();
}
@PostMapping
public Result save(@RequestBody Dept dept){
deptService.save(dept);
return Result.success();
}
}
获取Bean
@SpringBootTest
class SpringbootWebConfig2ApplicationTests {
@Autowired
private ApplicationContext applicationContext; // IOC容器
@Test
public void testGetBean() {
//根据bean的名称获取
DeptController bean1 = (DeptController) applicationContext.getBean("deptController");
System.out.println("bean1 = " + bean1);
//根据bean的类型获取
DeptController bean2 = applicationContext.getBean(DeptController.class);
System.out.println("bean2 = " + bean2);
//根局bean的名称 及 类型获取
DeptController bean3 = applicationContext.getBean("deptController", DeptController.class);
System.out.println("bean3 = " + bean3);
}
}
bean的作用域
//@Lazy //延迟初始化,第一次使用时实例化,后面就不会在实例化
@Scope("prototype") // 非单例
@RestController
@RequestMapping("/depts")
public class DeptController {
@Autowired
private DeptService deptService;
public DeptController() {
System.out.println("DeptController constructor ... ");
}
}
//bean的作用域
@Test
public void testScope() {
for (int i = 0; i < 10; i++) {
DeptController bean = applicationContext.getBean(DeptController.class);
System.out.println("bean" + i + " = " + bean);
}
}
第三方bean
- 1.xml
<?xml version="1.0" encoding="UTF-8" ?>
<emp>
<name>Tom</name>
<age>18</age>
</emp>
<!--Dom4j-->
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
</dependency>
@SpringBootApplication
public class SpringbootWebConfig2Application {
public static void main(String[] args) {
SpringApplication.run(SpringbootWebConfig2Application.class, args);
}
//声明第三方bean
@Bean //将当前方法的返回值对象交给IOC容器管理,成为IOC容器bean
public SAXReader saxReader() {
return new SAXReader();
}
}
//第三方的bean管理
@Test
public void testThirdBean() throws DocumentException {
SAXReader saxReader = new SAXReader();
//读取xml文件,对里面的内容进行解析
Document document = saxReader.read(this.getClass().getClassLoader().getResource("1.xml"));
Element rootElement = document.getRootElement();
String name = rootElement.element("name").getText();
String age = rootElement.element("age").getText();
System.out.println(name + " : " + age);
}
@Autowired
private SAXReader saxReader;
//第三方的bean管理
@Test
public void testThirdBean() throws DocumentException {
//SAXReader saxReader = new SAXReader();
//读取xml文件,对里面的内容进行解析
Document document = saxReader.read(this.getClass().getClassLoader().getResource("1.xml"));
Element rootElement = document.getRootElement();
String name = rootElement.element("name").getText();
String age = rootElement.element("age").getText();
System.out.println(name + " : " + age);
}
- CommonConfig.java
package com.itcast.config;
import org.dom4j.io.SAXReader;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration //配置类
public class CommonConfig {
//声明第三方bean
@Bean //将当前方法的返回值对象交给IOC容器管理,成为IOC容器bean
public SAXReader saxReader() {
return new SAXReader();
}
}
@Test
public void testGetBean2(){
Object saxReader = applicationContext.getBean("saxReader");
System.out.println("saxReader = " + saxReader);
}
@Configuration //配置类
public class CommonConfig {
//声明第三方bean
@Bean //将当前方法的返回值对象交给IOC容器管理,成为IOC容器bean
//通过@Bean注解的name/value属性指定bean名称,如果未指定,默认是方法名
public SAXReader saxReader(DeptService deptService) {
System.out.println(deptService.getClass());
return new SAXReader();
}
}
3 SpringBoot原理篇
起步依赖
自动配置
/**
* 自动配置原理测试
*/
@SpringBootTest
public class AutoConfigurationTests {
@Autowired
private Gson gson;
@Test
public void testJson(){
String json = gson.toJson(Result.success());
System.out.println(json);
}
}
自动配置原理
准备springboot-utils环境:
- TokenParser.java
@Component
public class TokenParser {
public void parse(){
System.out.println("TokenParser ... parse ...");
}
}
- HeaderGenerator.java
public class HeaderGenerator {
public void generate(){
System.out.println("HeaderGenerator ... generate ...");
}
}
- HeaderParser.java
public class HeaderParser {
public void parse(){
System.out.println("HeaderParser ... parse ...");
}
}
- HeaderConfig.java
@Configuration
public class HeaderConfig {
@Bean
public HeaderParser headerParser(){
return new HeaderParser();
}
@Bean
public HeaderGenerator headerGenerator(){
return new HeaderGenerator();
}
}
- MyImportSelector.java
public class MyImportSelector implements ImportSelector {
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.example.HeaderConfig"};
}
}
- EnableHeaderConfig.java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(MyImportSelector.class)
public @interface EnableHeaderConfig {
}
//@ComponentScan({"com.example","com.itcast"})
//@Import({TokenParser.class}) // 导入普通类,交给IOC容器
//@Import({HeaderConfig.class}) // 导入配置类,交给IOC容器
//@Import({MyImportSelector.class}) // 导入ImportSelector接口实现类
@EnableHeaderConfig
@SpringBootApplication
public class SpringbootWebConfig2Application {
public static void main(String[] args) {
SpringApplication.run(SpringbootWebConfig2Application.class, args);
}
//声明第三方bean
// @Bean //将当前方法的返回值对象交给IOC容器管理,成为IOC容器bean
// public SAXReader saxReader() {
// return new SAXReader();
// }
}
/**
* 自动配置原理测试
*/
@SpringBootTest
public class AutoConfigurationTests {
@Autowired
private Gson gson;
@Autowired
private ApplicationContext applicationContext;
@Test
public void testJson() {
String json = gson.toJson(Result.success());
System.out.println(json);
}
//获取TokenParser
@Test
public void testTokeParser() {
System.out.println(applicationContext.getBean(TokenParser.class));
}
//获取HeadParser
@Test
public void testHeaderParser() {
System.out.println(applicationContext.getBean(HeaderParser.class));
}
//获取HeaderGenerator
@Test
public void testHeadGenerator() {
System.out.println(applicationContext.getBean(HeaderGenerator.class));
}
}
自动配置原理–源码跟踪
自动配置原理–@Conditional
@Configuration
public class HeaderConfig {
@Bean
//@ConditionalOnClass(name = "io.jsonwebtoken.Jwts") // 环境中存在指定的这个类,才会将bean加入到IOC容器中
//@ConditionalOnMissingBean // 不存在该类型的bean,才会将该bean加入IOC容器中 -- 制定类型(value属性) 或 名称(name属性)
@ConditionalOnProperty(name = "name", havingValue = "itcast") // 配置文件中存在指定的属性与值,才会将该bean加入IOC容器中
public HeaderParser headerParser() {
return new HeaderParser();
}
@Bean
public HeaderGenerator headerGenerator() {
return new HeaderGenerator();
}
}
springboot 案例(自定义starter)
aliyun-oss-spring-boot-autoconfigure
- pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-oss-spring-boot-autoconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--阿里云OSS-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.15.1</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
<!--no more than 2.3.3-->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.3</version>
</dependency>
</dependencies>
</project>
- AliOSSProperties.java
@ConfigurationProperties(prefix = "aliyun.oss")//读取oss节点
public class AliOSSProperties {
private String endpoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketName;
public String getEndpoint() {
return endpoint;
}
public void setEndpoint(String endpoint) {
this.endpoint = endpoint;
}
public String getAccessKeyId() {
return accessKeyId;
}
public void setAccessKeyId(String accessKeyId) {
this.accessKeyId = accessKeyId;
}
public String getAccessKeySecret() {
return accessKeySecret;
}
public void setAccessKeySecret(String accessKeySecret) {
this.accessKeySecret = accessKeySecret;
}
public String getBucketName() {
return bucketName;
}
public void setBucketName(String bucketName) {
this.bucketName = bucketName;
}
}
- AliOSSUtils.java
/**
* 阿里云OSS工具类
*/
public class AliOSSUtils {
private AliOSSProperties aliOSSProperties;
public AliOSSProperties getAliOSSProperties() {
return aliOSSProperties;
}
public void setAliOSSProperties(AliOSSProperties aliOSSProperties) {
this.aliOSSProperties = aliOSSProperties;
}
/**
* 实现上传图片到OSS
*/
public String upload(MultipartFile file) throws IOException {
//获取阿里云OSS参数
String endpoint = aliOSSProperties.getEndpoint();
String accessKeyId = aliOSSProperties.getAccessKeyId();
String accessKeySecret = aliOSSProperties.getAccessKeySecret();
String bucketName = aliOSSProperties.getBucketName();
// 获取上传的文件的输入流
InputStream inputStream = file.getInputStream();
// 避免文件覆盖
String originalFilename = file.getOriginalFilename();
String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
//fileName上传到OSS指定文件目录
fileName = "tlias" + "/" + fileName;
//上传文件到 OSS
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
ossClient.putObject(bucketName, fileName, inputStream);
//文件访问路径
String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + fileName;
// 关闭ossClient
ossClient.shutdown();
return url;// 把上传到oss的路径返回
}
}
- AliOSSAutoConfiguration.java
@Configuration
@EnableConfigurationProperties(AliOSSProperties.class) // 直接导入到IOC容器中,成为IOC容器中的bean
public class AliOSSAutoConfiguration {
@Bean
public AliOSSUtils aliOSSUtils(AliOSSProperties aliOSSProperties) {
AliOSSUtils aliOSSUtils = new AliOSSUtils();
aliOSSUtils.setAliOSSProperties(aliOSSProperties);
return aliOSSUtils;
}
}
- src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.aliyun.oss.AliOSSAutoConfiguration
spring-oss-spring-boot-starter
- pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.aliyun.oss</groupId>
<artifactId>spring-oss-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-oss-spring-boot-autoconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
springboot-autoconfiguration-test
- pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.itcast</groupId>
<artifactId>springboot-autoconfiguration-test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>spring-oss-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- application.properties
##阿里云OSS配置
aliyun.oss.endpoint=https://oss-cn-hangzhou.aliyuncs.com
aliyun.oss.accessKeyId=xxxxxxxxxx
aliyun.oss.accessKeySecret=xxxxxxxxxxxxxxxxxxxxx
aliyun.oss.bucketName=web-tlias-20231216
- UploadController.java
@RestController
public class UploadController {
@Autowired
private AliOSSUtils aliOSSUtils;
@PostMapping("/upload")
public String upload(MultipartFile image) throws Exception {
//上传文件到阿里云 OSS
String url = aliOSSUtils.upload(image);
return url;
}
}
4 总结