Spring(学习笔记)-AOP基础应用配置!

重点标识

代理工厂内部,AOP的使用!

源码分析

我们知道,使用代理工厂,就能直接由工厂来帮我们进行代理,那代理工厂内部又是什么样子的呢,我们来看一下:
ProxyFactory#getProxy(),进入方法内部,我们可以看到如下:

	public Object getProxy() {
		return createAopProxy().getProxy();
	}

createAopProxy,顾名思义,就是创建一个AOP代理,看到这里,那大家就很清楚了,又和前面呼应上了,AOP的底层就是动态代理。

我们进入getProxy()方法中,就到了AopProxy代理的接口,在这里,我们可以看到getProxy()的实现类,如下。

就两个,jdk和Cglib代理方法。
在这里插入图片描述
回到之前的createAopProxy,什么情况下,系统会采用Cglib动态代理,又是什么情况下,使用Jdk动态代理呢,在这个方法给了我们答案。


	@Override
	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
	     //这里大概解释一下,先判断有没有开isOptimize,也就是优化
	     //然后看他是不是一个代理带,最后再看他是不是接口,只要满足其中一条,就会进入这个按断,去走Cglib动态代理,否则直接就是Jdk动态代理了
		if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
			Class<?> targetClass = config.getTargetClass();
			if (targetClass == null) {
				throw new AopConfigException("TargetSource cannot determine target class: " +
						"Either an interface or a target is required for proxy creation.");
			}
			//是不是接口,有没有代理类
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass)) {
				return new JdkDynamicAopProxy(config);
			}
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			return new JdkDynamicAopProxy(config);
		}
	}

好了,上面已经捋顺了AOP底层动态代理的源码,接下来,我们就来看一下,AOP到底是怎么使用的。

AOP的使用

AOP中有五种通知,
前置通知,目标方法执行之前通知。
后置通知,目标方法执行后通知
返回通知,有返回值后通知,这里注意,void也算。
异常通知,出现异常,进行通知。
环绕通知,包含前面四个,看例子理解。

XML配置AOP

和之前一样,准备一个计算接口,一个计算实现类,简单点。

public interface ICalculator {
    int add(int a,int b);
    }
    public class ICalculatorImpl implements ICalculator{
    @Override
    public int add(int a, int b) {
        return a+b;
    }
}

然后,再定义通知类,注解很详细,就不多说了。



public class LogAdvice {


    /**
     * 前置通知
     * @param joinPoint
     */
    public void before(JoinPoint joinPoint){

        System.out.println(joinPoint.getSignature().getName()+"方法开始执行了!");

    }

    /**
     *
     * 后置通知
     * @param joinPoint
     */

    public void after(JoinPoint joinPoint){

        System.out.println(joinPoint.getSignature().getName()+"方法执行结束了!");

    }

    /**
     * 返回通知
     * 知道目标方法的返回值
     * @param joinPoint
     * @param o 目标方法的返回值,这里注意,只有跟目标方法的返回值类型匹配的时候,
     *          才会拿到值,我这里拦截到所有
     */
    public void afterReturning(JoinPoint joinPoint,Object o){

        System.out.println(joinPoint.getSignature().getName()+"方法返回"+o);

    }

    /**
     * 异常通知
     * 抛出异常,会触发
     *
     * @param joinPoint
     * @param o  这里注意,异常参数要对应上
     */
    public void afterThrow(JoinPoint joinPoint,Throwable o){

        System.out.println(joinPoint.getSignature().getName()+"方法执行抛出"+o.getMessage());

    }

    /**
     *
     * 环绕通知
     */
    public Object around(ProceedingJoinPoint pjp){
        //前置通知
        Object proceed;
        try {
            proceed = pjp.proceed();
        } catch (Throwable e) {
            //异常通知
            throw new RuntimeException(e);
        }

        //后置通知
        System.out.println("环绕通知-后置"+pjp.getTarget().toString());
          return proceed;
    }
}

