spring框架学习-aop

前言

aop是spring框架中最为核心的一部分

1.什么是aop

  AOP(Aspect Oriented Programming),通常称为面向切面编程。它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

  什么是切面,什么是公共模块,那么我们概念少说,直接通过一个实例来看看 AOP 到底是什么。

2.aop的关键术语

  1.target:目标类,需要被代理的类。例如:UserService

  2.Joinpoint(连接点):所谓连接点是指那些可能被拦截到的方法。例如:所有的方法

  3.PointCut 切入点:已经被增强的连接点。例如:addUser()

  4.advice 通知/增强,增强代码。例如:after、before

  5. Weaving(织入):是指把增强advice应用到目标对象target来创建新的代理对象proxy的过程.

  6.proxy 代理类:通知+切入点

  7. Aspect(切面): 是切入点pointcut和通知advice的结合

3.aop的通知类型 

  Spring按照通知Advice在目标类方法的连接点位置,可以分为5类

  • 前置通知 org.springframework.aop.MethodBeforeAdvice
    • 在目标方法执行前实施增强,比如上面例子的 before()方法
  • 后置通知 org.springframework.aop.AfterReturningAdvice
    • 在目标方法执行后实施增强,比如上面例子的 after()方法
  • 环绕通知 org.aopalliance.intercept.MethodInterceptor
    • 在目标方法执行前后实施增强
  • 异常抛出通知 org.springframework.aop.ThrowsAdvice
    • 在方法抛出异常后实施增强
  • 引介通知 org.springframework.aop.IntroductionInterceptor

     在目标类中添加一些新的方法和属性

4.图解

  UserService是一个业务层的类,有三个方法

  现在有一个需求,要求在执行这三个方法的前后要开启关闭事务

  具体可以根据下面这张图来理解:

5.对于这个需求,一般有几种解决办法

  1.静态代理

  2.使用jdk动态代理(要求是必须实现接口)

  3.另外一种动态代理实现模式Gglib,则不需要实现接口

  在此对于这些都不做细述

  本章的内容:aop也是通过动态代理的原理来实现

个人案例

1.创建maven工程

2.在pom.xml添加相关的依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.cong</groupId>
    <artifactId>spring_aop</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>
    </dependencies>
</project>

3.在java目录下创建com.cong.pojo.User类

 1 package com.cong.pojo;
 2 
 3 public class User {
 4     private int id;
 5     private String name;
 6 
 7     public int getId() {
 8         return id;
 9     }
10 
11     public void setId(int id) {
12         this.id = id;
13     }
14 
15     public String getName() {
16         return name;
17     }
18 
19     public void setName(String name) {
20         this.name = name;
21     }
22 }

4.在java目录下创建com.cong.service.UserService接口

1 package com.cong.service;
2 
3 import com.cong.pojo.User;
4 
5 public interface UserService {
6     void addUser(User user);
7     void deleteUser(int id);
8 }

5.在service目录下创建实现类UserServiceImpl

 1 package com.cong.service;
 2 
 3 import com.cong.pojo.User;
 4 
 5 public class UserServiceImpl implements UserService {
 6     @Override
 7     public void addUser(User user) {
 8         System.out.println("add user:"+user.getName());
 9     }
10 
11     @Override
12     public void deleteUser(int id) {
13         System.out.println("delete user: "+id);
14     }
15 }

6.在java目录下创建com.cong.ui.MyTransaction类

 1 package com.cong.ui;
 2 
 3 public class MyTransaction {
 4     public void before(){
 5         System.out.println("开启事务...");
 6     }
 7     public void after(){
 8         System.out.println("关闭事务...");
 9     }
10 }

7.在resources目录下创建context.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.xsd
 7         http://www.springframework.org/schema/aop
 8         http://www.springframework.org/schema/aop/spring-aop.xsd">
 9     <bean id="user" class="com.cong.pojo.User">
