栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

@Aspect相关注解和属性详解

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

@Aspect相关注解和属性详解

这里写目录标题
      • expose-proxy和proxy-target-class
      • 具体例子
        • 新增接口和实现
        • 定义aop
        • 测试
      • 注解介绍
      • 指示符
        • Aspect通配符
        • within
        • @within
        • **@annotation**
        • bean
        • **execution**
        • this
        • target
        • @**target**
        • args
        • @args
        • 注意:
      • 通知参数

expose-proxy和proxy-target-class
 

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

匹配指定类或包(接口不算)下的方法

示例描述
within(cn.javass…*)cn.javass包及子包下的任何方法执行
within(cn.javass…IPointcutService+)cn.javass包或所有子包下IPointcutService类型及子类型的任何方法
within(@cn.javass…Secure *)持有cn.javass…Secure注解的任何类型的任何方法必须是在目标对象上声明这个注解,在接口上声明的对它不起作用
@within

配所有持有指定注解类型的方法

示例描述
@within(cn.javass.spring.chapter6.Secure)任何目标对象对应的类型持有Secure注解的类方法;
@annotation

匹配特定注解的所有方法

示例描述
@annotation(cn.javass.spring.chapter6.Secure )当前执行方法上持有注解 cn.javass.spring.chapter6.Secure将被匹配
bean

匹配特定名称的Bean对象的所有方法

示例描述
bean(*Service)匹配所有以Service命名(id或name)结尾的Bean
execution

匹配符合条件的所有方法

示例描述
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.Deprecatedcn.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 map, String str);将不匹配,必须使用“* *(java.util.HashMap, …)”进行匹配;而public void test(Map map, int i);也将不匹配,因为泛型参数不匹配
* *(java.util.Collection<@cn.javass…Secure *>)任何带有一个参数(类型为java.util.Collection)的方法,且该参数类型是有一个泛型参数,该泛型参数类型上持有@cn.javass…Secure注解;如public void test(Collection collection);Model类型上持有@cn.javass…Secure
* *(java.util.Set)任何带有一个参数的方法,且传入的参数类型是有一个泛型参数,该泛型参数类型继承与HashMap;Spring AOP目前测试不能正常工作
* *(java.util.List)任何带有一个参数的方法,且传入的参数类型是有一个泛型参数,该泛型参数类型是HashMap的基类型;如public voi test(Map map);Spring AOP目前测试不能正常工作
* (<@cn.javass…Secure *>)任何带有一个参数的方法,且该参数类型是有一个泛型参数,该泛型参数类型上持有@cn.javass…Secure注解;Spring AOP目前测试不能正常工作
this

使用“this(类型全限定名)”匹配当前AOP代理对象类型的执行方法

示例描述
this(cn.javass.spring.chapter6.service.IPointcutService)当前AOP对象实现了 IPointcutService接口的任何方法
this(cn.javass.spring.chapter6.service.IIntroductionService)当前AOP对象实现了 IIntroductionService接口的任何方法
target

匹配实现了目标接口的对象的所有方法

示例描述
target(cn.javass.spring.chapter6.service.IPointcutService)当前目标对象(非AOP对象)实现了 IPointcutService接口的任何方法
target(cn.javass.spring.chapter6.service.IIntroductionService)当前目标对象(非AOP对象) 实现了IIntroductionService 接口的任何方法, 不可能是引入接口
@target

匹配一个目标对象,这个对象必须有特定的注解

示例描述
@target (cn.javass.spring.chapter6.Secure)任何目标对象持有Secure注解的类方法;必须是在目标对象上声明这个注解,在接口上声明的对它不起作用
args

将首先查找通知方法上同名的参数,并在方法执行时(运行时)匹配传入的参数是使用该同名参数类型;如果匹配将把该被通知参数传递给通知方法上同名参数。

示例描述
args (java.io.Serializable,…)任何一个以接受“传入参数类型为 java.io.Serializable” 开头,且其后可跟任意个任意类型的参数的方法执行,args指定的参数类型是在运行时动态匹配的
@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机械键盘等精美礼品!
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1017616.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号