应用层面上创建代理的类:ProxyFactoryBean、ProxyFactory、AspectJProxyFactory
此处这里指的是Spring提供的应用层得方式,并不是指的底层实现方式。底层实现方式现在只有业界都熟悉的两种:JDK动态代理和CGLIB代理
1、ProxyFactoryBean是将AOP和IOC融合起来
2、ProxyFactory则是只能通过代码硬编码进行编写,一般都是给spring自己使用
3、AspectJProxyFactory 是集成AspectJ和Spring
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();
}
它和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();
}
}
其实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