SSM and SpringBoot(复习)

#  Spring

容器

控制反转(IOC)
组件:具有一定功能的对象(名字,类型,对象,作用域)
容器:管理组件(创建,获取,保存,摧毁)

组件注册

在这里插入图片描述

添加组件到容器:

单个组件:@Bean

  • @Configuration 配置类
  • @Controller 控制层
  • @Service 服务层
  • @Repository 持久层
  • @Component 组件(其他)

批量组件:@CompoentScan(basePackages = “com.ch.tool”) //只能扫描用了相关注解进入容器的组件

@Import(“aa.class”) // 引用的jar包中的类

获取容器中的组件

ioc.getBean() //名字,类型,名字+类型

注意:如果组件名重复了(只会放一个),有限按时间顺序,然后名称。


调整组件的作用域:@Scope
  • @Scope(“prototype”):非单实例
  • @Scope(“singleton”):单实例(默认值)
  • @Scope(“request”):同一个请求单实例
  • @Scope(“session”):同一次会话单实例

注意:容器创建完成前,就把所有的单实例对象创建完成,而不会创建非单实例对象。非单实例对象在什么时候获取什么时候创建

懒加载:@Lazy // 一般用于非单实例,在获取时创建

工厂制造复杂的Bean
@Component
public class BYDFactory implements FactoryBean<Car> {
   
// getObject(),制造的Bean
// getObjectType(),Bean的类型
// isSingleton(),是否单实例
}
BeanFactory的地层原理

在这里插入图片描述

三级缓存

在这里插入图片描述

条件注册@Conditional

判断不同环境,加载不同的Bean
1、写两个class进行Windows和Mac操作系统的判断

//MacCondition 
public class MacCondition implements Condition {
   
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
   
        // 判断环境变量是否包含 MacOS
        // 获取环境变量
        Environment environment = context.getEnvironment();
        //获取操作系统名称
        String os = environment.getProperty("OS");
        return os.contains("MacOS");
    }
}
//WindowsCondition 
public class WindowsCondition implements Condition {
   
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
   
        // 判断环境变量是否包含 Windows
        // 获取环境变量
        Environment environment = context.getEnvironment();
        //获取操作系统名称
        String os = environment.getProperty("OS");
        return os.contains("Windows");
    }
}

2、在Bean中添加@Conditional(),进行筛选

@Configuration
public class PersonConfig {
   
    // 注册自己的组件
    @Conditional(MacCondition.class)
    @Bean("zs")
    public Preson zs(){
   
        Preson preson = new Preson();
        preson.setName("张三");
        preson.setAge("20");
        preson.setGender("男");
        return preson;
    }
    @Conditional(WindowsCondition.class)
    @Bean("ls")
    public Preson ls(){
   
        Preson preson = new Preson();
        preson.setName("李四");
        preson.setAge("18");
        preson.setGender("男");
        return preson;
    }
}

注意:@Conditional()不仅可以用在方法上,还可以用在类上。

组件注入

在这里插入图片描述
依赖注入(DI)

自动装配 @Autowired
  • @Autowired //自动装配,底层:调用容器的getBean方法。
  • @Resource //自动装配, 底层:javaSE

面试题:@Autowired和@Resource的区别:
1、来源不同:@Autowired 来自 Spring 框架,而 @Resource 来自于(Java)JSR-250;
2、依赖查找的顺序不同:@Autowired 先根据类型再根据名称查询,而 @Resource 先根据名称再根据类型查询;
3、支持的参数不同:@Autowired 只支持设置 1 个参数,而 @Resource 支持设置 7 个参数;
4、依赖注入的用法支持不同:@Autowired 既支持构造方法注入,又支持属性注入和 Setter 注入,而 @Resource 只支持属性注入和 Setter 注入;
5、编译器 IDEA 的提示不同:当注入 Mapper 对象时,使用 @Autowired 注解编译器会提示错误,而使用 @Resource 注解则不会提示错误。

