Spring AOP学习(1)

Spring AOP学习(1)

AOP的概念和思想

什么是AOP

AOP是什么?引用百度百科的术语就是:AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

注:为什么会有面向切面编程(AOP)?我们知道Java是一个面向对象(OOP)的语言,但它有一些弊端,比如当我们需要为多个不具有继承关系的对象引入一个公共行为,例如日志、权限验证、事务等功能时,只能在在每个对象里引用公共行为。这样做不便于维护,而且有大量重复代码。AOP的出现弥补了OOP的这点不足。

简单地说:纵向关系 OOP,横向角度 AOP

AOP中的相关概念

  • Aspect(切面):通常是一个类,里面可以定义切入点和通知,是对横切关注点的抽象。

  • JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用,还可以是字段或则构造器。在Spring AOP中,一个连接点总是代表一个方法执行。

  • Advice(通知):AOP在特定的切入点上执行的增强处理,通知的类型包括before,after,afterReturning,afterThrowing,around等

  • Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式。dvice关联一个切点表达式。

  • AOP代理:为了实现aspect而被AOP框架所创建的一个对象。代理就是目标对象的加强。Spring中的AOP代理可以是一个JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类

  • 目标对象(Target object):代理的目标对象(被一个或多个aspect通知的对象)。

  • Waving(织入):将切面应用到目标对象并导致代理对象创建的过程

  • 引入(introduction):允许我们向现有的类添加新方法属性。这不就是把切面(也就是新方法属性:通知定义的)用到目标类中吗

SpringAOP & AspectJ

AspectJ切入点语言是一种强大的表达式语言,能够匹配各种类型的连接点。但是,Spring AOP只支持在其IoC容器中声明的Bean的方法执行连接点。

AOP术语

切面

什么是aspect(切面)?pointcut和advice结合起来就是aspect,它类似于OOP中定义的一个类,但它代表的更多是对象间横向的关系。

在Spring AOP配置中切面通常包含三部分:

  • 切面模块本身
  • 通知
  • 切入点

代码示例:

<!-- 目标业务逻辑代码 -->
<bean id="calc" class="com.lanou3g.spring.simple.CalcImpl"/>
<!-- 切面模块化对象(代表我们要附加到原始业务逻辑中的代码) -->
<bean id="calcAspect" class="com.lanou3g.spring.simple.CalcAspect" />
<!-- 示例说明: 将切面calcAspect中的代码插入到calc原始业务代码中 -->
<aop:config>
    <!-- 定义公用的切入点表达式,如果aspect中有多个通知,都可以通过pointcut-ref复用 -->
    <aop:pointcut id="all_calc_method" expression="execution(* com.lanou3g.spring.simpleCalcImpl.*(..))" />
    <aop:aspect ref="calcAspect">
        <!-- 切面包含的通知(什么时间)、切入点(什么地点) -->
        <aop:around method="computeTime" pointcut-ref="calc_method" />
    </aop:aspect>
</aop:config>

下面就分别介绍 pointcut(切入点)和advice(通知)来说说AOP的切面。

pointcut(切入点)

在前面AOP的概念我们已经简述过什么是pointcut(切入点)了,其本质上是一个捕获连接点的结构。在AOP中,可以定义一个pointcut,来捕获相关方法的调用。

下面说一下关于切入点的指示器及描述:

  • **execution:**用于匹配方法执行的连接点(最常用);

  • within:用于匹配指定类型内的方法执行;

  • this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配;

  • target:用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;

  • args:用于匹配当前执行的方法传入的参数为指定类型的执行方法;

  • @within:用于匹配所以持有指定注解类型内的方法;

  • @target用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;

  • @args:用于匹配当前执行的方法传入的参数持有指定注解的执行;

  • @annotation用于匹配当前执行方法持有指定注解的方法;

execution常见切入点表达式的例子:
  • 任意公共方法的执行:

    execution(public * *(..))
    
  • 任何一个以“set”开始的方法的执行:

    execution(* set*(..))
    
  • AccountService 接口的任意方法的执行:

    execution(* com.lanou3g.service.AccountService.*(..))
    
  • 定义在service包里的任意方法的执行:

    execution(* com.lanou3g.service.*.*(..))
    
  • 定义在service包或者子包里的任意类的任意方法的执行:

    execution(* com.lanou3g.service..*.*(..))
    
