spring的使用- aop

spring的aop编程:

1.OOP和AOP

       OOP:面向对象编程,主要关注的是一个类有哪些属性,要实现哪些功能。将这些属性和功能进行封装。

       AOP(Aspect Oriented Programming):面向切面编程:关注的是不同类的重复的代码,将重复代码提取,在需要的时候插入进去,实现功能增强

 

       OOP( 面向对象编程)针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。

    AOP 则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。这两种设计思想在目标上有着本质的差异。

      

spring整合junit测试:

       Spring整合junit4可以方便我们的测试。

       1. 需要导入一个spring-test-4.2.4.RELEASE.jar包

       2. 在测试类上配置:

              @RunWith(SpringJUnit4ClassRunner.class)      //spring整合junit

              @ContextConfiguration(locations = "classpath:applicationContext.xml")     //指定配置文件的位置

              public class AnnotationTest {

 

                     @Autowired

                     private IUserService userService; //将工厂中的对象注入到变量中

 

                     @Test

                     public void test2() {

                             userService.add();

                     }

              }

 

2.AOP专业名词:

       目标对象target: 指的是需要被增强的对象,由于spring aop是通过代理模式实现,从而这个对象永远是被代理对象。

       连接点(join point):所谓连接点是指那些被拦截到的点,在spring中这些点指的是方法,因为spring只支持方法类型的连接点

       切入点(pointcut):      表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方,简单说切入点是指我们要对哪些连接点进行拦截的定义

       通知(advice):             所谓通知是指拦截到连接点之后所要做的事情就是通知,通知分为前置通知,后置通知,异常通知,最终通知,环绕通知,Advice 定义了在 pointcut 里面定义的程序点具体要做的操作

       引介introduction:     引介是一种特殊的通知,在不修改类代码的前提下,introduction可以在运行期为类动态地添加一些方法或属性

       切面aspect:              是切入点和通知的结合

       织入weaving:           是将切面应用到目标对象从而创建出AOP代理对象的过程,织入可以在编译期,类装载期,运行期进行。Spring采用动态织入,而aspectj采用静态织入

       代理Proxy:               一个类被AOP织入增强后,就产生一个结果代理类

 

3.AOP底层实现介绍

       分为静态AOP和动态AOP

              静态AOP:是指AspectJ实现的AOP,他是将切面代码直接编译到Java类文件中。(编译时)

              动态AOP:是指将切面代码进行动态织入实现的AOP(运行时)

 

       spring 采用动态AOP,实现的技术为:

       JDK提供的动态代理技术 和 CGLIB(动态字节码增强技术)

4动态代理的实现

       a.JDK动态代理:只针对于接口操作,采用jdk proxy对象实现,获取到的代理对象与目标对象实现了相同的接口

      

       b.CGLIB:可以为没有实现接口的类去做代理  //代理对象本质上是被代理对象的子类

              1. spring框架的spring-core.jar包中已经集成了cglib与asm,不需要导入jar包

              2.代码:

                     private Object target;          //定义成员变量,存放目标对象

 

                     public CGLIBProxy(Object target){     //定义构造方法,传入目标对象

                            this.target=target;

                     }

                     public Object createProxy(){ //定义获取代理对象的方法

                           

                            Enhancer enhancer=new Enhancer(); //创建增强对象

                           

                            enhancer.setSuperclass(target.getClass()); //设置目标对象的字节码对象

                           

                            enhancer.setCallback(new MethodInterceptor() {     //设置回调方法

                                   @Override

                                   public Object intercept(Object arg0, Method method, Object[] arg2,

                                                 MethodProxy arg3) throws Throwable {

                                         

                                          System.out.println("cglibproxy");       //通知,增强的代码

                                          return method.invoke(target, arg2);          //调用目标对象

                                         

                                   }

                            });

                            return enhancer.create();            //返回代理对象

                     }

      

       问题:spring采用的是哪一种动态机制:

              如果目标对象有接口,优先使用jdk动态代理

              如果目标对象无接口,使用cglib动态代理。

       cglib代理:目标对象和代理对象是父子关系(代理对象为被代理对象的子类)

       jdk代理:  目标对象和代理对象是兄弟关系

       注意:

              配置<aop:config proxy-target-class="true">  后始终使用cglib代理

 

 

4.AOP开发

       必须导入AOP联盟包:com.springsource.org.aopalliance-1.0.0.jar

       在传统的spring aop开发中它支持增强(advice)有五种:

              1.    前置通知 : 目标方法执行前增强  org.springframework.aop.MethodBeforeAdvice

              2.    后置通知:  目标方法执行后增强  org.springframework.aop.AfterReturningAdvice

              3.    环绕通知:  目标方法执行前后进行增 org.aopalliance.intercept.MethodInterceptor

              4.    异常抛出通知: 目标方法抛出异常后的增 org.springframework.aop.ThrowsAdvice

              5.    引介通知: 在目标类中添加一些新的方法或属性(不讲解)

       AOP开发步骤:

              1. 定义目标

              2. 定义通知

              3. 定义切点

              4. 定义切面(通知+切点)

              5. 生成代理

 

