Spring编程:通过Aspect实现AOP编程完成日志记录
来源:互联网 发布:黑魂3故事剧情知乎 编辑:程序博客网 时间:2024/05/19 17:05
最近写一个项目要实现保存操作记录,就想到了AOP,先附上参考的地址
网上小实例:http://my.oschina.net/yangzg/blog/343945
建议先把小实例在电脑上跑通,然后看慕课的视频教学,效果不错。下面附上我学习的思路和代码吧
一、Spring jar包的导入
实现Spring编程,jar包少不了,主要用到了五个jar,其中有两个是测试用的
pom.xml依赖如下
<dependencies> <!-- https://mvnrepository.com/artifact/org.springframework/spring-context --><dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.0.RELEASE</version></dependency><!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --><dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.11</version></dependency><!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects --><dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.0.1.RELEASE</version></dependency><!-- https://mvnrepository.com/artifact/junit/junit --><dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope></dependency><!-- https://mvnrepository.com/artifact/org.springframework/spring-test --><dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.0.RELEASE</version> <scope>test</scope></dependency> </dependencies>注意依赖顺序,最好放最前,不用maven的项目可以去下载这些jar导入工程就好了
二、Spring 配置文件
<?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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <context:annotation-config /> <!-- 自动扫描的包路径 --> <context:component-scan base-package="com.aspect"/> <!-- 对aspectJ的支持配置 --> <aop:aspectj-autoproxy /> </beans>注意自动扫描路径正确性,maven项目放到resource下就好,其他项目放到src下面
三、项目文件目录结构
放代码前先看看项目包目录结构
四、代码实现
写日志用到了注解实现,就是在要方法前加入该方法说明的注解就能自动读取注解内容,且不改变方法本身,没有侵入性
1、逻辑业务接口
package com.aspect.applogic;/* * / 用户管理业务逻辑接口 */public interface UserManagerApplogic { public String addUser(String name); public void addOne(int type,int parentid); }
2、用户管理业务逻辑实现类
package com.aspect.impl;import org.springframework.stereotype.Component;import com.aspect.applogic.UserManagerApplogic;import com.aspect.annotation.BussAnnotation;/* * 用户管理业务逻辑实现类 */@Component("userManager")public class UserManagerApplogicImpl implements UserManagerApplogic { @BussAnnotation(moduleName="人员管理",option="添加用户") @Override public String addUser(String name) { System.out.println("add a User name is "+name); this.addOne(1, 1); // 测试异常放回抛出 // throw new RuntimeException("add failed!"); return "add success!"; } @BussAnnotation(moduleName="人员管理",option="添加新人") @Override public void addOne(int type, int parentid) { System.out.println("add a new one type : "+type+" \t perentid : "+parentid); } }我们看到方法前的注解,不用写入代码中就能实现该方法的说明,为此我们要自己声明这个注解
3、业务注释类
package com.aspect.annotation;import java.lang.annotation.Retention; import java.lang.annotation.ElementType; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /* * 业务注释类 */// RUNTIME代表的是表示在源码、编译好的.class文件中保留信息,在执行的时候会把这一些信息加载到JVM中去的。@Retention(RetentionPolicy.RUNTIME) // @Target里面的ElementType是用来指定Annotation类型可以用在哪一些元素上的.METHOD(方法)@Target ({ElementType.METHOD}) public @interface BussAnnotation { //模块名 String moduleName() default ""; //操作内容 String option() default ""; }上面的注解还有其他的类型,可参考开头给的链接
4、切面类
重点就是这个切面类
package com.aspect.aop;import java.lang.reflect.Method;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.AfterThrowing;import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component;import com.aspect.annotation.BussAnnotation;//切面类 http://my.oschina.net/yangzg/blog/343945 /* * 特别注意: Spring的配置文件中添加: * * <aop:aspectj-autoproxy /> * spring-mvc-dispatcher.xml中天机 * <!--通知spring使用cglib而不是jdk的来生成代理方法 AOP可以拦截到Controller--> * <aop:aspectj-autoproxy proxy-target-class="true"/> * * <aop:config>节点中proxy-target-class="true"不为true时。 * 当登录的时候会报这个异常java.lang.NoSuchMethodException: $Proxy54.login(), */ // aspect注解不能被自动扫描。配合component使用// 这个类为一个切面类且自己不会被代理/* * aspectJ是编译期的AOP,检查代码并匹配连接点和切入点代价高,要选择好的切入点 * execution get、set、call、handler等等选择 * within withincode确定范围 * this、target、@annotation 匹配上下文信息 */@Aspect @Component public class LogInterceptor { // 定义切入点 @Pointcut("execution(public * com.aspect..*.*(..))") -- 表示对com.aspect 包下的所有方法都可添加切入点 // 该注解下方法返回值为void@Pointcut("execution(public * addUser(..))") public void aApplogic() {} //定义切入点 -- 拦截指定的方法 这里拦截 com.aspect.demo3.aop1.impl.UserManagerApplogicImpl 的addOne()方法 @Pointcut("execution(public * addOne(..))") public void joinPointForAddOne(){} ///** //* 环绕通知 用于拦截指定内容,记录用户的操作 //* 切入点可以通过 && 和 || 进行一个拼接的作用//*/ //@Around(value = "aApplogic() && @annotation(annotation) &&args(object,..) ", argNames = "annotation,object") //public Object interceptorApplogic(ProceedingJoinPoint pj, //BussAnnotation annotation, Object object) throws Throwable { //System.out.println("moduleName:" + annotation.moduleName()); //System.out.println("option:" + annotation.option()); //pj.proceed();//// 打印方法名//System.out.println(pj.getSignature().getName()); //// 打印参数//for(Object obj : pj.getArgs()){ //System.out.println(obj.toString()); //} //return object; //} // // // /** // * 环绕通知 拦截指定的切点,这里拦截joinPointForAddOne切入点所指定的addOne()方法 // * // */ //@Around("joinPointForAddOne()") //public Object interceptorAddOne(ProceedingJoinPoint joinPoint) throws Throwable { //System.out.println("Aop start"); //String methodRemark = getMthodRemark(joinPoint); //Object result = null; // try { // // 记录操作日志...谁..在什么时间..做了什么事情.. // result = joinPoint.proceed(); // } catch (Exception e) { // // 异常处理记录日志..log.error(e); // throw e; // } // System.out.println(methodRemark); // System.out.println("Aop end"); //return result; // } //////// 获取方法的中文备注____用于记录用户的操作日志描述 //public static String getMthodRemark(ProceedingJoinPoint joinPoint) //throws Exception { // String targetName = joinPoint.getTarget().getClass().getName(); // String methodName = joinPoint.getSignature().getName(); // System. out.println("====调用" +methodName+"方法-开始!"); // Object[] arguments = joinPoint.getArgs(); //获得参数列表 // System.out.println("打印出方法调用时传入的参数,可以在这里通过添加参数的类型,进行一些简易逻辑处理和判断"); // if(arguments.length<=0){ // System.out.println("=== "+methodName+" 方法没有参数"); // }else{ // for(int i=0;i<arguments.length;i++){ // System.out.println("==== 参数 "+(i+1)+" : "+arguments[i]); // } // } // // @SuppressWarnings("rawtypes")// Class targetClass = Class.forName(targetName); // Method[] method = targetClass.getMethods(); // String methode = ""; // for (Method m : method) { // if (m.getName().equals(methodName)) { // @SuppressWarnings("rawtypes")//Class[] tmpCs = m.getParameterTypes(); // if (tmpCs.length == arguments.length) { // BussAnnotation methodCache = m.getAnnotation(BussAnnotation.class); // methode = methodCache.moduleName(); // break; // } // } // } //return methode; //} /* * imooc 例子 */ // 执行前 // 下面写法类型这个@Before("execution(public * add*(..))") @Before("aApplogic()")public void Before(){System.out.println("Before.");} // Advice可以获取参数 @Before("aApplogic()&&args(arg)")public void BeforeWithParame(String arg){System.out.println("BeforeWithParame." + arg);} // Advice获取注解,先定义一个注解例如:bussAnnotation那个,然后再方法那加上注解 // 注意注解获取在参数获取之前 //@Before("aApplogic()&&@annotation(annotation)")// 简单写法// 上述方法的简单写法@Pointcut("execution(public * addUser(..))&&@annotation(annotation)") public void aApplogicAnno(BussAnnotation annotation) {} @Before("aApplogicAnno(annotation)") public void BeforeWithAnnotation(BussAnnotation annotation){System.out.println("BeforeWithAnnotation." + annotation.moduleName());} // 正常返回后获取返回值 // returning 获取方法的返回值,放回类型不确定定义该类型为object @AfterReturning(pointcut="aApplogic()",returning="returnValue") public void afterReturning(Object returnValue){ System.out.println("AfterReturning:" + returnValue); } // 异常返回后获取异常值e 接收抛出的异常 @AfterThrowing(pointcut="aApplogic()",throwing="e") public void afterThrowing(RuntimeException e){ // 调试打出堆栈信息 // e.printStackTrace(); System.out.println("afterThrowing :" + e.getMessage()); } // 返回(无论返回正常还是异常)后释放资源:注意,after先执行,AfterReturning和AfterThrowing后执行 @After("aApplogic()") public void after(){ System.out.println("After"); } // 环绕通知 /* * 环绕通知通知方法的第一个参数必须是ProceedingJoinPoint类型 * 在通知内部调用ProceedingJoinPoint的proceed()方法会导致执行真正的方法 * 传入一个object[]对象,数组中的值将会作为参数传递个方法 * proceed()方法执行时才执行真正的方法,把真正的返回值个object类型 */ @Around("aApplogic()") public Object around(ProceedingJoinPoint pjp) throws Throwable{ /* 执行顺序 * 1、around 1 * 2、before * 3、method * 4、around 2 * 5、around : obj * 6、after * 7、afterReturning */ System.out.println("Around 1"); Object object = pjp.proceed(); System.out.println("Around 2"); System.out.println("Around :" + object); return object;}}
我只保留了慕课网上的Advic,可以先跑一下看看效果
5、测试方法
package com.aspect.test;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import org.springframework.beans.factory.annotation.Autowired;import com.aspect.applogic.UserManagerApplogic;// 使用所有注释前必须使用@RunWith(SpringJUnit4ClassRunner.class),让测试运行于Spring测试环境@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations="classpath:applicationContextmay.xml")public class test{ @Autowired private UserManagerApplogic userManager; @Test public void testAopAddUser(){ userManager.addUser("马良"); } @Test public void testAopAddOne(){ userManager.addOne(1, 1); } }
先测试testAopAddUser()方法可以看到控制台打印效果如下Around 1Before.BeforeWithAnnotation.人员管理BeforeWithParame.马良add a User name is 马良add a new one type : 1 perentid : 1Around 2Around :add success!AfterAfterReturning:add success!
五、实际使用
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContextmay.xml");LoginMainFrm loginMainFrm = (LoginMainFrm) context.getBean("loginMainFrm");用
ApplicationContext读取Spring配置文件,且想用aop的类全要Spring自动装配的bena,就是要spring管理起来。
多个项目还不知道怎么实现日志记录管理~先写这么多吧
阅读全文
0 0
- Spring编程:通过Aspect实现AOP编程完成日志记录
- 自定义注解 Aspect 实现aop 日志记录切面编程
- Spring AOP 完成日志记录
- Spring AOP 完成日志记录
- Spring AOP 完成日志记录
- Spring AOP 完成日志记录
- Spring AOP 完成日志记录
- Spring AOP 完成日志记录
- Spring AOP 完成日志记录
- Spring AOP 完成日志记录
- Spring AOP 完成日志记录
- Spring AOP 完成日志记录
- Spring的AOP(四):自动代理及Aspect J的方式实现Aop编程
- Spring注解实现AOP(面向切面编程Aspect Oriented Programming,AOP)
- Spring AOP编程中--@aspect 标签,切面编程
- 用Spring AOP完成数据库日志记录
- spring面向切面编程AOP(Aspect-orented programming)
- spring 基于Aspect和注解的切面编程(aop)
- C++速成(2)
- Spring 学习笔记(11)—— SpringMVC 拦截器
- 百度页面
- 解决网站html页面微信转发调用缩略图的问题
- C++速成(3)
- Spring编程:通过Aspect实现AOP编程完成日志记录
- 容器是什么,和Kubernetes是什么关系,为什么对OpenStack很重要
- CSS定位的使用
- 欧拉公式和改进的欧拉公式C++源码
- 01背包(第k优值)
- 开源项目实现下载,断点续传..
- 脉冲神经网络之Tempotron简介(一)
- Django网站开发学习(一)
- box-sizing:border-box;