学习Spring第四天

Spring PropertyPlaceholderConfigurer的使用

对于一些隐秘的或者是全局变量的操作我们希望可以统一的管理他,我们可以把这些配置写在properties或者yaml文件里,然后在xml配置文件里获取相应的值,PropertyPlaceholderConfigurer就可以帮助我们实现这个功能

我们还是从简单的jdbc开始,项目结构如下:

gradle依赖

testCompile group: 'junit', name: 'junit', version: '4.12'

compile group: 'org.springframework', name: 'spring-context', version: '5.0.0.RELEASE'

compile group: 'mysql', name: 'mysql-connector-java', version: '8.0.8-dmr'

compile group: 'org.springframework', name: 'spring-jdbc', version: '5.0.0.RELEASE'

compile group: 'org.springframework', name: 'spring-core', version: '5.0.0.RELEASE'

数据表如下:

首先我们新建一个Entity

Customer.java

package com.demo.Model;

public class Customer {
    private int custId;
    private String name;
    private int age;

    public void setCustId(int custId) {
        this.custId = custId;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getCustId() {
        return custId;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Cust_Id: " + custId + "\nName: " + name + "\nAge: " + age;
    }
}

Dao层接口实现

CustomerDao.java

package com.demo.Dao;

import com.demo.Model.Customer;

public interface CustomerDao {
    void insert(Customer customer);

    Customer findById(int custId);
}

Impl实现接口方法

CustomerDaoImpl.java

package com.demo.Impl;

import com.demo.Dao.CustomerDao;
import com.demo.Model.Customer;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;

import java.util.List;

public class CustomerDaoImpl extends JdbcDaoSupport implements CustomerDao {
    @Override
    public void insert(Customer customer) {

        String sql = "INSERT INTO customer VALUES (? ,? ,?)";
        getJdbcTemplate().update(sql, customer.getCustId(), customer.getName(), customer.getAge());
    }

    @Override
    public Customer findById(int custId) {
        String sql = "SELECT * FROM customer WHERE CUST_ID = ?";

        List<Customer> customers = getJdbcTemplate().query(sql, new Object[]{custId}, new BeanPropertyRowMapper<>(Customer.class));

        return customers.get(0);
    }
}

上面都是很基本的Jdbc操作,通过增删改查来测试代码

不同于之前,我们把数据库的变量写在properties里

data-config.properties

jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/Spring
jdbc.username=root
jdbc.password=19970819wy

Spring-Beans中配置dataSource和beans

<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="database-config.properties"/>
    </bean>

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <bean id="customer" class="com.demo.Impl.CustomerDaoImpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>
</beans>

这里我们首先通过PropertyPlaceholderConfigurer来导入properties里的变量(location属性定位properties文件)
然后我们就可以在dataSource里通过${}来获取对应的值

测试类

App.java

package com.demo;

import com.demo.Impl.CustomerDaoImpl;
import com.demo.Model.Customer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("Spring-Beans.xml");

        Customer customer = new Customer();
        customer.setCustId(1);
        customer.setName("RenBuRuGu");
        customer.setAge(20);

        CustomerDaoImpl impl = context.getBean(CustomerDaoImpl.class);

        impl.insert(customer);

        System.out.println(impl.findById(1));
    }
}

Bean的继承

继承的方式有两种 直接继承和抽象继承
子类可以继承父类的一系列属性

直接继承

我个人认为有两种 一种是class由父类指定,子类不指定class,那么父类和子类都会共享同一个实体类,子类可以省略父类既定的字段或者重写。第二种是父类子类都指定class,这里的class不一定要存在继承关系,只要相应的class有共享的字段即可(我感觉这样不是很好)

创建一个entity

package com.demo.Model;

public class Customer {
    private int type;
    private String action;
    private String country;

    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }

    public String getAction() {
        return action;
    }

    public void setAction(String action) {
        this.action = action;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

    @Override
    public String toString() {
        return "type: " + type + "\naction: " + action + "\ncountry: " + country;
        }
    }

xml配置文件

<bean id="baseBean" class="com.demo.Model.Customer">
    <property name="country" value="China"/>