5.基于AspectJ切点表达式的传统aop开发--半自动aop

       需要导入jar包com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

       定义目标

              public class OrderServiceImpl implements IOrderService {

       定义通知:

              public class OrderHelper implements MethodBeforeAdvice, AfterReturningAdvice, MethodInterceptor {    //不同的接口对应不同的通知类型

       xml配置文件:    //注意要引入aop名称空间

              <bean id="orderService" class="cn.itheima.aop.OrderServiceImpl"></bean>    //定义目标

              <bean id="orderServiceAdvice" class="cn.itheima.aop.OrderHelper"></bean> //定义通知

             

              使用<aop:xxx>标签来完成切面与切点声明:

              <aop:config>

                     <aop:pointcut id="orderServicePointCut" expression="execution(* cn.itheima.aop.IOrderService.*(..))"/> //定义切点

                     <aop:advisor advice-ref="orderServiceAdvice" pointcut-ref="orderServicePointCut" />          //定义切面,通知的类型,由通知类实现的接口决定

              </aop:config>

 

       切点表达式:返回值 包名 类名 方法名(..)

       1.    execution(public * *()) 所有的public的方法

       2.    execution(* cn.itheima.aop..*(..)) 所有的aop包及其子包下的所有类的方法

       3.    execution(* cn.itheima.aop.IOrderService.*(..)) IOrderService接口中定义的所有方法

       4.    execution(* cn.itheima.aop.IOrderService+.*(..)) 匹配实现特定接口所有类的方法

       5.    execution(* save*(..)) 区配所有的以save开头的方法

      

       缺点:

              通知类需要实现相应的接口,在其要实现的方法中定义不同的通知方法:

                     org.springframework.aop.MethodBeforeAdvice;            //前置通知

                     org.springframework.aop.AfterReturningAdvice;     //后置通知

                     org.aopalliance.intercept.MethodInterceptor;  //环绕通知

 

              所有的通知方法必须同时去增强一个目标方法

 

      

6全自动AOP--xml方式

       AspectJ框架它定义的通知类型有6种:

              1.    前置通知Before 相当于BeforeAdvice

              2.    后置通知AfterReturning 相当于AfterReturningAdvice

              3.    环绕通知 Around 相当于MethodInterceptor

              4.    抛出通知AfterThrowing 相当于ThrowAdvice

              5.    引介通知DeclareParents 相当于IntroductionInterceptor

              6.    最终通知After 不管是否异常,该通知都会执行

              相比spring 的传统AOP Advice多了一个最终通知

       定义目标,定义通知与半自动方式相同

       <aop:config> //定义切面

              <aop:aspect ref="userServiceAdvice">     //通知

                     <aop:pointcut expression="execution(* *..UserService Impl.add(..))" id="pointCut1"/> //切点,这个切点可以定义在aspect标签外,切点定义在标签外后,可以被所有的通知共

                     <aop:before method="before" pointcut-ref="pointCut1"/>  //指定通知类型,通知方法,引用切点

                     <aop:after-returning method="afterReturning" pointcut-ref="pointCut1"/>       //后置通知

                     <aop:around method="around" pointcut-ref="pointCut1"/> //环绕通知

                     <aop:after-throwing method="afterThrowing" pointcut-ref="pointCut1"/>       //异常通知,抛出异常才会执行

                     <aop:after method="after" pointcut-ref="pointCut1"/> //最终通知,无论是否出现异常,都会执行

              </aop:aspect>

             

              <aop:aspect ref="userServiceAdvice2">   //每一个通知的bean对应一个切面

                     。。。

              </aop:aspect>

       </aop:config>

       注意上面的标签中

      

       全自动aop和半自动aop定义切面的区别:

              半自动中与切点结合的通知为整个通知类,即通知类中所有的方法会同时作用在切点上

              全自动aop的通知为通知类中的方法,即可以指定某个方法作用在某个切点上,更加灵活

             

 

7.通知上的参数介绍:

       1.    通知上可以添加JoinPoint参数,通过它可以获取目标相关的信息

              public void before(JoinPoint jp) {       //JoinPoint对象可用于获取被增强对象的信息

                     System.out.println("拦截的目标类:" +jp.getSignature().getDeclaringTypeName());       //通过参数jp可以获取目标类的名称      signature:署名;签名;信号

                     System.out.println("拦截的方法名称:" + jp.getSignature().getName()); //通过参数jp可以获取目标方法的名称 

 

       2. 后置通知获取目标方法返回值

              public void afterReturning(JoinPoint jp,Object val) { //也可以传入joinpoint,同前置通知中的joinpoint

                     Object[] args = jp.getArgs(); //获取目标方法的参数列表

                     System.out.println("目标方法返回值:" + val);

                     System.out.println("后置通知");

              }

 

              注意:需要配置returning="val" 和参数对应

                     <aop:after-returning method="afterReturning" pointcut-ref="pointCut1" returning="val"/>

                    

       3. 环绕通知必须传入连接点参数:ProceedingJoinPoint             //环绕通知可以实现其他所有的通知

              public Object around(ProceedingJoinPoint pjp) throws Throwable {

 

                     System.out.println("环绕前");

                     Object result = pjp.proceed();     //此处调用执行被增强的方法

                     System.out.println("环绕后");

 

                     return result;

              }

 

       3.    异常通知获取异常信息

              public void afterThrowing(JoinPoint jp,Throwable ex) {   //也可以传入joinpoint,同前置通知中的joinpoint

                     System.out.println("发现了异常。。。。"+ex);

              }

 

              注意:需要配置throwing="ex" 和参数对应

              <aop:after-throwing method="afterThrowing" pointcut-ref="pointCut1" throwing="ex"/>

 

8.注解方式实现代理:

       第一步:定义目标类,通知类

      

       第二步:开启注解扫描,把通知和目标纳入Spring的管理当中,并配置aspectj自动代理aspectj-autoproxy:

              a. 开启注解扫描

                     <context:component-scan base-package="cn.itheima"/>

                    

              b. 将通知和目标类加入spring管理 //配置@Component

             

              c.配置aspectj自动代理

                     <aop:aspectj-autoproxy proxy-target-class="true"/>           //proxy-target-class默认为false,如果设置为true,表示始终采用cglib代理

 

       第三步:定义切面

              a. 在通知的类上添加注解@aspect,声明这个类为切面

              b. 根据不同的通知,在不同的方法上添加注解并定义切点表达式

                     声明切面       //在类上

                            @Aspect

                            public class CustomerServiceHelper {

 

                     前置通知

                            @Before("execution(* *.save(..))") //括号中传入切点表达式

                            public void before() {

                                      System.out.println("前置通知...");

                            }

                     后置通知

                            @AfterReturning(value = "execution(* *.update(..))", returning = "value")       //声明接收参数需要配置

                            public void afterReturning(JoinPoint jp, Object value) {   //value 为目标方法返回的参数

                                   System.out.println("后置通知,目标方法的返回是" + value);

                            }

 

                     环绕通知

                            @Around("execution(* *.s*(..))")

                            public Object around(ProceedingJoinPoint pjp) throws Throwable {     //必须传入ProceedingJoinPoint参数

                                   System.out.println("环绕前...");

                                   Object value = pjp.proceed();

                                   System.out.println("环绕后");

                                   return value;

                            }

 

                     抛异常通知

                            @AfterThrowing(value="execution(* *.s*(..))", throwing="tx") //需要配置异常参数

                            public void afterThrowing(Throwable tx) {        //tx接收异常参数

                                   System.out.println("异常抛出通知:" + tx);

                            }

 

                     最终通知

                            @After("execution(* *.s*(..))")

                            public void after() {

                                   System.out.println("最终通知");

                            }

                           

              公用切点的定义和使用

                     @Pointcut("execution(* *.s*(..))") //在通知的类中可以这种方式定义切点,这样的切点可以被所用通知公用

                     private void mypointcut() {

                     }

                    

                     使用方式:

                     @Before("mypointcut()")            //直接通过 方法名()    引用切点,也可以 mypointcut()||mypointcut2() 表示引用两个切点

                     public void before() {

                            System.out.println("前置通知...");

                     }

             

      

      

       springaop编程:

1.OOPAOP

       OOP:面向对象编程,主要关注的是一个类有哪些属性,要实现哪些功能。将这些属性和功能进行封装。

       AOP(Aspect Oriented Programming):面向切面编程:关注的是不同类的重复的代码,将重复代码提取,在需要的时候插入进去,实现功能增强

 

       OOP( 面向对象编程)针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。

    AOP 则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。这两种设计思想在目标上有着本质的差异。

      

spring整合junit测试:

       Spring整合junit4可以方便我们的测试。

       1. 需要导入一个spring-test-4.2.4.RELEASE.jar

       2. 在测试类上配置:

              @RunWith(SpringJUnit4ClassRunner.class)      //spring整合junit

              @ContextConfiguration(locations = "classpath:applicationContext.xml")     //指定配置文件的位置

              public class AnnotationTest {

 

                     @Autowired

                     private IUserService userService; //将工厂中的对象注入到变量中

 

                     @Test

                     public void test2() {

                             userService.add();

                     }

              }

 

2.AOP专业名词:

       目标对象target:指的是需要被增强的对象,由于spring aop是通过代理模式实现,从而这个对象永远是被代理对象。

       连接点(join point):所谓连接点是指那些被拦截到的点,在spring中这些点指的是方法,因为spring只支持方法类型的连接点

       切入点(pointcut)      表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方,简单说切入点是指我们要对哪些连接点进行拦截的定义

       通知(advice)             所谓通知是指拦截到连接点之后所要做的事情就是通知,通知分为前置通知,后置通知,异常通知,最终通知,环绕通知,Advice 定义了在 pointcut 里面定义的程序点具体要做的操作

       引介introduction     引介是一种特殊的通知,在不修改类代码的前提下,introduction可以在运行期为类动态地添加一些方法或属性

       切面aspect              是切入点和通知的结合

       织入weaving           是将切面应用到目标对象从而创建出AOP代理对象的过程,织入可以在编译期,类装载期,运行期进行。Spring采用动态织入,而aspectj采用静态织入

       代理Proxy               一个类被AOP织入增强后,就产生一个结果代理类

 

3.AOP底层实现介绍

       分为静态AOP和动态AOP

              静态AOP:是指AspectJ实现的AOP,他是将切面代码直接编译到Java类文件中。(编译时)

              动态AOP:是指将切面代码进行动态织入实现的AOP(运行时)

 

       spring 采用动态AOP,实现的技术为:

       JDK提供的动态代理技术 和 CGLIB(动态字节码增强技术)

4动态代理的实现

       a.JDK动态代理:只针对于接口操作,采用jdk proxy对象实现,获取到的代理对象与目标对象实现了相同的接口

      

       b.CGLIB:可以为没有实现接口的类去做代理  //代理对象本质上是被代理对象的子类

              1. spring框架的spring-core.jar包中已经集成了cglibasm,不需要导入jar

              2.代码:

                     private Object target;          //定义成员变量,存放目标对象

 

                     public CGLIBProxy(Object target){     //定义构造方法,传入目标对象

                            this.target=target;

                     }

                     public Object createProxy(){ //定义获取代理对象的方法

                           

                            Enhancer enhancer=new Enhancer(); //创建增强对象

                           

                            enhancer.setSuperclass(target.getClass()); //设置目标对象的字节码对象

                           

                            enhancer.setCallback(new MethodInterceptor() {     //设置回调方法

                                   @Override

                                   public Object intercept(Object arg0, Method method, Object[] arg2,

                                                 MethodProxy arg3) throws Throwable {

                                         

                                          System.out.println("cglibproxy");       //通知,增强的代码

                                          return method.invoke(target, arg2);          //调用目标对象

                                         

                                   }

                            });

                            return enhancer.create();            //返回代理对象

                     }

      

       问题:spring采用的是哪一种动态机制:

              如果目标对象有接口,优先使用jdk动态代理

              如果目标对象无接口,使用cglib动态代理。

       cglib代理:目标对象和代理对象是父子关系(代理对象为被代理对象的子类)

       jdk代理:  目标对象和代理对象是兄弟关系

       注意:

              配置<aop:config proxy-target-class="true">  后始终使用cglib代理

 

 

4.AOP开发

       必须导入AOP联盟包:com.springsource.org.aopalliance-1.0.0.jar

       在传统的spring aop开发中它支持增强(advice)有五种:

              1.    前置通知 : 目标方法执行前增强  org.springframework.aop.MethodBeforeAdvice

              2.    后置通知目标方法执行后增强  org.springframework.aop.AfterReturningAdvice

              3.    环绕通知目标方法执行前后进行增 org.aopalliance.intercept.MethodInterceptor

              4.    异常抛出通知: 目标方法抛出异常后的增 org.springframework.aop.ThrowsAdvice

              5.    引介通知: 在目标类中添加一些新的方法或属性(不讲解)

       AOP开发步骤:

              1. 定义目标

              2. 定义通知

              3. 定义切点

              4. 定义切面(通知+切点)

              5. 生成代理

 

5.基于AspectJ切点表达式的传统aop开发--半自动aop

       需要导入jarcom.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

       定义目标

              public class OrderServiceImpl implements IOrderService {

       定义通知:

              public class OrderHelper implements MethodBeforeAdvice, AfterReturningAdvice, MethodInterceptor {    //不同的接口对应不同的通知类型

       xml配置文件:    //注意要引入aop名称空间

              <bean id="orderService" class="cn.itheima.aop.OrderServiceImpl"></bean>    //定义目标

              <bean id="orderServiceAdvice" class="cn.itheima.aop.OrderHelper"></bean> //定义通知

             

              使用<aop:xxx>标签来完成切面与切点声明:

              <aop:config>

                     <aop:pointcut id="orderServicePointCut" expression="execution(* cn.itheima.aop.IOrderService.*(..))"/> //定义切点

                     <aop:advisor advice-ref="orderServiceAdvice" pointcut-ref="orderServicePointCut" />          //定义切面,通知的类型,由通知类实现的接口决定

              </aop:config>

 

       切点表达式:返回值 包名 类名 方法名(..

       1.    execution(public * *()) 所有的public的方法

       2.    execution(* cn.itheima.aop..*(..)) 所有的aop包及其子包下的所有类的方法

       3.    execution(* cn.itheima.aop.IOrderService.*(..)) IOrderService接口中定义的所有方法

       4.    execution(* cn.itheima.aop.IOrderService+.*(..)) 匹配实现特定接口所有类的方法

       5.    execution(* save*(..)) 区配所有的以save开头的方法

      

       缺点:

              通知类需要实现相应的接口,在其要实现的方法中定义不同的通知方法:

                     org.springframework.aop.MethodBeforeAdvice;            //前置通知

                     org.springframework.aop.AfterReturningAdvice;     //后置通知

                     org.aopalliance.intercept.MethodInterceptor;  //环绕通知

 

              所有的通知方法必须同时去增强一个目标方法

 

      

6全自动AOP--xml方式

       AspectJ框架它定义的通知类型有6:

              1.    前置通知Before 相当于BeforeAdvice

              2.    后置通知AfterReturning 相当于AfterReturningAdvice

              3.    环绕通知 Around 相当于MethodInterceptor

              4.    抛出通知AfterThrowing 相当于ThrowAdvice

              5.    引介通知DeclareParents 相当于IntroductionInterceptor

              6.    最终通知After 不管是否异常,该通知都会执行

              相比spring 的传统AOP Advice多了一个最终通知

       定义目标,定义通知与半自动方式相同

       <aop:config> //定义切面

              <aop:aspect ref="userServiceAdvice">     //通知

                     <aop:pointcut expression="execution(* *..UserService Impl.add(..))" id="pointCut1"/> //切点,这个切点可以定义在aspect标签外,切点定义在标签外后,可以被所有的通知共

                     <aop:before method="before" pointcut-ref="pointCut1"/>  //指定通知类型,通知方法,引用切点

                     <aop:after-returning method="afterReturning" pointcut-ref="pointCut1"/>       //后置通知

                     <aop:around method="around" pointcut-ref="pointCut1"/> //环绕通知

                     <aop:after-throwing method="afterThrowing" pointcut-ref="pointCut1"/>       //异常通知,抛出异常才会执行

                     <aop:after method="after" pointcut-ref="pointCut1"/> //最终通知,无论是否出现异常,都会执行

              </aop:aspect>

             

              <aop:aspect ref="userServiceAdvice2">   //每一个通知的bean对应一个切面

                     。。。

              </aop:aspect>

       </aop:config>

       注意上面的标签中

      

       全自动aop和半自动aop定义切面的区别:

              半自动中与切点结合的通知为整个通知类,即通知类中所有的方法会同时作用在切点上

              全自动aop的通知为通知类中的方法,即可以指定某个方法作用在某个切点上,更加灵活

             

 

7.通知上的参数介绍:

       1.    通知上可以添加JoinPoint参数,通过它可以获取目标相关的信息

              public void before(JoinPoint jp) {       //JoinPoint对象可用于获取被增强对象的信息

                     System.out.println("拦截的目标类:" +jp.getSignature().getDeclaringTypeName());       //通过参数jp可以获取目标类的名称      signature:署名;签名;信号

                     System.out.println("拦截的方法名称:" + jp.getSignature().getName()); //通过参数jp可以获取目标方法的名称 

 

       2. 后置通知获取目标方法返回值

              public void afterReturning(JoinPoint jp,Object val) { //也可以传入joinpoint,同前置通知中的joinpoint

                     Object[] args = jp.getArgs(); //获取目标方法的参数列表

                     System.out.println("目标方法返回值:" + val);

                     System.out.println("后置通知");

              }

 

              注意:需要配置returning="val" 和参数对应

                     <aop:after-returning method="afterReturning" pointcut-ref="pointCut1" returning="val"/>

                    

       3. 环绕通知必须传入连接点参数:ProceedingJoinPoint             //环绕通知可以实现其他所有的通知

              public Object around(ProceedingJoinPoint pjp) throws Throwable {

 

                     System.out.println("环绕前");

                     Object result = pjp.proceed();     //此处调用执行被增强的方法

                     System.out.println("环绕后");

 

                     return result;

              }

 

       3.    异常通知获取异常信息

              public void afterThrowing(JoinPoint jp,Throwable ex) {   //也可以传入joinpoint,同前置通知中的joinpoint

                     System.out.println("发现了异常。。。。"+ex);

              }

 

              注意:需要配置throwing="ex" 和参数对应

              <aop:after-throwing method="afterThrowing" pointcut-ref="pointCut1" throwing="ex"/>

 

8.注解方式实现代理:

       第一步:定义目标类,通知类

      

       第二步:开启注解扫描,把通知和目标纳入Spring的管理当中,并配置aspectj自动代理aspectj-autoproxy

              a. 开启注解扫描

                     <context:component-scan base-package="cn.itheima"/>

                    

              b. 将通知和目标类加入spring管理 //配置@Component

             

              c.配置aspectj自动代理

                     <aop:aspectj-autoproxy proxy-target-class="true"/>           //proxy-target-class默认为false,如果设置为true,表示始终采用cglib代理

 

       第三步:定义切面

              a. 在通知的类上添加注解@aspect,声明这个类为切面

              b. 根据不同的通知,在不同的方法上添加注解并定义切点表达式

                     声明切面       //在类上

                            @Aspect

                            public class CustomerServiceHelper {

 

                     前置通知

                            @Before("execution(* *.save(..))") //括号中传入切点表达式

                            public void before() {

                                      System.out.println("前置通知...");

                            }

                     后置通知

                            @AfterReturning(value = "execution(* *.update(..))", returning = "value")       //声明接收参数需要配置

                            public void afterReturning(JoinPoint jp, Object value) {   //value 为目标方法返回的参数

                                   System.out.println("后置通知,目标方法的返回是" + value);

                            }

 

                     环绕通知

                            @Around("execution(* *.s*(..))")

                            public Object around(ProceedingJoinPoint pjp) throws Throwable {     //必须传入ProceedingJoinPoint参数

                                   System.out.println("环绕前...");

                                   Object value = pjp.proceed();

                                   System.out.println("环绕后");

                                   return value;

                            }

 

                     抛异常通知

                            @AfterThrowing(value="execution(* *.s*(..))", throwing="tx") //需要配置异常参数

                            public void afterThrowing(Throwable tx) {        //tx接收异常参数

                                   System.out.println("异常抛出通知:" + tx);

                            }

 

                     最终通知

                            @After("execution(* *.s*(..))")

                            public void after() {

                                   System.out.println("最终通知");

                            }

                           

              公用切点的定义和使用

                     @Pointcut("execution(* *.s*(..))") //在通知的类中可以这种方式定义切点,这样的切点可以被所用通知公用

                     private void mypointcut() {

                     }

                    

                     使用方式:

                     @Before("mypointcut()")            //直接通过 方法名()    引用切点,也可以 mypointcut()||mypointcut2() 表示引用两个切点

                     public void before() {

                            System.out.println("前置通知...");

                     }

             

      

      

       

springaop编程:

1.OOPAOP

       OOP:面向对象编程,主要关注的是一个类有哪些属性,要实现哪些功能。将这些属性和功能进行封装。

       AOP(Aspect Oriented Programming):面向切面编程:关注的是不同类的重复的代码,将重复代码提取,在需要的时候插入进去,实现功能增强

 

       OOP( 面向对象编程)针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。

    AOP 则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。这两种设计思想在目标上有着本质的差异。

      

spring整合junit测试:

       Spring整合junit4可以方便我们的测试。

       1. 需要导入一个spring-test-4.2.4.RELEASE.jar

       2. 在测试类上配置:

              @RunWith(SpringJUnit4ClassRunner.class)      //spring整合junit

              @ContextConfiguration(locations = "classpath:applicationContext.xml")     //指定配置文件的位置

              public class AnnotationTest {

 

                     @Autowired

                     private IUserService userService; //将工厂中的对象注入到变量中

 

                     @Test

                     public void test2() {

                             userService.add();

                     }

              }

 

2.AOP专业名词:

       目标对象target:指的是需要被增强的对象,由于spring aop是通过代理模式实现,从而这个对象永远是被代理对象。

       连接点(join point):所谓连接点是指那些被拦截到的点,在spring中这些点指的是方法,因为spring只支持方法类型的连接点

       切入点(pointcut)      表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方,简单说切入点是指我们要对哪些连接点进行拦截的定义

       通知(advice)             所谓通知是指拦截到连接点之后所要做的事情就是通知,通知分为前置通知,后置通知,异常通知,最终通知,环绕通知,Advice 定义了在 pointcut 里面定义的程序点具体要做的操作

       引介introduction     引介是一种特殊的通知,在不修改类代码的前提下,introduction可以在运行期为类动态地添加一些方法或属性

       切面aspect              是切入点和通知的结合

       织入weaving           是将切面应用到目标对象从而创建出AOP代理对象的过程,织入可以在编译期,类装载期,运行期进行。Spring采用动态织入,而aspectj采用静态织入

       代理Proxy               一个类被AOP织入增强后,就产生一个结果代理类

 

3.AOP底层实现介绍

       分为静态AOP和动态AOP

              静态AOP:是指AspectJ实现的AOP,他是将切面代码直接编译到Java类文件中。(编译时)

              动态AOP:是指将切面代码进行动态织入实现的AOP(运行时)

 

       spring 采用动态AOP,实现的技术为:

       JDK提供的动态代理技术 和 CGLIB(动态字节码增强技术)

4动态代理的实现

       a.JDK动态代理:只针对于接口操作,采用jdk proxy对象实现,获取到的代理对象与目标对象实现了相同的接口

      

       b.CGLIB:可以为没有实现接口的类去做代理  //代理对象本质上是被代理对象的子类

              1. spring框架的spring-core.jar包中已经集成了cglibasm,不需要导入jar

              2.代码:

                     private Object target;          //定义成员变量,存放目标对象

 

                     public CGLIBProxy(Object target){     //定义构造方法,传入目标对象

                            this.target=target;

                     }

                     public Object createProxy(){ //定义获取代理对象的方法

                           

                            Enhancer enhancer=new Enhancer(); //创建增强对象

                           

                            enhancer.setSuperclass(target.getClass()); //设置目标对象的字节码对象

                           

                            enhancer.setCallback(new MethodInterceptor() {     //设置回调方法

                                   @Override

                                   public Object intercept(Object arg0, Method method, Object[] arg2,

                                                 MethodProxy arg3) throws Throwable {

                                         

                                          System.out.println("cglibproxy");       //通知,增强的代码

                                          return method.invoke(target, arg2);          //调用目标对象

                                         

                                   }

                            });

                            return enhancer.create();            //返回代理对象

                     }

      

       问题:spring采用的是哪一种动态机制:

              如果目标对象有接口,优先使用jdk动态代理

              如果目标对象无接口,使用cglib动态代理。

       cglib代理:目标对象和代理对象是父子关系(代理对象为被代理对象的子类)

       jdk代理:  目标对象和代理对象是兄弟关系

       注意:

              配置<aop:config proxy-target-class="true">  后始终使用cglib代理

 

 

4.AOP开发

       必须导入AOP联盟包:com.springsource.org.aopalliance-1.0.0.jar

       在传统的spring aop开发中它支持增强(advice)有五种:

              1.    前置通知 : 目标方法执行前增强  org.springframework.aop.MethodBeforeAdvice

              2.    后置通知目标方法执行后增强  org.springframework.aop.AfterReturningAdvice

              3.    环绕通知目标方法执行前后进行增 org.aopalliance.intercept.MethodInterceptor

              4.    异常抛出通知: 目标方法抛出异常后的增 org.springframework.aop.ThrowsAdvice

              5.    引介通知: 在目标类中添加一些新的方法或属性(不讲解)

       AOP开发步骤:

              1. 定义目标

              2. 定义通知

              3. 定义切点

              4. 定义切面(通知+切点)

              5. 生成代理

 

5.基于AspectJ切点表达式的传统aop开发--半自动aop

       需要导入jarcom.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

       定义目标

              public class OrderServiceImpl implements IOrderService {

       定义通知:

              public class OrderHelper implements MethodBeforeAdvice, AfterReturningAdvice, MethodInterceptor {    //不同的接口对应不同的通知类型

       xml配置文件:    //注意要引入aop名称空间

              <bean id="orderService" class="cn.itheima.aop.OrderServiceImpl"></bean>    //定义目标

              <bean id="orderServiceAdvice" class="cn.itheima.aop.OrderHelper"></bean> //定义通知

             

              使用<aop:xxx>标签来完成切面与切点声明:

              <aop:config>

                     <aop:pointcut id="orderServicePointCut" expression="execution(* cn.itheima.aop.IOrderService.*(..))"/> //定义切点

                     <aop:advisor advice-ref="orderServiceAdvice" pointcut-ref="orderServicePointCut" />          //定义切面,通知的类型,由通知类实现的接口决定

              </aop:config>

 

       切点表达式:返回值 包名 类名 方法名(..

       1.    execution(public * *()) 所有的public的方法

       2.    execution(* cn.itheima.aop..*(..)) 所有的aop包及其子包下的所有类的方法

       3.    execution(* cn.itheima.aop.IOrderService.*(..)) IOrderService接口中定义的所有方法

       4.    execution(* cn.itheima.aop.IOrderService+.*(..)) 匹配实现特定接口所有类的方法

       5.    execution(* save*(..)) 区配所有的以save开头的方法

      

       缺点:

              通知类需要实现相应的接口,在其要实现的方法中定义不同的通知方法:

                     org.springframework.aop.MethodBeforeAdvice;            //前置通知

                     org.springframework.aop.AfterReturningAdvice;     //后置通知

                     org.aopalliance.intercept.MethodInterceptor;  //环绕通知

 

              所有的通知方法必须同时去增强一个目标方法

 

      

6全自动AOP--xml方式

       AspectJ框架它定义的通知类型有6:

              1.    前置通知Before 相当于BeforeAdvice

              2.    后置通知AfterReturning 相当于AfterReturningAdvice

              3.    环绕通知 Around 相当于MethodInterceptor

              4.    抛出通知AfterThrowing 相当于ThrowAdvice

              5.    引介通知DeclareParents 相当于IntroductionInterceptor

              6.    最终通知After 不管是否异常,该通知都会执行

              相比spring 的传统AOP Advice多了一个最终通知

       定义目标,定义通知与半自动方式相同

       <aop:config> //定义切面

              <aop:aspect ref="userServiceAdvice">     //通知

                     <aop:pointcut expression="execution(* *..UserService Impl.add(..))" id="pointCut1"/> //切点,这个切点可以定义在aspect标签外,切点定义在标签外后,可以被所有的通知共

                     <aop:before method="before" pointcut-ref="pointCut1"/>  //指定通知类型,通知方法,引用切点

                     <aop:after-returning method="afterReturning" pointcut-ref="pointCut1"/>       //后置通知

                     <aop:around method="around" pointcut-ref="pointCut1"/> //环绕通知

                     <aop:after-throwing method="afterThrowing" pointcut-ref="pointCut1"/>       //异常通知,抛出异常才会执行

                     <aop:after method="after" pointcut-ref="pointCut1"/> //最终通知,无论是否出现异常,都会执行

              </aop:aspect>

             

              <aop:aspect ref="userServiceAdvice2">   //每一个通知的bean对应一个切面

                     。。。

              </aop:aspect>

       </aop:config>

       注意上面的标签中

      

       全自动aop和半自动aop定义切面的区别:

              半自动中与切点结合的通知为整个通知类,即通知类中所有的方法会同时作用在切点上

              全自动aop的通知为通知类中的方法,即可以指定某个方法作用在某个切点上,更加灵活

             

 

7.通知上的参数介绍:

       1.    通知上可以添加JoinPoint参数,通过它可以获取目标相关的信息

              public void before(JoinPoint jp) {       //JoinPoint对象可用于获取被增强对象的信息

                     System.out.println("拦截的目标类:" +jp.getSignature().getDeclaringTypeName());       //通过参数jp可以获取目标类的名称      signature:署名;签名;信号

                     System.out.println("拦截的方法名称:" + jp.getSignature().getName()); //通过参数jp可以获取目标方法的名称 

 

       2. 后置通知获取目标方法返回值

              public void afterReturning(JoinPoint jp,Object val) { //也可以传入joinpoint,同前置通知中的joinpoint

                     Object[] args = jp.getArgs(); //获取目标方法的参数列表

                     System.out.println("目标方法返回值:" + val);

                     System.out.println("后置通知");

              }

 

              注意:需要配置returning="val" 和参数对应

                     <aop:after-returning method="afterReturning" pointcut-ref="pointCut1" returning="val"/>

                    

       3. 环绕通知必须传入连接点参数:ProceedingJoinPoint             //环绕通知可以实现其他所有的通知

              public Object around(ProceedingJoinPoint pjp) throws Throwable {

 

                     System.out.println("环绕前");

                     Object result = pjp.proceed();     //此处调用执行被增强的方法

                     System.out.println("环绕后");

 

                     return result;

              }

 

       3.    异常通知获取异常信息

              public void afterThrowing(JoinPoint jp,Throwable ex) {   //也可以传入joinpoint,同前置通知中的joinpoint

                     System.out.println("发现了异常。。。。"+ex);

              }

 

              注意:需要配置throwing="ex" 和参数对应

              <aop:after-throwing method="afterThrowing" pointcut-ref="pointCut1" throwing="ex"/>

 

8.注解方式实现代理:

       第一步:定义目标类,通知类

      

       第二步:开启注解扫描,把通知和目标纳入Spring的管理当中,并配置aspectj自动代理aspectj-autoproxy

              a. 开启注解扫描

                     <context:component-scan base-package="cn.itheima"/>

                    

              b. 将通知和目标类加入spring管理 //配置@Component

             

              c.配置aspectj自动代理

                     <aop:aspectj-autoproxy proxy-target-class="true"/>           //proxy-target-class默认为false,如果设置为true,表示始终采用cglib代理

 

       第三步:定义切面

              a. 在通知的类上添加注解@aspect,声明这个类为切面

              b. 根据不同的通知,在不同的方法上添加注解并定义切点表达式

                     声明切面       //在类上

                            @Aspect

                            public class CustomerServiceHelper {

 

                     前置通知

                            @Before("execution(* *.save(..))") //括号中传入切点表达式

                            public void before() {

                                      System.out.println("前置通知...");

                            }

                     后置通知

                            @AfterReturning(value = "execution(* *.update(..))", returning = "value")       //声明接收参数需要配置

                            public void afterReturning(JoinPoint jp, Object value) {   //value 为目标方法返回的参数

                                   System.out.println("后置通知,目标方法的返回是" + value);

                            }

 

                     环绕通知

                            @Around("execution(* *.s*(..))")

                            public Object around(ProceedingJoinPoint pjp) throws Throwable {     //必须传入ProceedingJoinPoint参数

                                   System.out.println("环绕前...");

                                   Object value = pjp.proceed();

                                   System.out.println("环绕后");

                                   return value;

                            }

 

                     抛异常通知

                            @AfterThrowing(value="execution(* *.s*(..))", throwing="tx") //需要配置异常参数

                            public void afterThrowing(Throwable tx) {        //tx接收异常参数

                                   System.out.println("异常抛出通知:" + tx);

                            }

 

                     最终通知

                            @After("execution(* *.s*(..))")

                            public void after() {

                                   System.out.println("最终通知");

                            }

                           

              公用切点的定义和使用

                     @Pointcut("execution(* *.s*(..))") //在通知的类中可以这种方式定义切点,这样的切点可以被所用通知公用

                     private void mypointcut() {

                     }

                    

                     使用方式:

                     @Before("mypointcut()")            //直接通过 方法名()    引用切点,也可以 mypointcut()||mypointcut2() 表示引用两个切点

                     public void before() {

                            System.out.println("前置通知...");

                     }

             

      

      

       

posted @ 2018-11-17 13:49  foreast  阅读(206)  评论(0编辑  收藏  举报