SpringAop


1. Spring 中的 AOP 简介

  • AOP Aspect Oriented Programing 面向切面编程
  • AOP 采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)
  • Spring 中的 Aop 是纯 Java 来实现的,使用动态代理的方式增强代码
  • Spring 使用动态代理的机制是判断委托类是否实现了接口,如果实现了接口则使用 jdk 的动态代理,如果没有实现接口则使用 cglib 的动态代理
  • AOP 不是由 Spring 提出来的,是由 AOP 联盟定义的

2. 代理模式

  • 不使用对象来进行真实操作,使用我们自己创建的代理对象来操作
  • 为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

组成

  1. 抽象角色:通过接口或抽象类声明真实角色实现的业务方法
  2. 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作
  3. 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用

2.1 静态代理

1
2
3
4
5
//抽象角色,声明真实角色实现的业务方法
//委托类和代理类的接口
public interface KindWoman {
String hear(String msg, Integer score);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.uplooking.aop.agent;

/**
* 代理类
*/
public class Wang implements KindWoman{
private Pan pan;//委托对象

/**
* 在代理器的构造器中持有一个委托的对象
* @param pan
*/
public Wang(Pan pan) {
this.pan = pan;
}

@Override
public String hear(String msg, Integer score) {
System.out.println("Wang听到的msg为:" + msg);
String s = pan.hear(msg, score + 20);
return "我是wang,目标回复的内容是:"+ s;
}
}
1
2
3
4
5
6
7
8
9
10
11
/**
* 委托类
*/
public class Pan implements KindWoman{
@Override
public String hear(String msg, Integer score) {
System.out.println("pan听到的msg为:" + msg);
return score > 60 ? "yes" : "NONONO";

}
}
1
2
3
4
5
6
7
8
9
10
11
12
//发起者

public class XiMen {
public String say(String msg,Integer score){
//创建目标类对象
Pan pan = new Pan();
//创建代理类的对象
Wang wang = new Wang(pan);
//调用代理方法
return wang.hear(msg,score);
}
}
1
2
3
4
5
6
7
8
9
public class App {
public static void main(String[] args) {
XiMen xiMen = new XiMen();
String msg = "抽烟喝酒烫头";
System.out.println("XiMen说的msg为:" + msg);
String s = xiMen.say(msg, 50);
System.out.println(s);
}
}

2.2 动态代理

2.2.1 基于原生的 JDK 的动态代理

1
2
3
4
5
6
7
8
9
10
11
12
13
Pan pan = new Pan();
//创建代理对象
WindWoman windWoman = (WindWoman) Proxy.newProxyInstance(Test.class.getClassLoader(),pan.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("--------------我给你说呀,他这个女人真的很漂亮,她让我告诉你下面的内容--------------:");
Object obj = method.invoke(pan, args);
System.out.println("--------------记得啊,完事以后要给我好多钱,很辛苦的--------------");
return obj;
}
});

windWoman.say();

2.2.2 基于CGLIB的动态代理

  • 因为原生的 jdk 的动态代理存在缺陷,代理类和委托类必须实现同一个接口
  • 所以有个开源的动态代理框架出现了(CGLIB)
  • CGLIB不要求委托类必须实现接口===>因为CGLIB底层是基于继承实现的

2.2.3 jdk的动态代理与cglib

  • jdk 的动态代理底层实现是基于接口实现
  • cglib 的动态代理底层是基于继承

3. Aop的专业术语

  • Joinpoint(连接点) :目标类中可以被增强的方法(例如有四个方法)
  • **Pointcut(切入点,切点)**:目标类中要被增强的方法
  • Advice(通知/增强) :增强的代码
    • 前置增强(通知)
    • 后置增强(通知)
    • 环绕增强(通知)
  • **Target(目标对象)**:目标对象
  • Weaving(织入) :把增强应用切点的过程
  • **Proxy(代理)**: 一个类被 AOP 织入增强后,就产生一个结果代理类
  • Aspect(切面):是切点通知的结合

