代理(主要是动态)和SpringAOP

代理

  • 静态代理基于继承实现
  • 动态代理是基于接口实现
    业务层每次实现转账都需要执行,可以把他们拿出来当成一个切面,自己写出一个代理类,让业务层只执行业务的逻辑,重复的代码代理类来完成,然后调用代理类来执行。

    代理类
java 复制代码
package com.qcby.utils;

import com.qcby.service.AccountService;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//传入目标对象,生成该对象的代理对象,返回。对目标对象的方法进行增强
public class ProxyUtils {
    //获取代理对象,返回,增强目标对象的方法
    public static Object getProxy(final AccountService accountService){
        //使用jdk动态dialing生成代理对象
        Object proxy = Proxy.newProxyInstance(ProxyUtils.class.getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler() {
            //调用代理对象的方法,invoke方法就会去执行
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //目标对象的方法
                Object result = null;
                try {
                    //开启事务
                    TxUtils.startTransaction();
                    //目标对象的方法进行增强,作为结果返回
                    result = method.invoke(accountService,args);
                    //事务提交
                    TxUtils.commit();
                }catch (Exception e){
                    e.printStackTrace();
                    //事务回滚
                    TxUtils.rollback();
                }finally {
                    //资源关闭
                    TxUtils.close();
                }
                return result;
            }
        });
        return proxy;
    }
}

测试

java 复制代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Demo2 {
    @Autowired
    private AccountService accountService;
    @Test
    public void run1(){
        Account account1 = new Account();
        account1.setName("aaa");
        Account account2 = new Account();
        account2.setName("bbb");
        //创建代理对象
        AccountService proxy = (AccountService) ProxyUtils.getProxy(accountService);
        proxy.saveAll(account1,account2);
    }
}

AOP



配置文件形式:(IOC也是用的配置文件形式)
配置文件

java 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--将目标类配置到spring中-->
    <bean id="userService" class="com.qcby.demo1.UserServiceImpl"/>

    <!--将切面类配置到spring中-->
    <bean id="myXmlAspect" class="com.qcby.demo1.MyXmlAspect"/>
    <!--配置AOP的增强-->
    <aop:config>
        <!--配置切面 = 通知+切入点组成-->
        <aop:aspect ref="myXmlAspect">
            <!--通用写法-->
            <!--<aop:before method="log" pointcut="execution(public * com.qcby.*.*ServiceImpl.*(..))"/>-->
            <!--前置通知:无论方法成功与否都执行-->
            <!--<aop:before method="log" pointcut="execution(public void com.qcby.demo1.UserServiceImpl.save())"/>-->
            <!--最终通知:失败成功都执行-->
            <!--<aop:after method="log" pointcut="execution(public void com.qcby.demo1.UserServiceImpl.save())"/>-->
            <!--后置通知:方法成功执行之后执行-->
            <!--<aop:after-returning method="log" pointcut="execution(public * com.qcby.*.*ServiceImpl.*(..))"/>-->
            <!--异常通知:有异常才执行-->
            <!--<aop:after-throwing method="log" pointcut="execution(public void com.qcby.demo1.UserServiceImpl.save())"/>-->
            <!--环绕通知:目标方法执行前后都执行  执行方法成功与否对执行前的增强不影响(方法执行不成功也执行前置的)-->
            <aop:around method="aroundLog" pointcut="execution(public * com.qcby.demo1.*ServiceImpl.*(..))"/>
        </aop:aspect>
    </aop:config>

</beans>

切面类

java 复制代码
package com.qcby.demo1;

import org.aspectj.lang.ProceedingJoinPoint;

