什么是AOP?
基本概念
切面(aspect):横切关注点被模块化的特殊对象。
通知(advice):切面必需要完成的工做。切面中的每一个方向称之为通知。通知是在切面对象中的。
目标(target):被通知的对象。
代理(proxy):向目标对象应用通知后建立的对象。spring
链接点(joinpoint):目标对象的程序执行的某个特定位置。如某个方法调用前,调用后的位置。包括两个信息:1.目标程序的哪一个方法?2.方法执行
前仍是执行后?
切点(pointcut):每一个类会有多个链接点,AOP经过切点定位到特定的边接点。
类比,链接点至关于数所成方圆 中的记录,而切点至关于查询条件。一个切点匹配多个链接点。express
1.导入 spring包和aspectj的两个jar包编程
2.目标对象(要切入的对象)ide
首先为了避免违反开闭原则和更好的可扩展性,目标对象其实是实现了已定义好的某个接口模块化
接口:函数
package com.itnba.test; public interface IHuman { public void chifan(); public void shuijiao(); }
实现接口的两个类:ui
package com.itnba.test; import org.springframework.stereotype.Component; public class Chinese implements IHuman { @Override public void chifan() { // TODO 自动生成的方法存根 System.out.println("中国人吃饭"); } @Override public void shuijiao() { // TODO 自动生成的方法存根 System.out.println("中国人睡觉"); } }
package com.itnba.test; import org.springframework.stereotype.Component; public class American implements IHuman { @Override public void chifan() { // TODO 自动生成的方法存根 System.out.println("美国人吃饭"); } @Override public void shuijiao() { // TODO 自动生成的方法存根 System.out.println("美国人睡觉"); } }
3.定义一个切面类spa
package com.itnba.test; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; public class Qiemian { public void chifanqian(){ System.out.println("洗手"); } public void chifanhou(){ System.out.println("漱口"); } public void shuijiaoqian(){ System.out.println("洗澡"); } }
4.XML文件配置代理
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd" default-autowire="byName" > <!-- 首相要实例化目标对象类和切面类 --> <bean id="chinese" class="com.itnba.test.Chinese"></bean> <bean id="american" class="com.itnba.test.American"></bean> <bean id="qiemian" class="com.itnba.test.Qiemian"></bean> <aop:config> <!-- 要切入的对象 --> <aop:pointcut expression="execution(* com.itnba.test.*.chifan(..))" id="chifan"/> <aop:pointcut expression="execution(* com.itnba.test.*.shijiao(..))" id="shijiao"/> <!-- 切入点 --> <aop:aspect id="ha" ref="qiemian"><!-- 切面类 --> <!-- <aop:以前before、以后after... method="切面类中的方法" pointcut-ref="上面的切入的对象"/> --> <aop:before method="chifanqian()" pointcut-ref="chifan"/><!-- 以前通知 --> <aop:after method="chifanhou()()" pointcut-ref="chifan"/><!-- 以后通知 --> <aop:before method="shuijiaoqian()" pointcut-ref="shijiao"/> </aop:aspect> </aop:config> </beans>
5.运行日志
package com.itnba.test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); //要用接口接收 IHuman c= (IHuman)context.getBean("chinese"); c.chifan(); c.shuijiao(); System.out.println("*********************************"); //要用接口接收 IHuman a= (IHuman) context.getBean("american"); a.chifan(); a.shuijiao(); } }
结果:
1.导如 spring包和aspectj的两个jar包
2.配置文件中加入aop命名空间
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd" > <!-- 自动扫描包下的类,并将其实例化。多个包之间用,隔开 --> <context:component-scan base-package="com.itnba.test"></context:component-scan> <!-- 配置文件中启动AspectJ的注解功能 ,默认是false,要将其改成true --> <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy> </beans>
3.目标对象(要切入的对象)
首先为了避免违反开闭原则和更好的可扩展性,目标对象其实是实现了已定义好的某个接口
接口:
package com.itnba.test; public interface IHuman { public void chifan(); public void shuijiao(); }
实现接口的两个类:
package com.itnba.test; import org.springframework.stereotype.Component; @Component public class Chinese implements IHuman { @Override public void chifan() { // TODO 自动生成的方法存根 System.out.println("中国人吃饭"); } @Override public void shuijiao() { // TODO 自动生成的方法存根 System.out.println("中国人睡觉"); } }
package com.itnba.test; import org.springframework.stereotype.Component; @Component public class American implements IHuman { @Override public void chifan() { // TODO 自动生成的方法存根 System.out.println("美国人吃饭"); } @Override public void shuijiao() { // TODO 自动生成的方法存根 System.out.println("美国人睡觉"); } }
4.定义一个切面类,直接在里面注解
package com.itnba.test; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Aspect //声明该类是切面类 @Component//配置文件中启动自动扫描功能 public class Qiemian { @Before("execution(* com.itnba.test.*.chifan(..))")//在那个方法的以前通知 *表明所有..表明全部形参,无论有多少 public void chifanqian(){ System.out.println("洗手"); } @After("execution(* com.itnba.test.*.chifan(..))")//在那个方法以后通知 public void chifanhou(){ System.out.println("漱口"); } @Before("execution(* com.itnba.test.*.shuijiao(..))") public void shuijiaoqian(){ System.out.println("洗澡"); } }
5.写好后用main函数来运行一下
package com.itnba.test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); Chinese c= context.getBean(Chinese.class); c.chifan(); c.shuijiao(); System.out.println("*********************************"); American a= (American) context.getBean("american"); a.chifan(); a.shuijiao(); } }
结果:
这样,Spring的面向切面编程就简单介绍完了。
通知分类:
前置通知(@Before) -- 在目标方法执行前执行的通知。
后置通知(@After) -- 在目标方法执行后执行的通知。不管是否发生异常。后置通知中,没法读取目标方法返回的结果。
返回通知(@AfterReturnning) --在目标方法执行成功后执行的通知。在返回通知中能够访问目标返回结果的。
@AfterReturnning(value="execution(* com.itnba..*(..))",returning="result")
public void afterRun(Object result){
System.out.println(result);
}
异常通知(@AfterThrowing) -- 在目标方法执行出错后执行的通知。
@AfterThrowing(value="execution(* com.itnba..*(..))",throwing="ex")
public void afterThrowing(Exception ex){
System.out.println(ex.getMessage());
}
环绕通知(@Around) -- 须要切面方法携带ProceedingJoinPoion参数,相似于一个完整的动态代理,环绕通知必需要有一个返回值,是目标方法的返
回值。
@Around("execution(* com.itnba..*(..))")
public object aroundAdvice( ProceedingJoinPoint pjp){
object obj = null;
try{
//作前置通知
obj = pjp.proceed();
//作返回通知
}
catch(Exception ex){
//作异常通知
}
//作后置通知
return obj;
}
添加日志:
切面方法能够加入参数(JoinPoint) joinPost.getSignature().getXXX()得到相关方法信息切面方法中能够加入Object参数,用来得到目标方法的返回值(只对返回通知起做用)