SpringAOP 面向切面编程

一、SpringAOP 面向切面

1.定义

AOP(Aspect-Oriented Programming: 面向切面编程):将那些与业务无关,却为业务模块所共同调用的逻辑(例如事务处理、日志管理、权限控制等)封装抽取成一个可重用的模块,这个模块被命名为"切面"(Aspect),便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性;

2.Spring AOP 基于动态代理实现:

○ 如果被代理的对象,已经实现某个接口,则 Spring AOP 会使用 JDK Proxy(反射),基于接口的方式,创建代理对象(JDK动态代理的核心是InvocationHandler接口和Proxy类);

○ 如果被代理的对象,没有实现某个接口,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib,基于继承的方式,生成一个被代理对象的子类来作为代理(Cglib动态代理的核心是MethodInterceptor接口和Enhancer类);

3.AOP通知类型

AOP将抽取出来的共性功能称为通知;通知类型:以通知在上下文中的具体位置作为划分

  • 前置通知(Before)
  • 返回通知(After-returning)
  • 异常通知(After-throwing)
  • 后置通知(After)
  • 环绕通知(Around)
4.AOP连接点(Join point)

AOP将所有的方法都视为连接点,不管是接口里面的抽象方法,还是实现类里面的重写方法,都是连接点

5.AOP切点(Pointcut)

AOP将可能被抽取共性功能的方法称为切入点。切入点是连接点的子集

6.AOP目标对象(Target):

AOP目标对象(Target)就是挖掉功能的方法对应的类生的对象,这种对象是无法直接完成最终工作的

7.AOP织入(Weaving):

AOP织入(Weaving)就是将挖掉的功能回填的动态过程

8.AOP切面:切点+通知
9.SpringAOP+AspectJ实现步骤
  • 添加依赖,aop与aspectj表达式的依赖
  • 创建spring的主配置文件,bean内的命名空间要添加aop的
  • 创建业务代码并编写日志记录代码(事务管理代码)
  • 将业务层与日志记录层注入spring容器
  • <aop:config>--aop配置

aop:aspect--aop切面

aop:before--通知内容与通知类型

10.切点表达式配置语法:

execution(修饰符 返回值 包名称.类名称.方法名称(参数列表))

eg:

execution(public void com.apesource.service.ServiceImp.findAll())

①修饰符可以省略代表任意

execution(返回值 包名称.类名称.方法名称(参数列表))

②返回值可以使用"*"代表任意

execution(* 包名称.类名称.方法名称(参数列表))

③包名可以使用"*"代表任意名称

execution(* *.*.*.类名称.方法名称(参数列表))

④包名可以使用".."代表任意个数

execution(* *...类名称.方法名称(参数列表))

⑤类名与方法名可以使用"*"代表任意

execution(* *...*.*(参数列表))

⑥参数列表可以使用".."代表任意个数任意类型

execution(* *...*.*(..))

如果有参数

int======>int

String===>java.lang.String

二、使用xml方式实现在每个业务方法之前输出日志

案例:

1.坐标
java 复制代码
<dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>
2.IAccountService接口
java 复制代码
public interface IAccountService {

    //新增
    public void save(int i);

    //修改
    public void update();

    //删除
    public void delete();
}
3.AccountServiceImp实现类
java 复制代码
public class AccountServiceImp implements IAccountService{
    public void save(int i) {
        System.out.println("业务层的新增方法"+i);
    }

    public void update() {
        System.out.println("业务层的修改方法");
        //int a=10/0;

    }

    public void delete() {
        System.out.println("业务层的删除方法");

    }
}
4.创建一个util工具类包用来写Logger类

定义了一个名为Logger的类,它包含了一系列用于日志记录的方法,这些方法可以在方法执行的不同阶段(如前置、返回、异常、后置)进行日志记录。此外,它还包含一个特殊的方法arroundMethod,这是一个环绕通知的实现,用于在目标方法执行前后进行日志记录,并处理异常

java 复制代码
public class Logger {
    public void beforeMethod() {
        System.out.println("日志类Logger中调用beforeMethod方法进行日志记录");
    }

    public void returnMethod() {
        System.out.println("日志类Logger中调用returnMethod方法进行日志记录");
    }

    public void throwMethod() {
        System.out.println("日志类Logger中调用throwMethod方法进行日志记录");
    }

    public void afterMethod() {
        System.out.println("日志类Logger中调用afterMethod方法进行日志记录");
    }

