发表于: 2018-06-03 21:15:19

1 1150



今天主要学习了springmvc aop的知识点,这个一开始我不知道是什么,找了很多档案目录,其中一个很抽象的解释了什么是aop,


AOP图解

面向切面,面向方面,也叫刀削面。


这个图是不是很灵性,当时看的时候很懵逼(并且有一种想吃刀削面的想法)

具体来说的话,蘸料是切面(增强),刀削面是切入点,把蘸料加入到刀削面中,就是切面(要切入的代码段)切入到切入点中去。即AOP,


这应该也就是面向切面的编程aop,


看了那么多字面姿势(知识),应该要去动手做一做切面到底是要干什么了,

一开始没有方向怎么处理这个(查了很多的材料)

查到aop切面编程,记录日志的打印


首先配置依赖包

(依赖包的地址)

http://maven.outofmemory.cn/org.aspectj/aspectjweaver/1.7.4/


在这里添加依赖包,


(参考文档)

https://blog.csdn.net/turleslove/article/details/52575771


创建aop的类


1,controller aop

@Aspect 注解表示这是一个切面

@Component 表示这是一个bean,由Spring进行管理

@Around(value = "execution(* com.jnshu.controller.*.*(..))") 表示对com.jnshu.ControllerRest 这个类中的所有方法进行切面操作

@Aspect
@Component
public class TimerController {
   private static Log logger = LogFactory.getLog(TimerController.class);

   //一分钟,即60000ms
   private static final long ONE_MINUTE = 60000;

   //
   @Around(value = "execution (* com.jnshu.controller.*.*(..))") //这里定义了controller
   public Object timeAround(ProceedingJoinPoint joinPoint) throws Throwable {
       //定义返回对象、得到方法需要的参数
       Object obj = null;   //对象
       Object[] args = joinPoint.getArgs();
       long startTime = System.currentTimeMillis();

       try {
           obj = joinPoint.proceed(args);
       } catch (Throwable e) {
           logger.error("统计某方法执行消耗时环绕");
       }

       // 获取执行的方法名
       long endTime = System.currentTimeMillis();
       //方法声明的两个组件构成了方法签名 - 方法的名称和参数类型。
       MethodSignature signature = (MethodSignature) joinPoint.getSignature();
       //返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
       //方法后面接收的就是Class类的对象,而如:String.class、int.class这些字节码才是Class类的对象
       String methodName = signature.getDeclaringTypeName() + "." + signature.getName();

       //打印耗时的信息
       this.printExecTime(methodName, startTime, endTime);

       return obj;

   }


2,db.aop

  @Around(value = "execution (* com.jnshu.service.*.*(..))")
   //定义service的统计执行时间
   public Object serviceTime(ProceedingJoinPoint joinPoint)throws Throwable {
       //定义返回对象,需要的参数值
       Object db = null;
       Object[] arg = joinPoint.getArgs();
       long stTime = System.currentTimeMillis();
       //判断
       try {
           db = joinPoint.proceed(arg);
           //catch 错误执行
       } catch (Throwable e) {
           logger.error("统计某方法执行消耗时环绕");
       }
       //获取执行方法名
       long endtime = System.currentTimeMillis();
       //方法声明的两个组件构成了方法签名 - 方法的名称和参数类型。
       MethodSignature signature = (MethodSignature) joinPoint.getSignature();
       //返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
       //方法后面接收的就是Class类的对象,而如:String.class、int.class这些字节码才是Class类的对象
       String methodName = signature.getDeclaringTypeName() + "." + signature.getName();

       //打印消耗信息
       this.printExecTime(methodName, stTime, endtime);
       return db;


   }

   private void printExecTime(String methodName, long startTime, long endTime) {
       long diffTime = endTime - startTime;
       System.out.println("-----" + methodName + "方法执行耗时:" +diffTime + "ms");
       if (diffTime > ONE_MINUTE) {
           logger.warn("-----" + methodName + "方法执行耗时:" + diffTime + "ms");
       }
   }
}


SpringMVC关于AOP拦截controller的注意事项

controller.aop 的切面配置文件必须放在springmvc 目录下。

    <!-- 开启自动切面代理 -->
   <aop:aspectj-autoproxy/>
   <!-- controller aop -->
   <bean name="timerController" class="com.jnshu.aop.TimerController"/>

</beans>

