Spring AOP创建代理的方式ProxyFactoryBean、ProxyFactory、AspectJProxyFactory

应用层面上创建代理的类:ProxyFactoryBeanProxyFactoryAspectJProxyFactory

此处这里指的是Spring提供的应用层得方式,并不是指的底层实现方式。底层实现方式现在只有业界都熟悉的两种:JDK动态代理和CGLIB代理

Spring AOP创建代理的方式ProxyFactoryBean、ProxyFactory、AspectJProxyFactory_第1张图片
Spring AOP创建代理的方式ProxyFactoryBean、ProxyFactory、AspectJProxyFactory_第2张图片

1、ProxyFactoryBean是将AOP和IOC融合起来
2、ProxyFactory则是只能通过代码硬编码进行编写,一般都是给spring自己使用
3、AspectJProxyFactory 是集成AspectJ和Spring

一、ProxyFactoryBean

ProxyFactoryBean是创建AOP的最基本的方式。它是个工厂Bean,然后我们可以自定义我们的代理实现逻辑,最终交给Spring容器管理即可。可以看到还实现了FactoryBean

public class ProxyFactoryBean extends ProxyCreatorSupport
		implements FactoryBean<Object>, BeanClassLoaderAware, BeanFactoryAware {
		...
}

下面我们通过一个案例认识下ProxyFactoryBean,先创建一个通知,在通过ProxyFactoryBean给目标类添加绑定这个通知,当目标类被执行的时候,都会进到这个通知里面。

//1、创建一个通知
@Component("logMethodBeforeAdvice")
public class LogMethodBeforeAdvice implements MethodBeforeAdvice {

    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("this is LogMethodBeforeAdvice");
    }
}


// 注册一个代理Bean,这个Bean的作用就是把给目标绑定通知,生成代理对象。
@Bean
public ProxyFactoryBean proxyFactoryBean(@Autowired HelloService helloService) {
    ProxyFactoryBean factoryBean = new ProxyFactoryBean();

    //代理的目标对象  效果同setTargetSource(TargetSource targetSource)
    // 此处需要注意的是,这里如果直接new,那么该类就不能使用@Autowired之类的注入  因此建议此处还是从容器中去拿
    // 因此可以写在入参上(这也是标准的写法~~)
    //factoryBean.setTarget(new HelloServiceImpl());
    factoryBean.setTarget(helloService);

    // setInterfaces和setProxyInterfaces的效果是相同的。设置需要被代理的接口,
    // 若没有实现接口,那就会采用cglib去代理
    // 需要说明的一点是:这里不设置也能正常被代理(若你没指定,Spring内部会去帮你找到所有的接口,然后全部代理上,设置的好处是只代理指定的接口
    factoryBean.setInterfaces(HelloService.class);
    //factoryBean.setProxyInterfaces(new Class[]{HelloService.class});

    // 需要植入进目标对象的bean列表 此处需要注意:这些bean必须实现类 org.aopalliance.intercept.MethodInterceptor或org.springframework.aop.Advisor的bean ,配置中的顺序对应调用的顺序
    factoryBean.setInterceptorNames("logMethodBeforeAdvice");

    // 若设置为true,强制使用cglib,默认是false的
    //factoryBean.setProxyTargetClass(true);

    return factoryBean;
}


// main方法测试:
public static void main(String[] args) {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(RootConfig.class);

    //expected single matching bean but found 2: helloServiceImpl,proxyFactoryBean
    // 如果通过类型获取,会找到两个Bean:一个我们自己的实现类、一个ProxyFactoryBean所生产的代理类 而此处我们显然是希望要生成的代理类的  因此我们只能通过名称来(或者加上@Primary)
    //HelloService bean = applicationContext.getBean(HelloService.class);
    HelloService bean = (HelloService) applicationContext.getBean("proxyFactoryBean");
    bean.hello();
    
    System.out.println(bean); //com.fsx.service.HelloServiceImpl@4e50c791
    System.out.println(bean.getClass()); //class com.sun.proxy.$Proxy22 用得JDK的动态代理
    
    // 顺便说一句:这样也是没错得。因为Spring AOP代理出来的每个代理对象,都默认实现了这个接口(它是个标记接口)
    // 它这个也就类似于所有的JDK代理出来的,都是Proxy的子类是一样的思想~
    SpringProxy springProxy = (SpringProxy) bean;
}

