第3章AOP 面向切面编程(AspectJ 基于注解的 AOP 实现)

3.6.3 AspectJ 的开发环境
1 maven 依赖
<?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.it</groupId>
    <artifactId>spring3</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>springTest2</module>
        <module>springAop1</module>
        <module>springAopAspectJ</module>
        <module>SpringMyBatis</module>
    </modules>

    <!--指定jdk的版本-->
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- 单元测试的依赖  -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <!--Spring依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.5.RELEASE</version>
        </dependency>
<!--aspectJ依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.2.5.RELEASE</version>
        </dependency>
    </dependencies>

    <!--自动生成的生命周期的插件-->
    <build>
        <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
            <plugins>
                <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
                <plugin>
                    <artifactId>maven-clean-plugin</artifactId>
                    <version>3.1.0</version>
                </plugin>
                <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
                <plugin>
                    <artifactId>maven-resources-plugin</artifactId>
                    <version>3.0.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.0</version>
                </plugin>
                <plugin>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>2.22.1</version>
                </plugin>
                <plugin>
                    <artifactId>maven-jar-plugin</artifactId>
                    <version>3.0.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-install-plugin</artifactId>
                    <version>2.5.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-deploy-plugin</artifactId>
                    <version>2.8.2</version>
                </plugin>
                <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
                <plugin>
                    <artifactId>maven-site-plugin</artifactId>
                    <version>3.7.1</version>
                </plugin>
                <plugin>
                    <artifactId>maven-project-info-reports-plugin</artifactId>
                    <version>3.0.0</version>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>
2 ) 引入 AOP 约束
      在 AspectJ 实现 AOP 时,要引入 AOP 的约束。配置文件中使用的 AOP 约束中的标签, 均是 AspectJ 框架使用的,而非 Spring 框架本身在实现 AOP 时使用的。
      AspectJ 对于 AOP 的实现有注解和配置文件两种方式,常用是注解方式。
3.6.4 AspectJ 基于注解的 AOP 实现
AspectJ 提供了以注解方式对于 AOP 的实现。
1 ) 实现步骤
A Step1 :定义业务接口与实现类

BStep2:定义切面类

类中定义了若干普通方法,将作为不同的通知方法,用来增强功能 

CStep3:声明目标对象切面类对象 

DStep4:注册 AspectJ 的自动代理 

       在定义好切面 Aspect 后,需要通知 Spring 容器,让容器生成“目标类 + 切面”的代理 对象。这个代理是由容器自动生成的。只需要在 Spring 配置文件中注册一个基于 aspectj 自动代理生成器,其就会自动扫描到 @Aspect 注解,并按通知类型与切入点,将其织入,并 生成代理。
        <aop:aspectj-autoproxy/> 的底层是由 AnnotationAwareAspectJAutoProxyCreator 实现的。 从其类名就可看出,是基于 AspectJ 的注解适配自动代理生成器。
         其工作原理是, <aop:aspectj-autoproxy/> 通过扫描找到 @Aspect 定义的切面类,再由切 面类根据切入点找到目标类的目标方法,再由通知类型找到切入的时间点。
E Step5 :测试类中使用目标对象的 id

2 @Before 前置通知 - 方法有 JoinPoint 参数
         在目标方法执行之前执行。被注解为前置通知的方法,可以包含一个 JoinPoint 类型参 数。该类型的对象本身就是切入点表达式。通过该参数,可获取切入点表达式、方法签名、 目标对象等。
         不光前置通知的方法,可以包含一个 JoinPoint 类型参数,所有的通知方法均可包含该参数。
项目结构

1.创建SomeService接口

package com.it.bao1;

public interface SomeService {
    void doSome(String name,Integer age);
}

2.创建SomeServiceImpl实现类

package com.it.bao1;

public class SomeServiceImpl implements SomeService {

    @Override
    public void doSome(String name, Integer age) {
        System.out.println("=========目标方法doSome()=====");
    }
}

3.创建MyAspect类

package com.it.bao1;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

import java.util.Date;

//@Aspect是aspectJ框架中的注解,表示当前类是切面类
@Aspect
public class MyAspect {
//    切面类中的方法是public方法,方法没有返回值


