Spring框架

Spring框架

第三方编写的一套能够实现Ioc(DI)和Aop功能的框架
Spring被称之为java的"救世主"
Spring的出现改变了java既有的编程方式

Spring的主要功能
Spring主要功能有两个
分别是

1.Ioc(DI) ---- 控制反转(依赖注入)

控制反转
控制反转(Ioc:Inversion of Control)
要想了解控制反转,就要知道我们之前编写的程序都是"主动控制"
主动控制:是由程序员来主动管理和控制程序中需要使用的对象
控制反转:是将控制和管理程序中所使用到对象的权利交给外部容器

什么是DI
DI中文翻译就是依赖注入(dependency injection)
是Spring框架中实现Ioc的一种操作
什么是依赖
所谓依赖,就是一个对象在运行某个业务或方法时需要的原材料
我们称这个对象依赖这个原材料来实现这个业务
人依赖笔完成写字方法
关羽依赖青龙偃月刀完成战斗方法
什么是注入
将一个外部的对象,保存到指定对象的内部
比如将User对象注入到Spring容器中
通过上面对依赖和注入的分别解释,
依赖注入可以总结为:
使用注入的方式,将一个对象依赖的原材料直接保存到这个对象的属性中

编写低耦合的依赖注入
什么是高耦合(紧耦合)
所谓的高耦合,就是依赖对象和被依赖的对象的关系是紧密的,不可替换的
关羽类中声明依赖青龙偃月刀类型,就是依赖对象和被依赖对象关系是紧密的
因为关羽除了青龙偃月刀不能使用其他任何兵器完成战斗业务
我们编写程序,高耦合被认为是不好的,因为它不能应对程序的变化,不方便扩展和维护
我们要对现有的代码进行"解耦"
所谓解耦就是将现在高耦合的依赖状态变化为低耦合(松耦合)的
做法非常简单,就是提取当前被依赖类的抽象类或者接口
让依赖本类的类中声明为这个抽象类或接口即可

2.AOP ---- 面向切面编程

AOP:面向切面的程序设计

Aspect-oriented programming

也译作:剖面导向程序设计

是一种程序设计思想,

旨在将横切关注点与业务主体进行进一步分离,以提高程序代码的模块化程度。通过在现有代码基础上增加额外的通知(Advice)机制,能够对被声明为“切点(Pointcut)”的代码块进行统一管理与扩展。

AOP术语

切点(point cut):方法与方法之间的调用会产生切点,我们通过使用AOP拦截方法的调用并对调用进行更改或扩展

通知(Advice):设置具体的针对切点方法的运行时机

前置通知(before advice):被调用方法运行之前要运行

后置通知(after advice):被调用方法运行之后要运行

环绕通知(around advice):被调用方法运行之前和之后都运行

事后返回通知(afterReturning advice):被调用方法返回值之后运行

事后返回通知比较少见

异常通知(afterThrowing advice):被调用方法发生异常时运行

目标对象(target):调用方法的对象

织入(weaving):将编写的扩展方法镶嵌到指定的切点的操作

切面(aspect):是一个可以定义切点,编写各类通知和织入操作的内容

通知的分类

实际上AOP机制提供了如下的通知方式
@Before 前置
@After 后置
@AfterReturning 返回后
@Around 环绕
@AfterThrowing 异常时
在进行通知增强方法运行时
我们可以在运行的方法的参数中添加JoinPoint类型的参数
一旦添加这个类型的参数
可以通过这个参数获得切入点的目标方法一些信息

例如:目标对象,目标方法参数返回值等

下面我们演示一下各种通知的使用和JoinPoint的使用

