上篇文章主要介绍了@Profile注解:
可以对不同的开发环境(test,dev),选择性加载不同的组件,如果用AnnocationConfigApplicationContext,需要用无参构造函数,先自己getEnviroment,设置指定加载的test环境,还是dev环境。
IOC目录总结---Spring源码从入门到精通(十六)
Spring源码从入门到精通---@Profile(十五)
这篇文章主要介绍面向切面AOP:
动态代理,面向切面aop是一种开发思想,指在系统运行的情况下,吧代码插入到项目中指定的位子。
一、定义业务逻辑 类 和 切面类
首先必须把这两个类交给ioc容器管理,业务逻辑运行之后,切面类监听到业务逻辑正在运行,切面类可以用@pointCut指定监听的目标类,用切入点表达式,用@Aspect注解表示当前类是一个切面类。
导入aop切面maven包:
代码语言:javascript复制 <!--Aop-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
业务逻辑类:
代码语言:javascript复制
/**
* @author keying
*/
public class MathCalculator {
public int div(Integer i, Integer z) {
//我们可以吧日志写在这里,但这是一种耦合的方式,并不符合aop编程思想
//System.out.println("打印日志!");
System.out.println("div正在计算。。。");
return i / z;
}
}
日志监听类:
从下面代码可以看到,用了
1、@Apect告诉spring这是一个切面类。
2、分别写了四个方法,开始start(注解 @Before),结束end(注解@After),returnSuccess返回成功(注解@AfterReturning),returnException业务异常(注解@AfterThrowing)。可以在每个方法上单独标注切入的目标。下面代码用@PointCut注解指定了一个公用的方法。
3、JoinPoint可以获取到切面目标的方法名 和 参数名等信息。
代码语言:javascript复制
/**
* @author keying
* @Aspect:告诉springIOC,这是一个切面类
*/
@Aspect
public class LogAspects {
/**
* 抽取公共切入点表达式
*/
@Pointcut("execution(public int com.alibaba.aop.MathCalculator.*(..))")
public void pointCut() {
}
//业务方法之前切入
//@Before("public int com.alibaba.aop.MathCalculator.div(Integer,Integer)")
//@Before("public int com.alibaba.aop.MathCalculator.*(..)")
@Before("pointCut()")
public void start(JoinPoint joinPoint) {
//获取切面目标方法名 和 参数列表
Object[] objects = joinPoint.getArgs();
System.out.println(joinPoint.getSignature().getName() "计算开始 ---{" Arrays.asList(objects) "}");
}
//@After("public int com.alibaba.aop.MathCalculator.*(..)")
@After("com.alibaba.aop.LogAspects.pointCut()")
public void end() {
System.out.println("计算结束 ---{}");
}
/**
* 定义aop返回成功,用object接收返回成功参数
* 重点,重点,重点:joinPoint必须写在参数第一位,否则spring无法解析
*/
@AfterReturning(value = "pointCut()", returning = "object")
public void returnSuccess(JoinPoint joinPoint, Object object) {
System.out.println(joinPoint.getSignature().getName() "计算返回成功 ---{" object "}");
}
@AfterThrowing(value = "pointCut()", throwing = "exception")
public void returnException(Exception exception) {
System.out.println("计算异常 ---{" exception "}");
}
}
aop配置类:
下面用@Bean和@Import方法吧业务逻辑类 和 切面类交给springioc容器管理。@EnableAspectAutoProxy开启aop模式代理。
代码语言:javascript复制
/**
* AOP:动态代理。(编程思想)
* 在程序运行时候,动态的将某段代码插入项目中指定位子的编程方式。
*
* 测试思想:
* 定义一个业务逻辑类,在程序运行的时候,将他的日志打印(运行之前,运行之后,运行正常返回,计算异常等等)。
* 定义一个日志切面类,动态的感知业务逻辑运行到哪里。
* 通知方法:
* 1)、前置通知(@Before):start,目标业务运行之前运行
* 2)、后置通知(@After):end,目标业务运行结束之后运行。(无论正常结束还是异常结束)
* 3)、返回通知(@AfterReturning):returnSuccess,目标业务运行成功
* 4)、异常通知(@AfterThrowing):returnException,目标业务运行异常
* 5)、环绕通知(@Around):动态代理,手动推进目标业务运行,joinPoint。
*
* @EnableAspectAutoProxy开启基于注解的aop模式代理
*
* @author keying
*/
@EnableAspectJAutoProxy
@Configuration
@Import({LogAspects.class})
public class MyConfigAop {
@Bean(value = "mathCalculator")
public MathCalculator mathCalculator(){
return new MathCalculator();
}
/* @Bean(value = "logAspects")
public LogAspects logAspects(){
return new LogAspects();
}*/
}
junitTest:加载类改为aop配置类,从ioc容器中获取对象测试,不要自己创建对象测试,否则aop切面不生效。
代码语言:javascript复制
/**
* @author keying
*
* 自己实现AOP主要分为三步:
* 1、自定义业务逻辑类 和 aop切面类,aop类加上@Aspect切面注解。
* 2、给aop切面类每个方法加上注解,告知何时运行,切入点表达式@PointCut
* 3、在配置类注解开启aop模式:@EnableAsceptAutoProxy
*/
public class AOPTestPointCut {
@Test
public void test() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(
MyConfigAop.class);
//这种自己创建的对象不会触发aop切面,需要从ioc容器中获取
/* MathCalculator mathCalculator = new MathCalculator();
System.out.println(mathCalculator.div(1, 2));*/
MathCalculator mathCalculator = applicationContext.getBean(MathCalculator.class);
System.out.println("IOC容器获取后计算:" mathCalculator.div(1, 2));
applicationContext.close();
}
}
运行之后可以看到,切面日志 在div运行之前有运行,在计算结束之后会返回成功,并且目标类的方法名称 、 参数 、返回值 都可以获取到。