JAVAEE框架整合技术之Spring02-AOP面向切面编程技术

简介: JAVAEE框架整合技术之Spring02-AOP面向切面编程技术

Spring新注解

Spring5.0之后的注解称为新注解

使用上面的注解还不能全部替代xml配置文件,还需要使用注解替代的配置

注解 说明
@Configuration 表示当前类是一个配置类,用于代替配置文件,相当于applicationContext.xml
@Bean 作用于方法上,用于将方法的返回值存入spring容器中
@ComponentScan 用于指定扫描包路径
Value:用于指定路径 数组basePackages:和value一样
相当于 <context:component-scan base-package=“”/>

@Configuration

spring-new-annotation

/**
 *   @Configuration 相当于 applicationContext.xml配置文件
 *   @ComponentScan 相当于 <context:component-scan base-package="cn.yanqi"/>
 *   @Bean("person")相当于 <bean id="person" class="cn.yanqi.pojo.person"/>
 */
@Configuration
@ComponentScan(basePackages = "cn.yanqi")
public class SpringConfiguration {
    @Bean("person")
    public Person person(){
        return new Person();
    }
}
@Data
public class Person {
    @Value("11")
    private int id;
    @Value("jack")
    private String name;
    @Value("男")
    private String sex;
public class UserServiceTest {    
    @Test
    public void login() {
        //注意所使用对象:new AnnotationConfigApplicationContext(SpringConfiguration.class);
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        Person person = applicationContext.getBean("person", Person.class);
        System.out.println(person);
    }
}

@Bean

@Bean注入对象

/**
 * @Auther: yanqi
 * @Desc    @Configuration 相当于 applicationContext.xml配置文件
 *          @ComponentScan 相当于 <context:component-scan base-package="cn.yanqi"/>
 *          @Bean("person") 相当于 <bean id="person" class="cn.yanqi.pojo.person"/>
 */
@Configuration
@ComponentScan(basePackages = "cn.yanqi")
public class SpringConfiguration {
    @Bean("userService")
    public UserService userService(){
        //注入dao层对象
        return new UserServiceImpl(userDao());
    }
    @Bean("userDao")
    public UserDao userDao(){
        return new UserDaoImpl();
    }
}
public class UserServiceImpl implements UserService {
    private UserDao userDao;
    //提供UserServiceImpl有参构造
    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }
    @Override
    public void login() {
        System.out.println("业务层 登录!!!");
        userDao.login();
    }
}
/**
 * @Auther: yanqi
 * @Date: 18:35
 * @Desc
 */
public class UserDaoImpl implements UserDao {
    @Override
    public void login() {
        System.out.println("dao层登录成功!!!");
    }
}
@Test
    public void login() {
        //注意所使用对象:new AnnotationConfigApplicationContext(SpringConfiguration.class);
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        UserService userService = applicationContext.getBean("userService", UserService.class);
        userService.login();
    }

@Component

纯注解开发

/**
 * 
 *          @Configuration 相当于 applicationContext.xml配置文件
 *          @ComponentScan 相当于 <context:component-scan base-package="cn.yanqi"/>
 *          @Bean("person")相当于 <bean id="person" class="cn.yanqi.pojo.person"/>
 */
@Configuration
@ComponentScan(basePackages = "cn.yanqi")
public class SpringConfiguration {
}
@Service("userService")
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;
    @Override
    public void login() {
        System.out.println("业务层 登录!!!");
        userDao.login();
    }
}
@Repository("userDao")
public class UserDaoImpl implements UserDao {
    @Override
    public void login() {
        System.out.println("dao层登录成功!!!");
    }
}
public class UserServiceTest {
    @Test
    public void login() {
        //注意所使用对象:new AnnotationConfigApplicationContext(SpringConfiguration.class);
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        Person person = applicationContext.getBean("person", Person.class);
        System.out.println(person);
    }
}

@PropertySource

@PropertySouce是spring3.1开始引入的基于java config的注解。

通过@PropertySource注解将properties配置文件中的值存储到Spring的 Environment中,Environment接口提供方法去读取配置文件中的值,参数是properties文件中定义的key值。

