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
- 基于SSM利用SpringAOP切面及自定义注解 记录每次操作记录(操作日志 同理)
- 基于 自定义注解 和 aop 实现使用memcache 对数据库的缓存 示例
- 第37天(就业班) 静态代理与动态代理、oglib代理、手动实现aop编程、注解实现AOP编程、aop相关的几个注解、xml实现aop、切入点表达式、spring对aop模式
- SpringBoot 通过自定义注解实现AOP切面编程实例
- 基于注解的Spring AOP实现demo(测试通知顺序)
- spring_由注解实现AOP面向切面编程_实现动态代理
- 反射实现 AOP 动态代理模式(Spring AOP 的实现 原理)
- java SpringAOP拦截Controller,Service实现日志管理(自定义注解的方式)
- 反射实现 AOP 动态代理模式(Spring AOP 的实现 原理)
- SpringAOP拦截Controller,Service实现日志管理(自定义注解的方式)
- Spring 通过注解方式实现AOP切面编程
- 反射实现 AOP 动态代理模式(Spring AOP 的实现 原理)
- 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。
- 反射实现 AOP 动态代理模式(Spring AOP 的实现 原理)
- SpringAOP拦截Controller,Service实现日志管理(自定义注解的方式)
- 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别
- 反射实现 AOP 动态代理模式(Spring AOP 的实现 原理)
- 反射实现 AOP 动态代理模式(Spring AOP 的实现 原理)
- 反射实现 AOP 动态代理模式(Spring AOP 的实现 原理)
- 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。