advice(通知)

Advice(通知)是面向切面编程中的一个非常重要的概念。要知道AOP的目的在于对目标类或目标方法的逻辑增强(如:日志逻辑、统计逻辑、访问控制逻辑等),而Advice就代表要增强的具体逻辑。Advice接口由AOP联盟(aopalliance.org)定义,它只是一个标记接口,用来强调概念,没有定义任何功能(或者说没有定义增强方式或增强内容)。

Spring提供了5种Advice类型

  • Interception Around:JointPoint前后调用

  • Before:JointPoint前调用

  • After Returning:JointPoint后调用

  • Throw:JoinPoint抛出异常时调用

  • Introduction:JointPoint调用完毕后调用

前置通知(before)

在目标方法调用前通知切面, 什么参数也无法获取。也不能终止目标方法执行

before方法的实现在Advice中被配置到目标方法后,会在调用目标方法时被回调。具体的调用参数有:Method对象,这个参数是目标方法的反射对象;

用于before方法实现的方法

public void beforeM() {
        System.out.println("beforeM------目标方法开始执行");
    }

spring配置

 <aop:before method="beforeM" pointcut-ref="calc_method" />
后置通知(after-returning)

只有在目标方法 正常 执行结束后才会通知, 在通知方法中可以获取到方法的返回值

代码示例:

public void afterReturningM(Object retVal) {
        System.out.println("afterReturningM------目标方法执行结束,返回值: " + retVal);
}
 <aop:after-returning method="afterReturningM" pointcut-ref="calc_method" returning="retVal" />

异常通知(after-throwing)

只有在目标方法 出现异常 才会通知, 在通知方法中可以获取到抛出的异常信息

代码示例:

  public void afterThrowing(Throwable throwable) {
 System.out.println("afterThrowing------方法执行出错"+throwable);    }
            <aop:after-throwing method="afterThrowing" pointcut-ref="calc_method" throwing="throwable" />

环绕通知(around)

Spring中最基本的通知类型便是Around通知

  • 在目标方法执行前、后被通知, 可以获取连接点对象(ProceedingJoinPoint), 该对象可以获取被拦截方法的签名、参数、返回值、包括调用与否)
  • 该方法的返回值,即代表了真正业务逻辑代码的返回值
  • 可以选择终止或正常执行目标方法

代码示例;

//实现环绕通知(around)的方法
public Object aroundM(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取连接点代表的方法的签名
        Signature signature = joinPoint.getSignature();
        String methodName = signature.getName();
        Object[] args = joinPoint.getArgs();
        log.debug("[aroundM] ---- 目标方法"+methodName+"("+ Arrays.toString(args)+")开始执行");
        long start = System.currentTimeMillis();
        // 调用目标方法
        Object retVal = joinPoint.proceed();
        // 插入我们自己的逻辑代码
        long timer = System.currentTimeMillis() - start;
        log.debug("[aroundM] ---- 目标方法["+methodName+"("+ Arrays.toString(args)+")]执行结束,返回值: "+retVal+", 耗时: " + timer + "ms.");
        // 正常返回目标方法的返回值
        return retVal;
    }

接下来还要完成spring配置

 <aop:around method="aroundM" pointcut-ref="all_calc_method" />
最终通知(after)

在目标方法执行结束后通知切面, 什么参数也无法获取。无论目标方法是正常执行结束还是抛出异常终止,都会被通知

代码示例:


    public void afterFinallyM() {
        System.out.println("afterFinallyM------方法执行结束");
    }
 <aop:after method="afterFinallyM" pointcut-ref="all_calc_method" />
完整代码示例

上述,关于代码部分,只是简单的说一下,可能不是很清楚是如何使用的,下面就完整的展示代码实现

首先,创建一个CalcAspect类


