• Java EE学习笔记(三)


    Spring AOP

    1、Spring AOP简介

    1)、AOP的全称是Aspect-Oriented Programming,即面向切面编程(也称面向方面编程)。它是面向对象编程(OOP)的一种补充,目前已成为一种比较成熟的编程方式。

    2)、AOP采取横向抽取机制,将分散在各个方法中的重复代码提取出来,然后在程序编译或运行时,再将这些提取出来的代码应用到需要执行的地方。这种采用横向抽取机制的方式,采用传统的OOP思想显然是无法办到的,因为OOP只能实现父子关系的纵向的重用(纵向继承体系重复性代码)。虽然AOP是一种新的编程思想,但却不是OOP的替代品,它只是OOP的延伸和补充。 经典应用:事务管理。实现原理:AOP底层将采用代理机制进行实现。

    3)、类与切面的关系:

    AOP的使用,使开发人员在编写业务逻辑时可以专心于核心业务,而不用过多的关注于其他业务逻辑的实现,这不但提高了开发效率,而且增强了代码的可维护性

    4)、目前最流行的AOP框架有2个:Spring AOPAspectJ(一个基于Java语言的AOP框架)

    5)、AOP术语:Aspect、Joinpoint、Pointcut、Advice、Target Object、Proxy和Weaving。

    a)、Aspect(切面,切入点pointcut和通知advice的结合):该类要被Spring容器识别为切面,需要在配置文件中通过<bean>元素指定

    b)、Joinpoint(连接点,即那些可能被拦截到的方法):实际上是对象的一个操作,例如方法的调用或异常的抛出。在Spring AOP中,连接点就是指方法的调用

    c)、Pointcut(切入点,已被增强的连接点,是连接点的子集,例如:addUser():通常在程序中,切入点指的是类或者方法名,如某个通知要应用到所有以add开头的方法中,那么所有满足这一规则的方法都是切入点。

    d)、Advice(通知/增强处理,增强代码,例如:before()、after()等):AOP框架在特定的切入点执行增强处理,即在定义好的切入点处所要执行的程序代码。可以将其理解为切面类中的方法,它是切面的具体实现

    e)、Target Object(目标对象,需要被代理的类,例如:UserService):是指所有被通知的对象,也称为被增强对象。如果AOP框架采用的是动态的AOP实现,那么该对象就是一个被代理对象

    f)、Proxy(代理):将通知advice应用到目标对象target之后,被动态创建的对象

    g)、Weaving(织入):将切面代码(增强advice应用到)插入到目标对象target上,从而生成代理对象的过程

    2、动态代理

    1)、JDK动态代理(实现接口的代理方式)

    a)、JDK动态代理是通过java.lang.reflect.Proxy 类来实现的,我们可以调用Proxy类的newProxyInstance()方法来创建代理对象。对于使用业务接口的类,Spring默认会使用JDK动态代理来实现AOP

    b)、src->com.itheima.jdk

    ①UserDao.java

    1 package com.itheima.jdk;
    2 
    3 public interface UserDao {
    4     public void addUser(); // 添加方法
    5     public void deleteUser(); // 删除方法
    6 }

    ②目标类:UserDaoImpl.java(接口的实现类、即切入点pointcut

     1 package com.itheima.jdk;
     2 
     3 public class UserDaoImpl implements UserDao {
     4     // 本案例中会将实现类UserDaoImpl作为     目标类     ,对其中的方法进行增强处理
     5     
     6     public void addUser() {
     7         System.out.println("添加用户");
     8     }
     9 
    10     public void deleteUser() {
    11         System.out.println("删除用户");
    12     }
    13 }

    c)、src->com.itheima.aspect

    ③切面类(这里是通知类):MyAspect.java(存放多个通知advice)

     1 package com.itheima.aspect;
     2 
     3 // 切面类:可以存在多个通知Advice(即增强的方法)
     4 public class MyAspect {
     5     // 切面类中的这2个方法就是通知
     6     public void check_Permissions() { 
     7         System.out.println("模拟检查权限..."); 
     8     } 
     9     public void log() {
    10         System.out.println("模拟记录日志...");
    11     }
    12 }

    d)、src->com.itheima.jdk

    ④代理类:JdkProxy.java(编写工厂生成代理)

     1 package com.itheima.jdk;
     2 import java.lang.reflect.InvocationHandler;
     3 import java.lang.reflect.Method;
     4 import java.lang.reflect.Proxy;
     5 import com.itheima.aspect.MyAspect;
     6 /**
     7  * JDK代理类
     8  */
     9 public class JdkProxy implements InvocationHandler{ // 需要实现InvocationHandler接口,并编写代理方法
    10     
    11     // 1、声明目标类接口对象,通过下面来创建对目标类的代理
    12     private UserDao userDao;
    13     
    14     // 2、创建代理方法
    15     public  Object createProxy(UserDao userDao) {
    16         this.userDao = userDao;
    17         
    18         // 1)、类加载器:当前类名.class.getClassLoader();
    19         //         目标类实例.getClass().getClassLoader();
    20         ClassLoader classLoader = JdkProxy.class.getClassLoader();
    21         
    22         // 2)、被代理对象实现的所有接口
    23         Class[] clazz = userDao.getClass().getInterfaces();
    24         
    25         // 3)、使用代理类,进行增强,返回的是代理后的对象
    26         return  Proxy.newProxyInstance(classLoader,clazz,this);
    27         /* 参数1:classLoader(类加载器):动态代理类,运行时创建,任何类都需要类加载器将其加载到内存
    28          * 参数2:interfaces:代理类需要实现的所有接口
    29          *         方式1:目标类实例.getClass().getInterfaces(); 注意:只能获得自己的接口,不能获得父类接口
    30          *         方式2:new Class[]{UserDao.class}
    31          * 参数3:InvocationHandler 处理类(接口),必须进行实现类,一般采用匿名内部类
    32          */
    33     }
    34     
    35     /*
    36      * 所有动态代理类的方法调用,都会交由invoke()方法去处理,执行一次方法,则调用一次invoke()方法
    37      * proxy: 被代理后的对象 this
    38      * method: 将要被执行的方法信息(反射) 
    39      *       执行方法名:method.getName();
    40      *       执行方法:method.invoke(目标类接口实例,实际参数);
    41      * args: 执行方法时需要的参数
    42      */
    43     @Override   // 实现了接口中的invoke()方法
    44     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    45         // 1、声明切面类对象(即通知)
    46         MyAspect myAspect = new MyAspect();
    47         
    48         // 2、前增强(要执行advice,那么就要取创建对应的通知类,这里是MyAspect切面类)
    49         myAspect.check_Permissions();
    50         
    51         // 3、在目标类上调用方法,并传入参数, method.invoke(目标类接口实例, 实际参数);
    52         Object obj = method.invoke(userDao, args);
    53         
    54         // 4、后增强
    55         myAspect.log();
    56         
    57         return obj; // 返回代理对象
    58     }
    59 }

    ⑤测试类:JdkTest.java

     1 package com.itheima.jdk;
     2 public class JdkTest{
     3     public static void main(String[] args) {
     4         // 1、创建代理对象来组合通知和切入点足
     5         JdkProxy jdkProxy = new JdkProxy();
     6         
     7          // 2、创建目标类对象
     8         UserDao userDao= new UserDaoImpl();
     9         
    10         // 3、从代理对象中获取增强后的目标对象,然后每调用目标类的一个方法将执行(invoke())before()+调用的方法+after(),即首尾通知(切入点处增强代码)
    11         UserDao userDao1 = (UserDao) jdkProxy.createProxy(userDao);
    12         
    13         // 4、执行方法
    14         userDao1.addUser();
    15         userDao1.deleteUser();
    16     }
    17 }

    ⑥运行结果:

    2)、CGLIB代理

    a)、如果想代理没有实现接口的类(只有实现类),那么可以使用CGLIB代理使用动态代理的对象必须实现一个或多个接口

    b)、CGLIB(Code Generation Library)是一个高性能开源的代码生成包,它采用非常底层字节码技术,对指定的目标类生成一个子类并对子类进行增强

    c)、src->com.itheima.cglib

    ①UserDao.java

     1 package com.itheima.cglib;
     2 //目标类:UserDao 
     3 public class UserDao {
     4     public void addUser() {
     5         System.out.println("添加用户");
     6     }
     7     public void deleteUser() {
     8         System.out.println("删除用户");
     9     }
    10 }

    ②代理类:CglibProxy.java

     1 package com.itheima.cglib;
     2 import java.lang.reflect.Method;
     3 import org.springframework.cglib.proxy.Enhancer;
     4 import org.springframework.cglib.proxy.MethodInterceptor;
     5 import org.springframework.cglib.proxy.MethodProxy;
     6 import com.itheima.aspect.MyAspect;
     7 
     8 // 代理类CglibProxy(字节码增强框架cglib, 在运行时,创建目标类的子类,从而对目标类进行增强)
     9 public class CglibProxy implements MethodInterceptor{ // 需要实现MethodInterceptor接口,并实现接口中的intercept方法
    10     // 代理方法
    11     public  Object createProxy(Object target) {
    12         // 1、创建一个动态类对象,它是CGLIB的核心类
    13         Enhancer enhancer = new Enhancer();
    14         
    15         // 2、确定需要增强的(目标)类,设置其父类CglibProxy(确定父类)
    16         enhancer.setSuperclass(target.getClass());
    17         
    18         // 3、添加回调函数,this就是代理类
    19         enhancer.setCallback(this);
    20         
    21         // 4、返回创建的代理类
    22         return enhancer.create();
    23     }
    24     /**
    25      * intercept() 等效于jdk中的invoke()
    26      * proxy CGlib根据指定父类生成的代理对象
    27      * method 拦截的方法
    28      * args 拦截方法的参数数组
    29      * methodProxy 方法的代理对象,用于执行父类的方法 
    30      */
    31     
    32     @Override
    33     public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    34          // 1、创建切面类对象
    35         MyAspect myAspect = new MyAspect();
    36         
    37         // 2、前增强
    38         myAspect.check_Permissions();
    39         
    40         // 3、目标方法执行(执行代理类的父类的invoke()方法)
    41         Object obj = methodProxy.invokeSuper(proxy, args);
    42         
    43         // 4、后增强
    44         myAspect.log();    
    45         return obj;
    46     }
    47 }

    ③测试类:CglibTest.java

     1 package com.itheima.cglib;
     2 // 测试类
     3 public class CglibTest {
     4     public static void main(String[] args) {
     5         // 1、创建代理对象
     6         CglibProxy cglibProxy = new CglibProxy();
     7         
     8         // 2、创建目标对象
     9         UserDao userDao = new UserDao();
    10         
    11         // 3、获取增强后的目标对象
    12         UserDao userDao1 = (UserDao)cglibProxy.createProxy(userDao);
    13         
    14         // 4、执行方法
    15         userDao1.addUser();
    16         userDao1.deleteUser();
    17     }
    18 }

    ④运行结果:

     3、基于代理类的AOP实现

    1)、ProxyFactoryBean是FactoryBean接口的实现类,FactoryBean负责实例化一个Bean,而ProxyFactoryBean负责为其他Bean创建代理实例。在Spring中,使用ProxyFactoryBean是创建AOP代理的最基本方式。

    2)、Spring的通知类型:Spring按照通知在目标类方法的连接点位置,可以分为5种类型,具体如下:

    org.springframework.aop.MethodBeforeAdvice(前置通知)
    在目标方法执行前实施增强,可以应用于权限管理等功能。
    org.springframework.aop.AfterReturningAdvice(后置通知)
    在目标方法执行后实施增强,可以应用于关闭流、上传文件、删除临时文件等功能。
    org.aopalliance.intercept.MethodInterceptor(环绕通知)
    在目标方法执行前后实施增强,可以应用于日志、事务管理等功能。
    org.springframework.aop.ThrowsAdvice(异常抛出通知)
    在方法抛出异常后实施增强,可以应用于处理异常记录日志等功能。
    org.springframework.aop.IntroductionInterceptor(引介通知)
    在目标类中添加一些新的方法和属性,可以应用于修改老版本程序。

    3)、ProxyFactoryBean类中的常用可配置属性如下:

    4)、src->com.itheima.factorybean

    ①切面类:MyAspect.java(需要实现接口)

     1 package com.itheima.factorybean;
     2 import org.aopalliance.intercept.MethodInterceptor;
     3 import org.aopalliance.intercept.MethodInvocation;
     4 // 切面类MyAspect,要确定通知,需要实现不同接口,接口就是规范,从而确定方法名称。(环绕通知)
     5 public class MyAspect implements MethodInterceptor { // 实现该接口,并且实现接口中的invoke()方法
     6     
     7     @Override
     8     public Object invoke(MethodInvocation mi) throws Throwable {
     9         
    10         check_Permissions();
    11         // 手动执行目标方法
    12         Object obj = mi.proceed();
    13         log();
    14         return obj;
    15     }
    16     public void check_Permissions(){ // 以下2个是增强的方法,也就是通知
    17         System.out.println("模拟检查权限...");
    18     }
    19     public void log(){
    20         System.out.println("模拟记录日志...");
    21     }
    22 }

    ②配置文件:applicationContext.xml

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns="http://www.springframework.org/schema/beans"
     3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     4 xsi:schemaLocation="http://www.springframework.org/schema/beans 
     5 http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
     6 
     7     <!-- 1、 创建目标类 -->
     8     <bean id="userDaoID" class="com.itheima.jdk.UserDaoImpl" />
     9     
    10     <!-- 2、 创建切面类 --> 
    11     <bean id="myAspectID" class="com.itheima.factorybean.MyAspect" />
    12     
    13     <!-- 3、目标:使用Spring代理工厂定义一个名称为userDaoProxyID的代理对象
    14         使用工厂bean FactoryBean,底层调用getObject() 返回特殊的bean
    15         ProxyFactoryBean 用于创建代理工厂bean,生成特殊代理对象
    16      -->
    17     <bean id="userDaoProxyID" class="org.springframework.aop.framework.ProxyFactoryBean">
    18     
    19         <!-- 3.1 、指定代理实现的接口
    20             proxyInterfaces:确定接口
    21         -->
    22         <property name="proxyInterfaces" value="com.itheima.jdk.UserDao" />
    23         
    24         <!-- 3.2 、指定目标对象 
    25             target:确定目标类
    26         -->
    27         <property name="target" ref="userDaoID" />
    28         
    29         <!-- 3.3 、指定切面,织入环绕通知
    30             interceptorNames:通知切面类的名称(类型是String[]数组),如果设置一个值,采用一个值value=""
    31          -->
    32         <property name="interceptorNames" value="myAspectID" />
    33         
    34         <!-- 3.4 指定代理方式,true则使用cglib,false(默认)则使用jdk动态代理 -->
    35         <property name="proxyTargetClass" value="true" />
    36     </bean>
    37 </beans>

    ③测试类:ProxyFactoryBeanTest.java

     1 package com.itheima.factorybean;
     2 import org.springframework.context.ApplicationContext;
     3 import org.springframework.context.support.ClassPathXmlApplicationContext;
     4 import com.itheima.jdk.UserDao;
     5 // 测试类
     6 public class ProxyFactoryBeanTest {
     7     public static void main(String args[]) {
     8        String xmlPath = "com/itheima/factorybean/applicationContext.xml";
     9        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
    10        // 从Spring容器获得内容
    11        UserDao userDao = (UserDao) applicationContext.getBean("userDaoProxy");
    12        // 执行方法
    13        userDao.addUser();
    14        userDao.deleteUser();
    15     }
    16 }

    ④运行结果:

    4、AspectJ开发

    1)、使用AspectJ实现AOP有两种方式:一种是基于XML的声明式AspectJ,另一种是基于注解的声明式AspectJ

    2)、基于XML的声明式AspectJ

    a)、基于XML的声明式AspectJ是指通过XML文件来定义切面、切入点及通知,所有的切面、切入点和通知都必须定义在<aop:config>元素内。

    b)、<aop:config>元素及其子元素如下:

    c)、XML文件中常用元素的配置方式如下:

     1 <!--定义切面Bean -->
     2     <bean id="myAspect" class="com.itheima.aspectj.xml.MyAspect" /> 
     3         <aop:config>
     4             <!-- 配置切面 -->
     5             <aop:aspect  id="aspect" ref="myAspect"> 
     6             <!-- 配置切入点 -->
     7             <aop:pointcut expression="execution(* com.itheima.jdk.*.*(..))“ id="myPointCut" />
     8             <aop:before method="myBefore" pointcut-ref="myPointCut" />
     9             <aop:after-returning method="myAfterReturning“ pointcut-ref="myPointCut" returning="returnVal" />
    10             <aop:around method="myAround" pointcut-ref="myPointCut" />
    11             <aop:after-throwing method="myAfterThrowing“ pointcut-ref="myPointCut" throwing="e" />
    12             <aop:after method="myAfter" pointcut-ref="myPointCut" />
    13         </aop:aspect>
    14      </aop:config>

    d)、配置切面:在Spring的配置文件中,配置切面使用的是<aop:aspect>元素,该元素会将一个已定义好的Spring Bean转换成切面Bean,所以要在配置文件中先定义一个普通的Spring Bean(如myAspect)。配置<aop:aspect>元素时,通常会指定id和ref两个属性:

    e)、配置切入点:当<aop:pointcut>元素作为<aop:config>元素的子元素定义时,表示该切入点是全局切入点,它可被多个切面所共享;当<aop:pointcut>元素作为<aop:aspect>元素的子元素时,表示该切入点只对当前切面有效。在定义<aop:pointcut>元素时,通常会指定id和expression两个属性:

    f)、切入点表达式:execution(* com.itheima.jdk.*.*(..)) 是定义的切入点表达式,该切入点表达式的意思是匹配com.itheima.jdk包中任意类的任意方法的执行

    其中execution()是表达式的主体,第1个*表示的是返回类型,使用*代表所有类型;com.itheima.jdk表示的是需要拦截的包名,后面第2个*表示的是类名,使用*代表所有的类;第3个*表示的是方法名,使用*表示所有方法;后面(..)表示方法的参数,其中的".."表示任意参数。注意:第1个*与包名之间有一个空格

    g)、切入点表达式的基本格式:

    execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)

    带有问号(?)的部分表示可配置项,而其他部分属于必须配置项

    h)、配置通知:使用<aop:aspect>的子元素可以配置5种常用通知,这5个子元素不支持使用子元素,但在使用时可以指定一些属性,其常用属性及其描述如下:

    i)、src->com.itheima.aspectj.xml

    ①切面类:MyAspect.java

     1 package com.itheima.aspectj.xml;
     2 import org.aspectj.lang.JoinPoint;
     3 import org.aspectj.lang.ProceedingJoinPoint;
     4 /**
     5  *切面类,在此类中编写通知
     6  */
     7 public class MyAspect {
     8     // 前置通知
     9     public void myBefore(JoinPoint joinPoint) { //使用JoinPoint接口及其子接口ProceedingJoinPoint
    10         System.out.print("前置通知 :模拟执行权限检查...,");
    11         System.out.print("目标类是:"+joinPoint.getTarget() ); // 获得目标对象的类名
    12         System.out.println(",被织入增强处理的目标方法为:"+joinPoint.getSignature().getName()); // 获得目标方法
    13     }
    14     
    15     // 后置通知
    16     public void myAfterReturning(JoinPoint joinPoint) {
    17         System.out.print("后置通知:模拟记录日志...," );
    18         System.out.println("被织入增强处理的目标方法为:" + joinPoint.getSignature().getName());
    19     }
    20     /**
    21      * 环绕通知
    22      * ProceedingJoinPoint 是JoinPoint子接口,表示可以执行目标方法
    23      * 1.必须是Object类型的返回值
    24      * 2.必须接收一个参数,类型为ProceedingJoinPoint
    25      * 3.必须throws Throwable
    26      */
    27     public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    28         // 开始
    29         System.out.println("环绕开始:执行目标方法之前,模拟开启事务...");
    30         // 必须手动执行当前目标方法
    31         Object obj = proceedingJoinPoint.proceed();
    32         // 结束
    33         System.out.println("环绕结束:执行目标方法之后,模拟关闭事务...");
    34         return obj;
    35     }
    36     
    37     // 异常通知 
    38     public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {
    39         System.out.println("异常通知:" + "出错了" + e.getMessage());
    40     }
    41     
    42     // 最终通知
    43     public void myAfter() {
    44         System.out.println("最终通知:模拟方法结束后的释放资源...");
    45     }
    46 }

    ②配置文件:applicationContext.xml

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns="http://www.springframework.org/schema/beans"
     3    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     4    xmlns:aop="http://www.springframework.org/schema/aop"
     5 xsi:schemaLocation="http://www.springframework.org/schema/beans 
     6                     http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
     7                     http://www.springframework.org/schema/aop 
     8                     http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
     9     <!-- 1 、创建目标类 -->
    10     <bean id="userDaoID" class="com.itheima.jdk.UserDaoImpl" />
    11     
    12     <!-- 2 、创建切面类 -->
    13     <bean id="myAspectID" class="com.itheima.aspectj.xml.MyAspect" />
    14     
    15     <!-- 3 、aop编程,proxy-target-class = "true"声明使用cglib动态代理 -->
    16     <aop:config >
    17         
    18         <!-- 配置切面 ,切面在myAspectID-->
    19         <aop:aspect ref="myAspectID">
    20 
    21           <!-- 3.1 、配置切入点,通知最后增强哪些方法 (从目标对象中获得具体方法)-->
    22           <aop:pointcut expression="execution(* com.itheima.jdk.*.*(..))" id="myPointCutID" />
    23           
    24             <!-- 3.2 、关联通知Advice和切入点pointCut -->
    25             
    26             <!-- 3.2.1 、前置通知 ,pointcut-ref切入点引用-->
    27             <aop:before method="myBefore" pointcut-ref="myPointCutID" />
    28             
    29             <!-- 3.2.2 后置通知,在方法返回之后执行,就可以获得返回值
    30              returning属性:用于设置后置通知的第二个参数的名称,类型是Object 
    31              -->
    32             <aop:after-returning method="myAfterReturning"
    33                 pointcut-ref="myPointCutID" returning="returnVal" />
    34     
    35             <!-- 3.2.3 环绕通知 -->
    36             <aop:around method="myAround" pointcut-ref="myPointCutID" />
    37             
    38             <!-- 3.2.4 抛出通知:用于处理程序发生异常-->
    39             <!-- * 注意:如果程序没有异常,将不会执行增强 -->
    40             <!-- * throwing属性:用于设置通知第二个参数的名称,类型Throwable -->
    41             <aop:after-throwing method="myAfterThrowing"
    42                 pointcut-ref="myPointCutID" throwing="e" />
    43                 
    44             <!-- 3.2.5 最终通知:无论程序发生任何事情,都将执行,相当于try-catch-finally块 -->
    45             <aop:after method="myAfter" pointcut-ref="myPointCutID" />
    46             
    47         </aop:aspect>
    48     </aop:config>
    49 </beans>

    注意:后置通知只有在目标方法成功执行后才会被织入,而最终通知不论目标方法如何结束(包括成功执行和异常中止2种情况),它都会被织入。

    ③测试类:TestXmlAspectj.java

     1 package com.itheima.aspectj.xml;
     2 import org.springframework.context.ApplicationContext;
     3 import org.springframework.context.support.ClassPathXmlApplicationContext;
     4 import com.itheima.jdk.UserDao;
     5 // 测试类
     6 public class TestXmlAspectj {
     7     public static void main(String args[]) {
     8         String xmlPath = "com/itheima/aspectj/xml/applicationContext.xml";
     9         ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
    10         // 1 、从spring容器获得内容
    11         UserDao userDao = (UserDao) applicationContext.getBean("userDaoID");
    12         // 2、 执行方法
    13         userDao.addUser();
    14     }
    15 }

    ④运行结果:

    3)、基于注解的声明式AspectJ

    a)、AspectJ框架为AOP的实现提供了一套注解,用以取代Spring配置文件中为实现AOP功能所配置的臃肿代码。AspectJ的注解及其描述如下所示:

    b)、src->com.itheima.aspectj.annotation

    ①MyAspect.java

     1 package com.itheima.aspectj.annotation;
     2 import org.aspectj.lang.JoinPoint;
     3 import org.aspectj.lang.ProceedingJoinPoint;
     4 import org.aspectj.lang.annotation.After;
     5 import org.aspectj.lang.annotation.AfterReturning;
     6 import org.aspectj.lang.annotation.AfterThrowing;
     7 import org.aspectj.lang.annotation.Around;
     8 import org.aspectj.lang.annotation.Aspect;
     9 import org.aspectj.lang.annotation.Before;
    10 import org.aspectj.lang.annotation.Pointcut;
    11 import org.springframework.stereotype.Component;
    12 /**
    13  * 切面类,在此类中编写通知
    14  */
    15 @Aspect // 注解定义了切面类
    16 @Component // 由于Aspect类在Spring中是作为组件使用的,所以还要添加组件才能生效
    17 public class MyAspect {
    18     
    19     // 定义切入点表达式,声明公共切入点
    20     @Pointcut("execution(* com.itheima.jdk.*.*(..))")
    21     
    22     // 使用一个返回值为void、方法体为空的方法来命名切入点
    23     private void myPointCut(){}
    24     
    25     /**
    26      *     前置通知,替换
    27      * <aop:before method="myBefore" pointcut="execution(* com.itheima.jdk.*.*(..))" />
    28      */
    29     @Before("myPointCut()")
    30     public void myBefore(JoinPoint joinPoint) {
    31         System.out.print("前置通知 :模拟执行权限检查...,");
    32         System.out.print("目标类是:"+joinPoint.getTarget() );
    33         System.out.println(",被织入增强处理的目标方法为:" +joinPoint.getSignature().getName());
    34     }
    35     
    36     // 后置通知
    37     @AfterReturning(value="myPointCut()") // 注意有返回值,要有value
    38     public void myAfterReturning(JoinPoint joinPoint) {
    39         System.out.print("后置通知:模拟记录日志...," );
    40         System.out.println("被织入增强处理的目标方法为:" + joinPoint.getSignature().getName());
    41     }
    42     
    43     // 环绕通知    
    44     @Around("myPointCut()")
    45     public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    46         // 开始
    47         System.out.println("环绕开始:执行目标方法之前,模拟开启事务...");
    48         // 手动执行当前目标方法
    49         Object obj = proceedingJoinPoint.proceed();
    50         // 结束
    51         System.out.println("环绕结束:执行目标方法之后,模拟关闭事务...");
    52         return obj;
    53     }
    54     
    55     // 异常通知
    56     @AfterThrowing(value="myPointCut()",throwing="e")
    57     public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {
    58         System.out.println("异常通知:" + "出错了" + e.getMessage());
    59     }
    60     
    61     // 最终通知
    62     @After("myPointCut()")
    63     public void myAfter() {
    64         System.out.println("最终通知:模拟方法结束后的释放资源...");
    65     }
    66 }

    ②applicationContext.xml

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns="http://www.springframework.org/schema/beans"
     3   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     4   xmlns:aop="http://www.springframework.org/schema/aop"
     5   xmlns:context="http://www.springframework.org/schema/context"
     6   xsi:schemaLocation="http://www.springframework.org/schema/beans 
     7   http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
     8   http://www.springframework.org/schema/aop 
     9   http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
    10   http://www.springframework.org/schema/context 
    11   http://www.springframework.org/schema/context/spring-context-4.3.xsd">
    12       
    13       <!-- 指定需要扫描的包,使注解生效 -->
    14       <context:component-scan base-package="com.itheima" />
    15       
    16       <!-- 启动基于注解的声明式AspectJ支持,确定aop注解生效 -->
    17       <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    18 </beans>

    ③添加注解(DAO层):/chapter03/src/com/itheima/jdk/UserDaoImpl.java

     1 package com.itheima.jdk;
     2 
     3 import org.springframework.stereotype.Repository;
     4 
     5 @Repository("userDaoID") 
     6 //这里是aop基于注解的声明式AspectJ用到的:@Repository注解将UserDaoImpl(数据访问层DAO层)的类标识为Spring中的Bean,其写法相当于配置文件中
     7 //<bean id = "userDaoID" class = "com.itheima.annotation.UserDaoImpl" />的编写
     8 
     9 public class UserDaoImpl implements UserDao {
    10     // 本案例中会将实现类UserDaoImpl作为     目标类     ,对其中的方法进行增强处理
    11     
    12     public void addUser() {
    13         System.out.println("添加用户");
    14     }
    15 
    16     public void deleteUser() {
    17         System.out.println("删除用户");
    18     }
    19 }

    ④TestAnnotationAspectj.java

     1 package com.itheima.aspectj.annotation;
     2 import org.springframework.context.ApplicationContext;
     3 import org.springframework.context.support.ClassPathXmlApplicationContext;
     4 import com.itheima.jdk.UserDao;
     5 // 测试类
     6 public class TestAnnotationAspectj {
     7     public static void main(String args[]) {
     8         String xmlPath = "com/itheima/aspectj/annotation/applicationContext.xml";
     9         ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
    10         // 1、从spring容器获得内容
    11         UserDao userDao = (UserDao) applicationContext.getBean("userDaoID");
    12         // 2、 执行方法
    13         userDao.addUser();
    14     }
    15 }

    ⑤运行结果:(与上面的执行结果一样,只是在目标方法前后通知的执行顺序发生了变化)

  • 相关阅读:
    JDom写入XML例子
    hdu 2549
    hdu 1328
    hdu 1334
    hdu 2547
    hdu 2374
    hdu 2550
    hdu 1335
    hdu 2548
    hdu 1722
  • 原文地址:https://www.cnblogs.com/acgoto/p/10598660.html
Copyright © 2020-2023  润新知