【spring】AOP中如何使用动态代理

本文思路

  • 动态代理在jdk、cglib、spring中如何实现的。
  • spring实现动态代理的ProxyFactory详解。
  • 从ProxyFactory的使用来了解AOP实现的大概思路。

什么是代理

  • 为其他对象提供一种代理以控制对这个对象的访问,增强一个类中的某个方法,对程序进行扩展。
  • 常用的实现方式3种:jdk、cglib、spring的ProxyFactory。

如何创建动态代理的方式之JDK

  • jdk的动态代理必须有接口!
UserService target = new UserService();

// UserInterface接口的代理对象
// 第一个参数为类加载器、第二个参数为代理的接口,第三个参数为代理的具体逻辑
Object proxy = Proxy.newProxyInstance(UserService.class.getClassLoader(), new Class[]{UserInterface.class}, new InvocationHandler() {
    /**
     * proxy:代理对象
     * method:执行的方法
     * args:方法参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before...");
        Object result = method.invoke(target, args);
        System.out.println("after...");
        return result;
    }
});

UserInterface userService = (UserInterface) proxy;
userService.test();

如何创建动态代理的方式之cglib

UserService target = new UserService();

// 通过cglib技术
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);

// 定义额外逻辑,也就是代理逻辑
enhancer.setCallbacks(new Callback[]{new MethodInterceptor() {
    /**
     * o:代理对象
     * method:被代理对象的方法
     * objects:函数调用的参数
     * methodProxy:方法的代理
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("before...");
        Object result = methodProxy.invoke(target, objects);
        System.out.println("after...");
        return result;
    }
}});

// 动态代理所创建出来的UserService对象
UserService userService = (UserService) enhancer.create();

// 执行这个userService的test方法时,就会额外会执行一些其他逻辑
userService.test();

如何创建动态代理的方式之spring的ProxyFactory

// 得到原始对象
UserService target = new UserService();

// 创建代理工厂
ProxyFactory proxyFactory = new ProxyFactory();
// 指定接口使用jdk的动态代理,不指定接口使用cglib的动态代理
// proxyFactory.setInterfaces(UserInterface.class);
// 设置原始对象
proxyFactory.setTarget(target);
// 添加一个代理逻辑,各种Advice
proxyFactory.addAdvice(new MethodInterceptor() {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("before...");
        // 执行被代理的方法
        Object result = invocation.proceed();
        System.out.println("after...");
        return result;
    }
});

// 得到代理对象,执行
UserInterface userService = (UserInterface) proxyFactory.getProxy();
userService.test();

Advice有哪些

  • MethodBeforeAdvice:方法之前执行。可以new MethodBeforeAdvice重写before方法。
  • AfterReturningAdvice:方法return后执行。可以new AfterReturningAdvice重写afterReturning方法。
  • ThrowsAdvice:方法抛异常后执行。可以new ThrowsAdvice重写afterThrowing方法。afterThrowing的第四个参数是具体的异常,会进行异常的匹配。
  • After (finally) advice:方法执行完finally之后执行,这是最后的,比return更后。
  • MethodInterceptor:环绕执行这是功能最强大的Advice,可以自定义执行顺序。可以new MethodInterceptor重写invoke方法。

Advisor:在Advice基础上增加了判断!

  • Advisor是有一个Pointcut和一个Advice组成。
  • 通过Pointcut可以指定要需要被代理的逻辑。比如可以指定那个类的那个方法进行代理。
UserService target = new UserService();

ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAdvisor(new PointcutAdvisor() {
    // 进行逻辑匹配
    @Override
    public Pointcut getPointcut() {
        return new StaticMethodMatcherPointcut() {
            @Override
            public boolean matches(Method method, Class<?> targetClass) {
                return method.getName().equals("testAbc");
            }
        };
    }

    // 具体的代理逻辑
    @Override
    public Advice getAdvice() {
        return new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                System.out.println("before...");
                Object result = invocation.proceed();
                System.out.println("after...");
                return result;
            }
        };
    }

    // 无用的方法,目前看源码只有几个test方法调用了
    @Override
    public boolean isPerInstance() {
        return false;
    }
});

UserInterface userService = (UserInterface) proxyFactory.getProxy();
userService.test();

让动态代理的对象成为Bean:ProxyFactoryBean

/**
 * 具体代理的Bean
 */
@Bean
public ProxyFactoryBean userService(){
    // 定义原始对象
    UserService userService = new UserService();
    // 得到ProxyFactoryBean对象
    ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
    // 设置要代理的对象
    proxyFactoryBean.setTarget(userService);
    // 设置执行哪些BeanName的代理逻辑
    proxyFactoryBean.setInterceptorNames("zhangweiAroundAdvise");
    // 返回代理对象
    return proxyFactoryBean;
}

/**
 * 代理的逻辑
 */
@Bean
public MethodInterceptor zhangweiAroundAdvise(){
    return new MethodInterceptor() {
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("before...");
            Object result = invocation.proceed();
            System.out.println("after...");
            return result;
        }
    };
}

解决让Bean不需要自己指定代理逻辑,降低耦合度:BeanNameAutoProxyCreator

/**
 * BeanNameAutoProxyCreator是一个BeanPostProcessor,他在实例化前的时候会执行!
 */
@Bean
public BeanNameAutoProxyCreator beanNameAutoProxyCreator() {
    BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();

    // userSe开头的所有的BeanName,都会执行zhangweiAroundAdvise的代理逻辑
    beanNameAutoProxyCreator.setBeanNames("userSe*");
    beanNameAutoProxyCreator.setInterceptorNames("zhangweiAroundAdvise");
    beanNameAutoProxyCreator.setProxyTargetClass(true);

    return beanNameAutoProxyCreator;
}

解决BeanNameAutoProxyCreator只能根据BeanName进行匹配:可以进行方法匹配的DefaultAdvisorAutoProxyCreator

/**
 * 定义的一个Advisor。
 * 现在的这个逻辑表示只要方法是test,就会进行ZhangweiAfterReturningAdvise代理
 */
@Bean
public DefaultPointcutAdvisor defaultPointcutAdvisor(){
    NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
    pointcut.addMethodName("test");

    DefaultPointcutAdvisor defaultPointcutAdvisor = new DefaultPointcutAdvisor();
    defaultPointcutAdvisor.setPointcut(pointcut);
    defaultPointcutAdvisor.setAdvice(new ZhangweiAfterReturningAdvise());

    return defaultPointcutAdvisor;
}

/**
 * DefaultAdvisorAutoProxyCreator是一个BeanPostProcessor,他在初始化的时候会执行!
 * 他会寻找在spring容器中有哪些Bean的类型是Advisor。匹配容器中的名称是否匹配
 * 这个Bean的代码可以写成@Import(DefaultAdvisorAutoProxyCreator.class)
 */
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
 
    DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();

    return defaultAdvisorAutoProxyCreator;
}

再次进行简化:@Aspect

@Aspect
@Component
public class ZhangweiAspect {

    @Before("execution(public void com.zhangwei.service.UserService.test())")
    public void zhangweiBefore(JoinPoint joinPoint) {
        System.out.println("zhangweiBefore");
    }

}

结束语

  • 获取更多本文的前置知识文章,以及新的有价值的文章,让我们一起成为架构师!
  • 关注公众号,可以让你对MySQL、并发编程、spring源码有深入的了解!
  • 关注公众号,后续持续高效的学习JVM!
  • 这个公众号,无广告!!!每日更新!!!
    作者公众号.jpg
posted @ 2022-03-07 00:00  程序java圈  阅读(169)  评论(0编辑  收藏  举报