    /**
     * @Before:前置通知注解
     * 属性:value,是切入点表达式,表示切面功能执行的位置
     * 切入点表达式:访问修饰符,方法返回值,完整路径加方法名(里面写方法的参数个数,或方法的类型)
     * 在目标方法之前执行,不会影响目标方法的执行
     *
     */
    /**
     * 后置通知定义方法
     * 1.方法是public
     * 2.方法返回值是void
     * 3.方法名称自定义
     * 4.方法可以有参数,也可以没有参数。
     */
//    @Before(value = "execution(public void com.it.bao1.SomeServiceImpl.doSome(String,Integer))")
//    public void myBefore(){
//
//        System.out.println("切面类:在目标方法之前输出执行开始的时间:"+new Date());
//    }
    //指定通知方法中的参数:JoinPoint
@Before(value="execution(public void com.it.bao1.SomeServiceImpl.doSome(String,Integer))")
    public void myBefore(JoinPoint joinPoint){
        System.out.println("方法的签名:"+joinPoint.getSignature());
        System.out.println("方法的名称:"+joinPoint.getSignature().getName());
        //获取方法的实参
        Object[] args = joinPoint.getArgs();
        for (Object arg:args){
            System.out.println("参数="+arg);
        }
        System.out.println("切面类:在目标方法之前输出执行开始的时间:"+new Date());
    }
}

4.创建spring配置文件applicationContext.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
       https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--把对象交给spring容器,由spring容器统一创建,管理对象-->
<!--  声明目标对象-->
    <bean id="someService" class="com.it.bao1.SomeServiceImpl">

    </bean>
<!--    声明切面类对象-->
    <bean id="myAspect" class="com.it.bao1.MyAspect">

    </bean>
<!--    声明自动代理生成器:创建目标对象的代理对象,代理对象其实就是被修改后的目标对象-->
    <aop:aspectj-autoproxy/>

<!-- aspectj-autoproxy:会把spring容器中的所有目标对象,一次性都生成代理对象   -->


<!--    没有接口默认使用cglib动态代理,有接口默认使用jdk动态代理,
如果在有接口的情况下,修改成使用cglib动态代理,使用
   <aop:aspectj-autoproxy proxy-target-class="true" />
-->

</beans>

5.创建测试类

package com.it;

import static org.junit.Assert.assertTrue;

import com.it.bao1.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Unit test for simple App.
 */
public class AppTest 
{
@Test
    public void test1(){
    String config="applicationContext.xml";
    ApplicationContext ac=new ClassPathXmlApplicationContext(config);
    //从容器中获取目标对象
    SomeService proxy = (SomeService) ac.getBean("someService");
    //虽然已经获取了目标方法的对象,但在使用目标方法的对象时,其实是使用的目标对象的代理对象
    System.out.println("验证该目标对象,是否是代理对象:"+proxy.getClass().getName());
    //通过代理的对象执行方法,实现目标方法执行时,增强了功能
    proxy.doSome("小明",23);
}
}

项目结果

 3@AfterReturning 后置通知-注解有 returning 属性

       在目标方法执行之后执行。由于是目标方法之后执行,所以可以获取到目标方法的返回 值。该注解的 returning 属性就是用于指定接收方法返回值的变量名的。所以,被注解为后 置通知的方法,除了可以包含 JoinPoint 参数外,还可以包含用于接收返回值的变量。该变 量最好为 Object 类型,因为目标方法的返回值可能是任何类型。
项目结构
同上
1.创建接口
package com.it.bao2;

public interface SomeService {
    void doSome(String name, Integer age);
    String doOther(String name,Integer age);
}
2.创建接口实现类
package com.it.bao2;

public class SomeServiceImpl implements SomeService {

    @Override
    public void doSome(String name, Integer age) {
        System.out.println("=========目标方法doSome()=====");
    }

    @Override
    public String doOther(String name, Integer age) {
        System.out.println("===目标方法执行了doOther方法====");
        return "abcd";
    }


}
3.创建切面类
package com.it.bao2;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

import java.util.Date;

//@Aspect是aspectJ框架中的注解,表示当前类是切面类
@Aspect
public class MyAspect {
//    切面类中的方法是public方法,方法没有返回值

    /**
     * 后置通知定义方法
     * 1.方法是public
     * 2.方法返回值是void
     * 3.方法名称自定义
     * 4.方法是有参数的,参数推荐Object,参数名自定义
     */

    /**
     * @MyAfterReturning:后置通知
     * 属性:1.value切入点表达式
     * 2.returning,自定义变量,表示目标方法的返回值的。
     *               自定义变量名必须和通知方法的形参名一样。
     * 位置:在目标方法上面
     * 特点:在目标方法之后执行
     *
     * @param res
     */

