Spring AOP的3种创建代理方式

前言

参考文章: Spring AOP创建代理方式:ProxyFactoryBean、ProxyFactory、AspectJProxyFactory(JDK Proxy和CGLB)

Spring AOP是大家都非常熟悉的一个概念,在Spring家族体系中扮演着举足轻重的作用。

然后Spring作为一个优秀的框架,提供了多种应用层面上代理的方式:ProxyFactoryBean、ProxyFactory、AspectJProxyFactory
在这里插入图片描述

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

一、 ProxyFactory

ProxyFactory和Spring容器没啥关系,可以直接创建代理来使用,但是它只能通过代码硬编码进行编写,一般都是spring自己内部使用

案例

public interface HelloService { void hello();}
public class HelloServiceImpl implements  HelloService {
	public void hello() {System.out.println("hello");}
}

开始代理

ProxyFactory proxyFactory = new ProxyFactory(new HelloServiceImpl());

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

输出
在这里插入图片描述
其核心方法是getProxy(),其源码如下

public class ProxyFactory extends ProxyCreatorSupport {
	public Object getProxy() {
			return createAopProxy().getProxy();
	}
}

其核心逻辑是提供父类ProxyCreatorSupport的createAopProxy()方法获取代理工厂,然后通过代理工厂生成代理类

二、ProxyFactoryBean

ProxyFactoryBean将我们的AOP和IOC融合起来,它是个工厂Bean,然后我们可以自定义我们的代理实现逻辑,最终交给Spring容器管理即可

先定义一个前置通知

@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");
	    }
}

代理类

public interface HelloService { void hello();}
@Service //放到容器中
public class HelloServiceImpl implements  HelloService {
	public void hello() {System.out.println("hello");}
}

然后在配置类里,注册一个代理Bean

@Bean
public ProxyFactoryBean proxyFactoryBean(HelloService helloService) {
	   ProxyFactoryBean factoryBean = new ProxyFactoryBean();
	   //从容器中去拿HelloService
	   factoryBean.setTarget(helloService);
	   factoryBean.setInterfaces(HelloService.class);
	   factoryBean.setInterceptorNames("logMethodBeforeAdvice");
	   return factoryBean;
}

main方法测试


public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(RootConfig.class);
        HelloService bean = (HelloService) applicationContext.getBean("proxyFactoryBean");
        bean.hello();
    }

输出:
在这里插入图片描述
由此可见,我们的HelloServiceImpl就被成功代理了。下面我们稍微看看它是怎么创建这个代理的,直接来到getObject()方法:

public class ProxyFactoryBean extends ProxyCreatorSupport implements FactoryBean<Object>, BeanClassLoaderAware, BeanFactoryAware {
		
	
	public Object getObject() throws BeansException {
		//就是根据我们配置的interceptorNames来获取对应的bean,并却转化成Advisor。
		//this.advisorChainInitialized:标示是否已进行过初始化,若以初始化则不再进行初始化。
		initializeAdvisorChain();
	
		//生成代理对象时,因为Spring中有singleton类型和prototype类型这两种不同的Bean,所以要对代理对象的生成做一个区分
		if (isSingleton()) {
			//生成singleton的代理对象,调用的是其父类ProxyCreatorSupport的createAopProxy方法
			return getSingletonInstance();
		}
		else {
			// 生成原型的代理对象,调用的是其父类ProxyCreatorSupport的createAopProxy方法
			return newPrototypeInstance();
		}
	}
	
   private synchronized Object getSingletonInstance() {
		if (this.singletonInstance == null) {
			this.targetSource = freshTargetSource();
			if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
				//依赖AOP基础设施告诉我们要代理的接口。
				Class<?> targetClass = getTargetClass();
				if (targetClass == null) {
					throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
				}
				setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
			}
			// 初始化共享单例实例。
			super.setFrozen(this.freezeProxy);
			//底层还是父类ProxyCreatorSupport的createAopProxy()方法
			this.singletonInstance = getProxy(createAopProxy());
		}
		return this.singletonInstance;
	}
}
三、 AspectJProxyFactory

其实ProxyFactory拥有的功能AspectjProxyFactory都有,它可以使用编程的方式去创建代理

在低版本Spring中定义一个切面是比较麻烦的,需要实现特定的接口,并进行一些较为复杂的配置,低版本Spring AOP的配置是被批评最多的地方。Spring听取这方面的批评声音,并下决心彻底改变这一现状。在Spring2.0中,Spring AOP已经焕然一新,你可以使用@AspectJ注解非常容易的定义一个切面,不需要实现任何的接口

AspectJ是目前大家最常用的 起到集成AspectJ和Spring,也就是我们平时长谈的:自动代理模式。它整个代理的过程全部交给Spring内部去完成,无侵入。

我们只需要配置切面、通知、切点表达式就能自动的实现切入的效果,使用起来极其方便,对调用者可以说是非常透明化的。相信这也是为何当下最流行这种方式的原因~

案例

@Aspect
class MyAspect {

