一轮考核结束了,其实我对于自己的表现并不是特别满意。主要的问题还是语言的表述上,以及如何在有限的时间内去尽可能呈现自己的内容。最好对自己暂时解决不了的问题进行一定的思考,对于使用中的框架,应当思考如果自己来做,应该怎么解决这个问题。重要的是细节,特别是对于整个项目来说至关重要的细节,当意识到问题的时候,就应该第一时间去思考处理方案,哪怕暂时解决不了,也一定要记录下来。
接下来还是会恢复到日常的学习中,主要针对以下几个方向:
此外,也要注意到被落下的学科。一段事情的结束并不是轻松的开始,当决定要提升自己的时候,就注定要做出一定的牺牲。
山崖复远望,是仓皇,无告,不回的河流。
AOP(面向切面编程)是将与业务无关但需要多处使用的功能(如日志、事务、安全等)抽取出来,形成独立的模块,然后通过“织入”(weaving)的方式应用到目标代码中。
引入依赖:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-aopartifactId>
dependency>
入门程序:
@Slf4j
@Aspect // AOP的注解
@Component
public class RecordTimeAspect { // 类名命名规范:Aspect
@Around("execution(* com.itheima.service.impl.*.*(..))") // 定义作用的方法范围
public Object recodeTime(ProceedingJoinPoint pjp) throws Throwable {
long begin = System.currentTimeMillis();
Object result = pjp.proceed(); // 执行原生方法
long end = System.currentTimeMillis();
log.info("方法{}: 耗时{}ms", pjp.getSignature(), end - begin);
return result;
}
}
AOP通过将目标对象通过动态代理,生成代理对象并在其中执行原生方法和通知。
Class1 ----》 Class1@xxx(代理对象)
@Before
(方法执行前通知)@After
(方法执行后通知)@Around
(前后都通知)@AfterReturning
(方法执行后通知,抛出异常则不通知)@AfterThrowing
(方法抛出异常才通知)注意:
@Around
需要手动调用 proceed()
执行原始方法,其他自动执行方法。@Around
返回值为 Object
,用于接收原始方法返回值。@Order(数字)
来控制通知顺序,数字小 -> 字典序小。@Before("execution(public void com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))")
execution 主要根据方法的返回值、包名、类名、方法名、方法参数等信息来匹配,语法为:
execution(访问修饰符?返回值 包名.类名.?方法名(方法参数) throws 异常?)
其中带 ?
的表示可以省略的部分:
public
、protected
)。throws
异常:可省略(注意是方法上声明抛出的异常,不是实际抛出的异常)。可以使用通配符描述切入点:
*
:单个独立的任意符号,可以通配任意返回值、包名、类名、方法名、任意类型的一个参数,也可以通配包、类、方法名的一部分。execution(* com.*.service.*.update*(*))
..
:多个连续的任意符号,可以通配任意层级的包,或任意类型、任意个数的参数。execution(* com.itheima..DeptService.*(..))
@Pointcut("execution(* com.itheima.service.impl.*.*(..))")
public void pt() {} // 声明public时可以在外部切面类引用
@Around("pt()")
基于注解的切面表达式:
@Target(ElementType.METHOD) // 作用对象:方法
@Retention(RetentionPolicy.RUNTIME) // 保留策略:运行时有效
public @interface LogOperation {}
@Around("@annotation(com.itheima.anno.LogOperation)")
@LogOperation // 切面注解
@RequestMapping(value = "/depts", method = RequestMethod.POST)
public Result addDept(@RequestBody Dept dept) {
...
}
连接点:
@Around
中连接点为 ProceedingJoinPoint
,且作为方法的参数,其他为其父类 JoinPoint
,不作为方法的参数。String className = joinPoint.getTarget().getClass().getName();
Signature signature = joinPoint.getSignature();
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
ThreadLocal
是每个线程独立的存储空间(局部变量)。
public class CurrentHolder {
private static final ThreadLocal<Integer> CURRENT_LOCAL = new ThreadLocal<>();
// 常用的方法:set,get,remove
public static void setCurrentId(Integer id) {
CURRENT_LOCAL.set(id);
}
public static Integer getCurrentId() {
return CURRENT_LOCAL.get();
}
public static void remove() {
CURRENT_LOCAL.remove();
}
}
假设要记录操作员工的ID,就可以在 token 解析后存入 local 中:
// 将userId存入Local
Integer userId = Integer.valueOf(claims.get("id").toString());
CurrentHolder.setCurrentId(userId);
需要时从 local 中获取:
CurrentHolder.getCurrentId();
在处理完毕后清除(可放在放行之后作为递归清理):
// 放行
filterChain.doFilter(request, response);
// 清除local
CurrentHolder.remove();
从低到高,高优先级起效果:
application.yaml
application.yml
application.properties
-Dserver.port=xxx
)--server.port=xxx
)Spring 支持五种作用域,后三种在 web 环境才生效:
作用域 | 说明 |
---|---|
singleton | 容器内同名称的 bean 只有一个实例(单例)(默认) |
prototype | 每次使用该 bean 时会创建新的实例(非单例/多例) |
request | 每个请求范围内会创建新的实例(web 环境) |
session | 每个会话范围内会创建新的实例(web 环境) |
application | 每个应用范围内会创建新的实例(web 环境) |
定义域注解 @Scope
:
@Scope("singleton")
选择 singleton
/ prototype
:
singleton
单例即可保证线程安全。prototype
,防止多线程操作时出现的数据不一致问题。单例 Bean 默认是在项目启动时进行实例化,使用 @Lazy
注解可以实现懒加载。
对于第三方 Bean,可以放在配置类下,通过 @Bean
注解将返回值交给 IOC 管理:
@Configuration
public class CommonConfig {
@Bean
public AliyunOSSOperator aliyunOSSOperator(AliyunOSSProperties aliyunOSSProperties) {
return new AliyunOSSOperator(aliyunOSSProperties);
}
}
starter
作为根依赖,包域名 - spring-boot-starter。SpringBoot 在启动时自动将部分配置类和 bean 对象存入 IOC 容器中。
通过包扫描
@ComponentScan(basePackages = {"包名1", "包名2"})
@SpringBootApplication
public class TliasWebManagementApplication {
...
}
通过 @Import
注解
@Import({普通类Example.class})
@Import({配置类ExampleConfig.class})
@Import({ImportSelector接口实现类})
ImportSelector
接口实现类:用于批量导入配置类。
public class MyImportSelector implements ImportSelector {
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.example.HeaderConfig"};
}
}
通过注解(本质是对 ImportSelector
实现类的再封装)
【@EnableHeaderConfig】
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(MyImportSelector.class)
public interface EnableHeaderConfig {}
设置注册 bean 对象需要满足的条件:
@ConditionalOnClass(name = "io.jsonwebtoken.Jwts")
:判断环境中是否有对应的字节码文件,如果有就创建 bean。@ConditionalOnMissingBean
:判断环境中有没有对应的 bean,没有就创建 bean。@ConditionalOnProperty(name="myname", havingValue = "itheima")
:判断配置文件中myname
的属性值是否等于 itheima
,是就创建 bean。spring-boot-starter
。spring-boot-autoconfigure
。spring-boot-autoconfigure
进行配置:@EnableConfigurationProperties(AliyunOSSProperties.class)
@Configuration
public class AliyunOSSAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public AliyunOSSOperator aliyunOSSOperator(AliyunOSSProperties aliyunOSSProperties) {
return new AliyunOSSOperator(aliyunOSSProperties);
}
}
\META-INF\spring\org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件,将 Autoconfigure 全类名写入。平原不可见,晦暗,无声,未知的存亡。