AOP的概述和思想什么是AOP?
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。说的简单一点就是,在你的本身逻辑代码的前后你需要做一些你需要的操作,但是又不想改变原来的代码,使其受影响,这时候AOP就可以帮你简单的进行操作。
环绕通知(around)
在目标方法执行前、后被通知, 可以获取连接点对象(ProceedingJoinPoint, 该对象可以获取被拦截方法的签名、参数、返回值、包括调用与否)
该方法的返回值,即代表了真正业务逻辑代码的返回值
可以选择终止或正常执行目标方法
前置通知(before)
在目标方法调用前通知切面, 什么参数也无法获取。也不能终止目标方法执行
后置(返回值)通知(after returning)
只有在目标方法 正常 执行结束后才会通知, 在通知方法中可以获取到方法的返回值
后置(最终)通知 (after)
在目标方法执行结束后通知切面, 什么参数也无法获取。无论目标方法是正常执行结束还是抛出异常终止,都会被通知
异常通知(after throwing)
只有在目标方法 出现异常 才会通知, 在通知方法中可以获取到抛出的异常信息
连接点(JoinPoint)
连接点有很多种,比如方法执行期间(开始执行、执行结束、抛出异常)、字段修饰符、字段值被更改…
在Spring AOP中只支持方法连接点(因为Spring AOP底层是通过动态代理实现的)。
连接点与切入点的关系可以简单理解为: 切入点一定是连接点, 连接点不一定是切入点。
织入(Weaver)
织入的过程其实就是Spring AOP帮我们把切面中的代码织入到目标代码中的过程。
注解式实现AOP
切点表达式的抽取
同xml配置aop一样,我们可以将切点表达式抽取。抽取方式是在切面内定义方法,在该方法上使用@Pointcut注解定义切点表达式,然后在在增强注解中进行引用。具体如下:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component("myAspect")
@Aspect//标注当前MyAspect是一个切面类
public class MyAspect {
//配置前置通知
@Before("pointcut()")
public void before(){
System.out.println("前置增强....");
}
@AfterReturning("pointcut()")
public void afterReturning(){
System.out.println("后置增强....");
}
//ProceedingJoinPoint:正在执行的连接点 ==> 切点
@Around("pointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前增强....");
Object proceed = pjp.proceed();//切点方法
System.out.println("环绕后增强....");
return proceed;
}
@AfterThrowing("MyAspect.pointcut()")
public void afterThrowing(){
System.out.println("异常抛出增强....");
}
@After("MyAspect.pointcut()")
public void after(){
System.out.println("最终增强....");
}
//定义切点表达式
@Pointcut("execution(* com.itheima.anno.*.*(..))")
public void pointcut(){}
}
Spring中propagation的7种事务配置
REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。
<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
">
<!-- 注解式xml配置-->
<context:component-scan base-package="com.hua"></context:component-scan>
<!--配置springmvc DispatcherServlet-->
<mvc:annotation-driven></mvc:annotation-driven>
<mvc:view-resolvers>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/static/"></property>
<property name="suffix" value=".html"></property>
</bean>
</mvc:view-resolvers>
<!-- sqlSessionFactory的bean对象-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="mapperLocations" value="classpath:mapper/*.xml"></property>
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageInterceptor"></bean>
</array>
</property>
</bean>
<!-- 数据库池的bean对象-->
<bean id="dataSource" class="org.apache.ibatis.datasource.pooled.PooledDataSource">
<property name="driver" value="com.mysql.cj.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC"></property>
<property name="username" value="username"></property>
<property name="password" value="password"></property>
</bean>
<!-- 配置MapperScannerConfigurer-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.hua.mapper"></property>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
<bean id="TransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:advice id="interceptor" transaction-manager="TransactionManager">
<tx:attributes>
<tx:method name="query*" propagation="SUPPORTS"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="*" propagation="SUPPORTS"/>
</tx:attributes>
</tx:advice>
<bean id="myalladvice" class="com.hua.Advice.myalladvice"></bean>
<aop:config>
<aop:pointcut id="serviceaop" expression="execution(* com.hua.service.*.*(..))"/>
<aop:advisor pointcut-ref="serviceaop" advice-ref="interceptor"/>
<aop:aspect ref="myalladvice">
<aop:before method="before" pointcut-ref="serviceaop"></aop:before>
<aop:before method="after" pointcut-ref="serviceaop"></aop:before>
<!-- <aop:before method="around" pointcut-ref="serviceaop"></aop:before>-->
</aop:aspect>
</aop:config>
</beans>
log4j2的使用及配置详解
pom依赖:
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.18.0</version>
</dependency>
log4j2.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
<Configuration status="WARN" monitorInterval="60">
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<Properties>
<Property name="App">third-api</Property>
<Property name="logDir">/home/migu/portal-third-api/logs</Property>
<Property name="splitSize">100 MB</Property>
</Properties>
<Appenders>
<!-- 输出控制台日志的配置 -->
<Console name="console" target="SYSTEM_OUT">
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
<!-- 输出日志的格式 -->
<!-- 格式化输出:%date表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符-->
<!-- %logger{36} 表示 Logger 名字最长36个字符 -->
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/>
</Console>
<!-- 打印出所有的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档 -->
<RollingFile name="infoLog" fileName="${logDir}/${App}-info.log"
filePattern="${logDir}/${App}-info-%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS z} [%thread] %-5level %logger{36} %L %M - %msg%xEx%n"/>
<Policies>
<!--interval属性用来指定多久滚动一次,默认是1, 单位到底是月 天 小时 分钟,根据filePattern配置的日期格式而定,本处的格式为天,则默认为1天-->
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<!--按大小分-->
<SizeBasedTriggeringPolicy size="${splitSize}"/>
</Policies>
<Filters>
<!-- 只记录info和warn级别信息 -->
<!--<ThresholdFilter level="error" onMatch="DENY" onMismatch="NEUTRAL"/>-->
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
<!-- 指定每天的最大压缩包个数,默认7个,超过了会覆盖之前的 -->
<DefaultRolloverStrategy max="1000"/>
</RollingFile>
<!-- 存储所有error信息 -->
<RollingFile name="errorLog" fileName="${logDir}/${App}-error.log"
filePattern="${logDir}/${App}-error-%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS z} [%thread] %-5level %logger{36} %L %M - %msg%xEx%n"/>
<Policies>
<!--interval属性用来指定多久滚动一次,默认是1, 单位到底是月 天 小时 分钟,根据filePattern配置的日期格式而定,本处的格式为天,则默认为1天-->
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<!--按大小分-->
<SizeBasedTriggeringPolicy size="${splitSize}"/>
</Policies>
<Filters>
<!-- 只记录error级别信息 -->
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
<!-- 指定每天的最大压缩包个数,默认7个,超过了会覆盖之前的 -->
<DefaultRolloverStrategy max="1000"/>
</RollingFile>
<!--大云5分钟宽带查询接口单独打印-->
<RollingFile name="dayunLog" fileName="${logDir}/${App}-dayunInfo.log"
filePattern="${logDir}/${App}-dayunInfo-%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS z} [%thread] %-5level %logger{36} %L %M - %msg%xEx%n"/>
<Policies>
<!--interval属性用来指定多久滚动一次,默认是1, 单位到底是月 天 小时 分钟,根据filePattern配置的日期格式而定,本处的格式为天,则默认为1天-->
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<!--按大小分-->
<SizeBasedTriggeringPolicy size="${splitSize}"/>
</Policies>
<Filters>
<!-- 只记录info和warn级别信息 -->
<!--<ThresholdFilter level="error" onMatch="DENY" onMismatch="NEUTRAL"/>-->
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
<!-- 指定每天的最大压缩包个数,默认7个,超过了会覆盖之前的 -->
<DefaultRolloverStrategy max="1000"/>
</RollingFile>
</Appenders>
<Loggers>
<!-- root logger 配置,全局配置,默认所有的Logger都继承此配置 -->
<!-- AsyncRoot - 异步记录日志 - 需要LMAX Disruptor的支持 -->
<Root level="info">
<AppenderRef ref="infoLog"/>
<AppenderRef ref="errorLog"/>
<AppenderRef ref="console"/>
</Root>
<!--将logger中的 additivity 属性配置为 false,则这个logger不会将日志流反馈到root中。-->
<Logger name="dayunLogger" additivity="true" level="INFO">
<!--<appender-ref ref="sendCodeFile" level="INFO" />-->
<appender-ref ref="dayunLog" level="INFO" />
</Logger>
<!--第三方的软件日志级别 -->
<logger name="org.springframework" level="info" additivity="true">
</logger>
</Loggers>
</Configuration>