</bean>

<bean id="customerBean" parent="baseBean">
    <property name="action" value="buy"/>
    <property name="type" value="1"/>
</bean>

这样子类的country字段就变成China了 运行结果如下:

抽象继承

抽象继承也分为两种,一般抽象继承和纯抽象继承

抽象继承的目的是为了父类只提供属性模板而不可被实例化,直接抽象继承很简单,只需要在父类后面加一个abstract="true"即可

<bean id="baseBean" class="com.demo.Model.Customer" abstract="true">
    <property name="country" value="China"/>
</bean>

<bean id="customerBean" parent="baseBean">
    <property name="action" value="buy"/>
    <property name="type" value="1"/>
</bean>

纯抽象继承允许父类不设置class,只是为了共享字段使用

<bean id="baseBean" abstract="true">
    <property name="country" value="China"/>
</bean>

<bean id="customerBean" parent="baseBean" class="com.demo.Model.Customer">
    <property name="action" value="buy"/>
    <property name="type" value="1"/>
</bean>
  • 父类的属性值在子类中可以被覆盖

##依赖检查(感觉4.x这个功能不被支持,更多的使用注解完成(如@Required字段))

当我们在xml配置文件中没有对属性值进行赋值时,一般不会报错,如果我们需要它报错,就要用到依赖检查这个功能(dependency-check)
依赖检查分为四种 none(默认) simple objects all

默认方式为none,即他不会检查是否赋值

simple方式只检查基本数据类型(int, long,double…)和集合类型(map, list..),如果以上任何属性都没有设置,UnsatisfiedDependencyException将被抛出

objects方式检查对象类型的数据

all检查任何类型的数据

举一个objects的例子

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

    <bean id="CustomerBean" class="com.demo.Model.Customer" 
            dependency-check="objects">
        <property name="action" value="buy" />
        <property name="type" value="1" />
    </bean>

    <bean id="PersonBean" class="com.demo.Model.Person">
        <property name="name" value="RenBuRuGu" />
        <property name="address" value="address ABC" />
        <property name="age" value="20" />
    </bean>

</beans>

这里我们漏掉了person的注入,由于使用objects的依赖检查方式,将会收到UnsatisfiedDependencyException报错

@Required注解

对于必须的字段我们可以采用@Required注解的方式

public class Customer 
{
    private Person person;
    private int type;
    private String action;

    public Person getPerson() {
        return person;
    }

    @Required
    public void setPerson(Person person) {
        this.person = person;
    }
}

还要在xml配置文件中打开annotation注解,像以前一样,有两种方式

<context:annotation-config/><bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>

自定义required注解(个人感觉没什么用…)

首先自定义一个注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Mandatory {
}

现在我们可以在业务逻辑中使用它

public class Customer 
{
    private Person person;
    private int type;
    private String action;

    @Mandatory
    public void setPerson(Person person) {
        this.person = person;
    }

}

最后需要在xml中去注册它

<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor">
    <property name="requiredAnnotationType" value="com.demo.Annotation.Mandatory"/>
</bean>

<bean id="CustomerBean" class="com.demo.Model.Customer">
    <property name="action" value="buy" />
    <property name="type" value="1" />
</bean>

InitializingBean和DisposableBean接口

这两个接口在我理解中是两个钩子,分别用在bean初始化完成和即将被销毁时调用
InitializingBean中的afterPropertiesSet()用于执行初始化方法
DisposableBean中的destroy()用于执行bean被容器销毁之前的操作

Person.java

package com.demo.Model;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

public class Person implements InitializingBean,DisposableBean {
    private int age;
    private String name;
    private String address;

    @Override
    public void destroy() throws Exception {
        System.out.println("Destroy");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("Init");
    }
}

App.java

public class App {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("Spring-Beans.xml");

        Person person = context.getBean(Person.class);

        context.close();
    }
}

运行结果如下:

官方不推荐这么做,我也不推荐,因为如果钩子方法都写在业务代码中,就违反了Spring的低耦合机制,业务代码和Spring容器的耦合度大大加强。

init-method和destroy-method