输出成功

 在网上也找了不少资料,但是感觉网上都没说清楚,在这里我把自己亲自验证的结果和配置方法在这里详细的讲解:
       首先aop切面是可以拦截controller层的。这一点再次强调一下,只不过它是有条件的。(条件就是如下3点:请仔细,耐心的读完下面3句英文
        Indeed your controller (annotated by @Controller) and your aspects (annotated by @Aspect) should be in the same Spring context. (我自己翻译的,英文不好请别吐槽)
    1.Controller层和你自己定义的切面,必须在相同的spring上下文中(context).

    Usually people define their controllers in the dispatch-servlet.xml or xxx-servlet.xml and their service beans (including the aspects) in  the main applicationContext.xml. It will not work. 
    2.通常大家会把 controllers 定义在dispatch-servlet.xml 或者 xxx-servlet.xml 这样的配置文件中,但是把自定义的切面放在spring的主配置文件 applicationContext.xml中。这样子导致controller和你的切面不在同一个context中,从而你的切面类逻辑不会拦截对应的controller.

    When Spring initializes the MVC context, it will create a proxy for your controller but if your aspects are not in the same context, Spring     will not create interceptors for them. 
 
    

3.当spring初始化MVC的context的时候,它会同时为controller 创建代理,但是如果自定义切面没有和mvc在同一个context中,那么你的切面是不会去拦截这些controller 的



service.aop 的切面文件必须放在spring目录下


<!-- service aop -->
<context:component-scan base-package="com.jnshu.aop"/>
   <context:component-scan base-package="com.jnshu.service"/>
   <aop:aspectj-autoproxy/>

输出成功



重新配置了一下log4j.xml的文件,


pom.xml文件log4j 依赖包

<!-- slf4j日志包 -->
<dependency>
   <groupId>log4j</groupId>
   <artifactId>log4j</artifactId>
   <version>${log4j.version}</version>
</dependency>
<dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>slf4j-api</artifactId>
   <version>${slf4j.version}</version>
</dependency>
<dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>slf4j-log4j12</artifactId>
   <version>${slf4j.version}</version>
</dependency>

web.xml文件中配置log4j

监听器一定要在前面,

   <!-- 配置log4j.xml监听器 -->
  <context-param>
      <param-name>log4jConfigLocation</param-name>
      <param-value>WEB-INF/config/log4j.xml</param-value>
  </context-param>

  <!-- 配置log4j.xml变量,如果需要动态的就使用下面方式,使用方法${name} -->
  <context-param>
      <param-name>controller</param-name>
      <param-value>controller-log</param-value>
  </context-param>

  <context-param>
      <param-name>loggingLevel</param-name>
      <param-value>info</param-value>
  </context-param>

  <!-- 加载log4j配置文件 -->
  <listener>
      <!-- 在Spring 4.2.1中已经将其标记为过时了.如果使用spring4.2.1以上的版本又会造成不兼容
Log4jConfigListener必须要在Spring的Listener之前。 -->
      <listener-class>org.springframework.web.util.Log4jConfigListener
</listener-class>
  </listener>

log4j.xml

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<!--log4j.xml 配置文件 优先于 log4j.properties 配置文件 -->
<!-- * 1. 一个appender子元素定义一个日志输出目的地 * 2. 一个logger子元素定义一个日志写出器 -->
<!-- ========================== 自定义输出格式说明================================ -->
<!-- %p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL -->
<!-- %r 输出自应用启动到输出该log信息耗费的毫秒数 -->
<!-- %c 输出所属的类目,通常就是所在类的全名 -->
<!-- %t 输出产生该日志事件的线程名 -->
<!-- %n 输出一个回车换行符,Windows平台为“/r/n”,Unix平台为“/n” -->
<!-- %d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS} -->
<!-- 输出类似:2002年10月18日 22:10:28,921 -->
<!-- %l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main(TestLog4.java:10) -->
<!-- ========================================================================== -->