    //如果使用JionPoint必须写在参数第一位
    @AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))",returning = "res")
    public void MyAfterReturning(JoinPoint joinPoint,Object res){
        System.out.println(joinPoint.getSignature());
        System.out.println("后置通知:在目标方法执行之后取到的返回值是:"+res);
}


}
4.创建spring配置文件
<?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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--把对象交给spring容器,由spring容器统一创建,管理对象-->
<!--  声明目标对象-->
    <bean id="someService" class="com.it.bao2.SomeServiceImpl">

    </bean>
<!--    声明切面类对象-->
    <bean id="myAspect" class="com.it.bao2.MyAspect">

    </bean>
<!--    声明自动代理生成器:创建目标对象的代理对象,代理对象其实就是被修改后的目标对象-->
    <aop:aspectj-autoproxy/>

<!-- aspectj-autoproxy:会把spring容器中的所有目标对象,一次性都生成代理对象   -->


<!--    没有接口默认使用cglib动态代理,有接口默认使用jdk动态代理,
如果在有接口的情况下,修改成使用cglib动态代理,使用
   <aop:aspectj-autoproxy proxy-target-class="true" />
-->
</beans>
5.创建测试类
package com.it;

import com.it.bao2.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Unit test for simple App.
 */
public class AppTest2
{
@Test
    public void test2(){
    String config="applicationContext.xml";
    ApplicationContext ac=new ClassPathXmlApplicationContext(config);
    //从容器中获取目标对象
    SomeService proxy = (SomeService) ac.getBean("someService");
    //虽然已经获取了目标方法的对象,但在使用目标方法的对象时,其实是使用的目标对象的代理对象
    System.out.println("验证该目标对象,是否是代理对象:"+proxy.getClass().getName());
    //通过代理的对象执行方法,实现目标方法执行时,增强了功能
//    proxy.doSome("小明",23);
    String s=proxy.doOther("小红",22);

}
}
项目结果
4 @Around 环绕通知 - 增强方法有 ProceedingJoinPoint 参数
       在目标方法执行之前之后执行。被注解为环绕增强的方法要有返回值, Object 类型。并 且方法可以包含一个 ProceedingJoinPoint 类型的参数。接口 ProceedingJoinPoint 其有一个 proceed() 方法,用于执行目标方法。若目标方法有返回值,则该方法的返回值就是目标方法 的返回值。最后,环绕增强方法将其返回值返回。该增强方法实际是拦截了目标方法的执行。
接口增加方法:
项目结构
同上
1.创建接口
package com.it.bao3;

public interface SomeService {
    void doSome(String name, Integer age);
    String doOther(String name, Integer age);
    String doFirst(String name,Integer age);
}
2.创建接口实现类
package com.it.bao3;

public class SomeServiceImpl implements SomeService {

    @Override
    public void doSome(String name, Integer age) {
        System.out.println("=========目标方法doSome()=====");
    }

    @Override
    public String doOther(String name, Integer age) {
        System.out.println("===目标方法执行了doOther方法====");
        return "abcd";
    }

    @Override
    public String doFirst(String name, Integer age) {
        System.out.println("=====业务方法doFirst()=====");
        return "doFirst";
    }


}
3.创建切面类
package com.it.bao3;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

import java.util.Date;

//@Aspect是aspectJ框架中的注解,表示当前类是切面类
@Aspect
public class MyAspect {
//    切面类中的方法是public方法,方法没有返回值

    /**
     * 环绕通知定义方法
     * 1.方法是public
     * 2.方法必须有返回值,推荐Object
     * 3.方法名称自定义
     * 4.方法有参数的,参数是固定的:ProceedingJoinPoint
     */

    /**
     * @Around:环绕通知
     * 属性:value切入点表达式
     * 特点:
     * 1.它是功能最强的通知
     * 2.在目标方法的前后都可以增强功能。
     * 3.控制目标方法是够能调用执行
     * 4.修改原来的目标方法的执行结果,影响最后的执行结果。
     *
     * 环绕通知=jdk动态代理,invocationHandler接口
     * @return
     */
@Around(value = "execution(* *..SomeServiceImpl.doFirst(..))")
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    String arg1=null;
    Object[] args = proceedingJoinPoint.getArgs();
    if (args!=null && args.length>1){
        Object arg = args[0];
        arg1 = (String) arg;
        System.out.println(arg1);
    }
    //实现环绕通知
    Object result=null;
    System.out.println("环绕通知,在目标方法之前,输出时间:"+new Date());
    //1.目标方法的调用
    if ("李华".equals(arg1)){
        result=proceedingJoinPoint.proceed();//Object result=doFirst();
    }

