Spring 5 核心技术AOP基础项目实践详细记录

目录

一、认识Spring框架

二、控制反转和依赖注入(IoC+DI)

三、面向切面编程(AOP)

1、AOP原理

2、AOP六大基本概念

横向关注点

切面

连接点

切点

通知

扫描二维码关注公众号,回复: 12499545 查看本文章

四、AOP使用核心案例

1、新建Spring项目

2、创建简单业务类

3、创建切面类 

4、创建配置类

5、创建测试主类

五、BUG解决方法

1、Bug1:java.util.prefs.WindowsPreferences

2、Bug2:WARN Please initialize the log4j system properly


一、认识Spring框架

一直以来,久仰Spring大名,却不知是何物,望文生义,释义“春天”。最近从零开始学习,有了一些最基本的理解,故记录于此。

其实,我很好奇Spring框架名字的由来,特意查了一下:

名字来自于自然界,实际上Spring代表传统J2EE冬天的过去,标志着Java EE开发的新时代,所以认同了这个简单优雅的名字。

Spring框架的强大之处在于对J2EE开发进行了简化,对大部分常用的功能进行了封装。其中的实现原理依赖于两种技术原理:控制反转(Inverse of Control)和面向切面编程(Aspect Oriented Programing)。

Spring框架有各大模块组成,在Spring 5 中,分别有:

  1. 核心模块Core:依赖注入(DI)、面向切面编程(AOP)、事件处理(events)、资源访问(resources)、数据绑定(data binding)、i18n(国际化)等。
  2. 测试模块Testing:支持Spring MVC Test、WebTestClient、TestContext Framework、mock objects测试框架。
  3. 数据访问模块Data Access:事务处理(transaction)、数据访问对象(DAO)、数据库连接(JDBC)、对象关系映射(ORM)和处理XML(Marshalling XML)。
  4. Spring MVC模块Spring WebFlux Web Framework模块。 
  5. 集成模块Integration:远程访问(remoting)、消息服务(JMS)、加解密(JCA)、管理扩展(JMX)、邮件管理(mail)等。
  6. 支持语言Languages:支持使用Kotlin、Groovy等语言进行开发。

学到这里,发现Spring的功能特别全面和强大,“书山有路,学海无涯”再次给了最好的诠释,对于刚学习的小白来说,不知从何入手,所以接下来从最基础部分,也是很重要的技术原理开始学习。

二、控制反转和依赖注入(IoC+DI)

在平时普通的编程项目中,如果在A.java中要使用B.java类,那就必须在A中实例化B类对象,造成了A、B类之间的紧耦合,成为“侵入式开发”。假设随着项目的更新和升级,需要增加更多的功能,业务代码中B类需要进行大量扩展和改动,那这样在A类中也需要改动,很明显这样不利于软件的更新和维护。

因此,Spring的IoC技术解决了这种问题,通过反射技术,实现控制反转。IoC将调用者A和被调用者B分离,类与类直接关系解耦,满足“低耦合”的期望。

如上图,Spring框架的IoC技术实现A类和B类的解耦,A类中不用出现new B()的情况,而是将实例化任务交给Spring来完成,再使用反射机制将实例化对象赋值给A中的b对象。

原先是A主动实例化B类的对象,而现在成为被动方,有Spring框架来实现,实现了反转,所以说“控制反转”。


IoC在此成为了一种设计思想,具体在赋值的实现方式是依赖注入(DI)。在Spring中管理JavaBean的容器就是IoC容器,上面的B类对象有容器创建,容器再对A中的b进行对象值的注入,所以DI使用了反射技术进行了类属性的动态赋值。IoC/DI从编程技术看是将接口和实现方法进行分离,IoC容器管理JavaBean的生命周期和多个JavaBean的注入关系。

三、面向切面编程(AOP)

一种技术的诞生必是解决一类问题,Spring的AOP技术则是解决软件项目中具有通用性的问题,比如程序日志信息,包括了执行时间、执行者、效率和结果等信息。

如果在源程序中增删日志代码,一是会影响程序运行的效率,二是难以模块化维护软件,显得繁琐。所以,基于动态代理的AOP技术,可以在几乎不修改代码的基础上,对原模块的功能进行扩展,将通用性的日志模块解耦出来,易于模块化管理。

比如,在不改变Servlet代码的基础上加上日志功能;在不改变Spring MVC框架的控制层代码基础上增加数据库事务的功能。

1、AOP原理

Spring AOP技术是基于代理设计模式的原理,所谓代理,在这里的理解就是,为其他对象提供一种代理以控制对这个对象的访问。有时候,一个对象不适合或者不能直接引用另一个对象,需要代理对象在客户端和目标对象之间起中介的作用。

