您的位置:首页 > 编程语言 > Java开发

Spring对AOP切面支持实现及示例demo(基于自定义注解实现、代理模式实现、注解驱动、注入式)

2019-03-27 17:55 615 查看

1.@AspectJ 注解驱动的切面

定义切点

Spring支持通过AspectJ的切点表达式语言来定义 Spring 切面,同时增加通过bean的id指定bean的写法。

如:execution(* com.my.spring.bean..(…)) 指定com.my.spring.bean包下所有类的所有方法作为切点

其结构解析如下:

AspectJ切点表达式的指示器不只有execution:

  • arg() :限制连接点匹配参数为指定类型的执行方法
  • execution() :用于匹配是连接点的执行方法
  • this() :限制连接点匹配 AOP 代理的 bean 引用为指定类型的类
  • target :限制连接点匹配目标对象为指定类型的类
  • within() :限制连接点匹配指定的类型

各指示器之间可以通过&&(与),||(或),!(非)连接符进行连接实现多条件查询定义节点

如:execution(* com.my.spring.bean..(…))&&arg(java.lang.Integer)

示例Demo

导包

Spring AOP的实现依赖于spring-aop包和aspectjweaver包,多加一个log4j日志打印包并在根目录一般是src/main/resources这个创建log4j.properties,需在pom文件引入:

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>

<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>

log4j.properties内容

### 设置###
log4j.rootLogger = debug,stdout,D,E

### 输出信息到控制抬 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

### 输出DEBUG 级别以上的日志到=E://logs/error.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = E://logs/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

### 输出ERROR 级别以上的日志到=E://logs/error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =E://logs/error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

通过实例demo引入概念:

定义一个基础接口类BaseInterface

public interface BaseInterface {

/**
* 新增歌曲
*
* @param author
*            作者
* @param songTitle
*            歌曲名
*
* @return java.lang.Integer 返回当前歌曲总数
*
* @author xxx 2019/3/4
* @version 1.0
**/
Integer addSong(String author, String songTitle);

/**
* 删除歌曲
*
* @param author
*            作者
* @param songTitle
*            歌曲名
*
* @return java.lang.Integer 返回当前歌曲总数
*
* @author xxx 2019/3/4
* @version 1.0
**/
Integer delSong(String author, String songTitle);
}

创建实现类BaseBean

@Component
public class BaseBean implements  BaseInterface{

private String author;
private String songTitle;
private Integer count=0;

public Integer addSong(String author,String songTitle){
this.author = author;
this.songTitle = songTitle;
System.out.println("新增了一首歌:"+author+"-"+songTitle);
count++;
return count;
}

public Integer delSong(String author,String songTitle){
this.author = author;
this.songTitle = songTitle;
System.out.println("删除了一首歌:"+author+"-"+songTitle);
count--;
return count;
}
}

创建一个切面类BaseBeanAspect(这里将引用的包一起贴出来防止错用)

package aspectj;

import org.apache.log4j.Logger;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class BaseBeanAspect {

private Logger logger = Logger.getLogger(BaseBean.class);

/**
* 方法执行前的通知
*/
@Before("execution(* aspectj.*.*(..))")
public void beforeInvoke(){
logger.debug("方法执行前");
}

/**
* 方法执行后的通知
*/
@After("execution(* aspectj.*.*(..))")
public void afterInvoke(){
logger.debug("方法执行后");
}

/**
* 方法执行返回后的通知
*/
@AfterReturning("execution(* aspectj.*.*(..))")
public void afterReturning(){
logger.debug("==================方法执行完成");
}

/**
* 方法抛出异常的通知
*/
@AfterThrowing("execution(* aspectj.*.*(..))")
public void afterThrowing(){
logger.debug("==================方法执行报错");
}
}

创建自动化装配的配置类

@Configuration
@EnableAspectJAutoProxy//开启自动代理开关,启用切面
@ComponentScan(basePackages = {"aspectj"})
public class ComponentConfig {
}