其他

  • @Qualifier(“”) // 精确指定如果容器中这样的组件存在多个,则使用@Qualifier精确指定组件名

  • @Primary // 主组件(默认组件),如果容器中这样的组件存在多,在一个Bean上添加@Primary,则其为默认组件

  • 构造器注入

  • setter方法注入

  • xxxAware // 感知接口,实现其接口,直接调用相关方法。
    在这里插入图片描述

  • @Value // 1、给基本数据类型赋值;2、取出配置文件的某个值:@Value(“${aaa}”);3、编写Spring表达式语言:@Value(“#{10*20}”)

  • @PropertySource(“classpath:cat.properties”) // 把指定的文件导入容器中

  • ResourceUtils.getFile( resourceLocation:“classpath:abc.jpg”); // 导入资源

  • @Profile(“uat”) // 多环境。当这个环境被激活的时候,才会加入如下组件。

多环境 @Profile(“uat”)

1、通过@Profile标识环境

public class DataConfiger {
   
    // 1、定义环境标识:dev、app、test
    // 2、激活环境标识
    //    明确告诉spring使用哪个环境
    //    不说,默认就是default环境(报错)
    @Profile({
   "dev","default"})
    @Bean
    public void dev() {
   
        System.out.println("init");
    }

    @Profile("app")
    @Bean
    public void app() {
   
        System.out.println("init");
    }

    @Profile("test")
    @Bean
    public void test() {
   
        System.out.println("init");
    }
}

2、在项目配置文件(application.properties)中改就行:spring.profiles.active=test

xml注册组件和创建容器

注意:其实这些注解都来自于SpringBoot。在没有SpringBoot之前,是使用xml配置文件来管理Bean的(< bean id=" " class=" ">< /bean>),使用ClassPathXmlApplicationcontext ioc = new ClassPathXmlApplicationContext( configLocation: “classpath:ioc .xml”); 来创建容器的。

组件的生命周期

在这里插入图片描述

  • 生命周期的回调感知(不能对对象进行修改)
    在这里插入图片描述
  • BeanPostProcessor(可以对对象进行修改)
    在这里插入图片描述
    @Autowired是如何实现的?
    1、专门有一个处理@Autowired注解的AutowiredAnnotationBeanPostProcessor
    3、每个Bean创建以后,会调用BeanPostProcessor的postProcessBeforelnitialization 方法
    3、postProcessBeforelnitialization 里面就会利用反射,得到当前Bean的所有属性,利用反射得到Bean属性上标注的所有注解,看有没有Autowired 注解
    4、如果有,去容器中找到这个属性对应的组件(按类型,按名字),找到。

AOP

面向切面编程(),

日志

  • 1、硬编码:(不推荐),(通用逻辑+专用逻辑,耦合太多维护困难)
  • 2、静态代理: 编码时期间就决定好了代理的关系
    定义:代理对象,是目标对象的接口的子类型,代理对象本身并不是目标对象,而是将目标对象作为自己的属性。
    优点:同一种类型的所有对象都能代理。
    缺点:代理对象与目标对象实现相同的接口,当目标对象增加方法时,代理对象也要维护。
  • 3、动态代理: 运行期间才决定好了代理关系
    定义:目标对象在执行期间会被动态拦截,插入指定的逻辑。
    优点:可以代理所有。
    缺点:不好写(强制要求,目标对象必有接口。代理的也只是接口规定的方法。)。

所以Spring为了简化动态代理,发明了AOP

概念

在这里插入图片描述

实现

1、导入 AOP 依赖 (在pom文件中导入依赖)

<dependency>
        <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2、编写切面 Aspect (使用@Aspect标注该类为切面类,+@Component注入到ioc中)
3、编写通知方法
4、指定切入点表达式

  • 何时?
    【@Before @AfterRetruning @AfterThrowing @After】
  • 何地【写切入表达式】?
    *(“execution()”)
    全写法:【public int com.atguigu.spring.aop.calculator.MathCalculator.add(int,int) throws xxxx】
    省略写法:【int add (int, int)】
    通配符: * :任意字符 ; … :表示多个参数,任意类型
    例如:【@Before(“execution(int * (int,int))”)】
    在这里插入图片描述
    在这里插入图片描述

注意:只要被AOP给切了。则该对象为Spring的代理对象,代理对象为SpringCGLIB,他可以代理万物

5、JoinPoint
参数,可以用于获取对象
例如:

6、@Pointcut
用于简化重复写写入点表达式
例如:

@Pointcut("execution(int * (int,int))")
    public void pointCut1() {
   }
@After("pointCut1()")
    public void logEnd() {
   
        System.out.println("【切面 - 日志】结束...");
    }

底层原理:

1、Spring会为每个被切面切入的组件创建代理对象。
2、代理对象中保存了切面类里面所有通知方法构成的增强器链。
3、目标方法执行时,会先去执行增强器链中拿到需要提前执行的通知方法。
增强器链: 切面中的所有通知方法其实就是增强器。他们被组织成一个链路放到集合中。目标方法真正执行前后,会去增强器链中执行哪些需要提前执行的方法。

多个切面的顺序

可以使用@Order(1)进行排序,数字越小优先级越高,就在最外层

在这里插入图片描述

qa

工具类

TypeUtils
Reflectionutils
AnnotationUtils 注解相关的
ClassUtils 类相关的

环绕通知

@Around 修改目标方法的参数和执行结果
固定写法:

@Component
@Aspect
public class AroundAspect {
   
    /**
     * 环绕通知的固定写法如下:
     * Object: 返回值
     * ProceedingJoinPoint:切入点
     */
    @Around("execution(int * (int,int))")
    public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
   
        Object[] args = pjp.getArgs();// 获取方法参数

        System.out.println("环绕通知-前置通知");

        Object proceed = null;
        try {
   
            // 执行目标方法
            proceed = pjp.proceed();
        } catch (Throwable e) {
   
            System.out.println("环绕通知-异常通知");
            e.getMessage();
            throw e;	//让别人继续感知
        } finally {
   
            System.out.println("环绕通知-后置通知");
        }

        System.out.println("环绕通知-返回通知");

        return proceed;
    }

}

注意:写环绕通知的时候,必须将异常抛给下面,防止直接抛出导致下面走正常流程,不走异常。
在这里插入图片描述

场景使用

日志记录事务管理,**权限检查,**性能监控(有专业的框架),异常处理缓存管理,安全审计,自动化测试。
在这里插入图片描述

事务

声明式:通过注解的方式,告诉框架,我要做什么,框架会帮助我做什么。
编程式:通过代码的方式,告诉虚拟机,我要做什么,需要自己写代码实现。

使用

1、导入pom

   <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
    </dependency>

2、启动类加 @EnableTransactionManagement

3、方法前加 @Transactional

参数

注意:超时时间:方法进入到最后一次DAO操作完成的时间

  • value 或 transactionManager
    指定事务管理器的名称
  • propagation
    定义了事务的传播行为,即当前方法是否需要在一个新的事务中运行,或者是否可以加入到已存在的事务中。默认值为 Propagation.REQUIRED。
    • REQUIRED:如果当前存在事务,则加入该事务;如果没有,则创建一个新的事务
    • SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。
    • MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
    • REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则挂起当前事务。
    • NESTED: 如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则创建一个新的事务。
    • NOT_SUPPORTED: 以非事务方式执行操作,如果当前存在事务,则暂停当前事务。
    • NEVER: 以非事务方式执行,如果当前存在事务则抛出异常。
  • isolation
    设置事务的隔离级别,这影响了事务内的操作如何与其他事务中的操作相互作用。默认值为 Isolation.DEFAULT
    • READ_UNCOMMITTED: 允许读取尚未提交的数据变更。
    • READ_COMMITTED: 只允许读取已经提交的数据。
    • REPEATABLE_READ: 对同一数据的多次读取结果一致,即使有其他事务对数据进行了修改并提交。
    • SERIALIZABLE: 最高的隔离级别,完全阻止了脏读、不可重复读和幻读的情况。
  • timeout
    定义了事务必须在多长时间内完成(以秒为单位)
  • readOnly
    指示事务是否只读。
  • rollbackFor
    指定哪些异常触发事务回滚。
  • noRollbackFor
    指定哪些异常不会触发事务回滚。
    事务默认回滚机制:运行时异常回滚,编译时异常不回滚。

注意: mysql默认级别是REPEATABLE_READ,而Oracle是READ_COMMITTED。

底层原理

1、事务管

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值