5Spring动态代理开发小结

5Spring动态代理开发小结

1.为什么要有动态代理?

  1. 好处

    1.利于程序维护
    2.利于原始类功能的增强
    3.得益于JDK或者CGlib等动态代理技术使得程序扩展性很强
    
  2. 为什么说使得程序扩展性很强?

    静态代理运行一个增强类需要编译为.class文件,再进入到虚拟机之中运行,如果增加一个功能,就需要重新编译文件造成维护上的灾难
    动态代理会使用JDK或者CGlib等动态代理技术在JVM直接生成字节码文件也称为动态字节码文件,直接可以在虚拟机中运行,且可以在不重新编译前提下运行
    

2.如何开发动态代理对象

1.MethodBeforeAdvice

1.需要原始对象,被代理对象(Target)

​ 被代理对象的接口

import org.User;

public interface UserService {
    //这个User只是象征性的传入个对象
    public void register(User user);

    public Boolean login(String name, String password);
}

​ 原始对象

import org.User;

public class UserServiceImpl implements UserService {

    @Override
    public void register(User user) {
        System.out.println("UserServiceImpl.register");
    }

    @Override
    public Boolean login(String name, String password) {
        System.out.println("UserServiceImpl.login "+name+" "+password );
        return true;
    }
}

2.编写额外功能,它实现MethodBeforeAdvice接口(增强类)

​ 实现MethodBeforeAdvice 的运行在目标类之前

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class UserPoxyBefore implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("UserPoxyBefore.before");

    }
}

​ 3.在配置文件定义切入点

​ 首先得实现原始类和增强类

  <bean id="UserServicePiont" class="org.Service.UserServiceImpl"/>
  <bean id="UserPoxyBefore" class="org.Service.UserPoxyBefore"  />

​ 再定义切入点

  • ​ pointcut表示切入的地方,而里面的expression指定切入的方法,也就是使用这个增强类的地点
  • ​ advisor指定用哪个增强类在哪个切入点
<aop:config  >
        <aop:pointcut id="UserPoxyPC" expression="execution(* *(..))"/>
        <aop:advisor advice-ref="UserPoxyBefore" pointcut-ref="UserPoxyPC"/>
    </aop:config>

3.调用

​ 调用时的注意事项可以看这个点这个

@Test
    public void test2() {
        ApplicationContext context=new ClassPathXmlApplicationContext("/ApplicationContext2.XML");
       UserService userService= (UserService) context.getBean("UserServicePiont");
        userService.login("SY", "123456");
        userService.register(new User());
    }

​ 结果,可见代理类确实使用了

UserPoxyBefore.before
UserServiceImpl.login SY 123456
UserPoxyBefore.before
UserServiceImpl.register

2.MethodInterceptor(方法拦截器)

实现的MethodInterceptor可以运行在原始方法前中后

1.实现MethodInterceptor接口

在前面准备好了原始类接着直接开发增强功能就好,开发步骤和上面的一致,只不过第二步变为实现MethodInterceptor接口,如下

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class Intercepter implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
   
        return null;
    }
}

之后添加

  • invocation.proceed() 基本低效为原始方法
		Object ret=invocation.proceed();
        System.out.println("Intercepter.invoke");//增强的功能

组装起来

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class Intercepter implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {

        Object ret=invocation.proceed();
        System.out.println("Intercepter.invoke");
        return ret;
    }
}

2.配置文件

实现Intercepter 即可,后续配置和上面一致

<bean id= "intercepter" class="org.Service.Intercepter"/>
<aop:config  >
        <aop:pointcut id="UserPoxyPC" expression="execution(* *(..))"/>
        <aop:advisor advice-ref="intercepter" pointcut-ref="UserPoxyPC"/>
    </aop:config>

3.运行

直接运行上面的调用代码,不用改动,也体现了程序扩展性

结果

UserServiceImpl.login SY 123456
Intercepter.invoke
UserServiceImpl.register
Intercepter.invoke

4.如何让运行intercepter在原始方法的任意位置

由于invocation.proceed() 基本低效为原始方法,所以只需要把invocation.proceed() 放在不同位置即可

如调换位置

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class Intercepter implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("Intercepter.invoke");
        Object ret=invocation.proceed();
        return ret;
    }
}

运行结果,可以看到运行位置不同了

Intercepter.invoke
UserServiceImpl.login SY 123456
Intercepter.invoke
UserServiceImpl.register

3.对MethodBeforeAdvice的before方法参数分析

对于before有三个参数

  • ​ Method method
  • ​ Object[] args
  • ​ Object target

我们在它的接口实现设置断点

image-20210525174712096

接着DEBUG 测试方法

image-20210525174835071
看到method就是原始类的方法,也就是在配置文件定义的目标方法(pointcut里的expression)
args 就是原始类的方法传输的参数
target就是目标类,和JDK动态代理极其类似

接着继续DEBUG

image-20210525175210951

情况和上面一样,User我没有注入数据所以为null

对于MethodInterceptor也大致相同,就不再过多分析

posted on 2021-05-25 17:59  NathenJames  阅读(78)  评论(0编辑  收藏  举报