@Configuration
@PropertySource("classpath:jdbc.properties")
public class PropertiesWithJavaConfig {
   @Value(${jdbc.driver})
   private String driver;
   @Value(${jdbc.url})
   private String url;
   @Value(${jdbc.username})
   private String username;
   @Value(${jdbc.password})
   private String password;
   //要想使用@Value 用${}占位符注入属性,这个bean是必须的,这个就是占位bean,另一种方式是不用value直接用Envirment变量直接getProperty('key')  
   @Bean
   public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
      return new PropertySourcesPlaceholderConfigurer();
   }
}

动态代理

什么是代理

代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。


代理的核心角色

  • 抽象角色(接口类)

定义代理角色和真实角色公共对外的方法

  • 真实角色(实现类)

实现抽象角色,定义真实角色所要实现的业务逻辑,让代理角色调用

  • 真实角色-关注的是真正的业务逻辑
  • 代理角色(代理实现的类,最终使用的对象)

实现抽象角色,是真实角色的代理,通过真实角色 的业务逻辑方法来实现抽象方法,并可以附加 自己的操作

  • 将统一的流程控制放到代理角色中处理

代理的应用场景

  • 可以在不修改别代理对象代码的基础上,通过扩展代理类,进行一些功能的附加与增强
    这样我们就屏蔽了对真实角色的直接访问
  • Spring的AOP机制就是采用动态代理的机制来实现切面编程

代理的分类

  • 静态代理

需要我们手动定义静态代理类对象

  1. 目标角色固定
  2. 在应用程序执行前就得到目标角色
  3. 代理对象会增强目标对象的行为
  4. 有可能存在多个代理,引起"类爆炸"(缺点)
  • 动态代理
  • JDK 代理 : 基于接口的动态代理技术
  • cglib 代理:基于父类的动态代理技术

静态代理代码实现

  • 定义接口
package cn.yanqi.proxy;
// 抽象角色
public interface Star {
    /**      唱歌     */
    public void sing();
    /**   订票     */
    public void tickets();
    /** 签合同     */
    public void contract();
}
  • 真实对象
// 真实对象
public class RealStar implements Star {
    public void sing() {
        System.out.println("RealStar.sing.....");
    }
    public void tickets() {
        System.out.println("RealStar.tickets.....");
    }
    public void contract() {
        System.out.println("RealStar.contract.....");
    }
}
  • 代理对象
package cn.yanqi.proxy;
//代理对象
public class ProxyStar implements Star {
    private RealStar realStar;
    public ProxyStar(RealStar realStar) {
        this.realStar = realStar;
    }
    public void sing() {
        // 调用真实对象的唱歌功能
        realStar.sing();
    }
    public void tickets() {
        System.out.println("ProxyStar.tickets....");
    }
    public void contract() {
        System.out.println("ProxyStar.contract....");
    }
}
  • 测试
public class Test {
    public static void main(String[] args) {        
        RealStar realStar = new RealStar();
        ProxyStar proxyStar = new ProxyStar(realStar);
        proxyStar.tickets();
        proxyStar.contract();
        proxyStar.sing();
    }
}

静态代理对于代理的角色是固定的,如 dao 层有20个 dao 类,如果要对方法的访问权限进行代理,此时需要创建20个静态代理角色,引起类爆炸,无法满足生产上的需要,于是就催生了动态代理的思想。

动态代理代码实现

动态代理的实现有两种,JDK和CGLIB,在这里我们以JDK的方式来演示动态代理

创建出来的代理都是java.lang.reflect.Proxy的子类

  • 定义Star接口
public interface Star {
    /**  唱歌     */
    public String sing(double money);
    /** 订票     */
    public void tickets();
    /** 签合同   */
    public void contract();
}
  • 定义歌手类
public class RealStar implements Star {    
    @Override
    public String sing(double money) {
        System.out.println("真实对象收到:" + money + "元, 准备开始唱歌!");
        return "代理对象说,马上开始唱歌了!";
    }
    @Override
    public void tickets() {
        System.out.println("RealStar.tickets.....");
    }
    @Override
    public void contract() {
        System.out.println("RealStar.contract.....");
    }
}
  • 创建代理类