    System.out.println("环绕通知,在目标方法之后,提交事务:");
    //修改目标方法执行结果,会影响最终的返回结果
    if (result!=null){
        result="Hello AspectJ AOP";
    }
    //返回目标函数的结果
    return result;
    }


}
4.创建spring配置文件
<?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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--把对象交给spring容器,由spring容器统一创建,管理对象-->
<!--  声明目标对象-->
    <bean id="someService" class="com.it.bao3.SomeServiceImpl">

    </bean>
<!--    声明切面类对象-->
    <bean id="myAspect" class="com.it.bao3.MyAspect">

    </bean>
<!--    声明自动代理生成器:创建目标对象的代理对象,代理对象其实就是被修改后的目标对象-->
    <aop:aspectj-autoproxy/>

<!-- aspectj-autoproxy:会把spring容器中的所有目标对象,一次性都生成代理对象   -->


<!--    没有接口默认使用cglib动态代理,有接口默认使用jdk动态代理,
如果在有接口的情况下,修改成使用cglib动态代理,使用
   <aop:aspectj-autoproxy proxy-target-class="true" />
-->
</beans>
5.创建测试类
package com.it;

import com.it.bao3.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Unit test for simple App.
 */
public class AppTest3
{
@Test
    public void test1(){
    String config="applicationContext.xml";
    ApplicationContext ac=new ClassPathXmlApplicationContext(config);
    //从容器中获取目标对象
    SomeService proxy = (SomeService) ac.getBean("someService");
    //虽然已经获取了目标方法的对象,但在使用目标方法的对象时,其实是使用的目标对象的代理对象
    System.out.println("验证该目标对象,是否是代理对象:"+proxy.getClass().getName());
    //通过代理的对象执行方法,实现目标方法执行时,增强了功能
    String str = proxy.doFirst("李华", 25);
    //返回结果
    System.out.println("str=="+str);
}
}
项目结果

 5@AfterThrowing 异常通知-注解中有 throwing 属性

      在目标方法抛出异常后执行。该注解的 throwing 属性用于指定所发生的异常类对象。 当然,被注解为异常通知的方法可以包含一个参数 Throwable ,参数名称为 throwing 指定的 名称,表示发生的异常对象。
项目结构
同上
1.创建接口
package com.it.bao4;

public interface SomeService {
    void doSome(String name, Integer age);
    String doOther(String name, Integer age);
    String doFirst(String name, Integer age);
    void doSecond();
}
2.创建接口实现类
package com.it.bao4;

public class SomeServiceImpl implements SomeService {

    @Override
    public void doSome(String name, Integer age) {
        System.out.println("=========目标方法doSome()=====");
    }

    @Override
    public String doOther(String name, Integer age) {
        System.out.println("===目标方法执行了doOther方法====");
        return "abcd";
    }

    @Override
    public String doFirst(String name, Integer age) {
        System.out.println("=====业务方法doFirst()=====");
        return "doFirst";
    }

    @Override
    public void doSecond() {
        System.out.println("===执行业务方法doSecond()====异常通知"+(10/0));
    }


}
3.创建切面类
package com.it.bao4;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

import java.util.Date;

//@Aspect是aspectJ框架中的注解,表示当前类是切面类
@Aspect
public class MyAspect {
//    切面类中的方法是public方法,方法没有返回值

    /**
     * 异常通知定义方法
     * 1.方法是public
     * 2.方法没有返回值。
     * 3.方法名称自定义
     * 4.方法可以没有参数,如果有参数就是JoinPoint或Exception
     */

    /**
     * @AfterThrowing:异常通知
     * 属性:1.value切入点表达式
     * 2、throwing自定义变量,表示目标方法抛出的异常对象。变量名必须和方法的参数名一样。
     * 特点:
     * 1.在目标方法抛出异常时执行的
     * 2.做异常的监控程序,如果有异常发出通知
     *
     */

@AfterThrowing(value = "execution(* *..SomeServiceImpl.doSecond(..))",throwing = "e")
public void myAfterThrowing(Exception e){
    System.out.println("这是异常通知,在方法发生异常时执行"+e.getMessage());
    //发送邮件,短信通知开发人员

}


}
4.创建spring配置文件
<?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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--把对象交给spring容器,由spring容器统一创建,管理对象-->
<!--  声明目标对象-->
    <bean id="someService" class="com.it.bao4.SomeServiceImpl">

    </bean>
