1、AspectJ技术
因为在xml配置aop过程中太过繁琐,aopalliance 中出现了一种AspectJ技术。
2、 Spring AOP支持的AspectJ切入点指示符
切入点指示符用来指示切入点表达式目的,,在Spring AOP中目前只有执行方法这一个连接点,Spring AOP支持的AspectJ切入点指示符如下:
execution:用于匹配方法执行的连接点;
within:用于匹配指定类型内的方法执行;
this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配;
target:用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;
args:用于匹配当前执行的方法传入的参数为指定类型的执行方法;
@within:用于匹配所以持有指定注解类型内的方法;
@target:用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;
@args:用于匹配当前执行的方法传入的参数持有指定注解的执行;
@annotation:用于匹配当前执行方法持有指定注解的方法;
bean:Spring AOP扩展的,AspectJ没有对于指示符,用于匹配特定名称的Bean对象的执行方法;
reference pointcut:表示引用其他命名切入点,只有@ApectJ风格支持,Schema风格不支持。
3、类型匹配语法
首先让我们来了解下AspectJ类型匹配的通配符:
*:匹配任何数量字符;
…:匹配任何数量字符的重复,如在类型模式中匹配任何数量子包;而在方法参数模式中匹配任何数量参数。
+:匹配指定类型的子类型;仅能作为后缀放在类型模式后边。
代码:
java.lang.String 匹配String类型;
java.*.String 匹配java包下的任何“一级子包”下的String类型;
如匹配java.lang.String,但不匹配java.lang.ss.String
java..* 匹配java包及任何子包下的任何类型;
如匹配java.lang.String、java.lang.annotation.Annotation
java.lang.*ing 匹配任何java.lang包下的以ing结尾的类型;
java.lang.Number+ 匹配java.lang包下的任何Number的自类型;
如匹配java.lang.Integer,也匹配java.math.BigInteger
4、组合切入点表达式
AspectJ使用 且(&&)、或(||)、非(!)来组合切入点表达式。
在Schema风格下,由于在XML中使用“&&”需要使用转义字符“&&”来代替之,所以很不方便,因此Spring ASP 提供了and、or、not来代替&&、||、!。
5、切入点使用示例
execution:使用“execution(方法表达式)”匹配方法执行;
模式 | 描述 |
---|---|
public * *(…) | 任何公共方法的执行 |
* cn.javass…IPointcutService.*() | cn.javass包及所有子包下IPointcutService接口中的任何无参方法 |
* cn.javass….(…) | cn.javass包及所有子包下任何类的任何方法 |
6、AspectJ技术实例
-
通过注解配置切点(哪个方法需要被入侵)和通知(黑客)
-
自动生产代理对象
1)创建AppConfig.java
package com.lq.aspect;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration // 标示为配置类 相当于xml文件
//扫描指定包下包含@Component注解的类,将这个类加入spring bean工程
@ComponentScan(value = {
"com.lq.aspect", "com.lq.dao" })
@EnableAspectJAutoProxy // 启动aspectJ注解aop
public class AppConfig {
}
- 创建@Aspect 切面类
package com.lq.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class LogAspect {
/*
* 定义切点----需要增强的方法
*
*/
@Pointcut("execution(public int com.lq.dao.UserDapImpl.add(..))")
public void pointCut() {
}
// 环绕通知(循环通知/增强(黑客))
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("1.鉴权");
// 调用目标方法
Object[] args = joinPoint.getArgs();
args[0] = 22;
args[1] = 22;
Object result = joinPoint.proceed();
System.out.println("2.日志留痕");
return result;
}
}
3)实体类
package com.lq.dao;
import org.springframework.stereotype.Component;
@Component
public class UserDapImpl {
public int add(int a,int b) {
System.out.println("调用【未实现接口的】UserDaoImpl的add方法");
return a+b;
}
}
- 测试
package com.lq.proxy;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.lq.aspect.AppConfig;
import com.lq.dao.UserDapImpl;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class AspectJTest {
@Autowired
UserDapImpl userDap;
@Test
public void testAdd() {
System.out.println(userDap.add(11, 22));
}
}
- 测试结果为:
九月 29, 2020 10:40:17 下午 org.springframework.test.context.support.AbstractTestContextBootstrapper getDefaultTestExecutionListenerClassNames
信息: Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]
九月 29, 2020 10:40:17 下午 org.springframework.test.context.support.AbstractTestContextBootstrapper getTestExecutionListeners
信息: Using TestExecutionListeners: [org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@13fee20c, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@4e04a765, org.springframework.test.context.support.DirtiesContextTestExecutionListener@783e6358]
九月 29, 2020 10:40:18 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.GenericApplicationContext@100fc185: startup date [Tue Sep 29 22:40:18 CST 2020]; root of context hierarchy
1.鉴权
调用【未实现接口的】UserDaoImpl的add方法
2.日志留痕
33