    @Pointcut("execution(* hello(..))")
    private void beforeAdd() {
    }

    @Before("beforeAdd()")
    public void before1() {
        System.out.println("-----------before-----------");
    }

}

如果我们现在需要创建一个代理对象,其需要绑定的Advisor逻辑跟上面定义的切面类中定义的Advisor类似。则我们可以进行如下编程:

    public static void main(String[] args) {
        AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new HelloServiceImpl());
		// 注意:此处得MyAspect类上面的@Aspect注解必不可少
        proxyFactory.addAspect(MyAspect.class);
        //proxyFactory.setProxyTargetClass(true);//是否需要使用CGLIB代理
        HelloService proxy = proxyFactory.getProxy();
        proxy.hello();
        System.out.println(proxy.getClass()); //class com.sun.proxy.$Proxy6
    }

输出

-----------before-----------
this is my method~~
class com.sun.proxy.$Proxy6

这里面很有意思的地方在于:我们只是proxyFactory.addAspect(MyAspect.class);,就自动帮我们完成了方法、通知的绑定工作。

原理

public class AspectJProxyFactory extends ProxyCreatorSupport {
	/** Cache for singleton aspect instances */
	private static final Map<Class<?>, Object> aspectCache = new ConcurrentHashMap<>();
	//基于AspectJ时,创建Spring AOP的Advice  下面详说
	private final AspectJAdvisorFactory aspectFactory = new ReflectiveAspectJAdvisorFactory();

	public AspectJProxyFactory() {
	}
	public AspectJProxyFactory(Object target) {
		Assert.notNull(target, "Target object must not be null");
		setInterfaces(ClassUtils.getAllInterfaces(target));
		setTarget(target);
	}
	public AspectJProxyFactory(Class<?>... interfaces) {
		setInterfaces(interfaces);
	}

	// 这两个addAspect方法是最重要的:我们可以把一个现有的aspectInstance传进去,当然也可以是一个Class(下面)======
	public void addAspect(Object aspectInstance) {
		Class<?> aspectClass = aspectInstance.getClass();
		String aspectName = aspectClass.getName();
		
		AspectMetadata am = createAspectMetadata(aspectClass, aspectName);
		// 显然这种直接传实例进来的,默认就是单例的。不是单例我们就报错了~~~~
		if (am.getAjType().getPerClause().getKind() != PerClauseKind.SINGLETON) {
			throw new IllegalArgumentException(
					"Aspect class [" + aspectClass.getName() + "] does not define a singleton aspect");
		}
		
		// 这个方法就非常的关键了~~~ Singleton...是它MetadataAwareAspectInstanceFactory的子类
		addAdvisorsFromAspectInstanceFactory(
				new SingletonMetadataAwareAspectInstanceFactory(aspectInstance, aspectName));
	}
	public void addAspect(Class<?> aspectClass) {
		String aspectName = aspectClass.getName();
		AspectMetadata am = createAspectMetadata(aspectClass, aspectName);
		MetadataAwareAspectInstanceFactory instanceFactory = createAspectInstanceFactory(am, aspectClass, aspectName);
		addAdvisorsFromAspectInstanceFactory(instanceFactory);
	}

	// 从切面工厂里,把对应切面实例里面的增强器(通知)都获取到~~~
	private void addAdvisorsFromAspectInstanceFactory(MetadataAwareAspectInstanceFactory instanceFactory) {
		// 从切面工厂里,先拿到所有的增强器们~~~
		List<Advisor> advisors = this.aspectFactory.getAdvisors(instanceFactory);
		Class<?> targetClass = getTargetClass();
		Assert.state(targetClass != null, "Unresolvable target class");
		advisors = AopUtils.findAdvisorsThatCanApply(advisors, targetClass);
		AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(advisors);
		AnnotationAwareOrderComparator.sort(advisors);
		addAdvisors(advisors);
	}
}

注意:
需要注意的是在使用AspectjProxyFactory基于切面类创建代理对象时,我们指定的切面类上必须包含@Aspect注解。
虽然我们自己通过编程的方式可以通过AspectjProxyFactory创建基于@Aspect标注的切面类的代理,@EnableAspectJAutoProxy使用基于注解的Aspectj风格的Aop时,Spring内部不是通过AspectjProxyFactory创建的代理对象,而是通过ProxyFactory(这个在分析自动代理源码的时候有说到过~~~~)

总结

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

AspectJProxyFactory,ProxyFactoryBean,ProxyFactory 大体逻辑如下:

1、填充AdvisedSupportProxyCreatorSupport是其子类)

2、交给父类ProxyCreatorSupport处理调用createAopProxy方法得到JDK或者CGLIB的AopProxy代理对象。

3、调用这个代理对象时,会被invoke或者intercept方法拦截 (在JdkDynamicAopProxyCglibAopProxy中), 并且在这两个方法中调用ProxyCreatorSupportgetInterceptorsAndDynamicInterceptionAdvice方法去初始化advice和各个方法直接映射关系并缓存

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值