package cn.yanqi.proxy;
import org.springframework.cglib.proxy.InvocationHandler;
import org.springframework.cglib.proxy.Proxy;
import java.lang.reflect.Method;
public class JDKProxy {
    public static void main(String[] args) {
        //真实对象
        final RealStar realStar = new RealStar();
        /**
         * 创建代理对象
         *  参数一: 真实对象getClass().getClassLoader()
         *  参数二: 真实对象getClass().getInterfaces()
         *  参数三: 处理器new InvocationHandler
         */
        Star proxyObject = (Star) Proxy.newProxyInstance(realStar.getClass().getClassLoader(),
                realStar.getClass().getInterfaces(),new InvocationHandler() {
            /**
             * @param proxy  代理对象 一般不用
             * @param method 代理对象调用的方法,被封装为Method对象
             * @param args   代理对象调用方法时,传递的参数
             */
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 对sing方法进行增强
                if (method.getName().equals("sing")) {
                    // 对象参数进行增强
                    Double money = (Double) args[0];
                    // 代理收取代理费
                    money = money - 5000;
                    Object obj = method.invoke(realStar, money);
                    // 对象返回值进行增强
                    return obj + "开始之前,给大家一个小礼物!";
                } else {
                    // 其他方法,还是执行原有的逻辑
                    Object obj = method.invoke(realStar, args);
                    return obj;
                }
            }
        });
         //调用真实对象的sing方法
         String sing = proxyObject.sing(10000);
         System.out.println(sing);
    }
}
  • 测试

AOP概述

什么是 AOP

AOP 为 Aspect Oriented Programming 的缩写,意思为【面向切面编程】,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。

AOP 是 OOP 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

AOP 思想: 基于代理思想,对原来目标对象,创建代理对象,在不修改原对象代码情况下,通过代理对象,调用增强功能的代码,从而对原有业务方法进行增强 !

AOP 的作用及其优势

1. 在程序运行期间,在不修改源码的情况下对方法进行功能增强
2. 逻辑清晰,开发核心业务的时候,不必关注增强业务的代码
3. 减少重复代码,提高开发效率,便于后期维护

AOP 的底层实现

实际上,AOP 的底层是通过动态代理技术实现的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。

AOP 的动态代理技术

  • JDK 代理 : 基于接口的动态代理技术
  • cglib 代理:基于父类的动态代理技术

AOP相关专业术语

  • 目标对象(target)
目标对象指将要被增强的对象,即包含主业务逻辑的类对象。
  • 连接点(JoinPoint)
程序执行过程中明确的点,如方法的调用或特定的异常被抛出。
连接点由两个信息确定:
   ==方法(表示程序执行点,即在哪个目标方法)==
   ==相对点(表示方位,即目标方法的什么位置,比如调用前,后等)==
简单来说,连接点就是被拦截到的程序执行点,因为Spring只支持方法类型的连接点,所以在Spring中连接点就是被拦截到的方法。
  • 代理对象(Proxy)
AOP中会通过代理的方式,对目标对象生成一个代理对象,代理对象中会加入需要增强功能,通过代理对象来间接的方式目标对象,起到增强目标对象的效果。
  • 通知/增强(Advice)
需要在目标对象中实现增强的功能
  • 切入点(Pointcut)
用来指定需要将通知使用到哪些地方,比如需要用在哪些类的哪些方法上,切入点就是做这个配置的。
  • 切面(Aspect)
通知(Advice)和切入点(Pointcut)的组合。切面来定义在哪些地方(Pointcut)执行什么操作(Advice)。
  • 织入(Weaving)
把Advice加到Target上,被创建出代理对象的过程

切入点表达式

切点表达式语法

bean(bean Id/bean name) 
execution(* cn.yanqi.spring.CustomerServiceImpl.*(..)) 
execution(* cn.yanqi.spring..*.*(..)) 
  • 访问修饰符可以省略
  • 返回值类型、包名、类名、方法名可以使用星号*代替,代表任意
  • 包名与类名之间一个点 . 代表当前包下的类,两个点 … 表示当前包及其子包下的类
  • 参数列表可以使用两个点 … 表示任意个数,任意类型的参数列表

Aop 基于_xml配置

添加依赖

  • 添加aop依赖
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aspects</artifactId>
  <version>5.2.9.RELEASE</version>
</dependency>
<!--aop依赖包在context依赖中已存在-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aop</artifactId>
  <version>5.2.9.RELEASE</version>
</dependency>
  • 引入aop约束
<?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"> 
</beans>

实现步骤

第一步:目标(确定)

第二部:通知/增强(编写)

第三步:配置切面 (包括切入点和切面的结合)对哪些方法进行怎么的增强