<!-- ========================== 输出方式说明================================ -->
<!-- Log4j提供的appender有以下几种: -->
<!-- org.apache.log4j.ConsoleAppender(控制台), -->
<!-- org.apache.log4j.FileAppender(文件), -->
<!-- org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件), -->
<!-- org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件), -->
<!-- org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方) -->
<!-- ========================================================================== -->
<log4j:configuration>
   <!-- 输出到控制台 -->
   <!-- 可以配置多个appender来对应不同的输出,如文件输出,sql输出,控制台输出,邮件输出等 -->
   <!-- [控制台STDOUT] 不同的输出类型对应着不同的calss,如控制台输出class对应着 org.apache.log4j.ConsoleAppender -->
   <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
       <layout class="org.apache.log4j.PatternLayout"><!-- loyout表示输出方式,可以多种,class值区分,PatternLayout表示自定义格式 -->
           <param name="ConversionPattern" value="%d{ISO8601} 耗时:%r [日志来自:%-40.40c{3} 日志类型: %-5p 日志内容:%m]%n" />
       </layout>
   </appender>

   <!-- 输出到日志文件 每天一个日志 -->
   <appender name="LOGDEBUG" class="org.apache.log4j.DailyRollingFileAppender">
       <!-- Threshold屏蔽级别之下的日志输出 -->
       <param name="Threshold" value="DEBUG" />
       <param name="encoding" value="UTF-8" />
       <param name="File" value="${webapp.root}/logs/debug-log.log" />
       <param name="DatePattern" value="'debug_'yyyy-MM-dd'.log'" />
       <layout class="org.apache.log4j.PatternLayout">
           <param name="ConversionPattern" value="%d{ISO8601} 耗时:%r [日志来自:%-40.40c{3} 日志类型: %-5p 日志内容:%m]%n" />
       </layout>
   </appender>

   <appender name="LOGINFO" class="org.apache.log4j.DailyRollingFileAppender">
       <!-- <param name="Threshold" value="INFO" /> -->
       <param name="encoding" value="UTF-8" />
       <param name="File" value="${webapp.root}/logs/logInf-log.log" />
       <param name="DatePattern" value="'info_'yyyy-MM-dd'.log'" />
       <layout class="org.apache.log4j.PatternLayout">
           <param name="ConversionPattern" value="%d{ISO8601} 耗时:%r [日志来自:%-40.40c{3} 日志类型: %-5p 日志内容:%m]%n" />
       </layout>
       <!--限制输出级别 -->
       <!-- filter过滤器设置输出的级别:ALL < DEBUG < INFO < WARN < ERROR < FATAL < OFF
           如果levelMax为warn则输出的是debug到warn不会有error和fatal -->
       <!-- 输出所有日志 ALL  -->
       <filter class="org.apache.log4j.varia.LevelRangeFilter">
           <param name="LevelMin" value="INFO" />
           <param name="LevelMax" value="INFO" />
       </filter>
   </appender>

   <appender name="LOGERROR" class="org.apache.log4j.DailyRollingFileAppender">
       <!-- <param name="Threshold" value="ERROR" /> -->
       <param name="encoding" value="UTF-8" />
       <param name="File" value="${webapp.root}/logs/error-log.log" />
       <param name="DatePattern" value="'error_'yyyy-MM-dd'.log'" />
       <layout class="org.apache.log4j.PatternLayout">
           <param name="ConversionPattern" value="%d{ISO8601} 耗时:%r [日志来自:%-40.40c{3} 日志类型: %-5p 日志内容:%m]%n" />
       </layout>
       <filter class="org.apache.log4j.varia.LevelRangeFilter">
           <param name="LevelMin" value="ERROR" />
           <param name="LevelMax" value="ERROR" />
       </filter>
   </appender>

   <!-- 指定类appender 输出指定类日志到指定位置 -->
   <appender name="TimerController" class="org.apache.log4j.DailyRollingFileAppender">
       <param name="encoding" value="UTF-8" />
       <param name="File" value="${webapp.root}/logs/TimerController-log.log" />
       <param name="DatePattern" value="'.'yyyy-MM-dd'.log'" />
       <layout class="org.apache.log4j.PatternLayout">
           <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %t %5p [%c:%L] - %m%n" />
       </layout>
   </appender>
   <appender name="ControllerRest" class="org.apache.log4j.DailyRollingFileAppender">
       <param name="encoding" value="UTF-8" />
       <param name="File" value="${webapp.root}/logs/ControllerRest-log.log" />
       <param name="DatePattern" value="'.'yyyy-MM-dd'.log'" />
       <layout class="org.apache.log4j.PatternLayout">
           <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %t %5p [%c:%L] - %m%n" />
       </layout>
   </appender>

   <!-- additivity="false" 不继承root-->
   <category name="ControllerRest" additivity="false">
       <!-- 输出所有级别日志 -->
       <level value="ALL" />
       <appender-ref ref="ControllerRest" />
       <!-- 输出到控制台 -->
       <appender-ref ref="CONSOLE" />
   </category>
   <category name="TimerController" additivity="false">
       <level value="All" />
       <appender-ref ref="TimerController" />
       <!-- 输出到控制台 -->
       <appender-ref ref="CONSOLE" />
   </category>

   <!-- 日志的总开关设置 包括日志级别和 appender -->
   <!-- logger的作用: 1.[name属性]:指定你定义Logger对象时候的name -->
   <!-- 2. additivity : children-logger是否使用 rootLogger的配置, additivity在log4j默认为true。 -->
   <!-- 这解释了为什么有些时候,一个日志信息在屏幕上会有多次输出。 -->
   <!-- 3.还可以指定level(输出级别)、appender-ref(指定哪个append) -->
   <root>
       <!-- ALL < DEBUG < INFO < WARN < ERROR < FATAL < OFF -->
       <!-- all为输出所有级别日志-->
       <level value="DEBUG" />
       <appender-ref ref="CONSOLE" />
       <appender-ref ref="LOGDEBUG" />
       <appender-ref ref="LOGINFO" />
       <appender-ref ref="LOGERROR" />
   </root>

