一、日志功能
1.1 基本方式实现
在业务层类中的方法中打印日志,记录方法执行前后以及方法发生异常的时间点。
分析代码问题:复用性低 耦合度高
解决方式:动态代理
1.2 JDK动态代理
1.导入初始工程
2.确定目标类(被代理的类)(deptServiceImpl)
3.确定增强类(要给被代理类添加的功能)(Logger)
4.使用JDK技术创建代理对象,然后调用其方法(Test)
(步骤):1.准备代理对象(目标对象 private DeptService deptService )
2.编写增强逻辑(代理对象的技能 new InvocationHandler)
3.创建代理对象(Proxy.newPorxyInstance(类加载器,与目标类相同的接口,代理对象的技能))
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class DeptServiceTest {
@Autowired
private Logger logger;
//目标对象
@Autowired
private DeptService deptService;
@Test
public void test1() {
//1.准备好目标对象(deptService)
//2.编写增强逻辑
InvocationHandler invocationHandler = new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object obj = null;
try {
logger.m1();
//调用目标对象的方法
obj = method.invoke(deptService, args);
} catch (Exception e) {
logger.m3();
}
logger.m2();
return obj;
}
};
//3.创建代理对象
DeptService proxyInstance = (DeptService) Proxy.newProxyInstance(
deptService.getClass().getClassLoader(),//类加载器(目标对象一致)
deptService.getClass().getInterfaces(),
invocationHandler
);
//4.调代理对象的方法
proxyInstance.findById(1);
}
}
4.调代理对象的方法。
1.3 cglib动态代理
基于Cslib动态代理产生的代理对象与被代理对象是父子关系,代理对象是被被代理对象的儿子。
1.4 总结
开发阶段分别开发,运行阶段组装运行。
二、AOP
2.1 AOP介绍
AOP( 面向切面编程 )是一种思想,它的目的就是在不修改源代码的基础上,对原有功能进行增强。
SpringAOP是对AOP思想的一种实现,Spring底层同时支持jdk和cglib动态代理。
Spring会根据被代理的类是否有接口自动选择代理方式:
如果有接口,就采用jdk动态代理
如果没接口,就采用cglib的方式
AOP术语
* 目标对象(Target)
被代理的对象
* 连接点(JoinPoint)
目标对象中得所有方法
* 切入点(PointCut)
目标对象中得要进行功能增强那部分方法
* 增强 (Advice 通知)
一个具体增强功能(增强对象 增强方法)
* 切面 (Aspect)
切面是一种描述,描述的是: 哪个增强方法加入到了哪个切点的什么位置增强方法和切点方法的执行顺序。
2.2 入门案例
1.创建模块,导入依赖。
2.创建实体类。
3.创建业务层接口和实现类。
4.创建日志类。
5.配置切面。
(步骤:1.切面注解,标注在增强类上 2.定位切点 3.指定被标注的方法在切点之前执行)
@Component
@Aspect //切面(增强方法和切点方法的顺序)(第一步)
public class Logger {
//定位切点(第二步)
@Pointcut("execution(public java.util.List com.itheima.service.impl.DeptServiceImpl.findAll())")
//代表m1增强会在findAll之前执行
public void pt(){}
@Before("pt()")(第三步)
public void m1(){
System.out.println("进入方法之前");
}
}
6.创建配置类。
(激活切面自动代理)
@ComponentScan("com.itheima")
@EnableAspectJAutoProxy //激活切面自动代理
public class SpringConfig {
}
7.单元测试。
2.3 通知类型
1.四大通知
前置通知(before)、后置通知(After)、返回后通知(AfterReturning)、异常后通知(AfterThrowing)
@Around环绕通知是一种特殊的通知,他允许以编码的形式自定义通知。
//环绕通知
@Around("pt()")
public Object m5(ProceedingJoinPoint pjp){
Object o = null;
try {
System.out.println("进入方法之前");
o = pjp.proceed();
System.out.println("方法正常结束");
} catch (Throwable e) {
System.out.println("方法异常");
} finally {
System.out.println("离开方法之前");
}
return o;
}
通知顺序(多个切面的切点都匹配到了目标方法)(order(1)数字小的先执行,先进后出)
2. 自定义注解
步骤:1.创建一个自定义注解com.itheima.anno.LogAnno
2.在logger里定义切点@pointcut("@annocation(com.itheima.anno.LogAnno)")
public void pt(){}
//环绕通知
@Around("pt()")
public Object m5(ProceedingJoinPoint pjp){
Object o = null;
System.out.println("类名"+pjp.getTarget().getClass().getName());
MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
System.out.println("方法名"+methodSignature.getMethod().getName());
System.out.println("参数"+ Arrays.toString(pjp.getArgs()));
long begin = System.currentTimeMillis();
try {
o = pjp.proceed();
System.out.println("返回结果" + o);
} catch (Throwable e) {
System.out.println("异常" + e.getMessage());
throw new RuntimeException(e);
} finally {
long end = System.currentTimeMillis();
System.out.println("耗时" + (end - begin));
}
return o;
}
3.在切点上加入自定义注解
4.进行测试
三、事务管理
3.1 事务管理
1. 在配置类中激活声明式事务管理,向容器中放入事务管理器
//配置类
@ComponentScan("com.itheima")
@EnableTransactionManagement //激活声明式事务管理(第一步)
public class SpringConfig {
@Bean
//像容器中放入事务管理器(第二步)
public DataSourceTransactionManager transactionManager(DruidDataSource dataSource){
DataSourceTransactionManager manager = new DataSourceTransactionManager();
manager.setDataSource(dataSource);
return manager;
}
}
2.添加事务注解
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountMapper accountMapper;
@Override
@Transactional //标注在方法上 让Spring管理事务 (第三步)自动开启提交或者回滚事务
public void transfer(String out, String in, Float money) {
accountMapper.add(in,money);
//int i = 1/0;
accountMapper.diff(out,money);
}
}
3.测试
3.2 事务属性
1. 回滚:默认情况下,只有出现 RuntimeException 才回滚异常,rollbackFor属性用于控制让非运行时异常也回滚
2.传播行为:当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行事务控制。(propagation)
required:默认值,需要事务,有则加入,无则创建新事务
requires_new : 需要新事务,无论有无,总是创建新事务
(面试):比如:下订单前需要记录日志,不论订单保存成功与否,都需要保证日志记录能够记录成功。