<!--    声明切面类对象-->
    <bean id="myAspect" class="com.it.bao4.MyAspect">

    </bean>
<!--    声明自动代理生成器:创建目标对象的代理对象,代理对象其实就是被修改后的目标对象-->
    <aop:aspectj-autoproxy/>

<!-- aspectj-autoproxy:会把spring容器中的所有目标对象,一次性都生成代理对象   -->


<!--    没有接口默认使用cglib动态代理,有接口默认使用jdk动态代理,
如果在有接口的情况下,修改成使用cglib动态代理,使用
   <aop:aspectj-autoproxy proxy-target-class="true" />
-->
</beans>
5.创建测试类
package com.it;

import com.it.bao4.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Unit test for simple App.
 */
public class AppTest4 {
    @Test
    public void test1() {
        String config = "applicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        //从容器中获取目标对象
        SomeService proxy = (SomeService) ac.getBean("someService");
        //虽然已经获取了目标方法的对象,但在使用目标方法的对象时,其实是使用的目标对象的代理对象
        System.out.println("验证该目标对象,是否是代理对象:" + proxy.getClass().getName());
        //通过代理的对象执行方法,实现目标方法执行时,增强了功能
        proxy.doSecond();
    }
}
项目结果
没有异常时

 有异常时

6 @After 最终通知
无论目标方法是否抛出异常,该增强均会被执行。
项目结构
同上
1.创建接口
package com.it.bao5;

public interface SomeService {
    void doSome(String name, Integer age);
    String doOther(String name, Integer age);
    String doFirst(String name, Integer age);
    void doSecond();
    void doThird();
}
2.创建接口实现类
package com.it.bao5;

public class SomeServiceImpl implements SomeService {

    @Override
    public void doSome(String name, Integer age) {
        System.out.println("=========目标方法doSome()=====");
    }

    @Override
    public String doOther(String name, Integer age) {
        System.out.println("===目标方法执行了doOther方法====");
        return "abcd";
    }

    @Override
    public String doFirst(String name, Integer age) {
        System.out.println("=====业务方法doFirst()=====");
        return "doFirst";
    }

    @Override
    public void doSecond() {
        System.out.println("===执行业务方法doSecond()====异常通知"+(10/0));
    }

    @Override
    public void doThird() {
        System.out.println("======执行了doThird()方法========");
    }


}
3.创建切面类
package com.it.bao5;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;

//@Aspect是aspectJ框架中的注解,表示当前类是切面类
@Aspect
public class MyAspect {
//    切面类中的方法是public方法,方法没有返回值

    /**
     * 公共通知定义方法
     * 1.方法是public
     * 2.方法没有返回值。
     * 3.方法名称自定义
     * 4.方法可以没有参数,如果有参数就是JoinPoint
     */

    /**
     * @After:最终通知
     * 属性:1.value切入点表达式
     * 特点:
     * 1、总是会执行。
     * 2.是在目标方法执行之后执行的
     *
     */

@After(value = "execution(* *..SomeServiceImpl.doThird(..))")
public void muAfter(){
    System.out.println("这是执行最终通知,它总是会被执行,不管是否发生异常");
    //一般用来做资源清除工作的
}

}

4.创建spring配置文件
<?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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--把对象交给spring容器,由spring容器统一创建,管理对象-->
<!--  声明目标对象-->
    <bean id="someService" class="com.it.bao5.SomeServiceImpl">

    </bean>
<!--    声明切面类对象-->
    <bean id="myAspect" class="com.it.bao5.MyAspect">

    </bean>
<!--    声明自动代理生成器:创建目标对象的代理对象,代理对象其实就是被修改后的目标对象-->
    <aop:aspectj-autoproxy/>

<!-- aspectj-autoproxy:会把spring容器中的所有目标对象,一次性都生成代理对象   -->


<!--    没有接口默认使用cglib动态代理,有接口默认使用jdk动态代理,
如果在有接口的情况下,修改成使用cglib动态代理,使用
   <aop:aspectj-autoproxy proxy-target-class="true" />
-->
</beans>
5.创建测试类
package com.it;

import com.it.bao5.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Unit test for simple App.
 */