public class CalcAspect {
    /**
     * 计算方法耗时
     * 环绕通知
     * @param joinPoint 代表连接点对象,该对象可以获取被代理方法的所有信息
     */
    public Object aroundM(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取连接点代表的方法的签名
        Signature signature = joinPoint.getSignature();
        String methodName = signature.getName();
        Object[] args = joinPoint.getArgs();
        log.debug("[aroundM] ---- 目标方法"+methodName+"("+ Arrays.toString(args)+")开始执行");
        long start = System.currentTimeMillis();
        // 调用目标方法
        Object retVal = joinPoint.proceed();
        // 插入我们自己的逻辑代码
        long timer = System.currentTimeMillis() - start;
        log.debug("[aroundM] ---- 目标方法["+methodName+"("+ Arrays.toString(args)+")]执行结束,返回值: "+retVal+", 耗时: " + timer + "ms.");
        // 正常返回目标方法的返回值
        return retVal;
    }
    public void beforeM() {
        System.out.println("beforeM------目标方法开始执行");
    }
    public void afterReturningM(Object retVal) {
        System.out.println("afterReturningM------目标方法执行结束,返回值: " + retVal);
    }
    public void afterFinallyM() {
        System.out.println("afterFinallyM------方法执行结束");
    }
    public void afterThrowing(Throwable throwable) {
        System.out.println("afterThrowing------方法执行出错"+throwable);
    }
}

spring的xml文件配置

 <aop:config>
        <aop:pointcut id="all_calc_method" expression="execution(* com.lanou3g.spring.simple.CalcImpl.*(..))" />
        <aop:aspect ref="calcAspect">
            <aop:around method="aroundM" pointcut-ref="calc_method" />
            <aop:before method="beforeM" pointcut-ref="calc_method" />
            <aop:after-returning method="afterReturningM" pointcut-ref="calc_method" returning="retVal" />
            <aop:after-throwing method="afterThrowing" pointcut-ref="calc_method" throwing="throwable" />
            <aop:after method="afterFinallyM" pointcut-ref="calc_method" />
        </aop:aspect>
    </aop:config>

连接点

什么是连接点

什么是连接点(JoinPoint)? 程序执行的某个特定位置,就是spring允许你是通知(Advice)的地方,基本上每个方法的前、后(两者都有也行),或抛出异常是时都可能是连接点,spring只支持方法连接点。

连接点和切点的区别

具体举个例子:比如我们在一个小区,小区很多个门口(连接点),但是我们不会每个门口都会出去,只会选择我们需要的那个门(切点)出去。

简单可以理解为,每个出口都是连接点,但是我们使用的那个出口才是切点。每个应用有多个位置适合织入通知,这些位置都是连接点。但是只有我们选择的那个具体的位置才是切点

简单理解为: 切入点一定是连接点, 连接点不一定是切入点。

Spring 仅支持方法创建连接点

织入

织入的定义

织入(Weaving),AOP术语。把切面(aspect)连接到其它的应用程序类型或者对象上,并创建一个被通知(advised)的对象,这样的行为叫做织入。

Spring AOP把切面中的代码织入到目标代码中的过程就是织入的过程

AOP使用步骤

添加依赖包

将下面的依赖添加在maven工程pom.xml

<dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
 </dependency>

XML方式

加入aop schema
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     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">
</beans>
定义切面类,并配置到applicationContext中

创建切面类

