【SpringAop】【统一日志处理】注解方式理解以及使用

【注意:本次代码的demo会存在百度网盘,因为公司的保密,禁止上传,因此仅本人可见】java

目前公司在作数据资产项目,数据质量部分使用到了springaop作统一日志处理,之前对这块有了解,有点模糊不清,今天从新复习了一次,发现以前的概念理解有误,这里再作一次记忆。程序员

1、概念【手敲增长记忆】web

=====================================转乔志勇【csdn】=============================spring

  1,aop:是对OOP编程方式的一种补充,翻译为“面向切面编程”express

 能够理解为一个拦截器框架,可是这个拦截器会很是武断,若是它拦截一个类,那么它就会拦截这个类的全部方法。就像对一个目标类的代理,加强了目标类的全部方法。apache

两个解决办法:编程

  • 不优雅的作法:在添加加强时,根据方法名去判断,是否添加加强,可是这样就得一直去维护这个加强类;
  • 面向切面:将加强类和拦截条件组合在一块儿,而后将这个切面配置到ProxyFactory中,从而生成代理。

  2,aop和切面的关系json

  • 类与对象相比,aop和切面就是这样的一种关系【实例关系】;
  • 也能够将切面当作是aop的一个工具【实现关系】;

  3,几个概念后端

切面(Advidsor):是aop中的一个术语,表示从业务逻辑中分离出来的横切逻辑,好比性能监控,日志记录,权限控制等。api

这些功能均可以从核心的业务逻辑中抽离出去,能够解决代码耦合问题,职责更加单一。封装了加强和切点。

本身理解:切面就是一个抽象的概念,自己就是一个类,包含了加强通知与切点的定义;

加强(Advice):加强代码的功能的类,横切到代码中。

本身理解:加强通知就是要给你的目标类或者方法进行动态加强处理

目标:接口目标方法(JDK代理)或目标类(CGLIB代理)

代理:JDK代理,CGLIB代理,或者经过ProxyFactory类生产。

切点:经过一个条件来匹配要拦截的类,这个条件称为切点。如拦截全部带Controller注解的类,加强的条件。

本身理解:就是代理的一个对象或者方法,经过这个切点能够找到对应的目标;

链接点:做为加强方法的入参,能够获取到目标方法的信息;

本身理解:其实就是找到具体须要加强的地方,经过切点链接到目标类或者方法

  4,归纳一张图

  5,加强

    • Weaving(织入):对方法进行加强
      • 前置加强(BeforeAdvice):在目标方法前调用。
      • 后置加强(AfterAdvice):在目标方法后调用。
      • 环绕加强(AroundAdvice):将Before和After,甚至抛出加强和返回加强合到一块儿。
      • 返回加强(AfterReturningAdvice):在方法返回结果后执行,该加强能够接收到目标方法返回结果。
      • 抛出加强(AfterThrowingAdvice):在目标方法抛出对应的类型后执行,能够接收到对应的异常信息。
    • INtroduction(引入) :对类进行加强
      • 引入加强(DeclareParentsAdvice):想让程序在运行的时候动态去实现 某个接口,须要引入加强。

  6,SpringAop实现方式

    • 编程式【实现接口方式】
      • 前置加强,须要实现:MethodBeforeAdvice接口

        加强类:

public class CarBeforeAdvice implements MethodBeforeAdvice{
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println("before"); } }

        测试方法:

@Test
public void test03() {   ProxyFactory proxyFactory = new ProxyFactory();   proxyFactory.setTarget(new Car());   proxyFactory.addAdvice(new CarBeforeAdvice());   Wheel carProxy = (Wheel)proxyFactory.getProxy();   carProxy.run(); }
      • 后置加强:实现AfterReturningAdvice接口
      • 环绕加强:实现org.aopalliance.intercept.MethodInterceptor 接口,使用Object result = methodInvocation.proceed();调用目标方法,在目标方法先后添加加强。
    • 声明式【xml形式】:spring+Aspect

      开发步骤:

      1,定义切面类,将该切面类放入到IOC容器中