代理设计模式分为静态代理和动态代理,静态代理由于代理类绑定了固定的接口,不利于扩展,我们主要学习一下动态代理。

在Java中,实现动态代理有4种常见的方式:

  1. JDK提供的动态代理
  2. cglib框架
  3. javaassist框架
  4. Spring框架

后面案例会使用Spring框架实现AOP动态代理。

2、AOP六大基本概念

  • 横向关注点

横向关注点就是通用性的功能。常用的横向关注点有方法执行时间记录、访问权限验证、常规日志、数据库事务处理

它把通用性代码如日志功能代码和Service业务代码进行分离,达到解耦合的目的。与DI不同的性质,AOP的解耦主要是横向关注点的代码和业务代码之间的分离,而依赖注入是对象间的解耦。

  • 切面

切面是对横向关注点的模块化,每个切面是一个横向关注点功能的实现,它将通用性的代码提取出来放入单独的类中统一处理,方便复用。这个类就是切面类,面向切面编程就是主要对切面编写代码。

  • 连接点

连接点是在软件程序中能够插入切面的一个点。连接点的位置可以是调用方法前/后、抛出异常时、返回值后等。

  • 切点

切点则是部分连接点,减少了连接点数量的一个概念,针对切点应用切面,提高软件运行效率。

  • 通知

在Spring AOP中,通知分为5种:

(1)前置通知(Before):方法调用前。

(2)后置通知(After):方法调用后。

(3)环绕通知(Around):方法调用前后。

(4)返回通知(After-returning):方法有返回值。

(5)异常通知(After-throwing):方法出现异常。

切面包含了通知和切点,是两者的结合。更容易地理解为,通知定义了应用切面的时机;切点定义了在哪些连接点上放置切面。

四、AOP使用核心案例

前面大量知识或许还不能理解,“纸上得来终觉浅”,接下来在AOP简单应用的案例中,会更直观看到理论在实践中的应用。

IDE:Intellij IDEA U版本

JDK:jdk1.8.0

Spring:Spring 5.2.3

Aspect:Aspectj1.9

1、新建Spring项目

在src文件下创建包Spring_AOP,我的文件结构如下:

使用注解的方式实现前面提到的Advice,注解是Spring中的一个重要应用,可以减少人工配置xml的繁琐工作。

2、创建简单业务类



/*
 * UserinfoService.java
 * Copyright (c) 2021-02-25
 * LastModified:2021/2/25 上午10:48
 * Author : Charzous
 * Blog : https://blog.csdn.net/Charzous
 * All right reserved.
 */

package Spring_AOP.aopDemo.service;

import org.springframework.stereotype.Service;

@Service(value = "a")
public class UserinfoService {
    public void method1() {
        System.out.println("method 1 ");
    }

    public String method2() {
        System.out.println("method 2");
        return "我是方法2返回值";
    }

    public String method3() {
        System.out.println("method 3:测试异常");
        Integer.parseInt("a");//测试error
        return "我是方法3返回值";
    }
}

3、创建切面类 



/*
 * AspectObject.java
 * Copyright (c) 2021-02-25
 * LastModified:2021/2/25 上午10:55
 * Author : Charzous
 * Blog : https://blog.csdn.net/Charzous
 * All right reserved.
 */

