Spring — AOP面向切面编程(一)
一、代理模式概述
- 代理的特点:(目标对象即被代理者)
- 实现和目标对象相同的接口
- 具备和目标对象的方法
- 代理者不仅要做目标对象的方法 , 还要做一些额外的操作
- 使用场景:
- 事务
- 权限控制
- 统计每个方法的执行时间
二、静态代理
1. 静态代理优点
1. 解耦
2. 静态代理缺点:
1. 一个代理类只能服务于一个被代理 , 如果被代理类很多 , 则操作会非常繁琐
2. 代码复用性不高 , 重复代码太多
3. 代理类的通用性几乎为0
4. 案例: 把service层的事务管理操作解耦
事务管理类
@Component
public class TXManage {
public void startTx(){
System.out.println("开启事务");
}
public void commti(){
System.out.println("提交事务");
}
public void rollback(){
System.out.println("回滚事务");
}
}
静态代理类
注意
实现与代理目标相同的接口
需要指定id , 与注解的机制有关
@Component("personService")
public class StaticProxy implements PersonService{
@Autowired
private TXManage tx ;
@Autowired
private PersonServiceImpl pl;
@Override
public void savePerson(Person person) {
try{
tx.startTx();
pl.savePerson(person);
tx.commti();
}catch(Exception e){
tx.rollback();
}
}
}
三、动态代理
1. 动态代理分为两种:
1. JDK提供的动态代理
2. CGLib动态代理
2. JDK提供的动态代理
1. 优点:
1. 继承静态代理的优点 , 一个代理类可以为多个目标类服务
2. 提高代码复用性
2. 缺点:
1. 并不是所有的方法都需要代理 , 需要进行方法过滤 , 过程繁琐
3. 必须要求目标类有接口
4. 案例: 把service层的事务管理操作解耦
事务管理类
@Component
public class TXManage {
public void startTx(){
System.out.println("开启事务");
}
public void commti(){
System.out.println("提交事务");
}
public void rollback(){
System.out.println("回滚事务");
}
}
动态代理生成类
@Component
public class DynamicProxy {
@Autowired
private TXManage tx;
public Object getProxy(final Object tagert){
Object proxy = Proxy.newProxyInstance(tagert.getClass().getClassLoader(), tagert.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
tx.startTx();
Object result = method.invoke(tagert, args);
tx.commti();
return result;
}
});
return proxy;
}
}
改造调用service的层 , 使他调用的是代理之后的service
@Controller
public class PersonServlet {
@Autowired
private PersonService personService;
@Autowired
private DynamicProxy dp;
public void savePerson(Person person){
PersonService ps = (PersonService) dp.getProxy(personService);
ps.savePerson(person);
}
}
- CGLib动态代理
- 优点:
- 继承JDK动态代理的优点
- 并不需要目标对象有接口
- 缺点:
- 方法过滤繁琐
- 优点:
四、Spring AOP面向切面编程
- Spring AOP 继承了以上代理所有的优点 , 并且可以对方法进行精准过滤
- Spring AOP 内部实现就是通过两种动态代理来实现的 , 进行了封装
- AOP:
- 是一种编程思想 , 可以实现把某些具体的功能的代码放在一个切面(类)当中 , 在不改动原来项目结构的同时 , 把切面插入到工程中 。 使用 这种编程思想 , 可以使项目的耦合程度降到最低
- Spring AOP 的相关概念:
- 切面(Aspect):
- 就是一个类 , 如: 事务的切面类 、 权限控制的切面类 、 日志输出切面类 。。。
- 连接点(Joinpoint):
- 代表目标类中的方法
- 通知(Advice):
- 切面中的方法 , 分为五种通知:
- 前置通知: 目标方法执行之前的通知
- 后置通知: 目标方法执行之后的通知
- 最终通知: 目标方法抛出异常之后的通知(相当于 finally块)
- 环绕通知: 目标方法执行前后执行的通知
- 异常通知: 目标方法发生异常执行的通知(相当于catch块)
- 切面中的方法 , 分为五种通知:
- 切入点(Pointcut): 过滤方法的规则 , 只有满足切入点的规则才会执行切面中的通知
- 引入:
- 目标对象:
- 真正执行目标方法的类
- AOP代理对象:
- 在否和切入点的规则的吧前提下 , 真正执行的对象都是代理对象
- 织入:
- 代理对象的方法体 , 用来连接通知和目标对象
- 切面(Aspect):
AOP实现
- 实现步骤:
- 导入相关jar包
- 编写配置文件的头(导入Spring aop约束)
- 编写目标类
- 编写切面和通知
- 配置切入点表达式
- 配置切面和通知
- 导入相关jar包
- spring-aop.jar
- spring-aspects.jar
- aopectjrt.jar
- aspectjweaver.jar
- aopalliance.jar
编写配置文件的头
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd"> </beans>
编写目标类
@Service public class PersonServiceImpl implements PersonService{ @Autowired private PersonDao dao; @Override public void savePerson(Person person) { dao.savePerson(person); } }
编写切面和通知
@Component public class TxAspect { @Autowired private TXManage tx; public Object around(ProceedingJoinPoint pjp) throws Throwable{ tx.startTx(); pjp.proceed(); tx.commti(); return null; } }
配置切面表达式
<!-- 配置切面 --> <aop:config> <!-- 配置切入点 一定要在切面的上面配置--> <!-- within表达式只能过滤包和类 --> <aop:pointcut expression="within(com.tj.service.*)" id="pc"/> </aop:config>
配置切面和通知
<!-- 配置切面 --> <aop:config> <!-- 配置切入点 一定要在切面的上面配置--> <!-- within表达式只能过滤包和类 --> <aop:pointcut expression="within(com.tj.service.*)" id="pc"/> <!-- 配置切面 --> <aop:aspect ref="txAspect" > <!-- 配置切面中的通知 --> <aop:around method="around" pointcut-ref="pc"/> </aop:aspect> </aop:config>
- 实现步骤: