20210614_SpringAop手动定义切面及切点学习笔记
1概述
本节主要学习下自定义切面及切点,完成spring aop代理的功能。主要内容如下:
- 自定义切点Pointcut
- 自定义增强器 MethodInterceptor
- 自定义切面advisor(依赖pointcut和advice)
- 定义Configuration,实现SpringIOC容器的管理。
1.1AbstractAutoProxyCreator类图
[图片上传失败...(image-2c48ff-1623676489083)]
1.2maven项目依赖
<?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">
<parent>
<artifactId>technicaltools</artifactId>
<groupId>com.kikop</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>myaopcustomadvisor2demo</artifactId>
<name>myaopcustomadvisor2demo</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<description>myaopcustomadvisor2demo</description>
<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>
<!--springframework-->
<springframework.version>5.0.0.RELEASE</springframework.version>
<!-- mybatis -->
<mybatis.version>3.2.3</mybatis.version>
<mybatis-spring.version>1.2.5</mybatis-spring.version>
<!-- mysql jdbc -->
<mysql-connector-java.version>5.1.38</mysql-connector-java.version>
<!--jedis-->
<jedis.version>3.3.0</jedis.version>
<!--redisson对应 spring-boot-->
<!--<redisson.version>3.11.5</redisson.version>-->
<redisson.version>3.13.1</redisson.version>
<!--<redisson.version>3.10.6</redisson.version>-->
<!--fastjson-->
<fastjson.version>1.2.29</fastjson.version>
<!-- slf4j 日志文件管理包版本 -->
<slf4j.version>1.7.12</slf4j.version>
<!-- log4j2日志文件管理包版本 -->
<log4j.version>2.1</log4j.version>
<!--lombok-->
<!--<lombok.version>1.16.10</lombok.version>-->
<lombok.version>1.18.10</lombok.version>
</properties>
<dependencies>
<!--1.junit高版本-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<!--2.引入spring依赖-->
<!--spring对web的支持,依赖 spring-webmvc,此时jar包会自动下载-->
<!--spring-aop、spring-beans、spring-context-->
<!--spring-core、spring-expressionweb、spring-web-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${springframework.version}</version>
</dependency>
<!--2.1.spring-test-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${springframework.version}</version>
</dependency>
<!--2.2.spring-jdbc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${springframework.version}</version>
</dependency>
<!--10.log4j2 相关配置开始-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j.version}</version>
</dependency>
<!--兼容log4j1.x版本-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-1.2-api</artifactId>
<version>${log4j.version}</version>
</dependency>
<!-- 桥接:告诉commons logging 使用Log4j2 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-jcl</artifactId>
<version>${log4j.version}</version>
</dependency>
<!--清理web日志资源-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-web</artifactId>
<version>${log4j.version}</version>
<scope>runtime</scope>
</dependency>
<!--log4j2相关配置结束-->
<!--11.slf4j-->
<!--begin_slf4j api-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- 桥接:告诉Slf4j使用 Log4j2 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${log4j.version}</version>
</dependency>
<!--end_slf4j api-->
<!--12.公共模块中 lombok插件,其他模块用到时填-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<!--resources-->
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
</resource>
</resources>
<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代码实战
2.1MyCachePointCutWithFind
package com.kikop.aop2;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import java.lang.reflect.Method;
/**
* @author kikop
* @version 1.0
* @project Name: toparchdesigndemo2
* @file Name: MyCachePointCutWithQuery
* @desc 自定义一个切点(基于方法上), 不需注入 @Bean.代理所有类的 findVersion方法。
* @date 2021/6/05
* @time 18:00
* @by IDE: IntelliJ IDEA
*/
public class MyCachePointCutWithFind implements Pointcut, MethodMatcher {
// begin_MethodMatcher
/**
* 定义方法匹配规则
*
* @param method
* @param targetClass
* @return
*/
@Override
public boolean matches(Method method, Class<?> targetClass) {
String methodName = method.getName();
if ("findVersion".equals(methodName)) {
return true;
}
return false;
}
@Override
public boolean isRuntime() {
return false;
}
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
return false;
}
// end_MethodMatcher
// begin_Pointcut
/**
* 类匹配规则
*
* @return
*/
@Override
public ClassFilter getClassFilter() {
return ClassFilter.TRUE;
}
/**
* 方法匹配规则
*
* @return
*/
@Override
public MethodMatcher getMethodMatcher() {
return this;
}
// end_Pointcut
}
2.2MyCacheAdvice2
package com.kikop.aop2;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.StringUtils;
/**
* @author kikop
* @version 1.0
* @project Name: toparchdesigndemo2
* @file Name: MyCacheAdvice
* @desc 自定义一个增强
* @date 2021/6/05
* @time 18:00
* @by IDE: IntelliJ IDEA
*/
@Slf4j
public class MyCacheAdvice2 implements MethodInterceptor, ApplicationContextAware {
// applicationContext
private ApplicationContext applicationContext;
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// 缓存无数据
Object proceedResult = invocation.proceed();
if (!StringUtils.isEmpty(proceedResult)) {
if (log.isWarnEnabled()) {
log.warn("query data from db!");
}
}
// 类型转换
return proceedResult;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
2.3MyCacheAdvisor2
package com.kikop.aop2;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor;
/**
* @author kikop
* @version 1.0
* @project Name: toparchdesigndemo2
* @file Name: MyCacheAdvisor 依赖 pointcut切点、advice增强
* @desc 自定义一个切面
* @date 2021/6/05
* @time 18:00
* @by IDE: IntelliJ IDEA
*/
public class MyCacheAdvisor2 extends AbstractBeanFactoryPointcutAdvisor {
// Advisor分两大类:
// IntroductionAdvisor(引介通知器)和PointcutAdvisor(切点通知器)。
// 两类Advisor都是为了增强targetClass,但是作用不一样。
// 1.IntroductionAdvisor主要为了给targetClass追加接口(或者说追加更多的方法),这种增强属于类级别的增强;
// 2.PointcutAdvisor主要为了拦截方法,这种增强属于方法级别的增强。
// DefaultPointcutAdvisor 通用的,最强大的Advisor,
// 它是Spring提供的通用的,也被认为是最强大的Advisor。它可以把任意的两个Advice和Pointcut放在一起
private Pointcut pointcut = new MyCachePointCutWithFind();
/**
* 定义一个切点
*
* @return
*/
@Override
public Pointcut getPointcut() {
// <aop:pointcut id="mypointcut" expression="execution(* com.kikop.myspringaspects..*.*(..))">
// </aop:pointcut>
return pointcut;
}
}
// https://www.jianshu.com/p/d3e0aa9111e8
// https://blog.csdn.net/elim168/article/details/78445908
// 配置使用自定义的Advisor
// AbstractAdvisorAutoProxyCreator 子类有4个 DefaultAdvisorAutoProxyCreator InfrastructureAdvisorAutoProxyCreator
// <aop:config>就是自动定义了 AspectJAwareAdvisorAutoProxyCreator 类型的bean。
// <aop:aspectj-autoproxy/>就是自动定义了 AnnotationAwareAspectjAutoProxyCreator 类型的bean,
// 其实为了能够在创建目标bean的时候能够自动创建基于我们自定义的Advisor实现类的代理对象,
// 我们的bean容器中只要有AbstractAutoProxyCreator类型的bean定义即可,当然了你实现自己的BeanPostProcessor,
// 在其postProcessAfterInitialization方法中创建自己的代理对象也是可以的。
// @Bean
// public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
// return new DefaultAdvisorAutoProxyCreator();
// }
2.4MyCacheAppConfig
package com.kikop.config;
import com.kikop.aop2.MyCacheAdvice2;
import com.kikop.aop2.MyCacheAdvisor2;
import com.kikop.usermodule.UserService;
import com.kikop.usermodule.impl.UserServiceImpl;
import org.springframework.aop.framework.autoproxy.InfrastructureAdvisorAutoProxyCreator;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
/**
* @author kikop
* @version 1.0
* @project Name: mycustomadvisordemo
* @file Name: AppConfig
* @desc 基于Redis实现的缓存接口
* @date 2021/6/05
* @time 18:00
* @by IDE: IntelliJ IDEA
*/
@Configuration
// 指定扫描的组件包,@Bean,作为框架使用时,此处不需要
//@ComponentScan("com.kikop")
public class MyCacheAppConfig {
// 必须加:ROLE_INFRASTRUCTURE,否则不生成代理
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public MyCacheAdvice2 myCacheAdvice2() {
MyCacheAdvice2 myCacheAdvice2 = new MyCacheAdvice2();
return myCacheAdvice2;
}
// 必须加:ROLE_INFRASTRUCTURE,否则不生成代理
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public MyCacheAdvisor2 myCacheAdvisor2() {
MyCacheAdvisor2 myCacheAdvisor2 = new MyCacheAdvisor2();
myCacheAdvisor2.setAdvice(myCacheAdvice2());
return myCacheAdvisor2;
}
@Bean
public UserService userService() {
return new UserServiceImpl();
}
/**
* 实现自己的 BeanPostProcessor
*
* @return
*/
@Bean
public InfrastructureAdvisorAutoProxyCreator infrastructureAdvisorAutoProxyCreator() {
return new InfrastructureAdvisorAutoProxyCreator();
}
}
2.5模拟业务模块
package com.kikop.usermodule;
/**
* @author kikop
* @version 1.0
* @project Name: mycustomadvisordemo
* @file Name: UserService
* @desc
* @date 2021/6/05
* @time 18:00
* @by IDE: IntelliJ IDEA
*/
public interface UserService {
String queryVersion(String userId);
String findVersion(String userId);
}
package com.kikop.usermodule.impl;
import com.kikop.usermodule.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* @author kikop
* @version 1.0
* @project Name: mycustomadvisordemo
* @file Name: UserServiceImpl
* @desc 一般该Service类在业务系统模块
* @date 2021/6/05
* @time 18:00
* @by IDE: IntelliJ IDEA
*/
@Slf4j
// 作为框架使用时,此处不需要
//@Service
public class UserServiceImpl implements UserService {
/**
* 查询用户的版本
*
* @param userId
* @return
*/
@Override
public String queryVersion(String userId) {
log.info("begin queryVersion");
log.info("end queryVersion");
return "query";
}
@Override
public String findVersion(String userId) {
return "find";
}
}
2.6测试
2.6.1简单测试
package com.kikop;
import com.kikop.config.MyCacheAppConfig;
import com.kikop.usermodule.UserService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* @author kikop
* @version 1.0
* @project Name: mycustomadvisordemo
* @file Name: AdvisorAppTest
* @desc
* @date 2021/6/05
* @time 18:00
* @by IDE: IntelliJ IDEA
*/
public class AdvisorAppTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext =
new AnnotationConfigApplicationContext(MyCacheAppConfig.class);
UserService userService1 = annotationConfigApplicationContext.getBean("userService",UserService.class);
String queryResult = userService1.queryVersion("1000");
System.out.println(queryResult);
String findResult = userService1.findVersion("2000");
System.out.println(findResult);
}
}
2.6.2单元测试
2.6.2.1BaseJunit4Test
package com.kikop;
import com.kikop.config.MyCacheAppConfig;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author kikop
* @version 1.0
* @project Name: mycustomadvisordemo
* @file Name: BaseJunit4Test
* @desc
* @date 2021/6/05
* @time 18:00
* @by IDE: IntelliJ IDEA
*/
@RunWith(SpringJUnit4ClassRunner.class) //使用junit4进行测试
//@ContextConfiguration(locations={"classpath:applicationContext.xml"}) // 加载xml配置文件
@ContextConfiguration(classes = {MyCacheAppConfig.class}) // 加载 JavaConfig
public class BaseJunit4Test {
}
2.6.2.2CacheTest
package com.kikop;
import com.kikop.usermodule.UserService;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
/**
* @author kikop
* @version 1.0
* @project Name: mycustomadvisordemo
* @file Name: CacheTest
* @desc
* @date 2021/6/05
* @time 18:00
* @by IDE: IntelliJ IDEA
*/
public class CacheTest extends BaseJunit4Test {
@Autowired
private UserService userService;
@Test
public void userServiceTest() {
System.out.println("begin_测试Spring整合Junit4进行单元测试");
String version = userService.findVersion("0001");
System.out.println(version);
System.out.println("endn_测试Spring整合Junit4进行单元测试");
}
}
网友评论