public class AppTest5 {
    @Test
    public void test1() {
        String config = "applicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        //从容器中获取目标对象
        SomeService proxy = (SomeService) ac.getBean("someService");
        //虽然已经获取了目标方法的对象,但在使用目标方法的对象时,其实是使用的目标对象的代理对象
        System.out.println("验证该目标对象,是否是代理对象:" + proxy.getClass().getName());
        //通过代理的对象执行方法,实现目标方法执行时,增强了功能
        proxy.doThird();
    }
}
项目结果

7 @Pointcut 定义切入点
       当较多的通知增强方法使用相同的 execution 切入点表达式时,编写、维护均较为麻烦。 AspectJ 提供了 @Pointcut 注解,用于定义 execution 切入点表达式。
       其用法是,将 @Pointcut 注解在一个方法之上,以后所有的 execution value 属性值均 可使用该方法名作为切入点。代表的就是 @Pointcut 定义的切入点。这个使用 @Pointcut 注解 的方法一般使用 private 的标识方法,即没有实际作用的方法。

项目结构
同上
1.创建接口
package com.it.bao6;

public interface SomeService {
    void doSome(String name, Integer age);
    String doOther(String name, Integer age);
    String doFirst(String name, Integer age);
    void doSecond();
    void doThird();
}
2.创建接口实现类
package com.it.bao6;

public class SomeServiceImpl implements SomeService {

    @Override
    public void doSome(String name, Integer age) {
        System.out.println("=========目标方法doSome()=====");
    }

    @Override
    public String doOther(String name, Integer age) {
        System.out.println("===目标方法执行了doOther方法====");
        return "abcd";
    }

    @Override
    public String doFirst(String name, Integer age) {
        System.out.println("=====业务方法doFirst()=====");
        return "doFirst";
    }

    @Override
    public void doSecond() {
        System.out.println("===执行业务方法doSecond()====异常通知"+(10/0));
    }

    @Override
    public void doThird() {
        System.out.println("======执行了doThird()方法========");
    }


}
3.创建切面类
package com.it.bao6;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

//@Aspect是aspectJ框架中的注解,表示当前类是切面类
@Aspect
public class MyAspect {
//    切面类中的方法是public方法,方法没有返回值

    /**
     * @PointCut:定义切入点表达式,如果你的项目中有多个切入点表达式是重复的,
     * 那么就可以使用@PointCut
     * 属性:value切入点表达式,
     * 位置:在自定义的方法上面
     * 特点:
     */
@Pointcut(value = "execution(* *..SomeServiceImpl.doThird(..))")
    public void myPointCut(){}

@After(value = "myPointCut()")
public void muAfter(){
    System.out.println("这是执行最终通知,它总是会被执行,不管是否发生异常");
}

@Before(value = "myPointCut()")
public void myBefore(){
        System.out.println("这是执行前置通知,它在目标方法之前执行!");
    }

}

4.创建spring配置文件
<?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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--把对象交给spring容器,由spring容器统一创建,管理对象-->
<!--  声明目标对象-->
    <bean id="someService" class="com.it.bao6.SomeServiceImpl">

    </bean>
<!--    声明切面类对象-->
    <bean id="myAspect" class="com.it.bao6.MyAspect">

    </bean>
<!--    声明自动代理生成器:创建目标对象的代理对象,代理对象其实就是被修改后的目标对象-->
    <aop:aspectj-autoproxy/>

<!-- aspectj-autoproxy:会把spring容器中的所有目标对象,一次性都生成代理对象   -->


<!--    没有接口默认使用cglib动态代理,有接口默认使用jdk动态代理,
如果在有接口的情况下,修改成使用cglib动态代理,使用
   <aop:aspectj-autoproxy proxy-target-class="true" />
-->
</beans>

5.创建测试类
package com.it;

import com.it.bao6.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Unit test for simple App.
 */
public class AppTest6 {
    @Test
    public void test1() {
        String config = "applicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        //从容器中获取目标对象
        SomeService proxy = (SomeService) ac.getBean("someService");
        //虽然已经获取了目标方法的对象,但在使用目标方法的对象时,其实是使用的目标对象的代理对象
        System.out.println("验证该目标对象,是否是代理对象:" + proxy.getClass().getName());
        //通过代理的对象执行方法,实现目标方法执行时,增强了功能
        proxy.doThird();

        //当目标类没有接口时,使用cglib动态代理,spring框架会自动调用cglib

    }
}

项目结果

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

做一道光

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值