Day23 Spring AOP***

前言

我们学习 spring 的 aop,就是通过配置的方式,实现上一篇的功能。

AOP相关术语

  1. target(目标对象): 代理的目标对象。
    就是我们需要增强的那个类,如LaoZong.class
  2. proxy(代理):一个类被 AOP 织入增强后,就产生一个结果代理类。
    就是自定义的代理的对象 $Proxy0.class
  3. joinPoint(连接点):程序执行的某个特定位置:如类开始初始化前、类初始化后、类某个方法调用前、调用后、方法抛出异常后。Spring仅支持方法的连接点:即仅能在方法调用前、方法调用后、方法抛出异常时以及方法调用前后这些程序执行点织入增强。
    就是LaoZong.class中的eat()、sleep()方法前后。
  4. pointCut(切入点):就是在目标类中要实际增强的方法
    就是要增强的eat()和sleep()
  5. weave(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。
    就是把MiShu类中的方法放到eat()和sleep()前后的过程。
  6. 引介Introduction:引介是一种特殊的增强,它为类添加一些属性和方法(课程不使用)
  7. advice(通知):将代理对象中的方法应用到目标类的过程中产生的结果。
    给eat(),sleep()提供加强方法的类,如:MiShu类.
  8. aspect(切面):所有的切入点和代理对象的方法组成在一起 构成了切面。
    就是通知和切入点的结合。

Spring AOP的准备

pom.xml

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

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.7.2</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.2.9.RELEASE</version>
        </dependency>
    </dependencies>

IUser

//1 必须要有一个接口
public interface IUser {
    
    
    void work();
    boolean paly();
}

UserImpl

//2 类与接口是实现关系
public class UserImpl implements IUser {
    
    
    //连接点
    @Override
    public void work() {
    
    
        System.out.println("我加班我快乐");
    }
    
	@Override
    public boolean play(){
    
    
        System.out.println("越玩越颓废!");
        return true;
    }
}

Advice

//通知
public class Advice {
    
    
    public void writeLog(){
    
    
        System.out.println("写一个开发备忘录");
    }
}

在这里插入图片描述

Spring AOP的Xml配置

<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">


<!--在此进行AOP基于配置-->
    <!--1:创建目标类对象:UserImpl-->
    <bean id="userImpl" class="cn.cyl.service.impl.UserImpl"/>
	<!--2:创建增强对象,即配置通知:Advice-->
    <bean id="advice" class="cn.cyl.service.Advice"/>
	<!--3:开始基于AOP增强
         3.1 我要对UserImpl类的work方法进行后置增强,
         在work方法之后先执行Advice类中writeLog方法
    -->
    <aop:config >
    	<!--1:配置切入点(配置要增强的方法)
          	id:表示给要增强的方法切入点进行一个编号
          	execution:表示哪个类中的方法
        -->
        <aop:pointcut id="work" expression="execution(public void cn.cyl.service.impl.UserImpl.work())"/>
        <!--2:配置切面:增强的方式->在work方法执行之后调用一下writeLog
          ref:用来指定增强类
        -->
        <aop:aspect ref="advice">
        	 <!--配置通知的类型-->
        	 <!--配置增强的细节:要用那个方法去给work增强,是放在work的前面和后边-->
             <aop:after method="writeLog"  pointcut-ref="work"/>
        </aop:aspect>
    </aop:config>
</beans>

UserImplTest

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class UserImplTest {
    
    

    @Qualifier("userImpl")
    @Autowired
    IUser iUser;

    @Test
    public void work() {
    
    
        iUser.work();
        //xml文件中未增强play()方法
        iUser.play();
        System.out.println(iUser);
    }
}

运行结果
xml文件中未增强paly()方法,所以play()方法执行后不会执行writeLog()方法

我加班我快乐!
写一个开发备忘录
越玩越颓废!
cn.cyl.service.impl.UserImpl@2e1d27ba

AOP切面编程-切面表达式

  • (1)什么是切面表达式
    execution([修饰符] 返回值类型 包.类.方法(参数列表));
  • (2)切面表达式有什么用?
    符合表达式的方法,会被增强

使用* 表示任意的内容
使用..可以表示包与子包下面的类
使用..可以写在方法(…)表示任意参数

<aop:config>
		<!--
           切面表达式:
             execution([修饰符] 返回值类型  包.类.方法(参数列表) );

             1:完全写法
                execution(public void cn.cyl.service.impl.UserImpl.work(int) );
             2:修饰符省略
               execution( void cn.cyl.service.impl.UserImpl.work(int) );
             3:简化2-返回值类型写通配符
              execution(*  cn.cyl.service.impl.UserImpl.work(int) );
             4:简化3-包名写通配符
              execution(*  *.*.*.UserImpl.work(int) );
             5:简化4-包名通配符简化
              execution(*  *..UserImpl.work(int) );
             6:简化5-类名通配符
              execution(*  *..*.work(int) );
             7:简化6-参数写通配符
             execution(*  *..*.work(..) );
        -->
        <!--
        表示UserImpl类中的所有带参数和不带参数的方法
        <aop:pointcut id="mthods" expression="execution(* *..UserImpl.*(..))"/>
        -->
        <!--表示UserImpl类中的所有不带参数的方法-->
        <aop:pointcut id="mthods" expression="execution(* *..UserImpl.*())"/>
        <aop:aspect ref="advice">
             <aop:after method="writeLog"  pointcut-ref="work"/>
        </aop:aspect>
</aop:config>

再次执行UserImplTest
运行结果

我加班我快乐!
写一个开发备忘录
越玩越颓废!
写一个开发备忘录
cn.cyl.service.impl.UserImpl@7e9131d5

可以发现play()方法被增强了。

AOP切面编程-增强方式

  • (1)增强方式的方式有哪些?
    实质是增强的四个可选调用位置
  • (2)每个位置有对应的名称
    在这里插入图片描述
  • 示例

IUser

public interface IUser {
    
    
    void add(User user);
    void deleteById(Integer id);
}

UserImpl

public class UserImpl implements IUser {
    
    
    @Override
    public void add(User user) {
    
    
        System.out.println("add ...");
    }
    @Override
    public void deleteById(Integer id) {
    
    
        System.out.println("deleteById ...");
        //除0异常
        System.out.println(1/0);
    }

Advice

public class Advice {
    
    
    //方法之前执行
    public void before(){
    
    
        System.out.println("before————前面记录日志");
    }

    //程序正常执行后执行,出现异常不执行
    public void afterReturning(){
    
    
        System.out.println("afterReturning————后面记录日志");
    }

    //出现异常执行
    public void afterThrow(){
    
    
        System.out.println("afterThrow————异常记录日志");
    }

    //最终增强,最终都会执行
    public void after(){
    
    
        System.out.println("after————最终记录日志");
    }
}

applicationContext.xml

    <aop:config>
        <aop:pointcut id="all" expression="execution(* cn.cyl.service..*.*(..))"/>
        <aop:aspect ref="advice">
            <!--try-->
            <!--前置增强,方法之前执行-->
            <aop:before method="before" pointcut-ref="all"/>
            <!--后置增强,程序正常运行返回,出现异常不执行-->
            <aop:after-returning method="afterReturning" pointcut-ref="all"/>
            
            <!--catch-->
            <!--异常增强,出现异常执行-->
            <aop:after-throwing method="afterThrow" pointcut-ref="all"/>

            <!--finally-->
            <!--最终增强,最终都会执行-->
            <aop:after method="after" pointcut-ref="all"/>
        </aop:aspect>
    </aop:config>

测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class UserImplTest {
    
    

    @Autowired
    IUser iUser;

    @Test
    public void work1() {
    
    
        //没有异常,正常运行
        iUser.add(null);
    }

    @Test
    public void work2(){
    
    
        //有异常
        iUser.deleteById(1);
    }
}

执行结果
work1()
在这里插入图片描述
work2()
在这里插入图片描述

  • (3)arround环绕
    简化上面的代码
    重点:
    一个参数:ProceedingJoinPoint joinPoint:连接点,也就是目标类中所有方法。
    一个方法:joinPoint.proceed();//切到原来的目标方法,进行执行。等价于动态代理 obj = method.invoke(finalPerson, objects);
  • 示例

Advice

public class Advice {
    
    
    //环绕增强
    //参数:目标类中的所有方法
    public void  around(ProceedingJoinPoint joinPoint){
    
    
        try {
    
    
            //执行before
            before();
            System.out.println("around————");
            joinPoint.proceed();
            afterReturning();
        } catch (Throwable throwable) {
    
    
            //补救方法
            afterThrow();
            throwable.printStackTrace();
        }finally {
    
    
            //释放资源
            after();
        }
    }
}

applicationContext.xml

<aop:config>
        <aop:pointcut id="all" expression="execution(* cn.cyl.service..*.*(..))"/>
        <aop:aspect ref="advice">
        	<!--环绕增强-->
            <aop:around method="around" pointcut-ref="all"/>
        </aop:aspect>
    </aop:config>

执行结果
work01
在这里插入图片描述
work02
在这里插入图片描述

AOP切面编程-注解***

  • (1)spring注解配置
    一个xml开启注解自动代理

几个注解:

  1. 切面@Aspect+四个注解 @Before @AfterReturning @AfterThrowing@After
  2. 切面@Aspect+@Arround
  • (2)示例

在配置文件中导入 context 的名称空间

<?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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--包扫描创建对象,扫描包与它下面的子包的所有类-->
    <context:component-scan base-package="cn.cyl.service"/>

    <!--开启自动AOP代理-->
    <!--声明自动为spring容器中那些配置@aspectJ切面的bean创建代理,织入切面。-->
    <aop:aspectj-autoproxy/>

</beans>

资源使用注解配置
UserImpl

@Component
public class UserImpl implements IUser {
    
    
    @Override
    public void add(User user) {
    
    
        System.out.println("add ...");
    }
    @Override
    public void deleteById(Integer id) {
    
    
        System.out.println("deleteById ...");
        //除0异常
        System.out.println(1/0);
    }

在通知类上使用@Aspect 注解声明为切面

@Component
@Aspect  //让本类作为一个切面
public class Advice {
    
    
    //方法之前执行
    //try{}
//    @Before("execution(* cn.cyl.service..*.*(..))")
    public void before(){
    
    
        System.out.println("before————前面记录日志");
    }
    //出现异常不执行
//    @AfterReturning("execution(* cn.cyl.service..*.*(..))")
    public void afterReturning(){
    
    
        System.out.println("afterReturning————后面记录日志");
    }

    //catch
    //出现异常执行
//    @AfterThrowing("execution(* cn.cyl.service..*.*(..))")
    public void throwable(){
    
    
        System.out.println("throwable————异常记录日志");
    }

    //最终增强,最终都会执行
//    @After("execution(* cn.cyl.service..*.*(..))")
    public void after(){
    
    
        System.out.println("after————最终记录日志");
    }

    //环绕增强
    //参数:目标类中的所有方法
    @Around("execution(* cn.cyl.service..*.*(..))")
    public void  around(ProceedingJoinPoint point){
    
    
        try {
    
    
            //执行before
            before();
            System.out.println("around————");
            point.proceed();
            afterReturning();
        } catch (Throwable throwable) {
    
    
            //补救方法
            throwable();
            throwable.printStackTrace();
        }finally {
    
    
            //释放资源
            after();
        }
    }

}

猜你喜欢

转载自blog.csdn.net/qq_43639081/article/details/109124598