我们拿生活中的例子来让大家便于理解
明星 <<------ 经纪人(代理)<<----------大老板
现实世界中,如果一个大老板需要找明星代言或者表演是不会直接找到明星本人的,一般都是直接找明星的经纪人,让他再去找明星商谈代言和表演的相关事项。在这个模式中,真正表演和代言的是明星本人,而经纪人只是一个传达消息或者说帮明星处理一些杂事的人。
-
AOP面向切面编程 Aspect Oriented Programming (功能: 让关注点代码与业务代码分离)
面向切面编程就是对许多功能都重复的代码进行抽取,再在运行的时候向业务方法上动态植入“切面类代码” -
关注点:
重复的代码形成的方法就是关注点 -
切面:
由关注点形成的类就叫切面。
面向切面编程就是将代码中重复的部分进行抽取,再在运行的时候向业务方法上动态植入“切面类代码” -
切入点:
执行目标代码,动态植入切面代码
可以通过切入点表达式,指定拦截某些类的某些方法,给指定的类在运行的时候植入切面类代码 -
AOP与代理模式的联系:
既然AOP是为了完成重复代码的抽取,让开发人员更加关注于业务代码的编写,所以我们可以将业务代码放在一个类里面,然后将重复功能代码通过建立对象的方式执行。 -
AOP的使用: 权限认证、日志、事物
-
AOP代理选择特点:
1.在目标对象实现接口的情况下会默认采用JDK代理(动态代理)
2.在目标对象没有实现接口的情况下采用cglib代理 -
注解AOP的实现不单靠Spring-aop.jar还必须包含aopalliane.jar 和aspectweaver.jar
-
实现
我们拿明星开演唱会举例子
//明星类
@Component("Celebrity")
public class Celebrity implements ICelebrity {
/*
* (non-Javadoc)
*
* @see com.ncs.entity.ICelebrity#show()
*/
@Override
public void show() {
System.out.println("开始表演");
System.out.println("发送意外"+1/0);
}
}
//明星表演的接口类
public interface ICelebrity {
void show();
}
//切面类(重复代码组成的类)
@Aspect
@Component
public class CelebrityAspect {
//指定切入带你表达式
@Pointcut("execution(* com.ncs.entity.Celebrity.*(..))")
public void pointCut() {
}
//前置通知 在目标对象方法执行前执行
@Before("pointCut()")
public void beginTx() {
System.out.println("演出准备 @Before ");
}
//后置通知 在目标对象方法执行后执行 (无论目标方法是否出现异常都执行)
@After("execution(* com.ncs.entity.Celebrity.*(..))")
public void endTx() {
System.out.println("演出结束 @After ");
}
//异常通知 在目标对象方法出现异常时执行
@AfterThrowing("execution(* com.ncs.entity.Celebrity.*(..))")
public void occurAccident() {
System.out.println("拯救演出 @AfterThrowing");
}
//返回后通知 在目标方法执行结束尾执行 (假如目标方法出现异常,此方法不执行)
@AfterReturning("execution(* com.ncs.entity.Celebrity.*(..))")
public void leaveTx(){
System.out.println("演出散场 @AfterReturning");
}
//环绕通知 //环绕目标方法执行 (无论目标方法是否出现异常都执行)
@Around("execution(* com.ncs.entity.Celebrity.*(..))")
public void musicAround(ProceedingJoinPoint pJoinPoint) throws Throwable {
System.out.println("演出开始前 音乐起 @Around");
pJoinPoint.proceed();
System.out.println("演出开始后 音乐停 @Around");
}
}
//老板测试类
public class Boss {
@Test
public void test() {
//加载IOC配置文件
ApplicationContext context =new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
//获取目标对象
ICelebrity celebrity = (ICelebrity) context.getBean("Celebrity");
celebrity.show();
}
}
<!--别忘了要使用相关AOP注解需要开启AOP注解配置 -->
<?xml version="1.0" encoding="GBK"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd ">
<!--开启注解bean扫描 -->
<context:component-scan base-package="com.ncs"></context:component-scan>
<!--开启AOP注解配置 -->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
</beans>
结果:
//目标对象 方法里面的System.out.println("发送意外"+1/0);未注释时,控制台结果:
演出开始前 音乐起 @Around
演出准备 @Before
开始表演
演出结束 @After
拯救演出 @AfterThrowing
//目标对象 方法里面的System.out.println("发送意外"+1/0);注释时,控制台结果:
演出开始前 音乐起 @Around
演出准备 @Before
开始表演
演出开始后 音乐停 @Around
演出结束 @After
演出散场 @AfterReturning
总结:
注解AOP的实现不单靠Spring-aop.jar还必须包含aopalliane.jar 和aspectweaver.jar
@Pointcut("execution(* com.ncs.entity.Celebrity.*(..))") //指定切入带你表达式
@Before("pointCut()") //前置通知 在目标对象方法执行前执行
@After("execution(* com.ncs.entity.Celebrity.*(..))") //后置通知 在目标对象方法执行后执行 (无论目标方法是否出现异常都执行)
@AfterThrowing("execution(* com.ncs.entity.Celebrity.*(..))") //异常通知 在目标对象方法出现异常时执行
@AfterReturning("execution(* com.ncs.entity.Celebrity.*(..))") //返回后通知 在目标方法执行结束尾执行 (假如目标方法出现异常,此方法不执行)
@Around("execution(* com.ncs.entity.Celebrity.*(..))") //环绕通知 环绕目标方法执行 (无论目标方法是否出现异常都执行)
或者采用xml配置实现AOP:
<!--AOP手动xml配置 -->
<!--在IOC容器里面手动注入切面类对象 -->
<bean id="aopAspect" class="com.ncs.aspect.CelebrityAspect"></bean>
<aop:config>
<!--定义一个切入点表达式,拦截指定方法 -->
<aop:pointcut expression="execution(* com.ncs.entity.Celebrity.*(..))" id="pt"></aop:pointcut>
<!--指定切面类 -->
<aop:aspect ref="aopAspect">
<!--前置通知 -->
<aop:before method="beginTx" pointcut-ref="pt"/>
<!--后置通知 -->
<aop:after method="endTx" pointcut-ref="pt"/>
<!--异常通知 -->
<aop:after-throwing method="occurAccident" pointcut-ref="pt"/>
<!-- 返回后通知 -->
<aop:after-returning method="leaveTx" pointcut-ref="pt"/>
<!--环绕通知 -->
<aop:around method="musicAround" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>