- expose-proxy和proxy-target-class
- 具体例子
- 新增接口和实现
- 定义aop
- 测试
- 注解介绍
- 指示符
- Aspect通配符
- within
- @within
- **@annotation**
- bean
- **execution**
- this
- target
- @**target**
- args
- @args
- 注意:
- 通知参数
expose-proxy即是否需要将当前的代理对象使用 ThreadLocal 进行保存。
打开expose-proxy是因为若我们要拦截某个接口下的所有方法,但是该接口下的一个方法存在内部调用,此时不开启expose-proxy,对于内部调用的方法是不拦截的。比如:
public void test_1()
{
this.test_2();
}
public void test_2()
{
}
调用 test_1,此时 test_2 将不会被拦截进行增强,因为调用的是 AOP 代理对象而不是当前对象,而 在 test_1 方法内部使用的是 this 进行调用,所以 test_2 将不会被拦截增强,所以该属性 expose-proxy 就是用来解决这个问题的,即 AOP 代理的获取。
proxy-target-class:是否使用 CGLIB 进行代理。
因为 Spring AOP 的底层技术就是使用的是动态代理,分为 JDK 代理 和 CGLIB 代理,该属性的默认值为 false,表示 AOP 底层默认使用的使用 JDK 代理,当需要代理的类没有实现任何接口的时候才会使用 CGLIB 进行代理,如果想都是用 CGLIB 进行代理,可以把该属性设置为 true 即可。
具体例子定义需要 aop 拦截的方法,模拟一个 User 的增删改操作
新增接口和实现public interface IUserService {
void add(User user);
User query(String name);
List qyertAll();
void delete(String name);
void update(User user);
}
@Service("userServiceImpl")
public class UserServiceImpl implements IUserService {
@Override
public void add(User user) {
System.out.println("添加用户成功,user=" + user);
}
@Override
public User query(String name) {
System.out.println("根据name查询用户成功");
User user = new User(name, 20, 1, 1000, "java");
return user;
}
@Override
public List qyertAll() {
List users = new ArrayList<>(2);
users.add(new User("zhangsan", 20, 1, 1000, "java"));
users.add(new User("lisi", 25, 0, 2000, "Python"));
System.out.println("查询所有用户成功, users = " + users);
return users;
}
@Override
public void delete(String name) {
System.out.println("根据name删除用户成功, name = " + name);
}
@Override
public void update(User user) {
System.out.println("更新用户成功, user = " + user);
}
}
定义aop
下述的方法 before_1() 是对接口的 add() 方法进行 前置增强,即在 add() 方法执行之前执行,
@Component
@Aspect
public class UserAspectj {
// 在方法执行之前执行
@Before("execution(* main.tsmyk.mybeans.inf.IUserService.add(..))")
public void before_1(){
System.out.println("log: 在 add 方法之前执行....");
}
}
测试
1@RunWith(SpringJUnit4ClassRunner.class)
2@ContextConfiguration("/resources/myspring.xml")
3public class TestBean {
4
5 @Autowired
6 private IUserService userServiceImpl;
7
8 @Test
9 public void testAdd() {
10 User user = new User("zhangsan", 20, 1, 1000, "java");
11 userServiceImpl.add(user);
12 }
13}
14// 结果:
15// log: 在 add 方法之前执行....
16// 添加用户成功,user=User{name='zhangsan', age=20, sex=1, money=1000.0, job='java'}
注解介绍
@Aspect:作用是把当前类标识为一个切面供容器读取(必须标注在被spring识别的bean上面,即被@Compoment修饰或在xml配置好的类中) @Pointcut:Pointcut是植入Advice的触发条件。每个Pointcut的定义包括2部分,一是表达式,二是方法签名。方法签名必须是 public及void型。可以将Pointcut中的方法看作是一个被Advice引用的助记符,因为表达式不直观,因此我们可以通过方法签名的方式为 此表达式命名。因此Pointcut中的方法只需要方法签名,而不需要在方法体内编写实际代码。 @Around:环绕增强,相当于MethodInterceptor @AfterReturning:后置增强,相当于AfterReturningAdvice,方法正常退出时执行 @Before:标识一个前置增强方法,相当于BeforeAdvice的功能,相似功能的还有 @AfterThrowing:异常抛出增强,相当于ThrowsAdvice @After: final增强,不管是抛出异常或者正常退出都会执行指示符 Aspect通配符
- ***** 匹配任何数量字符;
- . . 匹配任何数量字符的重复,如在类型模式中匹配任何数量子包;而在方法参数模式中匹配任何数量参数。
- +:匹配指定类型的子类型;仅能作为后缀放在类型模式后边。
匹配指定类或包(接口不算)下的方法
| 示例 | 描述 |
|---|---|
| within(cn.javass…*) | cn.javass包及子包下的任何方法执行 |
| within(cn.javass…IPointcutService+) | cn.javass包或所有子包下IPointcutService类型及子类型的任何方法 |
| within(@cn.javass…Secure *) | 持有cn.javass…Secure注解的任何类型的任何方法必须是在目标对象上声明这个注解,在接口上声明的对它不起作用 |
配所有持有指定注解类型的方法
| 示例 | 描述 |
|---|---|
| @within(cn.javass.spring.chapter6.Secure) | 任何目标对象对应的类型持有Secure注解的类方法; |
匹配特定注解的所有方法
| 示例 | 描述 |
|---|---|
| @annotation(cn.javass.spring.chapter6.Secure ) | 当前执行方法上持有注解 cn.javass.spring.chapter6.Secure将被匹配 |
匹配特定名称的Bean对象的所有方法
| 示例 | 描述 |
|---|---|
| bean(*Service) | 匹配所有以Service命名(id或name)结尾的Bean |
匹配符合条件的所有方法
| 示例 | 描述 |
|---|---|
| public * *(…) | 任何公共方法的执行 |
| * cn.javass…IPointcutService.*() | cn.javass包及所有子包下IPointcutService接口中的任何无参方法 |
| * cn.javass….(…) | cn.javass包及所有子包下任何类的任何方法 |
| * cn.javass…IPointcutService.() | cn.javass包及所有子包下IPointcutService接口的任何只有一个参数方法 |
| * (!cn.javass…IPointcutService+) .*(…) | 非“cn.javass包及所有子包下IPointcutService接口及子类型”的任何方法 |
| * cn.javass…IPointcutService+.*() | cn.javass包及所有子包下IPointcutService接口及子类型的的任何无参方法 |
| * cn.javass…IPointcut*.test*(java.util.Date) | cn.javass包及所有子包下IPointcut前缀类型的的以test开头的只有一个参数类型为java.util.Date的方法,注意该匹配是根据方法签名的参数类型进行匹配的,而不是根据执行时传入的参数类型决定的,如定义方法:public void test(Object obj);即使执行时传入java.util.Date,也不会匹配的; |
| * cn.javass…IPointcut*.test*(…) throws IllegalArgumentException,ArrayIndexOutOfBoundsException,cn.javass | 包及所有子包下IPointcut前缀类型的的任何方法,且抛出IllegalArgumentException和ArrayIndexOutOfBoundsException异常 |
| * (cn.javass…IPointcutService+&& java.io.Serializable+).*(…) | 任何实现了cn.javass包及所有子包下IPointcutService接口和java.io.Serializable接口的类型的任何方法 |
| @java.lang.Deprecated * *(…) | 任何持有@java.lang.Deprecated注解的方法 |
| @java.lang.Deprecated @cn.javass…Secure* *(…) | 任何持有@java.lang.Deprecated和@cn.javass…Secure注解的方法 |
| @(java.lang.Deprecated | cn.javass…Secure) **(…) |
| (@cn.javass…Secure*) *(…) | 任何返回值类型持有@cn.javass…Secure的方法 |
| (@cn.javass…Secure).*(…) | 任何定义方法的类型持有@cn.javass…Secure的方法 |
| **(@cn.javass…Secure() ,@cn.javass…Secure()) | 任何签名带有两个参数的方法,且这个两个参数都被@ Secure标记了,如public void test(@Secure String str1, @Secure String str1); |
| ((@ cn.javass…Secure ))或(@ cn.javass…Secure) | 任何带有一个参数的方法,且该参数类型持有@ cn.javass…Secure; |
| 如public void test(Model model);且Model类上持有@Secure注解 | |
| * *(@cn.javass…Secure (@cn.javass…Secure *) ,@ cn.javass…Secure (@cn.javass…Secure *)) | 任何带有两个参数的方法,且这两个参数都被@ cn.javass…Secure标记了;且这两个参数的类型上都持有@ cn.javass…Secure; |
| * *(java.util.Map | 任何带有一个java.util.Map参数的方法,且该参数类型是以< cn.javass…Model, cn.javass…Model >为泛型参数;注意只匹配第一个参数为java.util.Map,不包括子类型;如public void test(HashMap |
| * *(java.util.Collection<@cn.javass…Secure *>) | 任何带有一个参数(类型为java.util.Collection)的方法,且该参数类型是有一个泛型参数,该泛型参数类型上持有@cn.javass…Secure注解;如public void test(Collection collection);Model类型上持有@cn.javass…Secure |
| * *(java.util.Set extends HashMap>) | 任何带有一个参数的方法,且传入的参数类型是有一个泛型参数,该泛型参数类型继承与HashMap;Spring AOP目前测试不能正常工作 |
| * *(java.util.List super HashMap>) | 任何带有一个参数的方法,且传入的参数类型是有一个泛型参数,该泛型参数类型是HashMap的基类型;如public voi test(Map map);Spring AOP目前测试不能正常工作 |
| * (<@cn.javass…Secure *>) | 任何带有一个参数的方法,且该参数类型是有一个泛型参数,该泛型参数类型上持有@cn.javass…Secure注解;Spring AOP目前测试不能正常工作 |
使用“this(类型全限定名)”匹配当前AOP代理对象类型的执行方法
| 示例 | 描述 |
|---|---|
| this(cn.javass.spring.chapter6.service.IPointcutService) | 当前AOP对象实现了 IPointcutService接口的任何方法 |
| this(cn.javass.spring.chapter6.service.IIntroductionService) | 当前AOP对象实现了 IIntroductionService接口的任何方法 |
匹配实现了目标接口的对象的所有方法
| 示例 | 描述 |
|---|---|
| target(cn.javass.spring.chapter6.service.IPointcutService) | 当前目标对象(非AOP对象)实现了 IPointcutService接口的任何方法 |
| target(cn.javass.spring.chapter6.service.IIntroductionService) | 当前目标对象(非AOP对象) 实现了IIntroductionService 接口的任何方法, 不可能是引入接口 |
匹配一个目标对象,这个对象必须有特定的注解
| 示例 | 描述 |
|---|---|
| @target (cn.javass.spring.chapter6.Secure) | 任何目标对象持有Secure注解的类方法;必须是在目标对象上声明这个注解,在接口上声明的对它不起作用 |
将首先查找通知方法上同名的参数,并在方法执行时(运行时)匹配传入的参数是使用该同名参数类型;如果匹配将把该被通知参数传递给通知方法上同名参数。
| 示例 | 描述 |
|---|---|
| args (java.io.Serializable,…) | 任何一个以接受“传入参数类型为 java.io.Serializable” 开头,且其后可跟任意个任意类型的参数的方法执行,args指定的参数类型是在运行时动态匹配的 |
匹配带该注解的参数的所有方法
| 示例 | 描述 |
|---|---|
| @args (cn.javass.spring.chapter6.Secure) | 任何一个只接受一个参数的方法,且方法运行时传入的参数持有注解 cn.javass.spring.chapter6.Secure;动态切入点,类似于arg指示符; |
通知参数除了execution和bean指示符,都可以使用args这种方式进行参数绑定。
如:
@Before(value="execution(* test(*)) && args(param)", argNames="param") public void before1(String param) { System.out.println("===param:" + param); }
-
JoinPoint:提供访问当前被通知方法的目标对象、代理对象、方法参数等数据
package org.aspectj.lang; import org.aspectj.lang.reflect.SourceLocation; public interface JoinPoint { String toString(); //连接点所在位置的相关信息 String toShortString(); //连接点所在位置的简短相关信息 String toLongString(); //连接点所在位置的全部相关信息 Object getThis(); //返回AOP代理对象 Object getTarget(); //返回目标对象 Object[] getArgs(); //返回被通知方法参数列表 Signature getSignature(); //返回当前连接点签名 SourceLocation getSourceLocation();//返回连接点方法所在类文件中的位置 String getKind(); //连接点类型 StaticPart getStaticPart(); //返回连接点静态部分 } -
ProceedingJoinPoint:用于环绕通知,使用proceed()方法来执行目标方法
public interface ProceedingJoinPoint extends JoinPoint { public Object proceed() throws Throwable; public Object proceed(Object[] args) throws Throwable; } -
JoinPoint.StaticPart:提供访问连接点的静态部分,如被通知方法签名、连接点类型等
public interface StaticPart { Signature getSignature(); //返回当前连接点签名 String getKind(); //连接点类型 int getId(); //唯一标识 String toString(); //连接点所在位置的相关信息 String toShortString(); //连接点所在位置的简短相关信息 String toLongString(); //连接点所在位置的全部相关信息 }
参考1
写的特别好的博客,参考2
参考3
暑期编程PK赛 得CSDN机械键盘等精美礼品!