目标类

/**
 * @Auther: yanqi
 * @Desc 目标类
 */
public interface CustomerService {
    public void save();
    public int findCount();
}
package cn.yanqi.service;
/**
 * @Auther: yanqi
 * @Desc 目标类
 */
public class CustomerServiceImpl implements CustomerService {
    public void save() {
        System.out.println("业务层:【客户保存了】-----------");
    }
    public int findCount() {
        System.out.println("业务层:【客户查询了】-----------");
        return 100;
    }
}
/**
 * @Auther: yanqi
 * @Desc 目标类
 */
public class ProductService {
    public void save(){
        System.out.println("业务层:【商品保存了】-----------");
    }
    public int findCount() {
        System.out.println("业务层:【查询商品了】-----------");
        return 200;
    }
}

增强类

/**
 * @Auther: yanqi
 * @Desc 配置增强类
 */
public class MyAdvice {
    public void before(){
        System.out.println("前置增强了-----------");
    }
    public void after(){
        System.out.println("后置增强了-----------");
    }
}

配置切面及切入点

<?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">
    <!--目标类-->
    <bean id="customerService" class="cn.yanqi.service.CustomerServiceImpl"/>
    <bean id="productService" class="cn.yanqi.service.ProductService"/>
    <!--增强类-->
    <bean id="myAdvice" class="cn.yanqi.advice.MyAdvice"/>
    <!--配置切面和切入点-->
    <aop:config>
        <!--配置切入点:其实就是要拦截到哪些方法,以service结尾的bean都被拦截,增强-->
        <aop:pointcut id="MyPointcut" expression="bean(*Service)"/>
        <!--配置切面: 关联切入点和切面,要对哪些方法进行怎么的增强-->
        <aop:aspect ref="myAdvice">
            <!--
                前置通知:
                    method:  调用增强类的方法
                    pointcut-ref:关联切入点
            -->
            <aop:before method="before" pointcut-ref="MyPointcut"/>            
            <!--后置通知-->
            <aop:after-returning method="after" pointcut-ref="MyPointcut"/>
        </aop:aspect>
    </aop:config>
</beans>
  • 测试
/**
 * @Auther: yanqi
 * @Desc  在不修改的原来代码情况下,对其目标对象进行功能增强
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations =  "classpath:applicationContext.xml")
public class CustomerServiceTest {
    //注入业务层
    @Autowired
    private CustomerService customerService;
    @Autowired
    private ProductService productService;
    @Test
    public void test() {
        this.customerService.findCount();
        this.customerService.save();
        this.productService.findCount();
        this.productService.save();
    }
}

分析各种通知应用

通知类型

名称 标签 说明 场景
前置通知 before 被增强方法执行之前执行 权限控制、日志记录等
后置通知 afterReturning 被增强方法正常执行完毕后执行(执行的过程中无异常) 提交事务/统计分析结果等
异常通知 afterThrowing 被增强方法出现异常时执行 回滚事务/记录异常的日志信息等
最终通知 after 被增强方法无论是否有异常,最终都要执行的一种操作 释放资源
环绕通知 around 可以自定义在被增强方法的什么时机执行(返回Object,参数 processedingJoinPoint) 缓存、性能日志、权限、事务管理

前置通知

package cn.yanqi.advice;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
/**
 * @Dese: 增强类
 */
public class MyAdvice {
    /**
     * 前置通知
     * 参数:
     *      joinPoint 连接点:就是可以拦截到的方法(方法和目标的包装类型)
     * 需求:
     *      权限控制(权限不足,抛出异常),记录调用方法的日志
     */
    public void before(JoinPoint joinPoint){
        System.out.println("前置增强了-----------");
        //joinPoint.getTarget().getClass().getName() 获取要执行的所在的类 包名+类名
        System.out.println("增强的对象:"+joinPoint.getTarget().getClass().getName());
        //joinPoint.getSignature().getName() 获取方法名
        if("findCount".equals(joinPoint.getSignature().getName())){
            throw new RuntimeException("当前用户没有权限");
        }
    }
}
<!--
前置通知:
method:  调用增强类的方法
pointcut-ref:关联切入点
--> 
<aop:before method="before" pointcut-ref="MyPointcut"/>

后置通知