ProxyFactoryBean也可以脱离IoC容器使用,虽然它大多数都是结合IoC容器一起使用,但是它脱离容器依然是可以单独使用的

public static void main(String[] args) throws Exception {
	//当然你也可以使用和容器相关的ProxyFactoryBean
	ProxyFactoryBean factory = new ProxyFactoryBean();
	factory.setTarget(new Person());

	//声明一个aspectj切点,一张切面
	AspectJExpressionPointcut cut = new AspectJExpressionPointcut();
	cut.setExpression(pointcutExpression); // 设置切点表达式

	//声明一个通知(此处使用环绕通知 MethodInterceptor )
	Advice advice = (MethodInterceptor) invocation -> {
	    System.out.println("============>放行前拦截...");
	    Object obj = invocation.proceed();
	    System.out.println("============>放行后拦截...");
	    return obj;
	};

	//切面=切点+通知
	// 它还有个构造函数:DefaultPointcutAdvisor(Advice advice); 用的切面就是Pointcut.TRUE,所以如果你要指定切面,请使用自己指定的构造函数
	// Pointcut.TRUE:表示啥都返回true,也就是说这个切面作用于所有的方法上/所有的方法
	// addAdvice();方法最终内部都是被包装成一个 `DefaultPointcutAdvisor`,且使用的是Pointcut.TRUE切面,因此需要注意这些区别
	Advisor advisor = new DefaultPointcutAdvisor(cut, advice);
	factory.addAdvisor(advisor);


	//拿到代理对象
	Person p = (Person) factory.getObject();	

}

二、ProxyFactory

它和Spring容器没啥关系,可议直接创建代理对象来使用

public static void main(String[] args) {
    ProxyFactory proxyFactory = new ProxyFactory(new HelloServiceImpl());

    // 添加两个Advise,一个匿名内部类表示
    proxyFactory.addAdvice((AfterReturningAdvice) (returnValue, method, args1, target) ->
            System.out.println("AfterReturningAdvice method=" + method.getName()));
    proxyFactory.addAdvice(new LogMethodBeforeAdvice());
    
    HelloService proxy = (HelloService) proxyFactory.getProxy();
    proxy.hello();
}

很显然它代理的Bean都是new出来的,所以比如HelloServiceImpl就不能和Spring IoC很好的结合了,所以一般都是Spring内部去使用。


public class ProxyFactory extends ProxyCreatorSupport {
	// 它提供了丰富的构造函数,通过构造函数传递参数,用来设置目标接口+拦截器或者通知
	。。。。。。。
	public ProxyFactory(Class<?> proxyInterface, Interceptor interceptor) {
		addInterface(proxyInterface);
		addAdvice(interceptor);
	}

	// 主义这是个静态方法,可以一步到位,代理指定的接口,底层走的也是JDK和Cglib
	public static <T> T getProxy(Class<T> proxyInterface, Interceptor interceptor) {
		return (T) new ProxyFactory(proxyInterface, interceptor).getProxy();
	}
	// 注意:若调用此方法生成代理,就直接使用的是CGLIB的方式的
	public static Object getProxy(TargetSource targetSource) {
		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.setTargetSource(targetSource);
		proxyFactory.setProxyTargetClass(true);
		return proxyFactory.getProxy();
	}
}

三、AspectJProxyFactory

其实ProxyFactory拥有的功能AspectjProxyFactory都有,它可以使用编程的方式去创建代理。pring内部不是通过AspectjProxyFactory创建的代理对象,而是通过ProxyFactory

四、总结

这三个类本身没有什么关系,但都继承自:ProxyCreatorSupport,创建代理对象的核心逻辑都是在ProxyCreatorSupport中实现的

AspectJProxyFactory,ProxyFactoryBean,ProxyFactory 大体逻辑都是:

1、填充AdvisedSupport的,然后交给父类ProxyCreatorSupport得到JDK或者CGLIB的AopProxy
2、代理调用时候被intercept方法拦截,这个拦截操作都是在JDK代理和Cglib代理生成时设置好的。都是在调用ProxyCreatorSupport的getInterceptorsAndDynamicInterceptionAdvice获取符合当前目标对象的拦截器集合。然后转成拦截器链。

关于Spring为代理对象添加拦截器链可以参考这篇文章:AOP添加拦截器

本文引至:https://blog.51cto.com/u_3631118/3121445

你可能感兴趣的:(Spring整理后,spring,java,后端)