package com.lanou3g.spring.simple;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import java.util.Arrays;
@Slf4j
public class CalcAspect {
    /**
     * 计算方法耗时
     * 环绕通知
     * @param joinPoint 代表连接点对象,该对象可以获取被代理方法的所有信息
     */
    public Object aroundM(ProceedingJoinPoint joinPoint) throws Throwable {

        // 获取连接点代表的方法的签名
        Signature signature = joinPoint.getSignature();
        String methodName = signature.getName();
        Object[] args = joinPoint.getArgs();
        log.debug("[aroundM] ---- 目标方法"+methodName+"("+ Arrays.toString(args)+")开始执行");
        long start = System.currentTimeMillis();
        // 调用目标方法
        Object retVal = joinPoint.proceed();
        // 插入我们自己的逻辑代码
        long timer = System.currentTimeMillis() - start;
        log.debug("[aroundM] ---- 目标方法["+methodName+"("+ Arrays.toString(args)+")]执行结束,返回值: "+retVal+", 耗时: " + timer + "ms.");
        // 正常返回目标方法的返回值
        return retVal;
    }
    public void beforeM() {
              log.debug("[beforeM] ---- 目标方法开始执行");
    }
    public void afterReturningM(Object retVal) {
       log.debug("[afterReturningM] ---- 目标方法执行结束,返回值: " + retVal);
    }
    public void afterFinallyM() {
        log.error("[afterFinallyM] ---- 方法执行结束");
    public void afterThrowing(Throwable throwable) {
        log.error("[afterThrowing] ---- 方法执行出错", throwable);
    }
}

配置到applicationContext.xml文件

<bean id="calcAspect" class="com.lanou3g.spring.simple.CalcAspect" />
定义<aop:config>
引用切面bean
<bean id="calcAspect" class="com.lanou3g.spring.simple.CalcAspect" />
定义切入点表达式

切入点表达式:对指定的方法进行拦截,并且生成代理表达式。

切入点表达式的语法格式

execution([权限修饰符] [返回值类型] [简单类名/全类名] 方法名)

xml配置文件代码示例(任意公共方法的执行):

<aop:pointcut id="calc_method" expression="execution(* com.lanou3g.spring.simple.CalcImpl.*(..))" />
定义通知
<aop:aspect ref="calcAspect">
            <aop:around method="aroundM" pointcut-ref="calc_method" />
            <aop:before method="beforeM" pointcut-ref="calc_method" />
            <aop:after-returning method="afterReturningM" pointcut-ref="calc_method" returning="retVal" />
            <aop:after-throwing method="afterThrowing" pointcut-ref="calc_method" throwing="throwable" />
            <aop:after method="afterFinallyM" pointcut-ref="calc_method" />
        </aop:aspect>

注解方式

在spring AOP中,常用的AOP实现方式有两种:一种是基于xml配置文件方式的实现,前面我们已经说过了,还有一种是基于注解的实现。接下来就说一下AOP的注解方式实现及步骤。

开启自动织入支持

若要使用注解方式,开启注解支持是必要的。在这提一下在在xml中开启和通过注解开启

在xml中开启
<!-- 开启aop注解支持 -->
<aop:aspectj-autoproxy />
<!-- 开启注解支持,同时强制指定代理机制为cglib -->
<aop:aspectj-autoproxy proxy-target-class="true" />
通过注解开启
@Configuration
// 开启注解支持,同时强制指定代理机制为cglib 
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AOPConf {}
定义切面类,添加注解

先来定义切面类,添加注解,然后在切面类中依次添加前置通知,后置通知,例外通知,最终通知,环绕通知等,介绍下在注解方式下,关于切面类的配置

代码示例:

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * 定义一个切面,负责收集方法调用的入参、出参(返回值)
 */
@Slf4j
@Aspect     // 表示该类是一个切面
@Component  // Aspect切面首先必须也是一个普通的bean
public class AspectMethod {

    // 指定该方法是一个环绕通知,通知注解的参数代表引用一个切入点表达式
    @Around("com.lanou3g.spring.PointcutExpression.SpeakMethod()")
    public Object aroundM(ProceedingJoinPoint joinPoint) throws Throwable {

        // 获取连接点方法的名称
        String methodName = joinPoint.getSignature().getName();

        // 获取连接点方法的参数
        Object[] args = joinPoint.getArgs();

        log.debug("[aroundM] "+methodName+"("+ Arrays.toString(args) +") 开始执行");
        Object retuVal = joinPoint.proceed();
        log.debug("[aroundM] "+methodName+"("+ Arrays.toString(args) +") 返回值: " + retuVal);
        return retuVal;
    }