10         <property name="id" value="1"></property>
11         <property name="name" value="cong"></property>
12     </bean>
13     <bean id="userService" class="com.cong.service.UserServiceImpl"></bean>
14     <bean id="myTransaction" class="com.cong.ui.MyTransaction"></bean>
15     <!-- 配置aop -->
16     <aop:config>
17         <!-- 切入点表达式,所谓切入点就是你需要增强的方法 -->
18         <aop:pointcut id="myPointcut" expression="execution(* com.cong.service.UserServiceImpl.*(..))"></aop:pointcut>
19         <!-- 配置切面,切面就是增强的过程中需要用到的类 -->
20         <aop:aspect ref="myTransaction">
21             <!-- 配置通知的类型,并且建立通知方法和切入点方法的关联-->
22             <!-- "before"就是MyTransaction中的方法 -->
23             <aop:before method="before" pointcut-ref="myPointcut"></aop:before>
24             <!--如果没有配置myPointcut,上面的语句可以写成-->
25             <!--<aop:before method="before" pointcut="execution(* com.cong.service.UserServiceImpl.*(..))"></aop:before>-->
26             <aop:after-returning method="after" pointcut-ref="myPointcut"></aop:after-returning>
27         </aop:aspect>
28     </aop:config>
29     <!--spring中基于XML的AOP配置步骤
30     1、把通知Bean也交给spring来管理
31     2、使用aop:config标签表明开始AOP的配置
32     3、使用aop:aspect标签表明配置切面
33             id属性:是给切面提供一个唯一标识
34             ref属性:是指定通知类bean的Id。
35     4、在aop:aspect标签的内部使用对应标签来配置通知的类型
36            我们现在示例是让printLog方法在切入点方法执行之前之前:所以是前置通知
37            aop:before:表示配置前置通知
38                 method属性:用于指定Logger类中哪个方法是前置通知
39                 pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中哪些方法增强
40 
41         切入点表达式的写法:
42             关键字:execution(表达式)
43             表达式:
44                 访问修饰符  返回值  包名.包名.包名...类名.方法名(参数列表)
45             标准的表达式写法:
46                 public void com.itheima.service.impl.AccountServiceImpl.saveAccount()
47             访问修饰符可以省略
48                 void com.itheima.service.impl.AccountServiceImpl.saveAccount()
49             返回值可以使用通配符,表示任意返回值
50                 * com.itheima.service.impl.AccountServiceImpl.saveAccount()
51             包名可以使用通配符,表示任意包。但是有几级包,就需要写几个*.
52                 * *.*.*.*.AccountServiceImpl.saveAccount())
53             包名可以使用..表示当前包及其子包
54                 * *..AccountServiceImpl.saveAccount()
55             类名和方法名都可以使用*来实现通配
56                 * *..*.*()
57             参数列表:
58                 可以直接写数据类型:
59                     基本类型直接写名称           int
60                     引用类型写包名.类名的方式   java.lang.String
61                 可以使用通配符表示任意类型,但是必须有参数
62                 可以使用..表示有无参数均可,有参数可以是任意类型
63             全通配写法:
64                 * *..*.*(..)
65 
66             实际开发中切入点表达式的通常写法:
67                 切到业务层实现类下的所有方法
68                     * com.itheima.service.impl.*.*(..)
69     -->
70 </beans>

8.在test.java目录下创建TestAop类

 1 import com.cong.pojo.User;
 2 import com.cong.service.UserService;
 3 import org.springframework.context.ApplicationContext;
 4 import org.springframework.context.support.ClassPathXmlApplicationContext;
 5 
 6 public class TestAop {
 7     public static void main(String[] args) {
 8         ApplicationContext context = new ClassPathXmlApplicationContext("context.xml");
 9         UserService userService = (UserService) context.getBean("userService");
10         User user = (User)context.getBean("user");
11         userService.addUser(user);
12         userService.deleteUser(10);
13     }
14 }

9.运行结果

10.完整目录

参考:https://www.cnblogs.com/ysocean/p/7476379.html#_label6

猜你喜欢

转载自www.cnblogs.com/ccoonngg/p/11223619.html
今日推荐