尚硅谷spring框架视频教程——学习笔记一(IOC、AOP)

文章目录

  • 前言
  • 一、控制反转(IOC)
    • [1. 底层原理](#1. 底层原理)
    • [2. 两种实现方式(接口)](#2. 两种实现方式(接口))
    • [3. bean管理(基于xml方式)](#3. bean管理(基于xml方式))
    • [4. bean管理(基于注解方式)](#4. bean管理(基于注解方式))
  • 二、面向切面编程(AOP)
    • [1. 底层逻辑](#1. 底层逻辑)
    • [2. 基于注解开发](#2. 基于注解开发)

前言

虽然不懂消化原理也不妨碍消化食物,但还是对消化原理感兴趣。

一、控制反转(IOC)

控制反转,就是把创建对象和对象之间的调用过程交给spring进行管理,以降低代码之间的耦合度。

1. 底层原理

控制反转是基于:xml解析、工厂模式和反射三部分实现的。

(1)创建对象及对象间调用的相关信息配置在配置文件中,spring通过xml解析获取相关信息;

(2)通过工厂模式将调用的类与被调用的类解耦;

(3)在工厂类中,使用获取到的配置信息,通过反射创建对象并设置属性;

2. 两种实现方式(接口)

(1)BeanFactory:加载配置文件时不创建对象,获取对象(使用)时才创建;

(2)ApplicationContext:BeanFactory的子接口,加载配置文件的时候就会创建对象,把耗费时间的步骤放在服务器启动的时候;

3. bean管理(基于xml方式)

(1)创建对象

  • 使用bean标签注入创建对象,默认调用无参的构造函数

(2)注入属性

  • set方法注入属性
  • 有参构造函数注入属性
  • p名称空间注入(了解)
  • 使用null标签注入空值
  • 特殊字符的注入可写到<![CDATA[]]>中
  • 通过ref注入外部bean
  • 使用array标签、list标签、map标签(内部使用entry标签)、set标签注入集合
  • 使用util名称空间(如<util:list>)提取重复注入部分

(3)工厂bean

  • 区别
    普通bean:在bean标签中定义什么类型,就返回什么类型;
    工厂bean:定义的类型和返回的类型可以不一致;
  • 实现
    创建类,实现FactoryBean接口,并定义泛型;
    实现getObject方法;

(4)bean的作用域(单实例或多实例)

通过bean标签中的scope属性设置作用域,默认是单实例(singleton,加载配置文件时创建对象),还可以设置为多实例(property,调用getBean时才创建对象)、请求(request,了解即可)、会话(session,了解即可)

(5)bean生命周期

配置后置处理器可在创建对象过程中加入一些自定义逻辑,如属性修改、依赖注入增强等。注意:配置后置处理器之后,默认会为当前配置文件中的所有bean都配置上后置处理器

  • 通过无参构造创建bean实例;
  • 设置属性值以及对其他bean的引用
  • 把bean实例传给后置处理器的postProcessBeforeInitialization方法(实现BeanPostProcessor接口)
  • 调用bean的初始化方法(通过bean标签中的init-method属性设置)
  • 把bean实例传给后置处理器的postProcessAfterInitialization方法(实现BeanPostProcessor接口)
  • 获取到bean对象,并使用
  • 当容器关闭时(手动调用context的close方法),调用bean的销毁方法(通过bean标签中的destroy-method属性设置)

(6)自动装配

在bean标签中,通过autowire配置自动装配方式,根据属性名称(byName)或者属性类型(byType)

(7)引入外部属性文件

比如数据库的配置可以放到properies为后缀的文件中,通过context名称空间引入外部属性文件,然后在设置属性值时,通过${}获取相应配置

4. bean管理(基于注解方式)

(1)什么是注解

注解是代码中的特殊标记,基本模式为@注解名称(属性名称=属性值),可以作用在类、方法、属性上。使用注解的目的是简化xml配置。

(2)开启组件扫描

可配置只扫描或者不扫描某种注解。

  • 可以使用命名空间context,并设置base-package的属性值
  • 也可以使用完全注解开发
java 复制代码
@Configuration
@ComponentScan(basePackages = {"com.xxx"})
public class SpringConfig {
}

(3)创建对象的注解

功能都是一样的,只是习惯加以区分。value的属性值可以不写,默认是将类名称的首字母改成小写

  • @Component(value="xxx"):用于普通的类对象
  • @Controller(value="xxx"):一般用在web层
  • @Service(value="xxx"):一般用在业务逻辑层
  • @Repository(value="xxx"):一般用在数据访问层(DAO层)

(4)注入属性的注解

  • @Autowired:根据属性类型注入类对象,属于spring的包
  • @Qualifier(value=xxx):根据属性名称注入类对象,属于spring的包
    需要和@Autowired一起使用
  • @Resource(name=xxx):根据类型/名称注入类对象,属于javax的包
  • @Value(value=xxx):注入普通类型属性

二、面向切面编程(AOP)

不修改原有代码的前提下,在主干逻辑中添加新功能,降低业务逻辑各部分之间的耦合度,比如为登录功能加上权限控制。

1. 底层逻辑

(1)有接口,使用JDK动态代理

代理对象和目标对象都实现同一个接口,代理对象方法内部调用目标对象的相应方法。代码来自

java 复制代码
// 目标接口
public interface OrderService {
    void add();
}
// 目标类
public class OrderServiceImpl implements OrderService {
    @Override
    public void add() {
        System.out.println("添加用户");
    }
}
// 代理类
public class OrderServiceProxy implements OrderService {
    @Override
    public void add() {
        long begin = System.currentTimeMillis();
        OrderService.add();
        long end = System.currentTimeMillis();
        System.out.println("耗时"+(end - begin)+"毫秒");
    }
}

代码实现使用了java.lang.reflect.Proxy的newProxyInstance方法。第一个参数:类加载器;第二个参数:接口类型;第三个参数:调用得到处理器代码来自

java 复制代码
public class ProxyHandler implements InvocationHandler {
    private Object targetObject;//被代理的对象
 
    public  Object newProxyInstance(Object targetObject){
        this.targetObject = targetObject;
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this);
    }
    //该方法在代理对象调用方法时调用
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("记录日志");
        return method.invoke(targetObject,args);
    }
}

(2)没有接口,使用CGLIB动态代理

代理对象继承目标类,重写相应的方法,并使用super.xxx()调用父类方法

java 复制代码
// 目标类
public class OrderService {
    public void add() {
        System.out.println("添加用户");
    }
}
// 代理类
public class OrderServiceProxy extends OrderService {
    @Override
    public void add() {
        long begin = System.currentTimeMillis();
        super.add();
        long end = System.currentTimeMillis();
        System.out.println("耗时"+(end - begin)+"毫秒");
    }
}

2. 基于注解开发

(1)操作术语

  1. 连接点:类中可以被增强的方法
  2. 切入点:实际被增强的方法
  3. 通知(增强):实际增强的逻辑部分
  4. 切面:是动作,把通知应用到切入点的过程

(2)使用方法

  1. 开启组件扫描(@ComponentScan),开启生成代理对象(@EnableAspectJAutoProxy(proxyTargetClass = true))
  2. 创建类,在类中写主干逻辑
  3. 创建增强类,编写增强逻辑,在类上加@Aspect注解(用于标识一个类为切面)

(3)五种通知类型

前置(@Before)、后置(@AfterReturning有异常不执行)、环绕(@Around环绕之后有异常不执行)、异常(@AfterThrowing)、最终(@After)

切入点表达式为execution([权限修饰符][返回类型][类全路径][方法名称](参数列表)),权限修饰符:public/private/protected,一般直接写*,表示不限制;返回类型可以不写。

java 复制代码
// 配置类
@Configuration
@ComponentScan(basePackages={"com.atguigu"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigAop() {}

// 主干逻辑
@Component
public class User {
   public void add() {
       System.out.println("add主逻辑");
   }
}

// 增强类
// 多个增强类对同一个方法进行增强,设置优先级,数字越小优先级越高
@Order(1)
@Aspect
@Component
public class UserProxy{
    // 相同切入点的抽取
    @Pointcut(value="execution(* com.atguigu.dao.BookDao.add(..))")
    public void pointdemo() {}
    
    // 对com.atguigu.dao.BookDao类里的add方法进行增强
    @Before(value="execution(* com.atguigu.dao.BookDao.add(..))")
    public void before() {
        System.out.println("增强的逻辑");
    }
    
    @After(value="pointdemo()")
    public void after() {
        System.out.println("增强的逻辑");
    }
}
相关推荐
大地之灯1 小时前
深度学习每周学习总结R5(LSTM-实现糖尿病探索与预测-模型优化)
深度学习·学习·lstm
dal118网工任子仪2 小时前
83,【7】BUUCTF WEB [MRCTF2020]你传你[特殊字符]呢
笔记·学习
明月清了个风3 小时前
数据结构与算法学习笔记----容斥原理
笔记·学习·算法
天天爱吃肉82183 小时前
笔记:使用ST-LINK烧录STM32程序怎么样最方便?
笔记·stm32·嵌入式硬件
CSDN_PBB3 小时前
[STM32 - 野火] - - - 固件库学习笔记 - - -十三.高级定时器
笔记·stm32·学习
HilariousDog3 小时前
clean code阅读笔记——如何命名?
windows·笔记
WarPigs3 小时前
Unity敌人逻辑笔记
笔记
lxl13073 小时前
学习数据结构(4)顺序表+单链表
数据结构·学习
程思扬3 小时前
Android笔记:android 动态设置backgroundTint
android·java·网络·笔记·android-studio