不继承接口,我们单纯的写两个方法,然后在xml配置文件中将他们指定为初始化和销毁之前执行的方法

package com.demo.Model;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

public class Person{
    private int age;
    private String name;
    private String address;

    public void destroy() throws Exception {
        System.out.println("Destroy");
    }

    public void init() throws Exception {
        System.out.println("Init");
    }
}

我们在xml中手动管理

<bean id="person" class="com.demo.Model.Person"
        init-method="init"
        destroy-method="destroy"
        p:name="RenBuRuGu"
        p:age="20"
        p:address="JiangSu NanJing"/>
  • @PostConstruct 和 @PreDestroy可以帮我们达到同样的目的 当然前提是需要在xml中开启注解

    package com.demo.Model;

    import org.springframework.beans.factory.DisposableBean;
    import org.springframework.beans.factory.InitializingBean;

    import javax.annotation.PostConstruct;
    import javax.annotation.PreDestroy;

    public class Person{

    private int age;
    private String name;
    private String address;
    
    @PreDestroy
    public void destroy() throws Exception {
        System.out.println("Destroy");
    }
    
    @PostConstruct
    public void init() throws Exception {
        System.out.println("Init");
    }
    

    }

xml中不需要写init和destroy方法

Spring中的EL表达式

el表达式可以简化代码量,我觉得还是很有必要了解的

1、在使用PropertyPlaceholderConfigurer从properties文件引入配置信息时,使用${SpEL expression}来引用
2、引用同一xml中的其他bean的属性,使用#{SpEL expression}

<bean id="itemBean" class="com.demo.Model.Item">
    <property name="name" value="RenBuRuGu"/>
    <property name="qty" value="10"/>
</bean>

<bean id="customer" class="com.demo.Model.Customer">
    <property name="item" value="#{itemBean}"/>
    <property name="itemName" value="#{itemBean.name}"/>
</bean>

Spring EL也可以使用纯注解的方式完成,但是有几个前提
1、每一个bean使用Component注解(个人理解相当于xml中配置bean)
2、相应的字段增加@Value注解,Spring EL表达式写在注解中
3、xml配置文件中开启自动扫描Components

item.java

package com.demo.Model;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("itemBean")
public class Item {
    @Value("RenBuRuGu")
    private String name;

    @Value("10")
    private int qty;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getQty() {
        return qty;
    }

    public void setQty(int qty) {
        this.qty = qty;
    }

    @Override
    public String toString() {
        return name + "  " + qty;
    }
}

Customer.java

package com.demo.Model;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("customerBean")
public class Customer {

    @Value("#{itemBean}")
    private Item item;

    @Value("#{itemBean.name}")
    private String itemName;

    public Item getItem() {
        return item;
    }

    public void setItem(Item item) {
        this.item = item;
    }

    public String getItemName() {
        return itemName;
    }

    public void setItemName(String itemName) {
        this.itemName = itemName;
    }

    @Override
    public String toString() {
        return item + "  " + itemName;
    }
}

Spring-Beans.xml

<context:component-scan base-package="com.demo.Model"/>
  • EL表达式可以直接引用Entity实例、属性和方法,也可以执行java内置的方法,运算符和三元表达式
  • EL表达式中的正则匹配 可以简单的使用matches关键字

    @Component(“customerBean”)
    public class Customer {

    String emailRegEx = "^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)" +
                "*@[A-Za-z0-9]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";


    @Value("#{'100' matches '\\d+' }")
    private boolean validDigit;


    @Value("#{ ('100' matches '\\d+') == true ? " +
                "'yes this is digit' : 'No this is not a digit'  }")
    private String msg;


    @Value("#{emailBean.emailAddress matches customerBean.emailRegEx}")
    private boolean validEmail;


}

ExpressionParser(不知道有什么用…)

看意思好像大概也许是提取字符并转化成相应的数据类型

ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'put spel expression here'");
String msg = exp.getValue(String.class);

下面给个例子自己感受一下

import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