/**
     * 后置通知: 会在方法执行后进行拦截增强
     * 需求:与业务相关,如网上营业厅查询余额自动下发短信
     * 参数1:连接点
     * 参数2:Object 接受返回值
     */
    public void AfterReturning(JoinPoint joinPoint ,Object returnValue){
        System.out.println("后置通知--下发短信的方法是: "+joinPoint.getSignature().getName());
        System.out.println("短信内容:尊敬的用户您的余额为:"+ returnValue);
    }
<!--后置通知:returning 返回的内容-->
<aop:after-returning method="AfterReturning" pointcut-ref="MyPointcut" returning="returnValue"/>

环绕通知

/**
     * 环绕通知: 在方法前后拦截增强
     * 需求: 日志,缓存,权限,性能监控,事务管理
     * 参数:ProceedingJoinPoint 正在执行的连接点
     * 必须抛出一个异常Throwable
     */
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("事务开启了");
        Object proceed = proceedingJoinPoint.proceed();
        System.out.println("事务关闭了");
        return proceed;
    }
<!--环绕通知-->
<aop:around method="around" pointcut-ref="MyPointcut"/>

抛出通知

/**
     * 抛出通知: 对目标对象发生异常下进行增强,有异常就执行,没有就不执行
     * 作用:目标代码出现异常,通知执行。记录异常日志、通知管理员(短信、邮件)
     * 应用场景:处理异常(一般不可预知),记录日志
     * 参数1: 连接点
     * 参数2: 异常类型
     */
    public void AfterThrowing(JoinPoint joinPoint, Throwable ex){
        System.out.println(
                "管理员您好:发生异常的类是:"+joinPoint.getTarget().getClass().getSimpleName()
                +" ,发生异常的方法是:"+joinPoint.getSignature().getName()
                +", 异常信息为:"+ex.getMessage());
    }
<!--抛出通知-->
<aop:after-throwing method="AfterThrowing" pointcut-ref="MyPointcut" throwing="ex"/>
public class CustomerServiceImpl implements CustomerService {
    public void save() {
        int a = 1/0; //手动给出异常
        System.out.println("业务层:【客户保存了】-----------");
    }
    public int findCount() {
        System.out.println("业务层:【客户查询了】-----------");
        return 100;
    }
}

最终通知

/**
     * 最终通知: 不管是否异常都会增强
     * 作用:不管目标方法是否发生异常,最终通知都会执行(类似于finally代码功能)
     * 应用场景:释放资源 (关闭文件、 关闭数据库连接、 网络连接、 释放内存对象 )
     */
    public void after(JoinPoint joinPoint){
        System.out.println("释放资源:"+joinPoint.getSignature().getName());
    }
<!--最终通知-->
<aop:after method="after" pointcut-ref="MyPointcut"/>

Aop基于_注解配置

注解添加的依赖和xml的依赖包是一样的

spring-aop-annocation

目标对象

public interface CustomerService {
    public void save();
    public int findCount();
}
//目标类
@Service("customerService")
public class CustomerServiceImpl implements CustomerService {
    @Override
    public void save() {
        //手动制造异常
        int a = 1/0;
        System.out.println("业务层:【客户保存了】-----------");
    }
    @Override
    public int findCount() {
        System.out.println("业务层:【客户查询了】-----------");
        return 100;
    }
}
//目标类
@Service("productService")
public class ProductService {
    public void save(){
        System.out.println("业务层:【商品保存了】-----------");
    }
    public int findCount() {
        System.out.println("业务层:【查询商品了】-----------");
        return 200;
    }
}
  • applicationContext.xml
<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
   http://www.springframework.org/schema/aop/spring-aop.xsd
   http://www.springframework.org/schema/context
   http://www.springframework.org/schema/context/spring-context.xsd">
    <!--开启注解扫描-->
    <context:component-scan base-package="cn.yanqi"/>
    <!--开启AspectJ 注解自动代理机制
        扫描含有@Aspect的bean
     -->
    <aop:aspectj-autoproxy/>
</beans>

增强类

/**
 * @Auther: yanqi
 * @Date:
 * @Dese: 增强类
 */
@Component("myAdvice")
@Aspect //相当于<aop:aspect ref="myAdvice">
public class MyAdvice {
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext-aop.xml")
public class SpringAopController {
    //注入业务层
    @Autowired
    private CustomerService customerService;
    @Autowired
    private ProductService productService;
    @Test
    public void testSpringAop(){
        //基于接口
        this.customerService.save();
        this.customerService.findCount();
        //基于类
        this.productService.save();
        this.productService.findCount();
    }
}

编写通知,配置切面

