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

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
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【优质项目推荐】 1、项目代码均经过严格本地测试,运行OK,确保功能稳定后才上传平台。可放心下载并立即投入使用,若遇到任何使用问题,随时欢迎私信反馈与沟通,博主会第一时间回复。 2、项目适用于计算机相关专业(如计科、信息安全、数据科学、人工智能、通信、物联网、自动化、电子信息等)的在校学生、专业教师,或企业员工,小白入门等都适用。 3、该项目不仅具有很高的学习借鉴价值,对于初学者来说,也是入门进阶的绝佳选择;当然也可以直接用于 毕设、课设、期末大作业或项目初期立项演示等。 3、开放创新:如果您有一定基础,且热爱探索钻研,可以在此代码基础上二次开发,进行修改、扩展,创造出属于自己的独特应用。 欢迎下载使用优质资源!欢迎借鉴使用,并欢迎学习交流,共同探索编程的无穷魅力! 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值