    @AfterReturning(pointcut = "com.lanou3g.spring.PointcutExpression.SpeakMethod()", returning = "ret")
    public Object afterRM(Object ret) {
        log.debug("[afterRM] 返回值: " + ret);
        return ret;
    }

}

@Aspect注解:用来表示该类是一个切面

@Component:注解用来定义Spring管理Bean

注:由于@Aspect注解没有让Spring作为组件bean扫描的能力,所以我们需要额外添加@Component注解

关于通知注解,下面有叙述。

定义切入点(通过方法+注解)

有了切面,接下来就要定义切入点,在这我们要用到@Pointcut,通过@Pointcut注解定义切入点表达式

// 此处表达式含义:拦截com.lanou3g.spring.simple.say包下所有类(包括子包中所有类)中的所有方法
    
    @Pointcut("execution(* com.lanou3g.spring.simple.say..*.*(..))")
    public void say_all_method() {}
定义通知(方法+注解)

再来说下注解通知,通知方法可以简单理解为标注有某种注解的简单的 Java 方法,在目标方法执行时的某个位置(时机)进行执行。

Spring AOP 支持 5 种类型的通知注解:

  • @Around: 环绕通知,包围一个连接点(join point)的通知,如方法调用。这是最强大的一种通知类型。 环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。

  • @Before: 前置通知, 在方法执行之前执行,这个通知不能阻止连接点前的执行(除非它抛出一个异常)。

  • @After: 后置通知, 在方法执行之后执行(不论是正常返回还是异常退出)。

  • @AfterRunning:返回通知, 在方法正常返回结果之后执行 。

  • @AfterThrowing: 异常通知, 在方法抛出异常之后。

  1. 环绕通知

环绕通知是所有通知类型中功能最为强大的, 能够全面地控制连接点. 甚至可以控制是否执行连接点.动态代理,手动推进目标方法运行(joinPoint.procced())

代码示例:

 public void SpeakMethod(){}
    // 指定该方法是一个环绕通知,通知注解的参数代表引用一个切入点表达式
@Around("com.lanou3g.spring.simple.Anno.MethodAspect.SpeakMethod()")

    public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
    // 获取连接点方法的名称
    String methodName=joinPoint.getSignature().getName();
    // 获取连接点方法的参数
    Object[] args=joinPoint.getArgs();

    log.debug("aroundMethod"+methodName+"("+ Arrays.toString(args)+")开始执行");
    //调用目标方法
    Object object=joinPoint.proceed();
    log.debug("aroundMethod"+methodName+"("+ Arrays.toString(args) +") 返回值: " + object);
    return object;
}

2.前置通知

前置通知使用 @Before 注解, 并将切入点表达式的值作为注解值.

@Before("com.lanou3g.spring.simple.Anno.MethodAspect.SpeakMethod()")  
public void beforeM() {
        log.debug("[beforeM] ---- 目标方法开始执行");
    }

3.后置通知

后置通知:在目标方法执行后执行,无论是否抛出异常。后置通知中不能访问目标方法执行后返回的结果。

@After("com.lanou3g.spring.simple.Anno.MethodAspect.SpeakMethod()") 
public void afterFinallyM() {
        log.error("[afterFinallyM] ---- 方法执行结束");
    }

4.返回通知

在返回通知中, 只要将 returning 属性添加到 @AfterReturning 注解中, 就可以访问连接点的返回值。 该属性的值即为用来传入返回值的参数名称。

  @AfterReturning(pointcut = "com.lanou3g.spring.GlobalPointcut.say_all_method()", returning = "ret")
    public Object afterRM(Object ret) {
        log.debug("[afterRM] 返回值: " + ret);
        return ret;
    }

5.异常通知

    public void afterThrowing(Throwable throwable) {
        log.error("[afterThrowing] ---- 方法执行出错", throwable);
    }

调用方法测试,对AOP相关注解的处理

前面的工作完成后,就是运行了,在这建一个AppAnnoation类

代码示例:

package com.lanou3g.spring;

import com.lanou3g.spring.simple.Anno.Speak;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Slf4j
@Configuration
@ComponentScan(basePackages = "com.lanou3g.spring.simple.Anno")
@EnableAspectJAutoProxy //开启对AOP相关注解的处理
public class AppAnnoation {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(AppAnnoation.class);
        Speak hello = ctx.getBean(Speak.class);
        hello.say();
        hello.say("你好我好大家好");
    }
}