最后,则在xml中进行配置,如下

   <bean class="org.tongzhou.ICalculatorImpl" id="iCalculator"/>

    
        <bean class="org.tongzhou.LogAdvice" id="advice"/>
    <!--        注册切面,切点和通知-->
        <aop:config>
    <!--        id配置切点,切点可以配置多个,expression 表达式配置拦截规则-->
    <!--        int  返回类型 可以用*代表任意类型  地址不指定方法名,则可以用*代表这个类下所有的方法,
                参数不知道的情况下,可以用..代替,表示任意参数-->
            <aop:pointcut id="pc1" expression="execution(int org.tongzhou.ICalculatorImpl.add(int,int))"/>
    <!--       指定通知类-->
            <aop:aspect ref="advice">
                <aop:before method="before" pointcut-ref="pc1"/>
                <aop:after method="after" pointcut-ref="pc1"/>
                <aop:after-returning method="afterReturning" returning="o" pointcut-ref="pc1"/>
                <aop:after-throwing method="afterThrow" throwing="o" pointcut-ref="pc1"/>
                <aop:around method="around" pointcut-ref="pc1"/>
    
            </aop:aspect>
        </aop:config>

调用,看看效果:


    public static void main(String[] args) {
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

        //这里注意,要用ICalculator接口获取,因为我们在xml中配置了aop,实现类就被动态代理了,所以在Spring容器中的就是一个代理类,和前面那篇一样,不大理解的可以对照前面那篇一起看。
        ICalculator bean = classPathXmlApplicationContext.getBean(ICalculator.class);
        bean.add(2,3);
    }

纯Java配置AOP

大概解释一下,@ComponentScan,进行包扫描,将Bean注册到Spring容器中去。


@Configuration
@ComponentScan
public class JavaConfig {
}

@Component
public class ICalculatorImpl implements ICalculator{
    @Override
    public int add(int a, int b) {
        return a+b;
    }
}

//注册到spring容器中
@Component
//切面
@Aspect
//开启动态代理
@EnableAspectJAutoProxy
public class LogAdvisor {

    //顶一个切点,拦截这个类下的所有方法,任意参数,任意类型的返回值
    @Pointcut("execution(* org.tongzhou.ICalculatorImpl.*(..))")
    public void pc1(){

    }
    /**
     * 前置通知
     * @param joinPoint
     */
    @Before("pc1()")
    public void before(JoinPoint joinPoint){

        System.out.println(joinPoint.getSignature().getName()+"方法开始执行了!");

    }

    /**
     *
     * 后置通知
     * @param joinPoint
     */
    @After("pc1()")
    public void after(JoinPoint joinPoint){

        System.out.println(joinPoint.getSignature().getName()+"方法执行结束了!");

    }

    /**
     * 返回通知
     * 知道目标方法的返回值
     * @param joinPoint
     * @param o 目标方法的返回值,这里注意,只有跟目标方法的返回值类型匹配的时候,
     *          才会拿到值,我这里拦截到所有
     */
    @AfterReturning(value = "pc1()",returning = "o")
    public void afterReturning(JoinPoint joinPoint,Object o){

        System.out.println(joinPoint.getSignature().getName()+"方法返回"+o);

    }

    /**
     * 异常通知
     * 抛出异常,会触发
     *
     * @param joinPoint
     * @param o  这里注意,异常参数要对应上
     */
    @AfterThrowing(value = "pc1()",throwing = "o")
    public void afterThrow(JoinPoint joinPoint,Throwable o){

        System.out.println(joinPoint.getSignature().getName()+"方法执行抛出"+o.getMessage());

    }

    /**
     *
     * 环绕通知
     */
    @Around(value = "pc1()")
    public Object around(ProceedingJoinPoint pjp){
        //前置通知
        Object proceed;
        try {
            proceed = pjp.proceed();
        } catch (Throwable e) {
            //异常通知
            throw new RuntimeException(e);
        }

        //后置通知
        System.out.println("环绕通知-后置"+pjp.getTarget().toString());
          return proceed;
    }
}

运行看一下:这里注意,这边使用的是Jdk动态代理,如果想强行让他使用Cglib动态代理,则@EnableAspectJAutoProxy(proxyTargetClass = true)就可以了。

    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(JavaConfig.class);

        ICalculator bean = annotationConfigApplicationContext.getBean(ICalculator.class);
        bean.add(2,3);
    }

XML+Java注解配置AOP

Java配置不动,采用xml进行包扫描,非常不建议,没啥意义,了解下就行。

    <context:component-scan base-package="org.tongzhou"/>

结语

每天努力一点点,就会成长一点点。

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值