代理模式(静态代理和动态代理)与SpringBoot AOP实现

1 篇文章 0 订阅

代理模式

在谈AOP之前,需要了解什么是代理模式。
代理模式分为静态代理和动态代理。
代理模式类似现实世界中的中介,比如租房,中介会为双方提供公共服务,如出租房屋、看房、签合同、收取费用等;通过中介(代理),业务双方就不用操心这些繁琐的事务,房东只要将钥匙交给中介,从中介那里收租金;房客只要从中介那里拿房子,交钱给中介即可。其他事情有中介办妥。

静态代理

静态代理中的角色:
1.抽象角色:业务的抽象,如租房这一业务,通常由接口或抽象类来实现
2.真实角色:被代理的角色,如房东
3.代理角色:代理真实角色,为业务双方提供服务,执行操作
4.客户:访问代理对象

静态代理优点:

  • 公共业务由代理来完成,实现业务的分工解耦
  • 当公共业务发生扩展的时候方便管理(方便扩展)

缺点:一个真实角色就会产生一个代理角色,即将原功能封装到代理类中,代码量翻倍,效率低

实现静态代理

下面实现简单的静态代理,这个代理类代理了UserServiceImpl类,并增加了日志功能。
UserService接口:

public interface UserService {
    public void add();
    public void delete();
    public void update();
}

UserServiceImpl类:

public class UserServiceImpl implements UserService {
    @Override
    public void add() {
        System.out.println("插入");
    }

    @Override
    public void delete() {
        System.out.println("删除");
    }

    @Override
    public void update() {
        System.out.println("更新");
    }
}

代理类:

public class UserServiceProxy implements UserService{

    private UserService userService;

    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    @Override
    public void add() {
        log("add");
        userService.add();
    }

    @Override
    public void delete() {
        log("delete");
        userService.delete();
    }

    @Override
    public void update() {
        log("update");
        userService.update();
    }

    public void log(String msg){
        System.out.println("执行了:" + msg);
    }
}

使用静态代理:

public class Client {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        UserServiceProxy proxy = new UserServiceProxy();
        proxy.setUserService(userService);
        proxy.add();
    }
}

执行结果:
在这里插入图片描述

可以看到,虽然静态代理可以在不修改源码的情况下增加功能,但每增加一个实现类,就要为其增加一个代理类,这样不便使用,效率低下。

那么如何解决静态代理中的问题呢?答案是使用动态代理。

动态代理

动态代理使用反射机制,不需要手动编写代理类,而是使用代理调用处理器来生成代理类(动态生成)。
动态代理通常分为基于接口和基于类的动态代理:jdk动态代理(基于接口),cglib(基于类)。

jdk动态代理

对于之前的UserService接口,我们使用jdk动态代理来实现,它代理的是一个接口,而不是一个类。这里涉及使用反射中的InvocationHandler接口和Proxy类。
编写代理调用处理器:

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

public class ProxyInvocationHandler implements InvocationHandler {
    //被代理的接口,这里用Object代替(实际上应使用UserServiceImpl接口)
    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }

    //生成代理类的实例
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    //通过代理类执行方法,返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(target, args);
        return result;
    }
}

使用代理:

public class Client {
    public static void main(String[] args){
        //创建真实角色
        UserServiceImpl userService = new UserServiceImpl();
        //创建代理生成器
        ProxyInvocationHandler handler = new ProxyInvocationHandler();
        //设置被代理的接口
        handler.setTarget(userService);
        //获取代理类实例
        UserService proxy = (UserService) handler.getProxy();
        //代理对象执行方法
        proxy.update();
    }
}

接下来我们想增加日志输出功能,在执行方法的时候打印日志,使用动态代理,只要在代理调用处理器中增加log方法即可。
在ProxyInvocationHandler中增加日志输出功能:

	@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log(method.getName());
        Object result = method.invoke(target, args);
        return result;
    }

    //增加日志功能
    private void log(String msg){
        System.out.println("执行了:" + msg);
    }

cglib动态代理

cglib是基于类的动态代理,当没有接口时,可以使用cglib来实现动态代理。接下来使用cglib实现对之前的UserServiceImpl类的代理并添加日志功能。
编写代理生成器:

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class ProxyMethodInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        log(method.getName());
        return methodProxy.invokeSuper(obj, args);
    }

    private void log(String msg){
        System.out.println("执行了:" + msg);
    }
}

使用代理:

public class Client {
    public static void main(String[] args) {
        UserServiceImpl service = new UserServiceImpl();
        ProxyMethodInterceptor interceptor = new ProxyMethodInterceptor();
        UserServiceImpl proxy = (UserServiceImpl) Enhancer.create(service.getClass(), interceptor);
        proxy.delete();
    }
}

AOP(面向切面编程)

理解了什么是代理,接下来看什么是AOP。一句话概括,AOP就是在不改动源代码的前提下动态的增强。可以增加具体业务之外的功能。

AOP相关概念:

  • 横切关注点:跨越应用程序多个模块的方法或功能。简单来说就是与具体业务无关的功能,如日志、验证等
  • 切面(ASPECT):横切关注点被模块化的特殊对象。(将附加功能抽象成一个类)
  • 通知(Advice):切面必须要完成的工作(即类中的方法)
  • 目标(Target):被通知对象(被代理的对象)
  • 代理(Proxy):向目标对象应用通知之后创建的对象(即代理对象)
  • 切入点(PointCut):切面通知执行的地点的定义(即在哪执行)
  • 连接点(JoinPoint):与切入点匹配的执行点(具体执行的方法)

SpringBoot注解实现AOP

接下来使用SpringBoot注解实现AOP添加日志功能。
引入依赖:

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

UserServiceImpl类:

@Service
public class UserServiceImpl implements UserService {
    @Override
    public void add() {
        System.out.println("插入");
    }

    @Override
    public void delete() {
        System.out.println("删除");
    }

    @Override
    public void update() {
        System.out.println("更新");
    }
}

Log类:
使用@Aspect注解表示一个切面。Spring AOP提供了@Before,@After,@AfterReturning,@Around等一系列注解来表示切面中执行方法的时刻。并且可以使用JoinPoint来获取连接点即被代理者所执行方法的信息。

@Aspect
@Component
public class Log {

    @Before("execution(* com.example.demo.demo04_springaop.UserServiceImpl.*(..))")
    public void logBefore(JoinPoint jp){
        System.out.println("执行前方法之前:" + jp.getSignature());
    }

    @After("execution(* com.example.demo.demo04_springaop.UserServiceImpl.*(..))")
    public void logAfter(JoinPoint jp){
        System.out.println("执行方法之后");
    }
}

测试:
为了测试,需要获取容器中托管的javabean。
获取Bean工具类:

public class BeanUtil {
    public static ConfigurableApplicationContext applicationContext;

    public static <T> T getBean(Class<T> c){
        return applicationContext.getBean(c);
    }
}

测试类:

public class Client {
    public void test(){
        //获取已经实例化的接口bean
        UserService service = BeanUtil.getBean(UserService.class);
        //执行bean中方法
        service.add();
    }
}

启动类:

@SpringBootApplication
public class SpringdemoApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext applicationContext = SpringApplication.run(SpringdemoApplication.class, args);
        BeanUtil.applicationContext = applicationContext;
        new Client().test();
    }
}

运行结果:
在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值