</log4j:configuration>

配置成功后 控制台的输出




遇到的问题:


期间



遇到这种报错,通过查看日志解决,


看看这里是不是少了什么标点符号,我一开始这里少了个点,定位不到具体的目录一直报错,



还有就是上面说的aop配置文件的问题,已经说明的很清楚了,在哪个下面配置扫描aop,


今天完成的事情:初步完成了aop日志的统计访问db连接的时间,


明天的计划:部署到服务器上编辑shell脚本运行查看


今天遇到的困难:一堆,如上看图都写出来了,而且都已经解决的差不多了。


收获:就是对AOP有了新的了解,还有对日志打印认识很重要,对linux系统,慢慢的熟悉起来。


------------------------------------------------------------我是分割线----------------------------------------------------------------

生活帖:

今天是6月4号啦,对于我来说已经来修真院一个月了,半个月前,也写了一篇生活帖,今天更新一下.

来修真院已经一个月了,剩下还有不到三个月的时间,想想刚来的时候,对java这一块什么都不懂,到现在的慢慢

熟悉,也算是从没有到有的一个进步的过程,其实我想来这里的百分之九十的人,都希望能够改变自己,不让自己

成为自己讨厌的那种人,有的也许是真的对编程,对it的热爱,也有的就是为了以后的生活能够更好的提升,不管

怎么说方向是好的,来这里说时间吧其实过得也很快,每次写完一大串代码的时候发现已经过去好几个小时,当我

们投入到一件事情当中的时候,时间已变成了不在那么重要,每天的早起晚睡已成为了常态,在做任务的时候遇到

难题可能自己会愁很多天,在做任务二的时候对jsp和restful,这两个知识点真的是快把自己逼到了绝路了,一个

restful的问题,可能对于刚接触的我是模糊的,当时对这个概念完全没有理解,不知道有什么用,查了也不懂,跟

着别人写代码,完全没逻辑,干脆先弄懂再说,于是花了三四天时间,查询各种示例资料,只为弄懂一个知识点,

后来慢慢的也会想通,会知道怎么写,师兄的日报,对于我们新手来说真的是条捷径,但是我感觉还是要多问自为

什么,为什么要做么做,为什么要这么写,为什么一定要写在这里,一定要多问自己一些为什么,这样对于我们的

提升也很大,来了这么久了没出过光谷,连小姐姐都没见到过了,每天的时间就是码代码,和吃饭睡觉,这几天还

有小矛盾,具体什么原因,就说了一句话“你每天都很忙么。” 是挺忙的,每天都要完成任务,在有限的时间里

做这么多事情,狠不得自己能分身一个出来。在修真院的时间是有限的,所以我想在有限的时间里能够加油努力做

到自己力所能及的事情,不让自己来这里最后带着后悔离开,也希望下个半月的生活帖 能够吹一次牛逼,说我做

到了。


晚安!

                                                                                                    3:00

                                                                                                2018-06-04


返回列表 返回列表
评论

    分享到