@Aspect//表示这个类是一个设置切面的类
@Component
@Slf4j
public class DemoAspect {//如果想对某些方法(或某个方法)设置通知,扩展功能的话
    //需要先声明这个方法的切入点
    //使用@Pointcut声明切入点
    //这个切入点指定的是TestController的test方法
    @Pointcut("execution(public * cn.tedu.straw.gateway." +
            "controller.TestController.test(..))")
    //这个方法不需要任何代码
    //这个方法的意义仅在于方法名会成为上面声明切入点的id
    public void pointCut() {
    }
    //声明一个前置通知
    //@Before("pointCut()")含义是下面的方法会在
    // pointCut()指定的切入点的方法运行之前运行
    @Before("pointCut()")
    public void before(JoinPoint joinPoint){
        //获得目标方法的信息
        Signature method= joinPoint.getSignature();
        log.info("在{}方法之前运行",method);
    }
    @After("pointCut()")
    public void after(JoinPoint joinPoint){
        Signature method= joinPoint.getSignature();
        log.info("在{}方法之后运行",method);
    }//环绕通知的JoinPoint类型比较特殊,它是ProceedingJoinPoint
    //是JoinPoint的子接口,相较于JoinPoint有更多的方法用于支持环绕通知
    @Around("pointCut()")
    public void around(ProceedingJoinPoint joinPoint)
            throws Throwable {
        log.info("环绕通知的前置运行...");
        //ProceedingJoinPoint接口的特有方法,放行运行目标方法
        joinPoint.proceed();//运行目标方法
        log.info("环绕通知的后置运行...");
    }
    //异常通知
    @AfterThrowing("pointCut()")
    public void afterThrowing(JoinPoint joinPoint){
        Signature method= joinPoint.getSignature();
        log.info("在{}方法发生异常时运行",method);
    }
    //返回后通知
    @AfterReturning("pointCut()")
    public void afterReturning(JoinPoint joinPoint){
        Signature method= joinPoint.getSignature();
        log.info("在{}方法返回值之后运行",method);
    }
}

切入点声明的格式

上面章节中我们使用@PointCut声明切入点

@Pointcut("execution(public * cn.tedu.straw.gateway." +
        "controller.TestController.test(..))")

上面的代码指定了切入点的方法

但实际上我们还可是使用更多变化来表示更丰富的含义

声明切入点的语法格式如下

execution(
modifier-pattern?
ret-type-pattern
declaring-type-pattern?
name-pattern(param-pattern)
throws-pattern?)

modifier-pattern:匹配修饰符(可省略)

ret-type-pattern:匹配返回值(必须写)

declaring-type-pattern:类路径\全类名(可省略)

name-pattern:匹配方法名(必须写)

param-pattern:匹配参数列表(必须写)

throws-pattern:匹配声明的异常(可省略)

下面根据几个例子来理解

execution(* *(..))  表示匹配所有方法
execution(public * com.test.TestController.*(..))
TestController类中的public修饰的所有方法
execution(* com.test..*.*(..))
com.test包下的所有类中的所有方法

AOP实际应用
我们现在要利用aop的环绕通知增强来检测faq模块中业务逻辑层所有方法每个方法的耗时
在faq模块
faq中创建一个aspect包,包中新建一个专门的类来编写检测性能的程序
代码如下

@Aspect
@Component
@Slf4j
public class PerformanceAspect {
    //声明切入点
    // 当前项目中业务逻辑层中所有公有方法的运行时间
    @Pointcut("execution(public * cn.tedu.straw.faq." +
            "service.*Service.*(..))")
    public void serviceCut(){}
    //测试性能的环绕增强
    @Around("serviceCut()")
    public Object perform(ProceedingJoinPoint joinPoint)
            throws Throwable {
        //记录纳秒时间
        long t1=System.nanoTime();
        Object obj=joinPoint.proceed();
        long t2=System.nanoTime();
        Signature method=joinPoint.getSignature();
        log.info("方法用时:{}:{}",method,t2-t1);
        return obj;
    }
}

利用JUnit进行测试

什么是JUnit

JUnit就是java单元测试,能够针对java程序中的某段代码或指定的方法进行测试

为什么需要JUnit

