1.问题引出
系统的基础业务无非就CRUD,但是当这些主干功能开发完之后,一定是会拓展系统功能的,比如加一个日志功能。这时候如果改源码,系统就很容易出问题。此时就需要一个解决方案。
我发现很多编程思想,就一个核心—解耦!系统开发是有生命周期的,时不断修改的,唯有解耦,才能减小开发后期的工作量。
窃以为,悟性还可以。在没接触AOP,IOC之前,在下使用MATLAB写代码,已经习惯将各个不同的方法进行封装,以此来实现解耦。
2.静态代理
2.1 说明解释
很多人都会用用中介搭线房东和租客的例子来说明静态代理,但是我觉得这个并不太靠谱。
窃以为,下面这个图比较合适。对于1. 中的问题,实体类可以在代理类中实现拓展。这样的话,既有的实体类没有改动,将新的功能写入代理类的接口方法中,就可以完成实体类功能的拓展。
当业务变动时,实体类可以有多个,但是不管如何,客户类只需要去访问代理类即可。由代理类来决定该调用哪个实体类。
2.2 代码示例
//接口
public interface Rent {
public void rentHouse();
}
//实体类,房东
public class Host implements Rent{
@Override
public void rentHouse() {
System.out.println("租客租房成功!");
}
}
//代理类
public class Proxy implements Rent{
Rent rent;
public proxy(Rent rent){
this.rent=rent;
}
@Override
public void rentHouse() {
//拓展实体类功能。
System.out.println("通过中介租房");
rent.rentHouse();
}
}
//客户类(测试类)
public class StaticProxyTest {
public static void main(String[] args) {
Rent rent = new Host();
Proxy proxy = new Proxy(rent);
proxy.rentHouse();
}
}
3.动态代理
太长了,另开一篇讲这个吧
4.Spring中的AOP
需要依赖一个包,id:aspectj
4.1. 名词解释
- 横切关注点:跨越多个模块的方法或功能称为横切,比如日志功能。
- 切面(aspect): 就是横切关注点的那个类,比如日志类。
- 通知(advice):切面到切入点的通知,即aspect中的方法。
- 目标(Target) :被通知的对象,比如房东对象。
- Proxy: 代理类。
- 切入点(PointCut) : advice执行的地点。
- 连接点(JointPoint) : 切入点的集合
4.2. 代码
继续解决那个向crud中插入日志功能的问题。
4.2.1. 方法1
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:https="http://www.springframework.org/schema/c"
xmlns:aop="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--使用原生spring api接口-->
<!--注册bean-->
<bean id="delete" class="路径/Delete"/>
<bean id="log" class="路径/Log"/>
<aop:config>
<!-- 定义切入点,本例中就是向Delete中的delete()插入日志功能
第一个*指的是所有的返回类型,第二个*指的是所有的方法
(..)指的是不限制参数类型-->
<aop:pointcut id="pointcut" expression="execution(* 路径/Delete.*(..))"/>
<!--通知切入点-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
</aop:config>
</beans>
4.2.2. 方法2
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:https="http://www.springframework.org/schema/c"
xmlns:aop="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--自定义类实现AOP-->
<!--注册bean-->
<bean id="log" class="路径/Log"/>
<aop:config>
<!--自定义切面-->
<aop:aspect ref="log">
<!--切入点-->
<aop:pointcut id="pointcut" expression="execution(* 路径/Delete.*(..))"/>
<!--通知,此例中,会在删除之后出现日志-->
<aop:after method="log(日志方法的名字)" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
</beans>
4.2.2. 方法3
//注解实现AOP
@Aspect
public class Log(
@Before("execution(* 路径/Delete.*(..))")
public void log(){
//doSomeing
}
}
<!--注册bean-->
<bean id="log" class="路径/Log"/>
<!--开启注解支持-->
<aop:aspectj-autoproxy/>