首页 > 其他分享 >Spring新注解

Spring新注解

时间:2023-11-04 17:33:41浏览次数:27  
标签:Spring void System 代理 println 注解 public out

Spring新注解

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

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

注解 说明
@Configuration 表示当前类是一个配置类,用于代替配置文件,相当于applicationContext.xml
@Bean 作用于方法上,用于将方法的返回值存入spring容器中
@ComponentScan 用于指定扫描包路径 <br>Value:用于指定路径 数组basePackages:和value一样<br>相当于 <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();
   }
}

<!--此处需要注意的是,spring5 及以上版本要求 junit 的版本必须是 4.12 及以上-->

动态代理

什么是代理

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

image-20210622154131432

代理的核心角色

  • 抽象角色(接口类)

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

  • 真实角色(实现类)

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

    ==真实角色-关注的是真正的业务逻辑==

  • 代理角色(代理实现的类,最终使用的对象)

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

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

    image-20210622174802949

代理的应用场景

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

代理的分类

  • 静态代理

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

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

    image-20210622172811916

静态代理代码实现

  • 定义接口
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();
    }
}

image-20210623070200307

静态代理对于代理的角色是固定的,如 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);
    }
}
  • 测试

image-20210628160736461

AOP概述

什么是 AOP

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

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

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

image-20211216205721967

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>

实现步骤

第一步:目标(确定)

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

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

<img src="assets/image-20211216225954666.png" alt="image-20211216225954666" style="zoom: 50%;" />

目标类

/**
 * @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();
    }

}

<img src="assets/image-20211216231549168.png" alt="image-20211216231549168" style="zoom:50%;" />

分析各种通知应用

通知类型

名称 标签 说明 场景
前置通知 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());
    }
    
}

标签:Spring,void,System,代理,println,注解,public,out
From: https://blog.51cto.com/teayear/8183989

相关文章

  • spring boot智慧工厂人员定位系统源码,可寻、可视、可防、可控的一体化管控平台
    随着经济的高速发展,现代制造业规模不断扩大,生产车间面积广阔,生产设备日益繁多,生产工人数量多且分散作业,难以进行有效管理和实施全方位风险管控。现代工厂安全管理极需向智慧工厂转型,通过科技手段提升安全及经济效益,成为企业生存发展的关键。智慧工厂人员定位系统通过在作业现场部署......
  • SpringBoot项目从resources目录读取文件
    SpringBoot从resources读取文件使用Spring给我们提供的工具类来进行读取Filefile=org.springframework.util.ResourceUtils.getFile("classpath:人物模板.docx");可能读取失败,出现如下错误:java.io.FileNotFoundException:classpathresource[人物模板.docx]cannot......
  • SpringCloud 基础
    SpringCloud基础微服务基础注意:此阶段学习推荐的电脑配置,至少配备4核心CPU(主频3.0Ghz以上)+16GB内存,否则卡到你怀疑人生。前面我们讲解了SpringBoot框架,通过使用SpringBoot框架,我们的项目开发速度可以说是得到了质的提升。同时,我们对于项目的维护和理解,也会更加的轻松。可见,Spr......
  • 二手商城系统(课设) springboot 免费源码
    项目源码获取方式放在文章末尾处项目技术数据库:Mysql5.7数据表数:6张开发工具:idea前端技术:html后端技术:Springboot+Mybatis功能简介该项目是一个校园二手商城系统,分为用户端,管理员端。管理员端:  用户管理   商品管理  分类管理  订单管理  留言管理商家端:  ......
  • eclipse配置springboot、maven
    1、配置java环境2、去官网下载一个http://maven.apache.org/download.cgi 3、下载完毕以后,解压后的文件>打开conf>编辑模式打开settings.xml4、配置服务器镜像<mirror><id>alimaven</id><name>aliyunmaven</name><url>http://maven.aliyun.com/nexus/content/group......
  • 医院绩效考核系统全套源码,采用springboot、avue框架开发
    医院绩效考核系统全套源码(演示+自主版权+医院应用案例)医院绩效考核系统,建立以医院发展目标为导向,以医务人员劳动价值、工作量为评价基础,统筹效率、质量、成本的绩效管理和绩效工资分配体系。系统支持RBRVS(以资源为基础的相对价值比率)和DRGs(疾病诊断相关分类)。支持与HIS系统对接。......
  • Spring Cloud 整合
    前言玩SpringCloud之前最好懂SpringBoot,别搞撑死骆驼的事。Servlet整一下变成Spring;SSM封装、加入东西就变为SpringBoot;SpringBoot再封装、加入东西就变为SpringCloud架构的演进单体应用架构单体架构:表示层、业务逻辑层和数据访问层即所有功能都在一个工程里,打成一个jar包、wa......
  • Spring Boot热部署:快速更新应用程序而无需重启服务器
    ......
  • SpringMVC简介
    SpringMVC简介我们首先要了解MVC是什么MVC是一种软件架构的思想,将软件按照模型、视图、控制器来划分M:Model,模型层,指工程中的JavaBean,作用是处理数据JavaBean分为两类:一类称为实体类Bean:专门存储业务数据的,如Student、User等一类称为业务处理Bean:指Service或Dao对象,专门用......
  • springboot自定义Starter(超简捷)
    啥也不说,新建一个新的Maven项目引入Spring必要依赖autoconfigure这个依赖是SpringBoot框架的自动配置依赖,它包含了大量的自动配置类,用于根据应用程序的配置和环境,在应用程序启动时自动配置各种组件和属性。通过这个依赖,可以实现一些常见的配置,如数据库连接、缓存、消息队列......