如果没有JUnit,我们使用项目中创建一个类,这个类中编写main方法的方式来进行测试,这样的方式一个类只能有一个main方法
测试不方便,而且测试用的main方法的类和其它类都写在同一个区域中,造成类的数量比较多,不好管理
Junit可以在我们编写正常java类的区域之外,再创建专门的测试区域
而且一个类可以测试多个方法,更专业更简单

Spring组件扫描

我们现在学习了怎么在Config类中使用@Bean来注入对象到Spring容器
但是Spring提供了很多中注入方法方便我们在不同情况下使用注入功能
什么时候使用组件扫描
当一个类不需要进行任何属性或状态的设置,单纯的直接实例化就注入到Spring容器时,就不需要使用@Bean的注入方式了,推荐使用组件扫描
使用组件扫描注入对象
在要注入的类名上(前一行)添加如下注解
@Component 这个单词是组件的意思
一般情况下,组件扫描方式注入的对象的id就是类名
但是对类名可能有细微的修改
当类名首字母是大写,第二个字母是小写时,id为首字母小写的样式
例:DragonBlade -> dragonBlade
当类名前两个字母连续大写时,id就是类名不变
例:AOPTest -> AOPTest

自定义组件id
如果我们不想使用类名做Spring容器中的id
我们也可以自定义组件的id
需要注意,实例开发中,很少有需要自定义id的需求和必要,一般情况下使用类名即可!!!

Spring容器中对象的管理
Spring容器中对象的作用域
Spring容器中对象可能会有不同的作用域
在不同的程序需求下,设置为不同的管理模式
根据程序中对象的出现形式,我们可以将程序中的对象分为两种
1.程序中只出现唯一一个的:单例(singleton)
2.程序中出现很多的:原型(prototype)
Spring容器中注入的对象,在程序中运行也要分出这两种对象
实际开发中,大部分的对象都是单例的,提高的内存的利用率,所以Spring在默认情况下,也将注入到Spring容器的对象设置为单例

Spring容器中对象的创建和销毁
如果想研究Spring容器对对象实例化和销毁的时机

将User类中添加如下代码
//在对象被创建时运行的代码
    @PostConstruct
    public void open(){
        System.out.println("User对象实例化完毕");
    }
    //在对象销毁时运行的代码
    @PreDestroy
    public void close(){
        System.out.println("User对象即将被销毁");
    }

再次使用单例测试
我们会发现单例的作用域下
在实例化acac对象即Spring容器时,User对象就已经实例化并注入到Spring容器中了
无论在测试代码中获得多少次User对象,都不会再次实例化
并且在acac对象销毁前,User对象也会自动销毁!
如果上述代码在原型的作用域下
我们会发现原型的作用域下
User对象不会随acac即Spring容器的实例化而实例化
每当测试代码中获得User对象时,都会实例化一个新的User对象反复获得时就会反复实例化
acac对象销毁时,原型状态下的User对象没有销毁
我们今后使用的比较多的是单例的
单例模式还有一个小缺点,需要额外的弥补一下
懒惰初始化
通过上面章节的测试,我们发现单例模式的对象会在Spring容器实例化的同时也实例化,这样情况下,就会造成Spring容器中单例对象较多时,实例化时间较长
如果很多单例的对象在程序实际运行中根本就用不到,这样这些实例化操作和内存就白白浪费了
这种情况下,我们就可以使用懒惰初始化
一个对象的注入一旦被标记为懒惰初始化,那么在acac实例化时,就不会同时实例化它了,而会在获得这个类型对象时,再实例化

@Autowired注解实现自动装配的过程

首先在Spring容器中寻找匹配类型的对象
如果没有,则注入失败
如果上面的寻找成功,并且只有唯一一个匹配类型对象,则注入成功
如果上面的寻找成功,并且匹配两个或以上的对象,那么就需要使用id的匹配,如果id匹配有对应的对象,则注入成功
上面3中的情况下,如果没有任何id匹配,则注入失败

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值