public class App {
    public static void main(String[] args) {

        ExpressionParser parser = new SpelExpressionParser();

        Expression exp = parser.parseExpression("'Hello World'");
        String msg1 = exp.getValue(String.class);
        System.out.println(msg1);

        Expression exp2 = parser.parseExpression("'Hello World'.length()");  
        int msg2 = (Integer) exp2.getValue();
        System.out.println(msg2);

        Expression exp3 = parser.parseExpression("100 * 2");  
        int msg3 = (Integer) exp3.getValue();
        System.out.println(msg3);

        Item item = new Item("RenBuRuGu", 100);

        StandardEvaluationContext itemContext = new StandardEvaluationContext(item);


        Expression exp4 = parser.parseExpression("name");
        String msg4 = exp4.getValue(itemContext, String.class);
        System.out.println(msg4);

        Expression exp5 = parser.parseExpression("name == 'RenBuRuGu'");
        boolean msg5 = exp5.getValue(itemContext, Boolean.class);
        System.out.println(msg5);

    }
}

Spring自动扫描组件

Spring的业务逻辑分为好多层(DAO,Service,Controller,Views),主要目的还是为了解耦

一般我们都会通过xml的方式来注册组件
Dao层
首先创建一个CustomerDao

package com.demo.Dao;

public class CustomerDao {
    @Override
    public String toString() {
        return "Hello, This is CustomerDao";
    }
}

Service层实现业务逻辑

package com.demo.Service;

import com.demo.Dao.CustomerDao;

public class CustomerService {
    CustomerDao customerDao;

    public void setCustomerDao(CustomerDao customerDao) {
        this.customerDao = customerDao;
    }

    @Override
    public String toString() {
        return "CustomerService [ customerDao = " +  customerDao + " ]";
    }
}

xml注册bean

<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
    <bean id="customerDao" class="com.demo.Dao.CustomerDao"/>

    <bean id="customerService" class="com.demo.Service.CustomerService">
        <property name="customerDao" ref="customerDao"/>
    </bean>
</beans>

测试函数

package com.demo;

import com.demo.Service.CustomerService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("Spring-Beans.xml");

        CustomerService service = context.getBean(CustomerService.class);

        System.out.println(service);
    }
}

运行结果如下:

这是一般做法

我们还可以通过自动扫描组件来完成相同的工作

像之前所说,给所有的实体类加上@Components注解,通过注解的方式注册实体类

CustomerDao.java

package com.demo.Dao;

import org.springframework.stereotype.Component;

@Component
public class CustomerDao {
    @Override
    public String toString() {
        return "Hello, This is CustomerDao";
    }
}

CustomerService.java

package com.demo.Service;

import com.demo.Dao.CustomerDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class CustomerService {
    private CustomerDao customerDao;

    @Autowired
    public void setCustomerDao(CustomerDao customerDao) {
        this.customerDao = customerDao;
    }

    @Override
    public String toString() {
        return "CustomerService [ customerDao = " +  customerDao + " ]";
    }
}

然后在xml中开启自动扫描beans

<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"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="com.demo"/>
</beans>

运行结果相同

为了区分开功能不同的各个模块,Spring官方给出了四种注解

@Component – 指示自动扫描组件。
@Repository – 表示在持久层DAO组件。
@Service – 表示在业务层服务组件。
@Controller – 表示在表示层控制器组件。

所有的注解最后都会被编译为Components,但是这样就可以很好的区分业务层和持久层的组件

附上代码

CustomerDao.java

package com.demo.Dao;

import org.springframework.stereotype.Repository;

@Repository
public class CustomerDao {
    @Override
    public String toString() {
        return "Hello, This is CustomerDao";
    }
}

CustomerService.java

package com.demo.Service;

import com.demo.Dao.CustomerDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class CustomerService {
    private CustomerDao customerDao;

    @Autowired
    public void setCustomerDao(CustomerDao customerDao) {
        this.customerDao = customerDao;
    }

    @Override
    public String toString() {
        return "CustomerService [ customerDao = " +  customerDao + " ]";
    }
}

component-scan过滤器的使用

过滤器有两种(include-filter和exclude-filter),顾名思义,一个是包含的过滤器另一个则是排除的过滤器.
filter可以通过很多种方式进行过滤,有以下几种类型:

annotation  注解方式
regix       正则表达式
custom      自定义方式
assignable  未知.....
aspectj     切片方式(面向切片编程)

通过正则过滤

<context:component-scan base-package="com.yiibai" >

    <context:include-filter type="regex" 
                    expression="com.demo.Dao.*DAO.*" />

    <context:include-filter type="regex" 
                    expression="com.demo.Services.*Service.*" />

</context:component-scan>

通过include的方式,Spring只会扫描在com.demo.Dao和com.demo.Service包下面名为_Dao._和_Service._的文件

通过annotation注解的方式

<context:component-scan base-package="com.demo">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>

通过exclude-filter方式,所有@Service注解的entity将会被Spring忽略

Spring AOP

  • 面向接口编程

我们首先来创建一个简单的小例子

创建Service层

CustomerService.java

package com.demo.Service;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class CustomerService {

    @Value("RenBuRuGu")
    private String name;

    @Value("https://wangyu1997.github.io/")
    private String url;

    public void printName() {
        System.out.println("Customer name: " + this.name);
    }

    public void printUrl() {
        System.out.println("Customer url: " + this.url);
    }

    public void printThrowException() {
        throw new IllegalArgumentException();
    }
}

创建入口测试函数

package com.demo;

import com.demo.Service.CustomerService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan({"com.demo.Service"})
public class App {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(App.class);

        CustomerService customerService = context.getBean(CustomerService.class);

        customerService.printName();
        customerService.printUrl();
        customerService.printThrowException();
    }
}

运行结果如下:

通过注解的方式加入AOP

1、之前通知

首先实现MethodBeforeAdvice方法

@Component("heiBeforeMethod")
public class HiBeforeMethod implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
        System.out.println("Method Before:  Hey Spring AOP !");
    }
}

在配置文件中注册一个代理bean

@Configuration
public class CustomerServiceProxy {

    @Bean("customerServiceProxy")
    public ProxyFactoryBean proxyFactoryBean() {
        ProxyFactoryBean bean = new ProxyFactoryBean();
        bean.setTargetName("customerService");
        bean.setInterceptorNames("heiBeforeMethod");

        return bean;
    }

}

测试类中调用

@Configuration
@ComponentScan({"com.demo.Service","com.demo.Impl"})
@Import({CustomerServiceProxy.class})
public class App {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(App.class);
        CustomerService customerService = (CustomerService) context.getBean("customerServiceProxy");

        customerService.printName();
        customerService.printUrl();
        try {
            customerService.printThrowException();
        }catch (IllegalArgumentException e){
            System.out.println("Throw exception...");
        }
    }
}

注意这里的引用不再是customerService而是customerServiceProxy

运行结果如下:

2、返回后通知

通过重写AfterReturningAdvice的afterReturning方法来实现功能

接着上面再加一个类

package com.demo.Impl;

import org.springframework.aop.AfterReturningAdvice;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Component("afterReturn")
public class AfterReturn implements AfterReturningAdvice {
    @Override
    public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable {
        System.out.println("Spring after returning: Spring AOP!");
    }
}

CustomerServiceProxy中InterceptorNames加入该类

bean.setInterceptorNames("afterReturn","heiBeforeMethod");

运行结果如下:

3、抛出后通知,即方法抛出异常后调用

同上,新建一个类实现ThrowsAdvice的afterThrowing方法即可

不过,我感觉,可能是在新版本中被删除了吧…

4、环绕通知

我感觉这种方式十分强大,首先实现MethodInterceptor的invoke方法,这里用到了反射

package com.demo.Impl;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component("aroundMethod")
public class AroundMethod implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("Method name : " + invocation.getMethod().getName());
        System.out.println("Method arguments : " + Arrays.toString(invocation.getArguments()));

        System.out.println("Around: Method Before");
        try {
            Object result = invocation.proceed();

            System.out.println("Around: Method After");

            return result;
        } catch (Exception ignored) {
            System.out.println("Around: Method Exception");

            throw ignored;
        }
    }
}

运行结果如下:

坚持原创技术分享,您的支持将鼓励我继续创作!