日志

在学习中用到了日志,在这说一下

配置步骤

1. 添加依赖包
<dependency>
	<groupId>ch.qos.logback</groupId>
	<artifactId>logback-classic</artifactId>
	<version>1.0.13</version>
</dependency>
2. 导入一个配置模板

你可以直接点击下面的链接下载一个logback基础配置模板

3. 添加自定义的配置项
<?xml version="1.0" encoding="UTF-8"?>
<configuration>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <layout class="ch.qos.logback.classic.PatternLayout">
      <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>
    </layout>
  </appender>
  
  <logger name="com.base22" level="TRACE"/>
  

  <root level="debug">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>
4. 在代码中使用logback输出日志
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LogbackTest {

    private final static Logger logger = LoggerFactory.getLogger(LogbackTest.class);

    public void testLog() {
        logger.trace("Hello World!");
        logger.debug("How are you today?");
        logger.info("I am fine.");
        logger.warn("I love programming.");
        logger.error("I am programming.");
    }
}
附录
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <property name="HOME_LOG" value="logs"/>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>
                %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
            </Pattern>
        </layout>
    </appender>

    <appender name="RollingFile"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>TRACE</level>
        </filter>

        <!-- 测试部署时使用如下配置 -->
        <!-- 可让每天产生一个日志文件,最多 30 个,更早的删除 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${HOME_LOG}/log-%d{yyyy-MM-dd}.log
            </fileNamePattern>
            <maxHistory>10</maxHistory>
        </rollingPolicy>

        <!--
             RollingFileAppender 一般情况下需要配置两个参数:
             RollingPolicy,负责滚动。TriggeringPolicy,决定是否以及何时进行滚动
             TimeBasedRollingPolicy比较特殊,它同时继承了RollingPolicy和TriggerPolicy。

        -->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger -
                %msg%n
            </pattern>
        </encoder>

        <!-- 正式部署时使用此配置 -->
        <!--
            <file>${app.home}/logs/log.log</file>
            <append>true</append>
            <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
                <fileNamePattern>${app.home}/logs/log.%i.log.zip
                </fileNamePattern>
                <minIndex>1</minIndex>
                <maxIndex>7</maxIndex>
            </rollingPolicy>

            <encoder>
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger -
                    %msg%n
                </pattern>
            </encoder>

            <triggeringPolicy
                class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
                <maxFileSize>50MB</maxFileSize>
            </triggeringPolicy>
         -->
    </appender>

    <logger name="com.lanou3g.spring" level="DEBUG"/>
    <logger name="org.springframework" level="ERROR"/>

    <root level="debug">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="RollingFile" />
    </root>
</configuration>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
信息数据从传统到当代,是一直在变革当中,突如其来的互联网让传统的信息管理看到了革命性的曙光,因为传统信息管理从时效性,还是安全性,还是可操作性等各个方面来讲,遇到了互联网时代才发现能补上自古以来的短板,有效的提升管理的效率和业务水平。传统的管理模式,时间越久管理的内容越多,也需要更多的人来对数据进行整理,并且数据的汇总查询方面效率也是极其的低下,并且数据安全方面永远不会保证安全性能。结合数据内容管理的种种缺点,在互联网时代都可以得到有效的补充。结合先进的互联网技术,开发符合需求的软件,让数据内容管理不管是从录入的及时性,查看的及时性还是汇总分析的及时性,都能让正确率达到最高,管理更加的科学和便捷。本次开发的医院后台管理系统实现了病房管理、病例管理、处方管理、字典管理、公告信息管理、患者管理、药品管理、医生管理、预约医生管理、住院管理、管理员管理等功能。系统用到了关系型数据库中王者MySql作为系统的数据库,有效的对数据进行安全的存储,有效的备份,对数据可靠性方面得到了保证。并且程序也具备程序需求的所有功能,使得操作性还是安全性都大大提高,让医院后台管理系统更能从理念走到现实,确确实实的让人们提升信息处理效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lic_dream

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值