创建测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class,classes = {ComponentConfig.class})
public class AppTest {

@Autowired
private BaseInterface baseInterface;

@Test
public void testBean(){
baseInterface.addSong("myBean","mySong");
baseInterface.delSong("myBean","mySong");
}
}

打印结果为

[DEBUG] 2019-03-27 11:55:28,075 method:aspectj.BaseBeanAspect.beforeInvoke(BaseBeanAspect.java:22)
方法执行前
新增了一首歌:myBean-mySong
[DEBUG] 2019-03-27 11:55:28,075 method:aspectj.BaseBeanAspect.afterInvoke(BaseBeanAspect.java:30)
方法执行后
[DEBUG] 2019-03-27 11:55:28,076 method:aspectj.BaseBeanAspect.afterReturning(BaseBeanAspect.java:38)
==================方法执行完成
[DEBUG] 2019-03-27 11:55:28,076 method:aspectj.BaseBeanAspect.beforeInvoke(BaseBeanAspect.java:22)
方法执行前
删除了一首歌:myBean-mySong
[DEBUG] 2019-03-27 11:55:28,076 method:aspectj.BaseBeanAspect.afterInvoke(BaseBeanAspect.java:30)
方法执行后
[DEBUG] 2019-03-27 11:55:28,077 method:aspectj.BaseBeanAspect.afterReturning(BaseBeanAspect.java:38)
==================方法执行完成
[DEBUG] 2019-03-27 11:55:28,077 method:org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener.beforeOrAfterTestMethod(AbstractDirtiesContextTestExecutionListener.java:106)

定义环绕通知:@Around

环绕通知是从方法执行前一直包裹直到方法执行完成后的一个通知,用的比较多,其中被定义的方法需要引入参数ProceedingJoinPoint,ProceedingJoinPoint对象封装了当前运行对象的具体信息,简单实现如下:

@Around("execution(* aspectj.*.*(..))")
public void aroundInvoke(ProceedingJoinPoint jp){
try {
logger.debug("=====================环绕执行方法开始");
Signature signature = jp.getSignature();
String methodName = signature.getName();
MethodSignature methodSignature = (MethodSignature) signature;
logger.debug("方法名:{}"+methodName);
List<Object> args = Arrays.asList(jp.getArgs());
logger.debug("参数列表:{}"+args);
Class<?> returnType = methodSignature.getMethod().getReturnType();
logger.debug("方法返回类型:{}"+returnType);
Object proceed = jp.proceed();
logger.debug("======================环绕执行方法结束,方法执行结果:{}"+proceed);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}

2.注入式 AspectJ 切面(适用于 Spring 各版本)

场景与节点定义

我们知道使用注解实现切面编程是很方便直接的,但是有时候我们并不一定拥有通知类的源码,也就无法给对应的方法添加注解,这时候就需要使用XML配置实现了。XML配置实现与注解实现十分类似,我们可以看一下基本的实现节点:

①定义切面:<aop:aspect ref=“切面类在xml文件中对应bean的id”>

②定义切点:<aop:pointcut id=“切点id” expression=“切点表达式”/>

③定义通知:

<aop:beafore method=“通知方法名” pointcut-ref=“切点id”/>-------------定义方法执行前的通知

<aop:after method=“通知方法名” pointcut-ref=“切点id”/>-------------定义方法执行后的通知

<aop:afterReturning method=“通知方法名” pointcut-ref=“切点id”/>-------------定义方法执行返回后的通知

<aop:afterThrowing method=“通知方法名” pointcut-ref=“切点id”/>-------------定义方法执行抛出异常后的通知

<aop:around method=“通知方法名” pointcut-ref=“切点id”/>-------------定义方法环绕通知

④开启自动代理:aop:aspectj-autoproxy/

⑤表示aop配置:aop:config</aop:config>

除了开启自动代理,aop的所有节点都需要包含在aop:config</aop:config>节点中。

示例demo

延用上面demo的基础接口类BaseInterface、实体类BaseBean。最好将@Component,虽然放在那儿我也没发现会出什么问题,不过基础接口中重复声明通知标签会出问题(不会报错)。

新建一个基础接口类NewBaseInterface

public class NewBaseBeanAspect {

private Logger logger = Logger.getLogger(BaseBean.class);

public void pointCut(){//被用于标识的空方法
System.out.println(">>>>>>>>>>>>pointcut");
}

public void beforeInvoke(){
logger.debug("pointcut方法执行前");
}

public void aroundInvoke(ProceedingJoinPoint jp){
try {
logger.debug("=====================环绕执行方法开始");
Signature signature = jp.getSignature();
String methodName = signature.getName();
MethodSignature methodSignature = (MethodSignature) signature;
logger.debug("方法名:{}"+methodName);
List<Object> args = Arrays.asList(jp.getArgs());
logger.debug("参数列表:{}"+args);
Class<?> returnType = methodSignature.getMethod().getReturnType();
logger.debug("方法返回类型:{}"+returnType);
Object proceed = jp.proceed();
logger.debug("======================环绕执行方法结束,方法执行结果:{}"+proceed);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}

/**
* 方法执行前的通知
*/
/*@Before("execution(* aspectj.*.*(..))")
public void beforeInvoke1(){
logger.debug("方法执行前");
}*/

/**
* 方法执行后的通知
*/
public void afterInvoke(){
logger.debug("方法执行后");
}

/**
* 方法执行返回后的通知
*/
public void afterReturning(){
logger.debug("==================方法执行完成");
}

/**
* 方法抛出异常的通知
*/
public void afterThrowing(){
logger.debug("==================方法执行报错");
}
}

创建配置文件aspectxml.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启自动代理-->
<aop:aspectj-autoproxy/>

<!--装配基本类-->
<bean class="aspectxml.BaseBean" id="baseBean" name="baseBean"/>

<!--装配切面类-->
<bean class="aspectxml.NewBaseBeanAspect" id="newBaseBeanAspect"/>

<!--aop配置-->
<aop:config>
<!--配置切面-->
<aop:aspect ref="newBaseBeanAspect">
<!--定义切点-->
<aop:pointcut id="pointCut" expression="execution(* aspectxml.*.*(..))"/>
<!--定义前置通知-->
<aop:before method="beforeInvoke" pointcut-ref="pointCut"/>
<!--定义后置通知-->
<aop:after method="afterInvoke" pointcut-ref="pointCut"/>
<!--定义方法执行返回后通知-->
<aop:after-returning method="afterReturning" pointcut-ref="pointCut"/>
<!--定义方法异常后通知-->
<aop:after-throwing method="afterThrowing" pointcut-ref="pointCut"/>
<!--定义方法环绕通知通知-->
<!-- <aop:around method="aroundInvoke" pointcut-ref="pointCut"/> -->
</aop:aspect>
</aop:config>
</beans>

创建测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:aspectxml.xml"})//将配置文件作为装配环境
public class AppXMLTest {

@Autowired
private BaseInterface baseInterface;

@Test
public void testBean(){
baseInterface.addSong("Mr D","The World!!");
baseInterface.delSong("Mr D","The World!!");
}
}

打印结果

[DEBUG] 2019-03-27 18:14:34,702 method:aspectxml.NewBaseBeanAspect.beforeInvoke(NewBaseBeanAspect.java:20)
pointcut方法执行前
新增了一首歌:Mr D-The World!!
[DEBUG] 2019-03-27 18:14:34,702 method:org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:251)
Returning cached instance of singleton bean 'newBaseBeanAspect'
[DEBUG] 2019-03-27 18:14:34,702 method:aspectxml.NewBaseBeanAspect.afterInvoke(NewBaseBeanAspect.java:53)
方法执行后
[DEBUG] 2019-03-27 18:14:34,702 method:org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:251)
Returning cached instance of singleton bean 'newBaseBeanAspect'
[DEBUG] 2019-03-27 18:14:34,703 method:aspectxml.NewBaseBeanAspect.afterReturning(NewBaseBeanAspect.java:60)
==================方法执行完成
[DEBUG] 2019-03-27 18:14:34,703 method:org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:251)
Returning cached instance of singleton bean 'newBaseBeanAspect'
[DEBUG] 2019-03-27 18:14:34,703 method:aspectxml.NewBaseBeanAspect.beforeInvoke(NewBaseBeanAspect.java:20)
pointcut方法执行前
删除了一首歌:Mr D-The World!!
[DEBUG] 2019-03-27 18:14:34,703 method:org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:251)
Returning cached instance of singleton bean 'newBaseBeanAspect'
[DEBUG] 2019-03-27 18:14:34,704 method:aspectxml.NewBaseBeanAspect.afterInvoke(NewBaseBeanAspect.java:53)
方法执行后
[DEBUG] 2019-03-27 18:14:34,704 method:org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:251)
Returning cached instance of singleton bean 'newBaseBeanAspect'
[DEBUG] 2019-03-27 18:14:34,704 method:aspectxml.NewBaseBeanAspect.afterReturning(NewBaseBeanAspect.java:60)
==================方法执行完成
[DEBUG] 2019-03-27 18:14:34,704 method:org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener.beforeOrAfterTestMethod(AbstractDirtiesContextTestExecutionListener.java:106)

