摘要:
在 Spring 的版本变迁过程中, 注解发生了很多的变化, 然而代理的设计也发生了微妙的变化, 从 Spring1.x 的 ProxyFactoryBean 的硬编码到 Spring2.x 的 Aspectj 注解, 最后到了现在广为熟知的自动代理.
说明:
ProxyConfig 代理的相关配置类
AdvisedSupport 实现了 Advised, 封装了对 Advice 和 Advisor 的操作
ProxyCreatorSupport
该类及其子类主要是利用代理工厂帮助创建 jdk 或者 cglib 的代理对象
ProxyProcessorSupport
该类及其子类才是我们目前用得做多的, 利用后置处理器来进行自动代理处理
- ProxyFactoryBean
- package com.GitHub.dqqzj.springboot.aop;
- import org.springframework.aop.MethodBeforeAdvice;
- import org.springframework.aop.TargetSource;
- import org.springframework.aop.framework.ProxyFactoryBean;
- import org.springframework.aop.target.SingletonTargetSource;
- import org.springframework.context.annotation.Bean;
- import org.springframework.stereotype.Component;
- import java.lang.reflect.Method;
- /**
- * @author qinzhongjian
- * @date created in 2019-08-24 11:05
- * @description: TODO
- * @since JDK 1.8.0_212-b10
- */
- @Component
- public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
- @Override
- public void before(Method method, Object[] args, Object target) throws Throwable {
- if (!method.getName().equals("toString")) {
- System.out.println(target.getClass().getName() + "#" + method.getName());
- }
- }
- /**
- * 代理的目标对象 效果同 setTargetSource(@Nullable TargetSource targetSource)
- * TargetSource targetSource = new SingletonTargetSource(aopService);
- * 可以从容器获取, 也可以类似下面这样直接 new, 使用区别需要熟悉 spring 机制.
- * factoryBean.setTarget(new AopService());
- *
- * 设置需要被代理的接口 效果同 factoryBean.setProxyInterfaces(new Class[]{AopService.class});
- * 若没有实现接口, 那就会采用 cglib 去代理
- * 如果有接口不指定的话会代理所有的接口, 否则代理指定的接口
- *
- * setInterceptorNames 方法源代码中有这样的一句话: Set the list of Advice/Advisor bean names. This must always be set
- * to use this factory bean in a bean factory.
- */
- @Bean
- public ProxyFactoryBean proxyFactoryBean(AopService aopService) {
- ProxyFactoryBean factoryBean = new ProxyFactoryBean();
- factoryBean.setTarget(aopService);
- //factoryBean.setInterfaces(AopService.class);
- factoryBean.setInterceptorNames("myMethodBeforeAdvice");
- // 是否强制使用 cglib, 默认是 false 的
- //factoryBean.setProxyTargetClass(true);
- return factoryBean;
- }
- }
源码分析:
- @Override
- @Nullable
- public Object getObject() throws BeansException {
- // 根据我们配置的 interceptorNames 来获取对应的 Advisor 并加入通知器执行链中
- initializeAdvisorChain();
- if (isSingleton()) {
- // 生成 singleton 的代理对象, 会利用 DefaultAopProxyFactory 去生成代理
- // 在内部如果你手动没有去设置需要被代理的接口, Spring 会代理你所有的实现接口.
- return getSingletonInstance();
- }
- else {
- if (this.targetName == null) {
- logger.warn("Using non-singleton proxies with singleton targets is often undesirable." +
- "Enable prototype proxies by setting the'targetName'property.");
- }
- // 和单利非常类似 只不过没有缓存了
- return newPrototypeInstance();
- }
- }
- private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
- if (this.advisorChainInitialized) {
- return;
- }
- if (!ObjectUtils.isEmpty(this.interceptorNames)) {
- // 最后一个不能是全局的 suffix *, 除非我们指定了 targetSource 之类的
- if (this.interceptorNames[this.interceptorNames.length - 1].endsWith(GLOBAL_SUFFIX) &&
- this.targetName == null && this.targetSource == EMPTY_TARGET_SOURCE) {
- throw new AopConfigException("Target required after globals");
- }
- for (String name : this.interceptorNames) {
- // 如国拦截器的名称是以 * 结尾的, 说明它要去全局里面都搜索出来
- // 全局: 去自己容器以及父容器中找, 类型为 Advisor.class 的, 名称是以这个名称为开头的 prefix 的 Bean.
- if (name.endsWith(GLOBAL_SUFFIX)) {
- addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
- name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
- }
- // 一般的情况下我们都是精确匹配
- else {
- Object advice;
- if (this.singleton || this.beanFactory.isSingleton(name)) {
- // 从容器里获取该 bean
- advice = this.beanFactory.getBean(name);
- }
- // 原型处理
- else {
- advice = new PrototypePlaceholderAdvisor(name);
- }
- addAdvisorOnChainCreation(advice, name);
- }
- }
- }
- this.advisorChainInitialized = true;
- }
- // 将 advice 对象添加到通知器链中
- private void addAdvisorOnChainCreation(Object next, String name) {
- // 这里调用 namedBeanToAdvisor 做了一下适配: 成统一的 Advisor
- Advisor advisor = namedBeanToAdvisor(next);
- addAdvisor(advisor);
- }
- // 方法中首先会调用 namedBeanToAdvisor(next) 方法, 将从 IoC 容器获取的普通对象转换成通知器 Advisor 对象
- private Advisor namedBeanToAdvisor(Object next) {
- try {
- return this.advisorAdapterRegistry.wrap(next);
- }
- }
- DefaultAdvisorAdapterRegistry
这个类还允许我们自定义适配器, 然后注册到里面就行.
- @Override
- public void registerAdvisorAdapter(AdvisorAdapter adapter) {
- this.adapters.add(adapter);
- }
ProxyFactoryBean 脱离 IoC 容器使用
ProxyFactory
说明: 这个类一般是 spring 自己内部使用的, 我们自定义的话很难与容器进行整合, 它一般都是返回的原型模式代理
AspectJProxyFactory
小结:
根据以上案例可以发现 都是首先进行 AdvisedSupport 的准备, 然后交给子类 ProxyCreatorSupport 根据条件
得到 JDK 或者 CGLIB 的 AopProxy, 当代理对象被调用的时候在 invoke 或者 intercept 方法中会调用 ProxyCreatorSupport 的 getInterceptorsAndDynamicInterceptionAdvice 方法去初始化 advice 和各个方法之间的映射关系并缓存
同类方法代理不生效原因?
很多时候会发现代理方法和非代理方法在同一个类中调用不生效和调用顺序有关系, 我们进行重构代码来分析一下原因
- public class AspectJProxyFactoryApplication {
- public static void main(String[] args) {
- AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new AopService());
- // 注意: 此处得 MyAspect 类上面的 @Aspect 注解必不可少
- proxyFactory.addAspect(MyAspect.class);
- //proxyFactory.setProxyTargetClass(true);// 是否需要使用 CGLIB 代理
- AopService proxy = proxyFactory.getProxy();
- proxy.test();
- }
- }
- @Aspect
- public class MyAspect {
- //@Pointcut("execution(* com.github..aop.*.*(..))")
- @Pointcut("execution(* com.github..aop.AopService.hello(..))")
- private void pointcut() {
- }
- @Before("pointcut()")
- public void before() {
- System.out.println("-----------MyAspect#before-----------");
- }
- }
- @Service
- public class AopService {
- public String hello() {
- System.out.println("hello, AopService");
- return "hello, AopService";
- }
- public String test() {
- System.out.println("test");
- return hello();
- }
- }
答案就是不会生效, 究竟是什么引起的呢? 其实就是我上面的小结的最后一个知识点.
这个时候 chain 没有我们的通知器在里面,
最终按照我们的程序执行, 下面进行修改切点表达式, 如果上面的例子看的咨询的话下面就可以忽略了, 主要就是是否增强就是第一个入口函数能否匹配上我们的切点表达式后续的根本不会关心你是否能匹配上.
- @Aspect
- public class MyAspect {
- @Pointcut("execution(* com.github..aop.*.*(..))")
- //@Pointcut("execution(* com.github..aop.AopService.hello(..))")
- private void pointcut() {
- }
- @Before("pointcut()")
- public void before() {
- System.out.println("-----------MyAspect#before-----------");
- }
- }
处理完后就会按照下面代码正常流程执行完
- if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
- return invokeJoinpoint();
- }
如果同一个类的方法调用都想让通知器生效怎么办? 这个就必须要让通知添加到执行链中才行, 根据上面所讲的内容就可以达到这个目的, 然后继续用此代理对象来调用该内嵌方法.
来源: https://www.cnblogs.com/qinzj/p/11405826.html