    public Object arroundMethod(ProceedingJoinPoint pjp) {

        Object obj = null;
        try {
            System.out.println("环绕通知===前置通知");
            //切点方法
            Object[] ars = pjp.getArgs();
            obj = pjp.proceed(ars);
            System.out.println("环绕通知===返回通知");
        } catch (Throwable e) {
            e.printStackTrace();
            System.out.println("环绕通知===异常通知");
        } finally {
            System.out.println("环绕通知===后置通知");
            return obj;
        }


    }
}
5.applicationContext.xml
java 复制代码
<!--注入业务层-->
    <bean id="accountServiceImp" class="com.ztt.service.AccountServiceImp"></bean>

    <!--注入日志记录层(通知)-->
    <bean id="logger" class="com.ztt.util.Logger"></bean>

    <!--配置AOP-->
    <aop:config>
        <!--配置切面-->
        <aop:aspect id="aopAspect" ref="logger">
            <!--切点-->
            <aop:pointcut id="dian" expression="execution(* com.ztt.service.*.*(..))"/>
            <!--通知-->
            <!--            <aop:before method="beforeMethod" pointcut-ref="dian"></aop:before>-->
            <!--            <aop:after-returning method="returnMethod" pointcut-ref="dian"></aop:after-returning>-->
            <!--            <aop:after-throwing method="throwMethod" pointcut-ref="dian"></aop:after-throwing>-->
            <!--            <aop:after method="afterMethod" pointcut-ref="dian"></aop:after>-->
            <aop:around method="arroundMethod" pointcut-ref="dian"></aop:around>
        </aop:aspect>
    </aop:config>
</beans>
6.测试类
java 复制代码
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = "classpath:applicationContext.xml")
    public class Test01 {
        @Autowired
        public IAccountService service;
    
        @Test
        public void show1(){
            service.update();
            System.out.println("==============");
    //        service.save(1);
    //        System.out.println("==============");
    //        service.update();
    
        }
    
    }

三、使用注解方式实现在每个业务方法之前输出日志

1.IAccountService接口
java 复制代码
public interface IAccountService {

    //新增
    public void save(int i);

    //修改
    public void update();

    //删除
    public void delete();
}
2.AccountServiceImp接口实现类
java 复制代码
@Service
public class AccountServiceImp implements IAccountService{
    public void save(int i) {
        System.out.println("业务层的新增方法"+i);
    }

    public void update() {
        System.out.println("业务层的修改方法");
        int a=10/0;

    }

    public void delete() {
        System.out.println("业务层的删除方法");

    }
}
3.Logger类
java 复制代码
/**
 * 日志记录
 */

@Component
@Aspect//切面
public class Logger {
    @Pointcut("execution(* com.ztt.service.AccountServiceImp.update())")
    public void dian() {

    }

    //前置通知
    @Before("dian()")
    public void BeforeLogger() {
        System.out.println("前置通知==>日志类logger中调用BeforeLogger方法进行日志记录");
    }

    //返回通知
    @AfterReturning("dian()")
    public void AfterReturningLogger() {
        System.out.println("返回通知==>日志类logger中调用AfterReturningLogger方法进行日志记录");
    }

    //异常通知
    @AfterThrowing("dian()")
    public void AfterThrowingLogger() {
        System.out.println("异常通知==>日志类logger中调用AfterThrowingLogger方法进行日志记录");
    }

    //后置通知
    @After("dian()")
    public void AfterLogger() {
        System.out.println("后置通知==>日志类logger中调用AfterLogger方法进行日志记录");
    }

    //环绕通知
    //@Around("dian()")
    public Object AroundLogger(ProceedingJoinPoint pjp) {
        Object returnobj = null;//保存主业务方法的返回值
        try {
            //1.前置通知
            System.out.println("环绕通知===》前置通知");

            Object[] objs = pjp.getArgs();//主业务方法的参数
            returnobj = pjp.proceed(objs);//调用主业务方法


            //3.后置通知
            System.out.println("环绕通知===》后置通知");
        } catch (Throwable tw) {
            //4.异常通知
            System.out.println("环绕通知===》异常通知");
        } finally {
            //5.最终通知
            System.out.println("环绕通知===》最终通知");
        }
        return returnobj;

    }


}
4.applicationContext.xml
java 复制代码
<!--扫描注入-->
    <context:component-scan base-package="com.ztt"></context:component-scan>

    <!--开启spring注解的aop动态代理-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
5.测试类
java 复制代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Test01 {

    @Autowired
    public IAccountService service;

    @Test
    public void show1(){

        service.update();
        System.out.println("===================");
        
    }

}

四、使用配置类方式实现在每个业务方法之前输出日志

在注解方式的基础上使用配置类替换掉配置文件applicationContext.xml

1.ApplicationConfig类
java 复制代码
@Configuration
@ComponentScan(basePackages = "com.ztt")
@EnableAspectJAutoProxy
public class ApplicationConfig {
}
相关推荐
救救孩子把13 分钟前
深入理解 Java 对象的内存布局
java
落落落sss16 分钟前
MybatisPlus
android·java·开发语言·spring·tomcat·rabbitmq·mybatis
万物皆字节21 分钟前
maven指定模块快速打包idea插件Quick Maven Package
java
夜雨翦春韭28 分钟前
【代码随想录Day30】贪心算法Part04
java·数据结构·算法·leetcode·贪心算法
简单.is.good34 分钟前
【测试】接口测试与接口自动化
开发语言·python
我行我素,向往自由35 分钟前
速成java记录(上)
java·速成
一直学习永不止步40 分钟前
LeetCode题练习与总结:H 指数--274
java·数据结构·算法·leetcode·数组·排序·计数排序
邵泽明41 分钟前
面试知识储备-多线程
java·面试·职场和发展
Yvemil71 小时前
MQ 架构设计原理与消息中间件详解(二)
开发语言·后端·ruby
程序员是干活的1 小时前
私家车开车回家过节会发生什么事情
java·开发语言·软件构建·1024程序员节