Java、JDBC、Spring IOC快速入门 、 Spring IOC 注解方式注入
面向切口编程(Aspect Oriented Programming),AOP是OOP的延续,是Spring框架的一个重要内容。
AOP利用称为"横切"的技术,剖解开封装的对象内部,把多个类的公共行为封装到一个可重用模块中,便于减少重复代码,降低模块之间的耦合度,AOP符合开闭原则,提高了代码的可拓展性。
通知(advice)
在已存在的业务方法的前、后、异常、最终处加入的方法。
切面(aspect)
通知advice
方法所在的类,一个 aspect
类中可以有多个通知方法。
连接点(joinPoint)
已存在的业务方法。
切入点(pointCut)
joinPoint
的集合。(为要添加advice
的方法划定范围。)
目标对象(target)``
joinPoint
方法所在的对象。
织入(Weave)``
把advice
方法放在joinPoint
的前、后、异常、最终处。(只有织入后,advice
才会有效。)
ps.温馨提示:你可以先看完Getting Started,再回来理解这些。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.aop">context:component-scan>
<aop:aspectj-autoproxy>aop:aspectj-autoproxy>
beans>
一大堆都是标签头,这里只启动了两个注解而已。
public interface IService {
String update(String msg);
}
业务方法update就是连接点 joinPoint
,我们要在它的前后插入其他的方法(通知 advice
)。
@Service
public class IServiceImpl implements IService {
@Override
public String update(int uid, String msg) {
System.out.println("serviceimpl运行了");
return "更新了数据:"+msg;
}
}
Componen
纳入容器
Aspect
配置为切面类
@Component
@Aspect
public class DemoAspect {
//配置切入点
@Pointcut("execution(* com.aop.service.*.*(..))")
public void myPointCut() {}
//配置前置通知
@Before("myPointCut()")
public void myBefore() {
System.out.println("——————前置通知————");
}
此处Pointcut注解有两个参数,result
是返回值的类型 , range
是指定范围来配置通知的类,两个参数用空格隔开。
@Pointcut("execution("result range")
对照
在演示代码中,我是用的 * com.aop.service.*.*(..))
,意义如下。
@Pointcut("execution(任意返回值 com包.aop包.service包.所有类.所有方法(任意参数))")
其中返回值、包、类、方法的通配符为 *
参数的通配符为 ..
此处Before有一个注解,是指定此通知的切入点。
即:myBefore()
通知的切入点为myPointCut()
@Before("myPointCut()")
public void myBefore() {
public class Test {
public static void main(String[] args) {
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
IService ser=ac.getBean(IService.class);
//update方法只是返回了这段字符串,所以并不会打印。
ser.update("测试字符串");
运行结果
service包下的所有类 的所有方法,都会被添加上此前置通知。
可以通过修改Pointcut的参数来选定范围。
例:只有IServiceImpl类的方法添加通知。
@Pointcut("* com.aop.service.IServiceImpl.*(..))")
只要连接点正常返回,就会执行返回通知。
@AfterReturning(pointcut="myPointCut()",returning="obj")
public void myAfterReturning(JoinPoint point,Object obj) {
System.out.println("——————返回通知————");
//获取连接点方法传入的实参
Object[] args=point.getArgs();
//获取连接点方法的方法名
String methodName=point.getSignature().getName();
//获取连接点方法所在的对象
Object targetObj=point.getTarget();
String targetClassName=point.getClass().getName();
}
通过JoinPoint
对象 可以获取连接点接收的实参,连接点的方法名,连接点所在的对象。
此处AfterReturning注解有两个参数,pointcut
切入点 , returning
为连接点的返回值,命名须与myAfterReturning()
方法的Object参数一致。
@AfterReturning(pointcut="myPointCut()",returning="objeeeee")
//注意那串eeeee了吗,那是为了引起你的注意,告诉你这两个命名须一致。
public void myAfterReturning(JoinPoint point,Object objeeeee) {
可以得到连接点信息和异常信息。
@AfterThrowing(pointcut="myPointCut()",throwing="eeeee")
public void myAfterThrowing(JoinPoint point,Exception eeeee) {
//注意那串eeeee了吗,那是为了引起你的注意,告诉你这两个命名须一致。
System.out.println("——————异常通知————"+eeeee.getMessage());
}
此处AfterThrowing注解有两个参数,pointcut
切入点 , throwing
为连接点的返回值,命名须与AfterThrowing()
方法的Exception 参数一致。
执行顺序在异常通知和后置通知 之前,得不到返回值和异常信息。
@After("myPointCut()")
public void myAfter(JoinPoint point) {
System.out.println("——————最终通知————"+point.getSignature().getName());
}
运行结果
这个顺序很迷,最终通知在返回通知的前面,等博主有空学习了Spring的源码再回来填坑。
@Around
环绕通知就是,包括以上以上所有通知。
环绕通知很强大,但不常用,通常我们只需要用到其中一两项通知。
环绕通知的返回值必须是Object
,形参必须是ProceedingJoingPoint
。
@Component
@Aspect
public class DemoAspect {
//配置切入点
@Pointcut("execution(* com.aop.service.*.*(..))")
public void myPointCut() {}
//环绕通知
@Around("myPointCut()")
public Object aroundAdvice(ProceedingJoinPoint proceeding) {
//和JoinPoint一样,ProceedingJoinPoint也可以获取
//连接点方法的实参
Object[] args=proceeding.getArgs();
//连接点方法的方法名
String methodName=proceeding.getSignature().getName();
//连接点方法所在的对象
Object targetObj=proceeding.getTarget();
String targetClassName=targetObj.getClass().getName();
Object result=null;
try {
System.out.println("前置通知====");
//执行连接点的方法 获取返回值
result=proceeding.proceed(args);
System.out.println("返回通知====");
}catch (Throwable e) {
System.out.println("异常通知===");
}finally {
System.out.println("最终通知===");
}
return result;
}