  • 注解切点表达式

注解切点表达式的写法与xml版本的切点表达式写法是一样的,在这里主要演示如何使用注解抽取切点表达式

@Component("myAdvice")
@Aspect //相当于<aop:aspect ref="myAdvice">
public class MyAdvice {
    /**
     * 前置通知
     * 参数:
     *      joinPoint 连接点:就是可以拦截到的方法(方法和目标的包装类型)
     * 需求:
     *      权限控制(权限不足,抛出异常),记录调用方法的日志
     */
    @Before("bean(*Service)")
    public void before(JoinPoint joinPoint){
        System.out.println("前置增强了-----------");
        //joinPoint.getTarget().getClass().getName() 获取要执行的所在的类 包名+类名
        System.out.println("增强的对象:"+joinPoint.getTarget().getClass().getName());
    }
    /**
     * 后置通知: 会在方法执行后进行拦截增强
     * 需求:与业务相关,如网上营业厅查询余额自动下发短信
     * 参数1:连接点
     * 参数2:Object 接受返回值
     */
    @AfterReturning(value = "target(cn.yanqi.service.CustomerServiceImpl)", returning = "returnValue")
    public void AfterReturning(JoinPoint joinPoint ,Object returnValue){
        System.out.println("后置通知--下发短信的方法是: "+joinPoint.getSignature().getName());
        System.out.println("短信内容:尊敬的用户您的余额为:"+ returnValue);
    }
    /**
     * 环绕通知: 在方法前后拦截增强
     * 需求: 日志,缓存,权限,性能监控,事务管理
     * 参数:ProceedingJoinPoint 正在执行的连接点
     * 必须抛出一个异常Throwable
     */
    @Around(value = "execution(* cn.yanqi..*.*(..))")
    // @Around(value = "execution(* cn..*.save(..))")
    // @Around(value = "execution(* cn..*.find*(..))")
    // @Around(value = "execution(* cn.yanqi.service.CustomerService+.*(..))")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("事务开启了");
        Object proceed = proceedingJoinPoint.proceed();
        System.out.println("事务关闭了");
        return proceed;
    }
    /**
     * 抛出通知: 对目标对象发生异常下进行增强,有异常就执行,没有就不执行
     * 作用:目标代码出现异常,通知执行。记录异常日志、通知管理员(短信、邮件)
     * 应用场景:处理异常(一般不可预知),记录日志
     * 参数1: 连接点
     * 参数2: 异常类型
     */
    @AfterThrowing(value = "within(cn..*)",throwing = "ex") //增强cn下的及其子包下面的所有bean的类型的类
    public void AfterThrowing(JoinPoint joinPoint, Throwable ex){
        System.out.println(
                "管理员您好:发生异常的类是:"+joinPoint.getTarget().getClass().getSimpleName()
                        +" ,发生异常的方法是:"+joinPoint.getSignature().getName()
                        +", 异常信息为:"+ex.getMessage());
    }
    /**
     * 最终通知: 不管是否异常都会增强
     * 作用:不管目标方法是否发生异常,最终通知都会执行(类似于finally代码功能)
     * 应用场景:释放资源 (关闭文件、 关闭数据库连接、 网络连接、 释放内存对象 )
     */
    @After("bean(*ce)")
    public void after(JoinPoint joinPoint){
        System.out.println("释放资源:"+joinPoint.getSignature().getName());
    }
}

抽取切入点

问题:如果直接在通知注解中写切入点表达式,会发生重复编写,后期不便于维护
解决: 统一配置切入点表达式 
在实际开发中,切入点都是单独/统一定义维护的,如:
  使用xml定义切入点<aop:pointcut>
  使用注解单独定义切入点@Pointcut
语法要求:
  切入点方法:必须是 private void 无参数方法,方法名为切点名
