1 Star 0 Fork 0

Bruce6213/常用设计模式

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

代理模式

JDK动态代理

核心

Proxy类和InvocationHandler接口

import com.example.proxy.service.OrderService;
import com.example.proxy.service.impl.OrderServiceInvocationHandler;
import com.example.proxy.service.impl.RealOrderServiceImpl;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import java.lang.reflect.Proxy;

@SpringBootTest
class ProxyApplicationTests {

    @Test
    void contextLoads() {

        // 创建代理对象
        OrderService proxy = (OrderService) Proxy.newProxyInstance(
                // 接口
                OrderService.class.getClassLoader(),
                new Class[]{OrderService.class},
                // 具体实现类
                new OrderServiceInvocationHandler(new RealOrderServiceImpl()));

        proxy.placeOrder();
    }
}

CGLIB代理

import com.example.proxy.service.OrderService;
import com.example.proxy.service.impl.OrderServiceInvocationHandler;
import com.example.proxy.service.impl.RealOrderServiceImpl;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

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

@SpringBootTest
class ProxyApplicationTests {

    @Test
    void contextLoads() {

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(RealOrderServiceImpl.class);
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("Before placing order");
                Object invoke = methodProxy.invokeSuper(o, objects);
                System.out.println("After placing order");
                return invoke;
            }
        });

        OrderService proxy = (OrderService) enhancer.create();
        proxy.placeOrder();
    }
}

两者缺点

  • 通知类型少,且耦合性太高
  • 扩展性差

AOP

Spring AOP的底层就是基于上述两种代理方式实现的;

默认情况下,如果目标对象实现了相关接口,Spring会使用JDK动态代理;否则使用CGLIB。

优点

  • 业务代码与功能性代码解耦合
  • 通知类型多样
  • 扩展性强

通知类型

  • 前置通知(@Before):在目标方法调用之前调用通知**(执行前)**
  • 后置通知(@After):在目标方法完成之后调用通知**(完成后)**
  • 环绕通知(@Around):在被通知的方法调用之前和调用之后执行自定义的方法**(before前和after后)**
  • 返回通知(@AfterReturning):在目标方法成功执行之后调用通知**(退出时,该方法在after之前)**
  • 异常通知(@AfterThrowing):在目标方法抛出异常之后调用通知

<img alt="image-20240410162013253">

Demo

引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

自定义注解

用于标注目标方法

import java.lang.annotation.*;

@Target({ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {

    /* 日志描述 */
    String value() default "";
}

定义切面

@Aspect
@Component
@Slf4j
public class LogAspect {

    @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "result")
    public void doAfterReturning(JoinPoint joinPoint, Object result, Log controllerLog){
        log.info("方法返回值:{}", result);
        log.info("注解描述:{}", controllerLog.value());
        log.info("方法名:{}", joinPoint.getSignature().getName());

    }
}

在目标方法上使用自定义注解

@Service
public class AopServiceImpl {

    @Log("AOP test")
    public String test(){
        System.out.println("AOP test");
        return "AOP success";
    }
}

注意事项

单元测试类中无法使用AOP

image-20240417101051549

网上有种说法:在单元测试的类上加注解@EnableAspectJAutoProxy(true),但亲测无效。

解决方案

Spring aop 是针对 Spring 的 bean 进行 AOP, 而 @Test 单元测试类不是Spring 的 Bean, 所以无法 aop.

故,需多封装一层

@Service
public class DataService {

    @Autowired
    private UserMapper userMapper;

//    @MyDataSource("master")
    @MyDataSource("slaver")
    public void test()
    {
        userMapper.insertUser("aop", "aop");
        System.out.println("动态数据源切换成功!");
    }
}

参考链接

SpringBoot 代理模式

空文件

简介

取消

发行版

暂无发行版

贡献者 (1)

全部

语言

近期动态

不能加载更多了
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/bruce6213/common-design-patterns.git
git@gitee.com:bruce6213/common-design-patterns.git
bruce6213
common-design-patterns
常用设计模式
proxy

搜索帮助