Spring 入门之AOP与日志

Spring 入门之AOP与日志

什么是切面?

AOP(Aspect Orient Programming),面向切面编程。
切面:处理一些公用的,重复的,通用的非主业务逻辑。
Aop就是将切面单拎出来开发,在调用目标方法时自动反织回去的处理。
 (Aop就是将一些公用的,重复的,通用的非主业务逻辑单独拿出来开发,
并且在调用目标方法时自动反织回去)。

使用切面有什么好处?又是怎么实现的?

好处:减少重复代码,专注业务。
底层实现:动态代理。 代理分为静态代理(父子继承,业务切面混合)和动态代理(jdk,cglib)后面具体实现。

日志

使用日志有利于我们在开发环境迅速定位到项目程序出现的问题,另一方面操作日志还可以协助我们完成对用户行为的收集,帮助我们后续进行分析。

当前市面上的日志框架众多,我们根据是否为具体实现这个标准,可以大致将主流的日志框架分为以下两类:

日志实现(日志实现框架):JDKLog、LogBack、log4j、log4j2

日志门面(日志标准接口框架):JCL(Jakarta Commons Logging)、slf4j(Simple Logging Facade
for Java)

LogBack可以说是Log4J的进化版,除了具备 Log4J的所有优点之外,还解决了Log4J不能使用占位符的问题。LogBack当前分成三个模块:logback-core、logback-classic和logback-access。

logback-core:是其它两个模块的基础模块。

logback-classic:是Log4J的一个改良版本。此外logback-classic完整实现SLF4J API使你可以很方便地更换成其它日记系统如Log4J或JDK14 Logging。

logback-access:访问模块与Servlet容器集成提供通过HTTP来访问日记的功能。

在这里插入图片描述

SLF4J日志门面

Facade for Java),即Java简单日志记录接口集,是一个日志的接口规范,它对用户提供了统一的日志接口,屏蔽了不同日志组件的差异。这样我们在编写代码的时候只需要看SLF4J接口文档即可,不需要去理会不同日之框架的区别。当我们需要更换日志组件的时候,我们只需要更换一个具体的日志组件Jar包就可以。
在这里插入图片描述

实现日志文件的配置
  1. 引入logback相关依赖

     <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-classic</artifactId>
                <version>1.2.11</version>
            </dependency>
    
  2. 编写logback.xml配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <!--
      scan,当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
      scanPeriod,设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
      debug,当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
    
    -->
    <configuration debug="false" scan="true" scanPeriod="1 seconds">
    
        <!--
        contextName ,设置日志上下文名称,可以通过%contextName来打印日志上下文名称
        -->
        <contextName>logback</contextName>
        <!--
        property可以用来设置变量,可以通过${name}来访问,有以下的属性
            name,用于${name}访问的key
            value,用于${name}访问的value
            file ,用于指定配置文件的路径,他的作用在于,如果你有多个配置信息的话,可以直接写在配置文件中,然后通过file引入
                   <property file="src/main/java/chapters/configuration/variables.properties" />
            resource作用和file一样,但是,它是可以直接从classpath路径下引入配置文件
                   <property resource="resource.properties" />
        -->
        <property name="log.path" value="D:\\log\\logback.log"/>
        <!--
        appender格式化日志输出节点,有俩个属性name和class,class用来指定哪种输出策略,常用就是控制台输出策略和文件输出策略。
        -->
        <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
            <!-- <filter class="com.example.logback.filter.MyFilter" /> -->
            <!--
            系统定义的拦截器,例如我们用ThresholdFilter来过滤掉INFO级别以下的日志不输出到控制台中
            -->
            <!--<filter class="ch.qos.logback.classic.filter.ThresholdFilter">-->
                <!--<!–-->
                <!--Logger可以被分配级别。级别包括:TRACE、DEBUG、INFO、WARN和ERROR,-->
               <!--程序会打印高于或等于所设置级别的日志,设置的日志等级越高,打印出来的日志就越少。-->
               <!--如果设置级别为INFO,则优先级高于等于INFO级别(如:INFO、 WARN、ERROR)的日志信息将可以被输出,小于该级别的如DEBUG将不会被输出。为确保所有logger都能够最终继承一个级别,根logger总是有级别,默认情况下,这个级别是DEBUG。-->
                <!--–>-->
                <!--<level>INFO</level>-->
            <!--</filter>-->
            <!--encoder和pattern节点组合用于具体输出的日志格式-->
            <encoder>
                <pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n
                </pattern>
            </encoder>
        </appender>
        <appender name="file"
                  class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>${log.path}</file>
            <!--
                rollingPolicy日志回滚策略,在这里我们用了TimeBasedRollingPolicy,基于时间的回滚策略,有以下子节点
                fileNamePattern,必要节点,可以用来设置指定时间的日志归档,例如我们上面的例子是每天将日志归档成一个zip包
                maxHistory ,可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件,,例如设置为30的话,则30天之后,旧的日志就会被删除
                totalSizeCap,可选节点,用来指定日志文件的上限大小,例如设置为3GB的话,那么到了这个值,就会删除旧的日志
    -->
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <fileNamePattern>${log.path}.%d{yyyy-MM-dd}.zip</fileNamePattern>
            </rollingPolicy>
            <encoder>
                <!--
                %logger{36} 表示logger名字最长36个字符,否则按照句点分割
                %date{HH:mm:ss.SSS}输出日志的打印日志,模式语法与java.text.SimpleDateFormat 兼容。看上去%d就已经够好了
                可选的格式修饰符位于“%”和转换符之间。第一个可选修饰符是左对齐标志,符号是减号“-”;
                m / msg / message 输出应用程序提供的信息。
                t / thread 输出产生日志的线程名。
                p / le / level输出日志级别。
                -->
                <pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n
                </pattern>
            </encoder>
        </appender>
        <!--
        root节点,必选节点,用来指定最基础的日志输出级别,他有俩个节点可以用来应用appender,格式化日志输出
        -->
        <root level="debug">
            <appender-ref ref="console"/>
            <appender-ref ref="file"/>
        </root>
        <!--logger节点,可选节点,用来具体指明包的日志输出级别,它将会覆盖root的输出级别-->
        <!--<logger name="com.example.logback" level="warn" />-->
    </configuration>
    <!--
        <dependency>
          <groupId>ch.qos.logback</groupId>
          <artifactId>logback-classic</artifactId>
          <version>1.2.11</version>
        </dependency>
    
         private static final Logger LOGGER= LoggerFactory.getLogger(Agent.class);
         LOGGER.info("事务开启...........");
    -->
    

