3.SpringBoot——IOC和AOP原理

发布时间 2023-10-08 18:03:38作者: 上瘾了

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);
    }
    
}