3.基于代理的经典 Spring AOP

静态代理demo

为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

比如,有两个有钱人张三和李四在杭州有一套房子要租出去,只有找到租客才能收租金,李四找了中介帮他,张三自己找人,其中的中介就起到代理的作用,李四可以不再去管租房的事情而是每个月收收房租做个快乐的房东,张三就不一样了,他必须自己找到租客。

创建一个业务接口RichPeople

public interface RichPeople {
/**
* 招人租房
*/
public void zufang();
/**
* 收租
*/
public void getmoney();
}

创建一个实现类People实现业务接口

public class People implements RichPeople{
private String name;

public People(){}

public People(String name){
this.name = name;
}

public String getName() {
return name;
}

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

public void zufang() {
System.out.println(this.name+"自己招租客。。。好累。。。");
}

public void getmoney() {
System.out.println(this.name+"自己收租金。。。有钱拿累一点不是事儿。。。");
}

}

创建一个代理类ZhongjieProxy,相当于一个中介公司

//某租房中介
public class ZhongjieProxy implements RichPeople{
//客户
private RichPeople richPeople;

public ZhongjieProxy(){}

public ZhongjieProxy(RichPeople richPeople){
this.richPeople = richPeople;
}

/**
* 中介公司帮忙找租客
*/
public void zufang() {
System.out.println("帮客户找租客。。。顾客就是上帝。。。");
}

/**
* 租金让客户自己去收
*/
public void getmoney() {
richPeople.getmoney();
}

}

创建测试类ProxyTest

public class ProxyTest {
@Test
public void method(){
//有钱人张三
RichPeople zhangsan = new People("张三");
//有钱人李四
RichPeople lisi = new People("李四");

//张三自己找租客收租金
zhangsan.zufang();
zhangsan.getmoney();
//李四找中介公司收租金
ZhongjieProxy zhongjie = new ZhongjieProxy(lisi);
zhongjie.zufang();
zhongjie.getmoney();
}
}

打印结果

张三自己招租客。。。好累。。。
张三自己收租金。。。有钱拿累一点不是事儿。。。
帮客户找租客。。。顾客就是上帝。。。
李四自己收租金。。。有钱拿累一点不是事儿。。。

JDK动态代理demo

面向接口生成代理,原理就是类加载器根据接口,在虚拟机内部创建接口实现类
Proxy.newProxyInstance(classloader,interfaces[], invocationhandler );
invocationHandler 通过invoke()方法反射来调用目标类中的代码。

创建一个业务接口Fly

// 被代理接口
public interface Fly {

public void gotoFly();

public void stopFly();
}

创建一个实体类Bird实现Fly接口