模拟Aop功能实现

主要的目的是进行业务逻辑与切面的解耦合。完全分离业务逻辑和切面。

分为五个版本:

1)版本一:业务和切面紧耦合在一起。

2)版本二:子类代理实现切面功能增强。

3)版本三:使用静态代理分离业务。

4)版本四:使用静态代理分离业务和切面。

版本五:使用动态代理优化业务和切面的解耦合

版本一:业务和切面紧耦合在一起
 public void buy(){
        try {
            System.out.println("事务开启");
            System.out.println("事务进行");
            System.out.println("事务结束");
        } catch (Exception e) {
            System.out.println("事务回滚");
        }
    }
版本二:子类代理实现切面功能增强。
public class BookServiceImpl {
    public void buy(){
        System.out.println("买书----");
    }
}
public class SubBookServiceImpl extends BookServiceImpl{
    @Override
    public void buy() {
        try {
            System.out.println("事务开启");
            super.buy();
            System.out.println("事务结束");
        } catch (Exception e) {
            System.out.println("事务回滚");
        }
    }
}
版本三:使用静态代理分离业务
public class Agent implements Service{
    Service service;//灵活业务
    public Agent(Service service) {
        this.service = service;
    }

    @Override
    public void buy() {
        try {
            System.out.println("事务开启");
            service.buy();
            System.out.println("事务结束");
        } catch (Exception e) {
            System.out.println("事务回滚");
        }
    }
}
public class BookServiceImpl implements Service{
    @Override
    public void buy() {
        System.out.println("卖书---");
    }
}
public class ProductServiceImpl implements Service{
    @Override
    public void buy() {
        System.out.println("产品销售");
    }
}
public interface Service {
    void buy();
}
版本四:使用静态代理分离业务和切面。
public interface Service {
    void buy();
}
public interface Aop {
    default void before(){}
    default void after(){}
    default void exception(){}
}
public class BookServiceImpl implements Service {
    @Override
    public void buy() {
        System.out.println("卖书---");
    }
}
public class LogAop implements Aop {
    @Override
    public void before() {
        System.out.println("日志前置");
    }
public class ProductServiceImpl implements Service {
    @Override
    public void buy() {
        System.out.println("产品销售");
    }
}
public class TransAOP implements Aop{
    @Override
    public void before() {
        System.out.println("事务开启");
    }

    @Override
    public void after() {
        System.out.println("事务提交");
    }
}
public class Agent implements Service {
    Aop aop;
    Service service;//灵活业务
    public Agent(Service service,Aop aop) {
        this.service = service;
        this.aop=aop;
    }

    @Override
    public void buy() {
        try {
            aop.before();
            service.buy();
            aop.after();
        } catch (Exception e) {
            aop.exception();
        }
    }
}
 @Test
    public void testAgent3(){
        Agent agent = new Agent(new BookServiceImpl(), new TransAOP());
        Agent agent1 = new Agent(agent, new LogAop());
        agent1.buy();
    }

结果:

日志前置
事务开启
卖书---
事务提交
版本五:使用动态代理优化业务和切面的解耦合
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyFactory {
   //生成动态代理对象
    public static Object getAgent(Service service,Aop aop){
        return Proxy.newProxyInstance(
                service.getClass().getClassLoader(),
                service.getClass().getInterfaces(),
                // 处理业务和切面的方法,并且返回目标方法对象
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //返回业务方法
                        Object object=null;
                        try {
                            aop.before();
                            object = method.invoke(service, args);
                            aop.after();
                        } catch (Exception e){
                            aop.exception();
                        }
                        return object;
                    }
                }
        );
    }

}
 @Test
    public void testAgent(){
        Service agent = (Service) ProxyFactory.getAgent(new BookServiceImpl(), new LogAop());
        System.out.println(agent.getClass());
        agent.buy();
    }
结果:
class jdk.proxy2.$Proxy7
日志前置
卖书---
 @Test
    public void testAgent(){
        Service agent = (Service) ProxyFactory.getAgent(new BookServiceImpl(), new TransAOP());
        Service agent1 = (Service) ProxyFactory.getAgent(agent, new LogAop());
        System.out.println(agent1.getClass());
        agent1.buy();
    }
测试结果:
日志前置
事务开启
卖书---
事务提交
  • 13
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值