Spring之AOP

参考文章
https://blog.csdn.net/tenghu8888/article/details/122444743

https://cloud.tencent.com/developer/article/1497770

JDK和CGLIB动态代理:
jdk动态代理实现

@Test
public void test(){
    IPerson target = new ManPerson();
    IPerson proxy = (IPerson)Proxy.newProxyInstance(
        target.getClass().getClassLoader(), 
        target.getClass().getInterfaces(), 
        new PersonInvocationHandler(target));
    proxy.eat();
    proxy.sleep();
}
 
// 代理对象
class PersonInvocationHandler implements InvocationHandler{
    private Object target;
	
    public PersonInvocationHandler(Object target){
        this.target = target;
    }
	
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
        System.out.println("start");
	Object result = method.invoke(target, args);
	System.out.println("end");
		
	return result;
    }
}
 
// 目标对象
class ManPerson implements IPerson{
    @Override
    public void eat(){
	System.out.println("吃饭中......");
    }
	
    @Override
    public void sleep(){
	System.out.println("睡觉中......");
    }
}
 
// 目标对象接口
interface IPerson{
    void eat();
	
    void sleep();
}

cglib动态代理

@Test
public void test(){
    Person proxy = (Person)Enhancer.create(Person.class, new PersonMethodInterceptor());
    proxy.eat();
    proxy.sleep();
}
 
// 代理对象
class PersonMethodInterceptor implements MethodInterceptor{
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable{
        System.out.println("start");
	Object result = methodProxy.invokeSuper(o, objects);
	System.out.println("end");
		
	return result;
    }
}
// 目标对象
public class Person{
    public void eat(){
	System.out.println("吃饭中......");
    }
 
    public void sleep(){
	System.out.println("睡觉中......");
    }
}

JDK和CGLIB动态代理区别:
1、JDK动态代理是实现了被代理对象的接口,Cglib是继承了被代理对象
2、JDK动态代理只能对实现了接口的类生成代理,而不能针对类
3、Cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法进行增强,但是因为采用的是继承,所以该类或方法最好不要声明为final,对于final类或方法,是无法继承的。

Spring如何选择是用JDK还是cglib?
1、当bean实现接口时,会用JDK代理模式

2、当bean没有实现接口,会用cglib实现

3、可以强制使用cglib,@EnableAspectJAutoProxy(proxyTargetClass = true)

AOP使用举例:

@Aspect  //切面类
@Component
public class ControllerHandler {

     //切入点,切入点是匹配连接点的拦截规则
     //@within指向类@executionexecution(* com.threebody.*.*.biz.impl.*BoImpl.*(..)) 指向类中的方法
    @Pointcut("@within(com.threebody.one.Controller) || " +
              "@within(com.threebody.two.Controller))")
    public void controller() { }

    /**
     * 处理数据
     * @param point
     */
    @Around("controller()")
     //JoinPoint连接点,Spring只支持方法类型的连接点,所以在Spring AOP中的一个连接点表示一个方法的执行
    public Object controller(ProceedingJoinPoint point) throws Throwable {

        Object result = point.proceed();

        return result;
    }
}

AOP源码总结:
1.在spring容器refresh时
2.会调用invokeBeanFactoryPostProcessors方法,这个方法会获取实现了BeanFactoryPostProcessors的类,对BeanFactory做增强处理,其中有一个类叫ConfigurationClassPostProcessor
3.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry方法会去调用ConfigurationClassParser.parse方法
4.ConfigurationClassParser.parse方法–》processConfigurationClass(ConfigurationClass configClass)方法–》doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)方法,此方法会去解析启动类中的@PropertySource、@ComponentScan、 @ImportResource、 @Bean、 @Import注解,将这些注解标志的类,注册到beanFactory中

5.ConfigurationClassParser解析@EnableAspectJAutoProxy下的@Import(AspectJAutoProxyRegistrar.class)注解:
AspectJAutoProxyRegistra(自动代理导入器)会去注册一个AnnotationAwareAspectJAutoProxyCreator(自动代理注册器)

6.在bean进行初始化initializeBean时,AnnotationAwareAspectJAutoProxyCreator(自动代理注册器)作为一个BeanPostProcessor,在postProcessAfterInitialization()中时执行了AbstractAutoProxyCreator.wrapIfNecessary();

7.获取当前系统中所有的切面类封装为Advisor,筛选出当前需被代理bean的Advisor集合,根据Advisor集合生成代理类

	@Override
	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

wrapIfNecessary方法


protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    // 判断当前bean是否在TargetSource缓存中存在,如果存在,则直接返回当前bean。这里进行如此判断的
    // 原因是在上文中,我们讲解了如何通过自己声明的TargetSource进行目标bean的封装,在封装之后其实
    // 就已经对封装之后的bean进行了代理,并且添加到了targetSourcedBeans缓存中。因而这里判断得到
    // 当前缓存中已经存在当前bean,则说明该bean已经被代理过,这样就可以直接返回当前bean。
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
        return bean;
    }
 
    // 这里advisedBeans缓存了已经进行了代理的bean,如果缓存中存在,则可以直接返回
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }
 
    // 这里isInfrastructureClass()用于判断当前bean是否为Spring系统自带的bean,自带的bean是
    // 不用进行代理的;shouldSkip()则用于判断当前bean是否应该被略过
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        // 对当前bean进行缓存
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }
 
    // 获取当前bean的Advisor集合
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        // 对当前bean的代理状态进行缓存
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 根据获取到的Advisor集合为当前bean生成代理对象
        Object proxy = createProxy(
            bean.getClass(), beanName, specificInterceptors, 
            new SingletonTargetSource(bean));
        // 缓存生成的代理bean的类型,并且返回生成的代理bean
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }
 
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
		extendAdvisors(eligibleAdvisors);
		if (!eligibleAdvisors.isEmpty()) {
			eligibleAdvisors = sortAdvisors(eligibleAdvisors);
		}
		return eligibleAdvisors;
	}

findEligibleAdvisors方法

@5
 @Bean
    public Advisor txAdviceAdvisor(DataSourceTransactionManager transactionManager) {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
        return new DefaultPointcutAdvisor(pointcut, txAdvice(transactionManager));
    }

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    // 获取当前系统中所有实现了Advisor的类,比如上边@5我们通过@Bean注入的DefaultPointcutAdvisor
    //以及获取系统中所有的引入@Aspect注解的类
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    // 对获取到的所有Advisor进行判断,看其切面定义是否可以应用到当前bean,从而得到最终需要应用的Advisor
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, 
        beanClass, beanName);
    // 提供的hook方法,用于对目标Advisor进行扩展
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
        // 对需要代理的Advisor按照一定的规则进行排序
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}
@Override
	protected List<Advisor> findCandidateAdvisors() {
		  // 获取当前系统中所有实现了Advisor的类,比如上边@5我们通过@Bean注入DefaultPointcutAdvisor
		List<Advisor> advisors = super.findCandidateAdvisors();
		//以及获取系统中所有的引入@Aspect注解的类
		if (this.aspectJAdvisorsBuilder != null) {
			advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
		}
		return advisors;
	}

Spring AOP相关概念:
横切关注点:一些具有横切多个不同软件模块的行为,通过传统的软件开发方法不能够有效地实现模块化一类特殊关注点。横切关注点可以对某些方法进行拦截,拦截后对原方法进行增强处理。
切面(Aspect):切面就是对横切关注点的抽象,这个关注点可以横切多个对象,在代码中用一个类来表示。
连接点(JoinPoint):连接点是在程序执行过程中某个特定的点,比如某个方法调用的时候或处理异常的时候,由于Spring只支持方法类型的连接点,所以在Spring AOP中的一个连接点表示一个方法的执行。
切入点(Pointcut):切入点是匹配连接点的拦截规则,在满足这个切入点的连接点上运行通知。切入点的表达式如何与连接点相匹配是AOP的核心,Spring默认使用AspectJ切入点语法。
通知(Advice):在切面上拦截到某个特定的连接点之后执行的操作
目标对象(Target Object):目标对象,被一个或多个切面所通知的对象,及业务中需要进行增强的业务对象。
织入(Weaving):织入是把切面作用到目标对象,然后产生一个代理对象的过程。
引入(Introduction):引入是用来在运行时给一个类声明额外的方法或属性,即不需为实现一个接口,就能使用接口中的方法。
SpringAOP有以下通知类型:

前置通知[Before advice]:在连接点前面执行,前置通知不会影响连接点的执行,除非此处抛出异常。
正常返回通知[After returning advice]:在连接点正常执行完成后执行,如果连接点抛出异常,则不会执行。
异常返回通知[After throwing advice]:在连接点抛出异常后执行。
返回通知[After (finally) advice]:在连接点执行完成后执行,不管是正常执行完成,还是抛出异常,都会执行返回通知中的内容。
环绕通知[Around advice]:环绕通知围绕在连接点前后,比如一个方法调用的前后。这是最强大的通知类型,能在方法调用前后自定义一些操作。环绕通知还需要负责决定是继续处理join point(调用ProceedingJoinPoint的proceed方法)还是中断执行。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值