// 委托类
public class Bird implements Fly {

public void gotoFly() {
System.out.println("鸟儿张开翅膀要飞起来了。。。。");
}

public void stopFly() {
System.out.println("准备降落。。。。");
}

public void eatBug(){
System.out.println("鸟要吃虫子,补充体力。。。");
}
}

创建一个代理类

// 拦截器
public class InvocationHandlerProxy implements InvocationHandler{
// 委托类
private Object obj;
public InvocationHandlerProxy(){}
// 初始化委托类
public InvocationHandlerProxy(Object obj){
this.obj = obj;
}

/**
* proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0
* method:我们所要调用某个对象真实的方法的Method对象
* rgs:指代代理对象方法传递的参数
* 代理类执行代理方法时,回调此方法,并将自己this作为实参传给proxy
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 拦截方法
if (method.getName().equals("gotoFly")) {
System.out.println("被拦截了,鸟飞不走了。。。");
return null;
}
// 以反射的形式,调用委托类的方法
return method.invoke(obj, args);
}

}

创建测试类ProxyTest

public class ProxyTest {

@Test
public void demo1() {
// JDK自动代理的原理是根据类加载器和接口创建代理类(此代理类是接口的实现类,所以必须使用接口)

// 1、 创建目标业务对象的引用
Fly fly = new Bird();
// 2、创建一个代理类对象
InvocationHandler handler = new InvocationHandlerProxy(fly);
// 3、使用目标业务对象类加载器和接口,在内存中创建代理对象
Fly proxy = (Fly) Proxy.newProxyInstance(fly.getClass().getClassLoader(), fly.getClass().getInterfaces(),
handler);

//这里可以通过运行结果证明proxy是Proxy的一个实例,这个实例实现了Fly接口
System.out.println(proxy instanceof Proxy);

//这里可以看出proxy的Class类是$Proxy0,这个$Proxy0类继承了Proxy,实现了Fly接口
System.out.println("proxy的Class类是:"+proxy.getClass().toString());

System.out.print("proxy中的属性有:");

Field[] field=proxy.getClass().getDeclaredFields();
for(Field f:field){
System.out.print(f.getName()+", ");
}

System.out.print("\n"+"proxy中的方法有:");

Method[] method=proxy.getClass().getDeclaredMethods();

for(Method m:method){
System.out.print(m.getName()+", ");
}

System.out.println("\n"+"proxy的父类是:"+proxy.getClass().getSuperclass());

System.out.print("\n"+"proxy实现的接口是:");

Class<?>[] interfaces=proxy.getClass().getInterfaces();

for(Class<?> i:interfaces){
System.out.print(i.getName()+", ");
}

System.out.println("\n\n"+"运行结果为:");
proxy.gotoFly();
}
}

打印结果:

被拦截了,鸟飞不走了。。。

若调用不拦截的方法,则会调用真实要调用的方法。
InvocationHandler中invoke()方法的调用问题

CGLIB动态代理demo

创建一个委托类Cat

//委托类
public class Cat {
public void run(){
System.out.println("猫可以跑。。。捉老鼠");
}
}

创建一个拦截器MethodInterceptorHandler

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

public class MethodInterceptorHandler implements MethodInterceptor{
/**
* 前三个参数同jdk方式
* methodProxy,委托类中的每一个被代理方法都对应一个MethodProxy对象
*/
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//拦截方法
if (method.getName().equals("run1")) {
System.out.println("cat的run方法被拦截了。。。。");
return null;
}

// MethodProxy为cgli生成的对象,性能更高也体现在此
return methodProxy.invokeSuper(proxy, args);
}

}

测试类ProxyTest

import org.junit.Test;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;

public class ProxyTest {

@Test
public void demo2() {
// cglib 动态代理在目标业务类没有实现接口的情况下

// 1、创建真实业务类的子类
// cglib自带的字节码增强器
Enhancer enhancer = new Enhancer();
// 2、将委托类设置成父类
enhancer.setSuperclass(Cat.class);

MethodInterceptor methodInterceptor = new MethodInterceptorHandler();

// 3、设置回调函数、拦截器
enhancer.setCallback(methodInterceptor);
Cat proxy = (Cat) enhancer.create();

proxy.run();
}
}