@Component("myAdvice")
@Aspect //相当于<aop:aspect ref="myAdvice">
public class MyAdvice {
    /**
     * 配置切入点统一管理,统一维护
     */
    @Pointcut("bean(*Service)")
    private void myPointcut(){}
    @Pointcut("bean(customerService)")
    private void myPointcut2(){}
    /**
     * 前置通知
     * 参数:
     *      joinPoint 连接点:就是可以拦截到的方法(方法和目标的包装类型)
     * 需求:
     *      权限控制(权限不足,抛出异常),记录调用方法的日志
     */
    @Before(value = "myPointcut() || myPointcut2() || bean(*Service)")
    public void before(JoinPoint joinPoint){
        System.out.println("前置增强了-----------");
        //joinPoint.getTarget().getClass().getName() 获取要执行的所在的类 包名+类名
        System.out.println("增强的对象:"+joinPoint.getTarget().getClass().getName());
    }
}
oid after(JoinPoint joinPoint){
System.out.println(“释放资源:”+joinPoint.getSignature().getName());
}
}
## 抽取切入点 
```markdown
问题:如果直接在通知注解中写切入点表达式,会发生重复编写,后期不便于维护
解决: 统一配置切入点表达式 
在实际开发中,切入点都是单独/统一定义维护的,如:
  使用xml定义切入点<aop:pointcut>
  使用注解单独定义切入点@Pointcut
语法要求:
  切入点方法:必须是 private void 无参数方法,方法名为切点名
@Component("myAdvice")
@Aspect //相当于<aop:aspect ref="myAdvice">
public class MyAdvice {
    /**
     * 配置切入点统一管理,统一维护
     */
    @Pointcut("bean(*Service)")
    private void myPointcut(){}
    @Pointcut("bean(customerService)")
    private void myPointcut2(){}
    /**
     * 前置通知
     * 参数:
     *      joinPoint 连接点:就是可以拦截到的方法(方法和目标的包装类型)
     * 需求:
     *      权限控制(权限不足,抛出异常),记录调用方法的日志
     */
    @Before(value = "myPointcut() || myPointcut2() || bean(*Service)")
    public void before(JoinPoint joinPoint){
        System.out.println("前置增强了-----------");
        //joinPoint.getTarget().getClass().getName() 获取要执行的所在的类 包名+类名
        System.out.println("增强的对象:"+joinPoint.getTarget().getClass().getName());
    }
}


相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
16天前
|
存储 安全 Java
事件的力量:探索Spring框架中的事件处理机制
事件的力量:探索Spring框架中的事件处理机制
28 0
|
25天前
|
缓存 Java Spring
Spring 框架中 Bean 的生命周期
Spring 框架中 Bean 的生命周期
33 1
|
22小时前
|
Java Spring
Spring Boot脚手架集成校验框架
Spring Boot脚手架集成校验框架
6 0
|
3天前
|
安全 Java Maven
[AIGC] Spring Boot中的切面编程和实例演示
[AIGC] Spring Boot中的切面编程和实例演示
|
3天前
|
安全 Java 数据库连接
[AIGC] Spring框架的基本概念和优势
[AIGC] Spring框架的基本概念和优势
|
3天前
|
Java Nacos 开发者
Java从入门到精通:4.2.1学习新技术与框架——以Spring Boot和Spring Cloud Alibaba为例
Java从入门到精通:4.2.1学习新技术与框架——以Spring Boot和Spring Cloud Alibaba为例
|
3天前
|
Dubbo Java 应用服务中间件
Java从入门到精通:3.2.2分布式与并发编程——了解分布式系统的基本概念,学习使用Dubbo、Spring Cloud等分布式框架
Java从入门到精通:3.2.2分布式与并发编程——了解分布式系统的基本概念,学习使用Dubbo、Spring Cloud等分布式框架
|
8天前
|
安全 Java API
第5章 Spring Security 的高级认证技术(2024 最新版)(上)
第5章 Spring Security 的高级认证技术(2024 最新版)
34 0
|
9天前
|
Java 关系型数据库 MySQL
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
UWB (ULTRA WIDE BAND, UWB) 技术是一种无线载波通讯技术,它不采用正弦载波,而是利用纳秒级的非正弦波窄脉冲传输数据,因此其所占的频谱范围很宽。一套UWB精确定位系统,最高定位精度可达10cm,具有高精度,高动态,高容量,低功耗的应用。
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
|
15天前
|
Java 数据库 Spring
切面编程的艺术:Spring动态代理解析与实战
切面编程的艺术:Spring动态代理解析与实战
26 0
切面编程的艺术:Spring动态代理解析与实战