4. 基于 Jdk 动态代理实现自定义AOP

  • 使用 JDK 的动态代理,使用 Proxy 类里面的方法创建代理对象

  • 调用 newProxyInstance(1:ClassLoder,2:类<?>[] interfances,3:InvocationHand)

    参数的作用:1. 类的加载器;2. 增强方法所在类,这个类实现的接口,支持多个接口,所以以数组的形式展出;3. 实现这个接口InvocationHand,创建代理的对象,写增强的方法。

    方法的作用:将方法调用到处理程序,具有指定的代理类,调用处理程序的代理实例,该代理类,由指定的类加载器定义并实现指定的接口

  • method.invoke:调用 method 类代表的方法,其中 obj 是对象名,args 是传入 method 方法的参数

  • pom依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    <dependencies>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>4.3.10.RELEASE</version>
    </dependency>

    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>4.3.10.RELEASE</version>
    </dependency>


    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.3.10.RELEASE</version>
    </dependency>

    <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.10</version>
    </dependency>
    </dependencies>
  • 创建接口 UserService

    1
    2
    3
    4
    public interface UserService {
    void addUser();
    void deleteUser();
    }
  • 定义目标类 UserServiceImpl

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class UserServiceImpl implements UserService {
    @Override
    public void addUser() {
    System.out.println("添加用户");
    }

    @Override
    public void deleteUser() {
    System.out.println("删除用户");
    }
    }
  • 定义切面 MyAspect

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class MyAspect {
    public void before() {
    System.out.println("前置通知");
    }
    public void after() {
    System.out.println("后置通知");
    }
    public void around() {
    System.out.println("环绕通知");
    }
    }
  • 定义目标对象的工厂类(用于创建目标对象说对应的代理对象) UserServiceFactory

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public class UserServiceFactory {

    public UserService newUserServiceProxy() {
    //创建目标对象
    UserServiceImpl userService = new UserServiceImpl();
    //创建切面对象
    MyAspect myAspect = new MyAspect();
    //创建代理对象
    UserService userServiceProxy = (UserService) Proxy.newProxyInstance(UserServiceFactory.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), new InvocationHandler() {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    myAspect.before();
    Object result = method.invoke(userService, args);
    myAspect.after();
    return result;
    }
    });
    return userServiceProxy;
    }
    }
  • 入口类

    1
    2
    3
    4
    5
    public static void main(String[] args) {
    UserServiceFactory factory = new UserServiceFactory();
    UserService userService = factory.newUserServiceProxy();
    userService.addUser();
    }

5. AOP联盟定义的通知类型

Spring 按照通知 (Advice) 在目标类方法的切点的位置,分为5类:

  • 前置通知org.springframework.aop.MethodBeforeAdvice
    • 在目标方法执行前实施增强
  • 后置通知org.springframework.aop.AfterReturningAdvice
    • 在目标方法执行后实施增强
  • 环绕通知org.aopalliance.intercept.MethodInterceptor
    • 在目标方法执行前后实施增强,环绕通知,必须手动执行目标方法
  • 异常抛出通知org.springframework.aop.ThrowsAdvice
    • 在方法抛出异常后实施增强
  • 引介通知 org.springframework.aop.IntroductionInterceptor
    • 在目标类中添加一些新的方法和属性

6. Spring中的AOP(不建议使用)

6.1 Spring中的半自动AOP

  • 定义接口

    1
    2
    3
    4
    public interface UserService {
    void addUser();
    void deleteUser();
    }
  • 定义目标类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class UserServiceImpl implements UserService {
    @Override
    public void addUser() {
    System.out.println("添加用户");
    }

    @Override
    public void deleteUser() {
    System.out.println("删除用户");
    }
    }
  • 定义切面(实现Spring提供的拦截器)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public class MyAspect implements org.aopalliance.intercept.MethodInterceptor {
    //定义三个通知
    public void before() {
    System.out.println("前置通知");
    }

    public void after() {
    System.out.println("后置通知");
    }

    public void around() {
    System.out.println("环绕通知");
    }


    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
    before();
    Object result = methodInvocation.proceed();
    after();
    return result;
    }
    }

  • 配置Spring中自带的工厂 applicationContext.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    <?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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    https://www.springframework.org/schema/context/spring-context.xsd">

    <!--<bean id="userServiceFactory" class="com.it.framework.UserServiceFactory"></bean>-->

    <!--定义目标对象-->
    <bean id="userServiceImpl" class="com.it.framework.UserServiceImpl"></bean>
    <!--定义切面对象-->
    <bean id="myAspect" class="com.it.framework.MyAspect"></bean>
    <!--定义工厂对象-->
    <bean id="userServiceproxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    <!--配置接口-->
    <property name="interfaces" value="com.it.framework.UserService"/>
    <!--配置目标对象-->
    <property name="targetName" value="userServiceImpl"/>
    <!--配置拦截器-->
    <property name="interceptorNames" value="myAspect"/>
    <!--配置动态代理方式,true是使用cglib的方式,默认使用jdk的动态代理-->
    <property name="optimize" value="true"></property>
    </bean>
    </beans>

6.2 Spring中的全自动AOP

  • pom依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    <dependencies>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>4.3.10.RELEASE</version>
    </dependency>

    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>4.3.10.RELEASE</version>
    </dependency>


    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.3.10.RELEASE</version>
    </dependency>
    <!--spring全自动aop依赖aspectj联盟的依赖-->
    <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.10</version>
    </dependency>
    </dependencies>
  • 定义接口===>同半自动AOP一致

  • 定义目标类===>同半自动AOP一致

  • 定义切面(实现Spring提供的拦截器)===>同半自动AOP一致

  • 配置applicationContext.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <!--定义目标对象-->
    <bean id="userServiceImpl" class="com.it.framework.UserServiceImpl"></bean>

    <!--定义切面对象-->
    <bean id="myAspect" class="com.it.framework.MyAspect"></bean>

    <!--
    proxy-target-class=true表示使用cglib动态代理
    -->
    <aop:config proxy-target-class="true">
    <!--定义切点表达式-->
    <aop:pointcut id="myPointCut" expression="execution(* com.it.framework.UserServiceImpl.*(..))"/>
    <aop:advisor advice-ref="myAspect" pointcut-ref="myPointCut"></aop:advisor>
    </aop:config>
  • 入口类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <!--定义目标对象-->
    <bean id="userServiceImpl" class="com.it.framework.UserServiceImpl"></bean>

    <!--定义切面对象-->
    <bean id="myAspect" class="com.it.framework.MyAspect"></bean>

    <!--
    proxy-target-class=true表示使用cglib动态代理
    -->
    <aop:config proxy-target-class="true">
    <!--定义切点表达式-->
    <aop:pointcut id="myPointCut" expression="execution(* com.it.framework.UserServiceImpl.*(..))"/>
    <aop:advisor advice-ref="myAspect" pointcut-ref="myPointCut"></aop:advisor>
    </aop:config>

