3.SpringBoot——IOC和AOP原理
Spring框架概述
Spring是轻量级的Java EE框架。Spring有两个核心:IOC和AOP
IOC:Invention Of Control控制反转,把创建对象过程交给 Spring 进行管理的思想。
AOP:Aspect Oriented Programming面向切面编程,不修改源代码进行功能增强。是对OOP的补充
特点
- 方便解耦(修改一处代码,往往要修改很多相关联的代码,牵一发而动全身),简化开发。
- 支持AOP面向切面编程。
- 方便和其他框架进行整合。
- 方便进行事务操作。
IOC思想
核心概念
使用对象时由new主动创建对象转换成由Spring提供对象,对象的创建权由程序转移到Spring,这种思想叫IOC(控制反转)。
Spring提供了一个IOC容器,基于IOC容器实现IOC思想。
IOC容器负责对象进行创建、初始化等,被创建的对象在IOC容器中称为Bean。
底层实现原理
工厂模式、xml解析、反射
工厂类提供静态方法,返回new创建的对象。其他类可以通过工厂得到这个对象。但是仍然存在一定的耦合度。
改进:工厂类里面的new对象改为xml解析+反射
创建对象。(反射就是得到class文件,从而创建实例化得到类中的内容)
所以IOC容器底层(本质上)就是对象工厂。
IOC容器的实现
两种方式(两个接口):
(1)BeanFactory:是 Spring 内部的使用接口,不提供开发人员进行使用。加载配置文件的时候不会创建对象,在使用时才创建对象。
(2)ApplicationContext(推荐):BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人员进行使用。加载配置文件的时候就会创建对象。
Bean作用域
作用域:设置创建的Bean实例是单实例还是多实例(每次都建新的对象)。
默认创建的Bean实例是单实例(每次获取的Bean都是同一个地址的对象)。
作用域类别:singleton——单实例,prototype——多实例
singleton在加载配置文件的时候就会创建单实例对象。prototype是在调用getBean的时候创建对象
使用@Scope注解声明Bean的作用域。@Scope("singleton")
Bean生命周期
生命周期:从对象创建到对象销毁的过程
1、通过空参构造器创建Bean实例。
2、调用set方法设置Bean的属性值。
2.5、初始化之前,将Bean实例传递给Bean后置处理器的postProcessBeforeInitialization方法
3、调用初始化方法(需要xml配置)。
3.5、初始化之后,将Bean实例传递给Bean后置处理器的postProcessAfterInitialization方法
4、Bean实例创建完成。
5、容器关闭,调用Bean的销毁的方法(需要xml配置)。
实例化——》属性赋值——》初始化——》销毁
注解方式实现Bean管理
Bean管理:创建对象(实例) + 属性注入
创建对象
@Controller
@Service
@Repository
@Component
//都可以同来创建Bean实例,所以可以混用,但不推荐
使用:
1、引入aop依赖
2、在xml配置文件开启组件扫描,扫描被管理的Bean
3、使用注解,获取Bean实例
属性注入
//根据类型byType注入,搭配@Qualifier可以根据名称byName注入
@Autowired
@Qualifier("")
//既可以根据名称注入(默认),又可以根据类型注入
@Resource(name = "", type = "")
//
@Value(value = "")
配置类替代xml配置文件
(如SpringBoot的核心注解)
@Configuration声明配置类
@ComponentScan(basePackages = {"",""})组件扫描
AOP
核心概念
面向切面编程,不修改源码在主业务逻辑中添加新功能。
如将日志记录、性能统计等代码从业务逻辑中分离出来,或者登陆的业务逻辑中增加一个权限判断。
利用AOP对业务逻辑的各个部分进行隔离,降低业务逻辑各个部分之间的耦合度,提高开发效率。
底层实现原理
动态代理
有接口的动态代理
使用JDK动态代理。接口实现类的代理对象和接口实现类对象的功能是相同的,通过代理对象增强类的方法。
JDK动态代理
使用Proxy类里面的newProxyInstance()方法创建代理对象。
/**
* 返回指定接口的代理实例,该接口将方法调用分派给指定的调用处理程序。
* loader - 定义代理类的类加载器
* interfaces - 要实现的代理类的接口列表
* h - 调度方法调用的调用处理程序(增强的部分)
*/
public static Object newProxyInstance
(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h);
func() {
//创建接口实现类代理对象
Class[] interfaces = {UserDao.class};
UserDaoImpl userDao = new UserDaoImpl();
//接口等于实现类的代理对象
UserDao dao = (UserDao)Proxy.newProxyInstance
(JDKProxy.class.getClassLoader()
, interfaces, new UserDaoProxy(userDao);
//执行增强后的方法
dao.add();
}
//创建代理对象
class UserDaoProxy implements InvocationHandler {
//1、谁的代理对象,就传入谁,可以通过构造器传入
private Object obj;
public UserDaoProxy(Object obj) {
this.obj = obj;
}
//增强的逻辑
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//方法之前
//执行原来的方法
Object result = method.invoke(obj, args);
//方法之后
return result;
}
}
没有接口的动态代理(了解)
使用CGLIB动态代理。之前可以通过子类重写父类的方法,从而增强类的方法。创建子类的代理对象,增强类的方法。
AOP术语
连接点:可以被增强的方法。
切入点:实际被增强的方法。
通知(增强):代码中增加的逻辑部分。有前置通知、后置通知、环绕通知、异常通知、最终通知
切面:是把通知应用到切入点的动作。
AOP操作实例
日志管理:
在controller层的接口上添加自定义注解
XxxController:
@LogAnnotation(module = "", operator = "")
common.annotation.LogAnnotation:
//TYPE代表可以放在类上面,METHOD代表可以放在方法上
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAnnotation {
String module() default "";
BusinessType businessType() default BusinessType.OTHER;
/**
* 是否保存请求参数
*
* @date 2023/6/28
* @return boolean
*/
boolean isSaveRequestData() default true;
/**
* 是否保存响应参数
*
* @date 2023/6/28
* @return boolean
*/
boolean isSaveResponseData() default true;
}
common.aspect.LogAspect:
@Component
@Aspect//切面,定义通知和切入点之间的关系
@Slf4j
public class LogAspect {
//定义切点(注解在哪哪就是切点)
@Pointcut("@annotation(com.gok.pboot.common.annotation.LogAnnotation)")
public void logPointcut() {}
//定义通知类——环绕通知
@Around("logPointcut()")
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
long beginTime = System.currentTimeMillis();
Object result = null;
//执行方法
result = joinPoint.proceed();
long duration = System.currentTimeMills() - beginTime;
//记录日志
this.recordLog(jointPoint, duration);
return result;
}
/**
* 记录日志
*/
private void recordLog(ProceedingJoinPoint joinPoint, long duration) {
log.info("================log start================");
//获取注解
LogAnnotation annotation = this.getAnnotation(joinPoint);
//模块名
String module = annotation.module();
log.info("module:{}", module);
//业务类型
BusinessType businessType = annotation.businessType();
//获取方法名
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
methodName = new StringBuilder().append(className)
.append(".").append(methodName).append("()").toString();
log.info("request method:{}", methodName);
//请求参数
boolean isSaveRequestData = annotation.isSaveRequestData();
String params = getParameterToJson(joinPoint, isSaveRequestData);
log.info("requestParams:{}", params);
//获取request,设置IP地址(HttpContextUtils、IpUtils工具类)
log.info("execute time:{} ms", duration);
log.info("================log end================");
}
/**
* 获取注解
*/
private LogAnnotation getAnnotation(ProceedingJoinPoint point) {
//通过反射得到方法
MethodSignature signature = (MethodSignature) point.getSignature();
Class<? extends Object> targetClass = point.getTarget().getClass();
LogAnnotation targetLog
= targetClass.getAnnotation(LogAnnotation.class);
if (targetLog != null) {
return targetLog;
} else {
Method method = signature.getMethod();
LogAnnotation logAnnotation
= method.getAnnotation(LogAnnotation.class);
return logAnnotation;
}
}
/**
* 获取请求参数(转换json格式)
*/
private String getParameterToJson
(ProceedingJoinPoint point, boolean isSaveRequestData) {
//不保存请求参数
if (!isSaveRequestData) {
return "";
}
List<Object> argList = new ArrayList<>();
//参数值
Object[] argValues = point.getArgs();
//参数名称
String[] argNames
= ((MethodSignature)point.getSignature()).getParameterNames();
if(argValues != null){
for (int i = 0; i < argValues.length; i++) {
Map<String, Object> map = new HashMap<>();
String key = argNames[i];
map.put(key, argValues[i]);
argList.add(map);
map = null;
}
}
if (argList.size() == 0) {
return "";
}
return argList.size() == 1 ? JSON.toJSONString
(argList.get(0)) : JSON.toJSONString(argList);
}
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· spring官宣接入deepseek,真的太香了~