/*定义切面类 = 切入点(表达式)+通知*/
//在配置文件里配置成切面类=增强的方法(通知)+需要增强的方法(切入点)
public class MyXmlAspect {
    /*通知*/
    public void log(){
        //发送手机短信
        //发送邮件、记录日志、事务管理
        System.out.println("增强的方法执行了....");
    }
    public void log1(){
        //发送手机短信
        //发送邮件、记录日志、事务管理
        System.out.println("前置增强的方法执行了....");
    }
    public void log2(){
        //发送手机短信
        //发送邮件、记录日志、事务管理
        System.out.println("后置增强的方法执行了....");
    }
    /*环绕通知*/
    public void aroundLog(ProceedingJoinPoint proceedingJoinPoint){
        try {
            log1();
            proceedingJoinPoint.proceed();
            log2();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
}

测试类

java 复制代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Demo1 {
    @Autowired
    private UserService userService;

    /*测试*/
    @Test
    public void run1(){
        userService.save();
    }
}

半注解方式
切面类=通知+切入点(现在的切面类已经在通知上添加了切入点)

java 复制代码
package com.qcby.demo2;

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

@Component  // 把该类交给 IOC 去管理
@Aspect // 声明是切面类 == <aop:aspect ref="myXmlAspect">
public class MyAnnoAspect {

    //@Before(value = "execution(public * com.qcby.*.*ServiceImpl.*(..))")
    public void log1(){
        System.out.println("前置通知增强的方法执行...");
    }

    //@AfterReturning(value = "execution(public * com.qcby.*.*ServiceImpl.*(..))")
    public void log2(){
        System.out.println("后置通知增强的方法执行...");
    }

    //@After(value = "execution(public * com.qcby.*.*ServiceImpl.*(..))")
    public void log3(){
        System.out.println("最终通知增强的方法执行...");
    }

    //@AfterThrowing(value = "execution(public * com.qcby.*.*ServiceImpl.*(..))")
    public void log4(){
        System.out.println("异常通知增强的方法执行...");
    }

    @Around(value = "execution(public * com.qcby.*.*ServiceImpl.*(..))")
    public void log5(ProceedingJoinPoint proceedingJoinPoint){
        try {
            log1();
            proceedingJoinPoint.proceed();
            log2();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
}

配置文件

java 复制代码
<!--配置文件中开启自动代理-->
<aop:aspectj-autoproxy/>
<!--开启注解扫描-->
<context:component-scan base-package="com.qcby" />

测试类

java 复制代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Demo2 {
    @Autowired
    private AccountService accountService;
    @Test
    public void run1(){
        accountService.save();
    }
}

纯注解形式
配置类

java 复制代码
@Configuration  // 配置类
@ComponentScan(value = "com.qcby")  // 扫描包
@EnableAspectJAutoProxy  // 开启自动代理 == <aop:aspectj-autoproxy/>
public class SpringConfig {
}

切面类
与半注解形式一样
测试类

java 复制代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class Demo2 {
    @Autowired
    private AccountService accountService;
    @Test
    public void run1(){
        accountService.save();
    }
}

出现异常时:前置方法,最终方法,异常方法都会执行

相关推荐
booooooty2 小时前
基于Spring AI Alibaba的多智能体RAG应用
java·人工智能·spring·多智能体·rag·spring ai·ai alibaba
极光雨雨2 小时前
Spring Bean 控制销毁顺序的方法总结
java·spring
Spirit_NKlaus3 小时前
解决HttpServletRequest无法获取@RequestBody修饰的参数
java·spring boot·spring
lwb_01184 小时前
SpringCloud——Gateway新一代网关
spring·spring cloud·gateway
程序猿小D5 小时前
[附源码+数据库+毕业论文]基于Spring+MyBatis+MySQL+Maven+jsp实现的电影小说网站管理系统,推荐!
java·数据库·mysql·spring·毕业设计·ssm框架·电影小说网站
CodeWithMe6 小时前
【Note】《深入理解Linux内核》 Chapter 15 :深入理解 Linux 页缓存
linux·spring·缓存
llwszx7 小时前
Spring中DelayQueue深度解析:从原理到实战(附结构图解析)
java·后端·spring·delayqueue·延迟任务
C182981825758 小时前
OOM电商系统订单缓存泄漏,这是泄漏还是溢出
java·spring·缓存
hello早上好9 小时前
JDK 代理原理
java·spring boot·spring
何苏三月10 小时前
SpringCloud系列 - Sentinel 服务保护(四)
spring·spring cloud·sentinel