7. 切点表达式

  • 用于描述需要增强的方法(切点)

  • 语法

    1
    execution(修饰符  返回值  包.类.方法名(参数) throws异常)
    • 修饰符,一般省略

      1
      2
      public    		 公共方法
      * 任意
    • 返回值类型,不能省略

      1
      2
      3
      void         返回没有值
      String 返回值字符串
      * 任意
    • 包 可以省略

      1
      2
      com.it.spring       固定包
      com.it.spring.. spring包下面的所有子包(含自己)
    • 类,可以省略

      1
      2
      3
      4
      UserServiceImpl			指定类
      *Impl 以Impl结尾
      User* 以User开头
      * 任意
    • 方法名,不能省略

      1
      2
      3
      4
      addUser					固定方法
      add* 以add开头
      *Do 以Do结尾
      * 任意
    • (参数)

      1
      2
      3
      4
      ()						无参
      (int) 一个整型
      (int ,int) 两个
      (..) 参数任意
    • throws ,可省略,一般不写

  • eg

    1
    UserServiceImpl类中任意参数的方法 * com.it.framework.UserServiceImpl.*(..)

9. 基于 Aspectj 的 AOP

  • AspectJ 是一个基于 Java 语言的面向切面的 AOP 框架
  • Spring2.0 以后新增了对 AspectJ 切点表达式支持
  • @AspectJ 是 AspectJ1.5 新增功能,通过 JDK5 注解技术,允许直接在 Bean 类中定义切面
  • 新版本 Spring 框架,建议使用 AspectJ 方式来开发 AOP

9.1 AspectJ 的增强类型

  • @Before 前置通知,相当于 BeforeAdvice
1
2
3
4
@Before("execution(* com.uplooking.aop.UserDao.add*(..))")
public void beforeAdvice() {
System.out.println("前置通知....");
}
  • @AfterReturning 后置通知,相当于AfterReturningAdvice
1
2
3
4
@AfterReturning(value = "execution(* com.uplooking.aop.UserDao.add*(..))", returning = "ret")
public void afterReturningAdvice(String ret) {
System.out.println("后置通知.." + ret);
}
  • @Around 环绕通知,相当于MethodInterceptor
1
2
3
4
5
6
@Around("execution(* com.uplooking.aop.UserDao.add*(..))")
public void arounrAdvice(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕通知前..");
pjp.proceed();
System.out.println("环绕通知后..");
}
  • @AfterThrowing抛出通知,相当于ThrowAdvice
1
2
3
4
@AfterThrowing("execution(* com.uplooking.aop.UserDao.add*(..))")
public void throwAdvice() {
System.out.println("异常通知....");
}
  • @After 最终final通知,不管是否异常,该通知都会执行
1
2
3
4
@After(value = "execution(* com.uplooking.aop.UserDao.add*(..))")
public void afterAdvice() {
System.out.println("最终通知....");
}

9.2. 基于 Aspectj 的Aop

  • pom依赖
1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<!--Spring整合Aspectj的包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
  • 定义切面

    切面 = 切点  +  通知
    
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 切面 = 切点(切点表达式) + 通知(方法)
*/
@Aspect
@Component
public class MyAspect {

@Before("execution(* add*(..))")
public void beforeAdvice() {
System.out.println("前置通知....");
}
}

  • 定义要增强的类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Repository
public class UserDao {
public void add() {
System.out.println("添加用户...");
}

public void add1() {
System.out.println("add1...");
}

public void delete() {
System.out.println("删除用户...");
}

public void update() {
System.out.println("修改用户...");
}

public void select() {
System.out.println("查询用户...");
}
}
  • 配置applicationContext.xml
1
2
3
4
5
<!--组件扫描器-->
<context:component-scan base-package="com.uplooking.aop"/>

<!--aop自动创建代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
  • 测试程序
1
2
3
4
5
6
7
8
9
10
11
12
@RunWith(SpringRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class UserDaoTest {
@Autowired
private UserDao userDao;
//edu.uplooking.com passwd :80684
@Test
public void testAdd() {
userDao.add();
userDao.add1();
}
}

文章作者: Yang Shiyu
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Yang Shiyu !
  目录