@Component
public class XMLLoggingAspect { public void beforeAdvice(JoinPoint point) { System.out.println("xml aspects logging before"); } public void afterAdvice(JoinPoint point) { System.out.println("xml aspects logging after"); } public void afterReturningAdvice(JoinPoint point, Object result) { System.out.println("xml aspects logging afterReturningAdvice"); } public void afterThrowingAdvice(JoinPoint point, Exception e) { System.out.println("xml aspects logging afterThrowingAdvice"); } public Object aroundAdvice(ProceedingJoinPoint point) throws Throwable { Object result = point.proceed(); System.out.println("xml aspects logging aroundAdvice"); return result; } }

      2,SpringAop配置都必须定义在<aop:config>元素内部

      3,在<aop:config>中,每一个切面都须要建立一个<aop:aspect>元素

      4,为具体的切面实现引用后端的bean实例。

下面展现  前置加强,后置加强,环绕加强,返回加强,抛出加强的【织入加强】例子:

<aop:config>
  <aop:aspect ref="XMLLoggingAspect">
    <aop:pointcut id="carPointcut" expression="execution(void run())"/>
    <aop:before method="beforeAdvice" pointcut-ref="carPointcut"/>
    <aop:after method="afterAdvice" pointcut-ref="carPointcut"/>
    <aop:after-returning method="afterReturningAdvice" pointcut-ref="carPointcut" returning="result"/>
    <aop:after-throwing method="afterThrowingAdvice" pointcut-ref="carPointcut" throwing="e"/>
    <aop:around method="aroundAdvice" pointcut-ref="carPointcut"/>
  </aop:aspect>
</aop:config>

控制台输出:

xml aspects logging before
i am a car, i can run.
xml aspects logging aroundAdvice
xml aspects logging afterReturningAdvice
xml aspects logging after

基于声明式的spring aspect织入加强配置说明:

  支持配置两个级别的公共切点表达式,一个是针对某个切面的全部方法(定义在<aop:aspect>节点内),另外一个是针对全部切面(定义在<aop:config>节点内),使用ponitcut-ref来引入切点。

下面展现【引入加强】的一个例子:

对于引入加强,只须要配置<aop:aspect>节点下就能够,不须要去切面类中添加任何属性

//types-matching这是目标类
//implement-interface 这是代理的接口,default-impl这是代理接口的实现类
<aop:config>
  <aop:aspect ref="XMLLoggingAspect">
    <aop:declare-parents
          types-matching="com.nucsoft.spring.target.impl.Student"         implement-interface="com.nucsoft.spring.target.Fly"          default-impl="com.nucsoft.spring.target.impl.SuperMan"/>   </aop:aspect> </aop:config>

测试:

@Test
public void test04() {   Person student = context.getBean(Student.class);   System.out.println(student.say("james"));   Fly fly = (Fly) student;   fly.fly(); }

控制台输出

hello,james
i am super man, i can fly.

基于声明式的spring aspect 引入加强配置说明:

  1,引入加强是类级别,因此不存在切点表达式

  2,利用<aop:declare-parents>节点在<aop:aspect>内部声明

  3,types-matching属性,要加强的目标类,这里须要全类名

  4,implement-interface属性:动态的加强类接口

  5,default-impl属性:动态加强类接口的实现

    • 注解方式【注解方式】:spring+aspect

对切面类添加@Aspect注解,将切面类和目标类放入到IOC容器中,能够经过<context :component-scan base-package=""/>进行扫描

添加加强方法(包括加强类型和切点表达式,以及链接点)

在Spring Config文件中添加(<aop:aspectj-autoproxy proxytarget-class="true">,proxy-target-class属性,false只能代理接口(JDK动态代理),true代理类(CGLIB动态代理))

---------------------------------------------------------------------------------------------------------------------------

  1,经过切点表达式(AspectJ execution)进行拦截

  spring-congfig.xml

<context:component-scan base-package="com.nucsoft.spring"/>
<aop:aspectj-autoproxy proxy-target-class="true"/>

  Person接口:

public interface Person {
    String say(String name);
}

Person实现类Student:

@Component
public class Student implements Person{ @Override public String say(String name) { return "hello," + name; } }

测试类:

public class SpringTest {
    @Test
    public void test01() { ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml"); Student student = context.getBean(Student.class); String james = student.say("james"); System.out.println(james); } }

    (1)前置加强:关键字:@Before,JoinPoint,execution切点表达式,表达式内容支持通配符;

@Aspect
@Component
public class LoggingAspect { @Before("execution(String say(String))") public void before(JoinPoint point) { System.out.println("before"); } }

控制台输出:

before
hello,james

    (2)后置加强:关键字:@After

@Aspect
@Component
public class LoggingAspect { @After("execution(String say(String))") public void afterAdvice(JoinPoint point) { System.out.println("after..."); } }

控制台输出:

hello,james

after...

    (3)环绕加强,关键字:@around,Proceeding JoinPoint

将Student类还原,切面类:

@Aspect
@Component
public class LoggingAspect { @Around("execution(String say(String))") public Object aroundAdvice(ProceedingJoinPoint point) throws Throwable { before();  Object result = point.proceed(); after(); return result; } private void before(){ System.out.println("before"); } private void after(){ System.out.println("after"); } }

控制台输出:

before

hello james

after

注意:

  <1>加强方法的返回值为Object类型,该返回值与目标方法返回值一致

  <2>Object rusult = point.proceed();该result即为目标方法执行后的返回值

  <3>在环绕通知中须要明确调用ProceedingJoinPoint的proceed()方法来执行被代理的方法,若是忘记这样作会致使通知被执行了,可是目标方法没有被执行;

  <4>环绕通知的方法须要返回目标方法执行以后的结果,即调用joinPoint.proceed()的返回值,不然会出现空指针异常

    (4)返回加强:关键字:@AfterReturning,returning,JoinPoint

@Aspect
@Component
public class LoggingAspect { @AfterReturning(value = "execution(String say(String))", returning = "str") public void aferRetruningAdvice(JoinPoint point, String str) { System.out.println("str:" + str); System.out.println("aferRetruningAdvice"); } }

控制台输出:

str:hello,james
aferRetruningAdvice
hello,james

    (4)抛出加强:关键字:@AfterThrowing,throwing。注意:抛出的异常类型必须和切面抛出加强接收的异常类型相同或是其子类。

更改Student类,手动抛出一个异常:

@Component
public class Student implements Person{
    @Override
    public String say(String name) {
        throw new RuntimeException("exception");
    }
}

切面类:

@Aspect
@Component
public class LoggingAspect { @AfterThrowing(value = "execution(String say(String))", throwing = "e") public void AfterThrowingAdvice(JoinPoint point, Exception e) { String message = e.getMessage(); System.out.println(message); System.out.println("AfterThrowingAdvice"); } }

控制台输出:

exception
AfterThrowingAdvice

---------------------------------------------------------------------------------------------------------------------------

    2,经过切点注解表达式(AspectJ @annotation)进行拦截

开发步骤:

  (1)自定义定义注解类

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthorityTag {}

  (2)为切面类中加强指定注解表达式

@Aspect
@Component
public class AuthorityAspect { @Before("@annotation(com.nucsoft.spring.annotation.AuthorityTag)") public void before(JoinPoint point) { System.out.println("authority before"); } }

  (3)在目标类目标方法上标注注解

@Component
public class Car implements Wheel{ @AuthorityTag @Override public void run() { System.out.println("i am a car, i can run."); } }

------------------------------------------------------------------------------------------------------------------------------

各类加强的使用:

1,前置加强
上面的步骤就是一个前置加强

控制台输出:

authority before
i am a car, i can run.

2,后置加强

@Aspect
@Component
public class AuthorityAspect { @Before("@annotation(com.nucsoft.spring.annotation.AuthorityTag)") public void before(JoinPoint point) { System.out.println("authority before"); } @After("@annotation(com.nucsoft.spring.annotation.AuthorityTag)") public void afterAdvice(JoinPoint point) { System.out.println("authority after"); } }

控制台输出:

authority before
i am a car, i can run.
authority after

3,为每一个加强使用不一样的注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BeforeAuthorityTag {} @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface AfterAuthorityTag {}

切面:

@Aspect
@Component
public class AuthorityAspect { @Before("@annotation(com.nucsoft.spring.annotation.BeforeAuthorityTag)") public void before(JoinPoint point) { System.out.println("authority before"); } @After("@annotation(com.nucsoft.spring.annotation.AfterAuthorityTag)") public void afterAdvice(JoinPoint point) { System.out.println("authority after"); } }

使用:

@Component
public class Car implements Wheel{ @BeforeAuthorityTag @AfterAuthorityTag @Override public void run() { System.out.println("i am a car, i can run."); } }

控制台输出:

authority before
i am a car, i can run.
authority after

4,环绕加强

注解类:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AroundAuthorityTag {}

切面加强:

@Around(value = "@annotation(com.nucsoft.spring.annotation.AroundAuthorityTag)")
public Object aroundAdvice(ProceedingJoinPoint point) throws Throwable {   Object result = point.proceed();   after();   System.out.println("authority aroundAdvice");   return result; } private void after() {   System.out.println("after"); }

 

目标类:

@Component
public class Car implements Wheel{ @AroundAuthorityTag @Override public void run() { System.out.println("i am a car, i can run."); } }

控制台输出:

i am a car, i can run.
after
authority aroundAdvice

(5)返回加强

注解类:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AfterReturningAuthorityTag {}

切面加强:

@AfterReturning(value = "@annotation(com.nucsoft.spring.annotation.AfterReturningAuthorityTag)", returning = "result")
public void afterReturningAdvice(JoinPoint point, Object result) {   System.out.println("authority afterReturning"); }

目标类:

@Component
public class Car implements Wheel{ @AfterReturningAuthorityTag @Override public void run() { System.out.println("i am a car, i can run."); } }

控制台输出:

i am a car, i can run.
authority afterReturning

(6)抛出加强

注解类:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AfterThrowingAuthorityTag {}

切面加强:

@AfterThrowing(value = "@annotation(com.nucsoft.spring.annotation.AfterThrowingAuthorityTag)", throwing = "e")
public void afterThrowAdvice(JoinPoint point, Exception e) {   System.out.println(e.getMessage());   System.out.println("authority afterThrowing"); }

目标类:

@Component
public class Car implements Wheel{ @AfterThrowingAuthorityTag @Override public void run() { System.out.println("i am a car, i can run."); throw new RuntimeException("throw a new runtimeException"); } }

控制台输出:

i am a car, i can run.
throw a new runtimeException

java.lang.RuntimeException: throw a new runtimeException
authority afterThrowing

(7)引入加强:关键字:@DeclareParents

被代理的类与接口实现类实现了同一个接口,也就是说,A类被加强(被接口代理,实现接口),接口实现类B类与A类共同实现了接口,在A类多态转向接口时,能够直接调用接口实现类B类中的方法

将要引入的接口:

public interface Fly {
    void fly(); }

将要引入的接口的实现:

public class SuperMan implements Fly{
    @Override
    public void fly() { System.out.println("i am super man, i can fly."); } }

切面类:

@Aspect
@Component
public class LoggingAspect {   @DeclareParents(value = "com.nucsoft.spring.target.impl.Student", defaultImpl = SuperMan.class) private Fly fly; }

测试类:

public class SpringTest {
    @Test
    public void test01() { ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml"); Person student = context.getBean(Student.class); String james = student.say("james"); System.out.println(james); Fly fly = (Fly) student; fly.fly(); } }

控制台输出:

hello,james
i am super man, i can fly.

说明:

1,在Aspect类中定义一个须要引入加强的接口,它也就是运行时须要动态实现的接口。

2,@DeclareParents注解:

  value属性:目标类,能够是一个Aspect类型的表达式,能够引入到多个类中;

  defaultImpl属性:引入接口的默认实现类。

3,虽然切面类中标注有@DeclareParents注解的属性能够是任意的,可是通常仍是将其设置为引入加强类型。

4,从ApplicationContext中获取到的student对象实际上是一个代理对象,能够转型为本身静态实现的接口Person,也能够转型为动态实现接口fly,切换起来很是方便。【我的感受有点像包装类型与基本数据类型的装箱与拆箱】

 

注意:

  1,能够为多个加强使用同一个注解;

  2,也能够为每一个加强使用不一样的注解【例以下面的例子,能够为Controller层和Service层分别编写各自的自定义注解,或者在同一个方法上使用不一样的注解】;

 

    3.3对比基于xml声明的切点表达式和注解表达式

注解表达式:更加灵活,可是在开发过程当中,须要程序员手动的去为每一个须要添加加强的方法添加对应的注解,更加容易扩散。

切点表达式:能够写出通用的加强,也不须要程序员手动的去为每一个方法添加加强,可是须要切点表达式适配。

------------------------------------------------------------------------------------------------------------------------------

 小结:

1,利用方法签名编写AspectJ切点表达式:execution * com.alan.spring.Calcultor.*(..);匹配Calcultor中声明的全部方法;

第一个*表明任意修饰符以及任意返回值,第二个*表明任意方法..匹配任意数量的参数,若目标类与接口与该切面在同一个包中,能够省略包名。

execution public * com.alan.spring.Calcultor.*(..); 匹配ArthmeticCalculator接口的全部公共方法;

execution public double com.alan.spring.Calcultor.*(..);匹配Calculator中返回double类型的全部公共方法

execution * com.alan.spring.Calcultor.*(double,..);匹配Calculator中第一个参数为double类型的方法

 ecution * com.alan.spring.Calcultor.*(double,double);匹配Calculator中参数为double,double类型的方法

2,能够合并切点表达式 使用&& || ! 来合并,如:

execution(void run()) || execution(void say())

3,切面优先级

能够经过实现Order接口或者利用@Order注解指定

  (1)实现Orderd接口,getOrder()方法返回的值越小,优先级就越高

  (2)使用@Order注解,须要出如今注解中,一样是值越小优先级越高。

4,重用切点定义

在Aspect切面中,能够经过@pointcut注解讲一个切入点声明成简单的方法,切入点的方法体一般是空的,由于将切入点定义与应用程序逻辑混在一块儿是不合理的。

切入点方法的访问控制符同时也控制着这个切入点的可见性【也就是与目标链接的条件】。若是切入点要在多个切面中共用,最好将它们集中在一个公共的类中【抽取成父类】。

在这种状况下,它们必须被声明为public。在引入这个切入点时,必须将类名也包括在内,若是类没有与这个切面放在同一个包中,还必须包含包名。

其余加强通知能够经过方法名称引入该切入点。

例如:

@Pointcut("execution(void run())")
public void LoggingPointcut(){}; @Before("LoggingPointcut()") public void before() {   System.out.println("before"); }

 

总结:

  1,在学习springAOP以前,最好先理解了静态代理(普通对象),JDK动态代理(接口),CGLIB动态代理(类)。其springaop就是一个拦截,能够定义权限认证,统一日志,统一异常,事务,减小代码复用性等等,很强大。

  2,明白切面和加强以及切点之间的关系:切面包含加强和切点,切点能够经过注解条件/通则表达式找到目标类/方法,加强(通知)就是对目标的加强(前置,后置,环绕)。

  3,几个加强之间的区别

  除了环绕加强外,全部的链接点使用的都是JoinPoint类型的入参,而环绕加强使用的是ProceedingJoinPoint

  返回加强能够接收到返回值

  抛出加强能够接收到抛出的异常

  环绕加强的返回值类型为目标方法返回值类型

  4,springaop支持xml声明式和注解方式,注解优先。

  5,步骤:

    1,定义切面,以及加强,编写切点表达式

    2,若是切点表达式是基于注解的,还须要对目标方法添加对应的注解。

=====================================转乔志勇【csdn】=============================

 

2、小demo【经过切点注解表达式、织入方式对其加强】

==============================================小demo开始==============================================

  1.建立一个maven_web工程【具体参考maven-web工程的建立】,而后导入依赖

 <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <version.spring>4.3.3.RELEASE</version.spring>
  </properties>

  <dependencies>
      <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
        <scope>test</scope>
      </dependency>

      <!-- javax.servlet -->
      <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
      </dependency>
      <!-- spring framework -->
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${version.spring}</version>
      </dependency>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${version.spring}</version>
      </dependency>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${version.spring}</version>
      </dependency>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>${version.spring}</version>
      </dependency>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>${version.spring}</version>
      </dependency>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${version.spring}</version>
      </dependency>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>${version.spring}</version>
      </dependency>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>${version.spring}</version>
      </dependency>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${version.spring}</version>
      </dependency>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>${version.spring}</version>
      </dependency>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>${version.spring}</version>
        <scope>test</scope>
      </dependency>
      <dependency>
        <groupId>javax.inject</groupId>
        <artifactId>javax.inject</artifactId>
        <version>1</version>
      </dependency>
      <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>2.2.2</version>
      </dependency>
      <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.10</version>
      </dependency>
      <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-1.2-api</artifactId>
        <version>2.6.2</version>
      </dependency>
      <!-- 切面aspectj -->
      <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.8.8</version>
      </dependency>
      <!-- springmvc依赖jar包jackson -->
      <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.6.2</version>
      </dependency>
      <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.6.2</version>
      </dependency>
      <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
        <version>2.6.2</version>
      </dependency>
      <!-- fastjson -->
      <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.1.39</version>
      </dependency>
  </dependencies>

  2.配置springmvc 与spring配置文件

关于spring-servlet.xml与applicationContext.xml的区别:
1,用途不一样: applicationContext-*.xml文件一般用于加载spring系统级别的组件,好比bean的初始化; spring-servlet.xml文件一般用于加载controller层须要的类,好比拦截器,mvc标签加载的类 2,加载位置不一样: applicationContext-*.xml加载在标签中,做位FrameworkServlet的参数属性 spring-servlet.xml文件当作DispatcherServlet的参数属性进行加载 3,补充: classpath*与classpath的区别: classpath:指classes路径下文件 classpath*:除了classes路径下文件,还包含jar包中文件

 

 

 

 

 

 

 

 

  3,而后咱们建立一个接口,并配置好web.xml

  4,能够访问成功,使用postman便可【接口不要忘记添加responseBody注解】

  5,编写自定义注解,由于此springaop代码是经过注解方式实现,其实也能够经过xml形式实现,网上有不少相关资料,由于公司项目用的注解,我理解的也快,也方便记忆,好维护。

  springaop是经过此注解定义切点,而后定位到对应的链接点;

  

/**
 * 此注解做用到方法和参数上,运行时加载有效,这个切点对应的注解做用于Controller层上
 */
@Target({ElementType.METHOD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ControllerLogAnnotation { /** * 描述 */ String description() default ""; /** * 是否记录返回内容 */ boolean isContent() default false; /** * 返回内容是否须要加密 */ boolean isContentEncrypted() default false; /** * 参数是否须要加密 */ boolean isParametersEncrypted() default false;

 

/**
 * 这个切点对应的注解做用于service层
 */
@Target({ElementType.PARAMETER,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ServiceLogAnnotation { /** * 描述 */ String description() default ""; /** * 是否记录返回内容 */ boolean isContent() default false; /** * 返回内容是否须要加密 */ boolean isContentEncrypted() default false; /** * 参数是否须要加密 */ boolean isParametersEncrypted() default false; }

  6,编写切面类,idea很智能,在新建时直接能够新建一个切面类,新建Aspect,选择下拉@Aspect

  7,关于切面类:

    • 首先配置切点,根据这个切点找到对应的自定义注解上,也就链接上实际注解对应的方法

  

     //service aspect 服务层切点
    @Pointcut("@annotation(ServiceLogAnnotation)")
    public void serviceAspect() { } //controller aspect 控制层切点 @Pointcut("@annotation(ControllerLogAnnotation)") public void controllerAspect() { }
    • 而后开始配置通知,根据链接点能够获取相应的信息,以及session等,其实就是对这个切点进行动态加强,原本这个代理切点方法中没有任何内容,链接点会从新对这个切点对应的方法进行加强,前置、后置、环绕等;
    • 这里只以before前置通知为例
/**
     * 前置通知 用于拦截Controller层记录用户的操做
     *
     * @param joinPoint 切点
     */
    @Before("controllerAspect()") public void doBefore4control(JoinPoint joinPoint) { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); HttpSession session = request.getSession(); //读取session中的用户 User user = (User) session.getAttribute("USER"); if( user == null ) { user = new User(); user.setId("1"); user.setName("测试帐号"); } //请求的IP String ip = request.getRemoteAddr(); try { //*========控制台输出=========*// System.out.println("=====control 前置通知开始====="); System.out.println("请求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")); System.out.println("方法描述:" + getControllerMethodDescription(joinPoint)); System.out.println("请求人ID:" + user.getId()); System.out.println("请求人NAME:" + user.getName()); System.out.println("请求IP:" + ip); System.out.println("=====前置通知结束====="); Map<String, Object> map = new HashMap<>(); map.put("controlPath", (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")); map.put("menthodDescr", getControllerMethodDescription(joinPoint)); map.put("id", user.getId()); map.put("name", user.getName()); map.put("ip", ip); map.put("date", new Date()); } catch (Exception e) { //记录本地异常日志 logger.error("==前置通知异常=="); logger.error("异常信息:{}", e.getMessage()); } }

  7,而后在你想要加强的方法上标记你的自定义注解便可。这样,在执行这个方法前,就会执行前置通知方法中对应的操做;

@ControllerLogAnnotation(description = "测试test")

控制台输出结果:

==============================================小demo结束==============================================

相关文章
相关标签/搜索