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. }

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

Java EE学习笔记(三)的更多相关文章

  1. Java IO学习笔记三

    Java IO学习笔记三 在整个IO包中,实际上就是分为字节流和字符流,但是除了这两个流之外,还存在了一组字节流-字符流的转换类. OutputStreamWriter:是Writer的子类,将输出的 ...

  2. Java IO学习笔记三:MMAP与RandomAccessFile

    作者:Grey 原文地址:Java IO学习笔记三:MMAP与RandomAccessFile 关于RandomAccessFile 相较于前面提到的BufferedReader/Writer和Fil ...

  3. JAVA WEB学习笔记(三):简单的基于Tomcat的Web页面

    注意:每次对Tomcat配置文件进行修改后,必须重启Tomcat 在E盘的DATA文件夹中创建TomcatDemo文件夹,并将Tomcat安装路径下的webapps/ROOT中的WEB-INF文件夹复 ...

  4. Java设计模式学习笔记(三) 工厂方法模式

    前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 简介 上一篇博客介绍了简单工厂模式,简单工厂模式存在一个很严重的问题: 就是当系统需要引入 ...

  5. java jvm学习笔记三(class文件检验器)

    欢迎装载请说明出处:http://blog.csdn.net/yfqnihao 前面的学习我们知道了class文件被类装载器所装载,但是在装载class文件之前或之后,class文件实际上还需要被校验 ...

  6. Java基础学习笔记三 Java基础语法

    Scanner类 Scanner类属于引用数据类型,先了解下引用数据类型. 引用数据类型的使用 与定义基本数据类型变量不同,引用数据类型的变量定义及赋值有一个相对固定的步骤或格式. 数据类型 变量名 ...

  7. 《Thinking in Java》学习笔记(三)

    1>Java中的常量 使用final和static来修饰的变量称为常量,常量用大写字母表示,字母间用下划线连接. Java中定义常量有以下几种方式: interface ConstantInte ...

  8. Java EE学习笔记(九)

    MyBatis的关联映射 1.关联关系概述 1).实际的开发中,对数据库的操作常常会涉及到多张表,这在面向对象中就涉及到了对象与对象之间的关联关系.针对多表之间的操作,MyBatis提供了关联映射,通 ...

  9. Java EE学习笔记(八)

    动态SQL 1.动态SQL中的元素 1).作用:无需手动拼装SQL,MyBatis已提供的对SQL语句动态组装的功能,使得数据库开发效率大大提高! 2).动态SQL是MyBatis的强大特性之一,My ...

随机推荐

  1. (转)定制findbugs规则

    转载自http://www.51testing.com/html/97/13997-211893.html 这类文章极少,字节码操作需要对becl库及jvm字节码操作有一定常识.参考: http:// ...

  2. Sublime Text 相关教程(转)

    曾经有人说过,世界上有两种编辑器,好用和不好用的:而在好用的编辑器中,又分两种,免费的和死贵死贵的.譬如说VIM 和 TextMate,就是免费和死贵的典型.很不幸,今天的主角 Sublime Tex ...

  3. html5--3.22 综合实例03

    html5--3.22 综合实例03 学习要点 通过一个综合实例来回顾学过的内容 这一章的内容比较多,不必强求一下子全记住,多做一些练习,用得多了自然就可以记住了 可以自己找一些实例练练手,比如各网站 ...

  4. 【C/C++】计算两个整数的最大公约数和最小公倍数

    算法一 任何>1的整数都可以写成一个或多个素数因子乘积的形式,且素数乘积因子以非递减序出现. 则整数x,y可以分别标记为:x=p1x1p2x2...pmxm y=p1y1p2y2...pmym ...

  5. Keras 可视化 model

    参考:https://keras.io/visualization/ error解决参考:http://blog.csdn.net/wangjian1204/article/details/50346 ...

  6. Java笔记(六)

    IO流: 字符流和字节流: 字符流两个基类: InputStream OutputStream 字节流两个基类: Reader Writer FileWriter: import java.io.Fi ...

  7. phpStorm的远端部署

    首先远端服务器的路径: /var/www -rwxrwxrwx jiangzhaowei jiangzhaowei 6月 index.html* lr-xr-xr-x root root 2月 php ...

  8. 配置web应用

    web应用配置虚拟主机1.web应用的虚拟路径映射,就是web应用的真实存在的路径配置一个虚拟路径 在conf目录下的Server.xml 的<Host>标签中,配置<Context ...

  9. py-day2-sys模块、os模块、运算符、列表、字典

    一.sys 模块 import sys print (sys.path)#打印环境变量 print(sys.aegv) #打印脚本的名字相对路径 print(sys.aegv)1 2 3 4 prin ...

  10. TQ210——S5PV210 uboot顶层mkconfig分析

    转自:http://blog.csdn.net/wqx521/article/details/52491300 ############################################ ...