打印

cat的run方法被拦截了。。。。

4.基于自定义注解的切面实现

创建一个自定义注解AspectMsg

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/***
*
* 短信切面注解
*
**/

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AspectMsg {

final String name = "自定义的短信切面注解";
}

创建一个实现类DemoAnnotetionService

import org.springframework.stereotype.Service;
/**
*
*
* 根据自定义的注解,给所在的bean的特殊的方法标识上注解标识,好让切点反射处理方法过滤到
*
*/
@Service
public class DemoAnnotetionService {

@AspectMsg
public void add(){
System.out.println("调用注解方法类DemoAnnotetionService方法add:");
}
}

定义切面类LogAspect

import java.lang.reflect.Method;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.hesc.sifa.utils.SendMessageUtil;

@Aspect
@Component
public class LogAspect {

/**
* 对指定注解,进行横切,创建一个横切的对象方法
* 此处注意自定义注解的全限定名(路径)
*/
@Pointcut("@annotation(com.hesc.sifa.utils.test.AspectMsg)")
public void annotationPoint(){};

/**
* 对横切方法,进行反射处理,对使用了注解方法“前”:不仅可以捕捉到注解内容,还有方法名等,
* 此处的作用主要是:可以对使用注解使用的方法,进行方法特殊逻辑处理(可以具体到哪个方法使用了哪个注解内容进行特殊处理)
*
*/
@Before("annotationPoint()")
public void BeforeAnnotation(JoinPoint joinPoint){
MethodSignature signature=(MethodSignature) joinPoint.getSignature();
Method method=signature.getMethod();
AspectMsg action=method.getAnnotation(AspectMsg.class);
//获取参数
Object[] params = joinPoint.getArgs();
for (Object object : params) {
System.out.println(object);
}
System.out.println("注解的拦截方法名注解内容前:"+action.name);
}

/**
* 对横切方法,进行反射处理,对使用了注解方法“后”:不仅可以捕捉到注解内容,还有方法名等,
* 此处的作用主要是:可以对使用注解使用的方法,进行方法特殊逻辑处理(可以具体到哪个方法使用了哪个注解内容进行特殊处理)
*
*/
@After("annotationPoint()")
public void AfterAnnotation(JoinPoint joinPoint){
MethodSignature signature=(MethodSignature) joinPoint.getSignature();
Method method=signature.getMethod();
AspectMsg action=method.getAnnotation(AspectMsg.class);
System.out.println("注解的拦截方法名注解内容后:"+action.name);
}
}

创建自动化装配的配置类

此处扫描路径测试时指定到底

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

//此处扫描路径问题注意
@Configuration
@ComponentScan( basePackages={"com.hesc.sifa.utils.test"} )
@EnableAspectJAutoProxy//开启切面的支持
public class RootConfig {
}

创建测试类

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 org.springframework.test.context.support.AnnotationConfigContextLoader;

@RunWith(SpringJUnit4ClassRunner.class)//使用Spring提供的测试包进行测试,主要帮助实现bean的装载环境
@ContextConfiguration(loader = AnnotationConfigContextLoader.class,classes = {RootConfig.class})//配置类指向CDConfig
public class AspectTest {
@Autowired
private DemoAnnotetionService demoAnnotetionService;

@Test
public void usemethod() {
System.out.println("Annotion test!!");
demoAnnotetionService.add();
String message = "Annotion test from";
System.out.println(message);
}
}

打印

Annotion test!!
[DEBUG] 2019-04-02 16:09:01,193 method:org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:251)
Returning cached instance of singleton bean 'logAspect'
注解的拦截方法名注解内容前:自定义的短信切面注解
调用注解方法类DemoAnnotetionService方法add:
注解的拦截方法名注解内容后:自定义的短信切面注解
Annotion test from
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