package Spring_AOP.aopDemo.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class AspectObject {


    @Around(value = "execution(* Spring_AOP.aopDemo.service.UserinfoService.*(..))")//
    //注解实现环绕通知
    public Object around(ProceedingJoinPoint point) {
        Object returnObject = null;
        try {
            System.out.println("环绕开始");
            returnObject = point.proceed();
            System.out.println("环绕结束");
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return returnObject;
    }

    //*:任意返回值;service.UserinfoService.* service包下UserinfoService类中任意方法;.. 任意类型的参数
    @Before(value = "execution(* Spring_AOP.aopDemo.service.UserinfoService.*(..))")
    public void before() {
        System.out.println("Before 方法");
    }

    @After(value = "execution(* Spring_AOP.aopDemo.service.UserinfoService.*(..))")
    public void after() {
        System.out.println("After 方法");
    }

    @AfterReturning(value = "execution(* Spring_AOP.aopDemo.service.UserinfoService.*(..))")
    public void afterReturning() {
        System.out.println("afterReturning");
    }

    @AfterThrowing(value = "execution(* Spring_AOP.aopDemo.service.UserinfoService.*(..))")
    public void afterThrowing() {
        System.out.println("afterThrowing");
    }

}

 在代码中实现了前置、后置、返回、环绕和异常通知,其中使用到切面表达式进行业务类的匹配。

可以看到,切面表达式是一致的,进一步优化,可以通过全局切点来减少表达式冗余。



/*
 * AspectObject.java
 * Copyright (c) 2021-02-25
 * LastModified:2021/2/25 上午10:55
 * Author : Charzous
 * Blog : https://blog.csdn.net/Charzous
 * All right reserved.
 */

package Spring_AOP.aopDemo.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class AspectObject {
    //使用全局切点,使表达时全局化,减少冗余
    @Pointcut(value = "execution(* Spring_AOP.aopDemo.service.UserinfoService.*(..))")
    public void publicPointCut(){

    }

    @Around(value = "publicPointCut()")//"execution(* Spring_AOP.aopDemo.service.UserinfoService.*(..))"
    //注解实现环绕通知
    public Object around(ProceedingJoinPoint point) {
        Object returnObject = null;
        try {
            System.out.println("环绕开始");
            returnObject = point.proceed();
            System.out.println("环绕结束");
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return returnObject;
    }

    //*:任意返回值;service.UserinfoService.* service包下UserinfoService类中任意方法;.. 任意类型的参数
    @Before(value = "publicPointCut()")
    public void before() {
        System.out.println("Before 方法");
    }

    @After(value = "publicPointCut()")
    public void after() {
        System.out.println("After 方法");
    }

    @AfterReturning(value = "publicPointCut()")
    public void afterReturning() {
        System.out.println("afterReturning");
    }

    @AfterThrowing(value = "publicPointCut()")
    public void afterThrowing() {
        System.out.println("afterThrowing");
    }

}

4、创建配置类

使用注解的方式,扫描指定包中的各个类,Spring会完成自动配置。该类同时实现了Spring动态代理,注意需要使用包全名,才能正确配置。



/*
 * MyContext.java
 * Copyright (c) 2021-02-25
 * LastModified:2021/2/20 下午10:28
 * Author : Charzous
 * Blog : https://blog.csdn.net/Charzous
 * All right reserved.
 */

package Spring_AOP.aopDemo;


import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = {"Spring_AOP.aopDemo.service","Spring_AOP.aopDemo.aspect"})
public class MyContext {
}

5、创建测试主类



/*
 * aopDemo_test.java
 * Copyright (c) 2021-02-25
 * LastModified:2021/2/20 下午6:58
 * Author : Charzous
 * Blog : https://blog.csdn.net/Charzous
 * All right reserved.
 */

package Spring_AOP.aopDemo;

import Spring_AOP.aopDemo.service.UserinfoService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class aopDemo_test {
    public static void main(String[] args) {
        ApplicationContext context=new AnnotationConfigApplicationContext(MyContext.class);
        UserinfoService service=(UserinfoService) context.getBean(UserinfoService.class);
        service.method1();
        System.out.println();
        System.out.println("get method 2 return value="+service.method2());

        System.out.println();
        System.out.println("get method 3 return value="+service.method3());
    }
}

 可以看到,ApplicationContext使用了注解方式来解析上下文,另一种方式是XML文件解析。

method 3测试异常通知,结果运行如下:

五、BUG解决方法

1、Bug1:java.util.prefs.WindowsPreferences <init>

我在测试运行程序时候,出现了Warning警告。

java.util.prefs.WindowsPreferences <init>

WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs at root 0x80000002. Windows RegCreateKeyEx(...) returned error code 5.

虽然不影响运行,但还是解决为好。

解决:

打开Windows注册表编辑器,找到下面的目录

HKEY_LOCAL_MACHINE\Software\JavaSoft

然后新建文件夹 Prefs。

2、Bug2:WARN Please initialize the log4j system properly

出现另一种警告为:

log4j:WARN No appenders could be found for logger (org.springframework.XXX).
log4j:WARN Please initialize the log4j system properly.

这是Mybatis中log4j消息日志框架,因为我创建的另一个包用到Mybatis框架,对这次运行影响不大,输出警告信息。

解决:

只需要在src下新建log4j.properties文件,内容为:

#
# log4j.properties
# Copyright (c) 2021-02-25
# LastModified:2021/2/24 下午9:42
# Author : Charzous
# Blog : https://blog.csdn.net/Charzous
# All right reserved.
#

log4j.rootLogger=WARN, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n

 到此,Spring 5 核心技术AOP简单基础实践全部完成,Spring框架的强大之处在于对J2EE开发进行了简化,对大部分常用的功能进行了封装。其中的实现原理依赖于两种技术原理:控制反转(IoC)和面向切面编程(AOP)。这篇主要认识Spring的基础知识和实现AOP,有了一些最基本的理解,故记录于此。

如果觉得不错欢迎“一键三连”哦,点赞收藏关注,有问题直接评论,交流学习! 


我的CSDN博客:https://blog.csdn.net/Charzous/article/details/114044697

猜你喜欢

转载自blog.csdn.net/Charzous/article/details/114044697