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 {
}
相关推荐
2的n次方_2 分钟前
二维费用背包问题
java·算法·动态规划
皮皮林5512 分钟前
警惕!List.of() vs Arrays.asList():这些隐藏差异可能让你的代码崩溃!
java
莳光.2 分钟前
122、java的LambdaQueryWapper的条件拼接实现数据sql中and (column1 =1 or column1 is null)
java·mybatis
程序猿麦小七7 分钟前
基于springboot的景区网页设计与实现
java·spring boot·后端·旅游·景区
敲敲敲-敲代码8 分钟前
游戏设计:推箱子【easyx图形界面/c语言】
c语言·开发语言·游戏
weisian15114 分钟前
认证鉴权框架SpringSecurity-2--重点组件和过滤器链篇
java·安全
蓝田~15 分钟前
SpringBoot-自定义注解,拦截器
java·spring boot·后端
ROC_bird..17 分钟前
STL - vector的使用和模拟实现
开发语言·c++
.生产的驴18 分钟前
SpringCloud Gateway网关路由配置 接口统一 登录验证 权限校验 路由属性
java·spring boot·后端·spring·spring cloud·gateway·rabbitmq
MavenTalk23 分钟前
Move开发语言在区块链的开发与应用
开发语言·python·rust·区块链·solidity·move