[AOP] 之让人一脸蒙哔的面向切面编程
最近接触到了面向切面编程,看来很多的文档,算是有一点点了解了,趁自己还有点印象,先把它们给写出来
什么是AOP
AOP(Aspect-Oriented Programming), 即 面向切面编程。
AOP 中的基本单元是 Aspect(切面)
AOP-术语
Aspect (切面)
包含了横切逻辑的定义,也包括了连接点的定义
Spring AOP就是负责实施切面的框架, 它将切面所定义的横切逻辑织入到切面所指定的连接点中.
Aspect(切面)也就是封装了Advice(增强)与 Pointcut(切点 )
AOP的工作重心在于如何将增强织入目标对象的连接点上, 这里包含两个工作:
如何通过 pointcut 和 advice 定位到特定的 joinpoint 上
如何在 advice 中编写切面代码.
advice(增强)
由 aspect 添加到特定的 join point(即满足 point cut 规则的 join point) 的一段代码.
连接点(join point)
程序运行中的一些时间点, 例如一个方法的执行, 或者是一个异常的处理.
在 Spring AOP 中, join point 总是方法的执行点, 即只有方法连接点.
切点(point cut)
Advice 是和特定的 point cut 关联的, 并且在 point cut 相匹配的 join point 中执行.
在 Spring 中, 所有的方法都可以认为是 joinpoint, 但是我们并不希望在所有的方法上都添加 Advice,
而 pointcut 的作用就是提供一组规则(使用 AspectJ pointcut expression language 来描述) 来匹配joinpoint, 给满足规则的 joinpoint 添加 Advice.
关于join point 和 point cut 的区别
所有的方法执行都是 join point. 而 point cut 是一个描述信息, 它修饰的是 join point, 通过 point cut, 我们就可以确定哪些 join point 可以被织入 Advice.
因此 join point 和 point cut 本质上就是两个不同纬度上的东西.
advice 是在 join point 上执行的, 而 point cut 规定了哪些 join point 可以执行哪些 advice
introduction
为一个类型添加额外的方法或字段. Spring AOP 允许我们为 目标对象
引入新的接口(和对应的实现).
例如我们可以使用 introduction 来为一个 bean 实现 IsModified 接口, 并以此来简化 caching 的实现.
目标对象(Target)
织入 advice 的目标对象. 目标对象也被称为 advised object
.
因为 Spring AOP 使用运行时代理的方式来实现 aspect, 因此 adviced object 总是一个代理对象(proxied object)
注意, adviced object 指的不是原来的类, 而是织入 advice 后所产生的代理类.
AOP proxy
一个类被 AOP 织入 advice, 就会产生一个结果类, 它是融合了原类和增强逻辑的代理类.
在 Spring AOP 中, 一个 AOP 代理是一个 JDK 动态代理对象或 CGLIB 代理对象.
织入(Weaving)
将 aspect 和其他对象连接起来, 并创建 adviced object 的过程.
根据不同的实现技术, AOP织入有三种方式:
编译器织入, 这要求有特殊的Java编译器.
类装载期织入, 这需要有特殊的类装载器.
动态代理织入, 在运行期为目标类添加增强(Advice)生成子类的方式.
Spring 采用动态代理织入, 而AspectJ采用编译器织入和类装载期织入.
advice 的类型
before advice, 在 join point 前被执行的 advice. 虽然 before advice 是在 join point 前被执行, 但是它并不能够阻止 join point 的执行, 除非发生了异常(即我们在 before advice 代码中, 不能人为地决定是否继续执行 join point 中的代码)
after return advice, 在一个 join point 正常返回后执行的 advice
after throwing advice, 当一个 join point 抛出异常后执行的 advice
after(final) advice, 无论一个 join point 是正常退出还是发生了异常, 都会被执行的 advice.
around advice, 在 join point 前和 joint point 退出后都执行的 advice. 这个是最常用的 advice.
关于 AOP Proxy
Spring AOP 默认使用标准的 JDK 动态代理(dynamic proxy)技术来实现 AOP 代理, 通过它, 我们可以为任意的接口实现代理.
如果需要为一个类实现代理, 那么可以使用 CGLIB 代理.
当一个业务逻辑对象没有实现接口时, 那么Spring AOP 就默认使用 CGLIB 来作为 AOP 代理了.
即如果我们需要为一个方法织入 advice, 但是这个方法不是一个接口所提供的方法, 则此时 Spring AOP 会使用 CGLIB 来实现动态代理.
鉴于此, Spring AOP 建议基于接口编程, 对接口进行 AOP 而不是类.
个人理解及总结:
Aspect是一个面向编程的实现,而它包含了pintcunt(切点)和advice(增强的东西),也就是说,要增强的操作+要增强的位置=切面
而join point 是所有可以增强的点,可以理解为所有的方法。而point cut 则是想要切入的那个方法,也就是切入点。
在通过上述操作之后,就产生了一个目标对象(Target),这个目标对象实际上是一个代理对象(proxied object),是 advice织入之后产生的。
adivce中的前置通知+后置通知=环绕通知
AOP和OOP的不同:
OOP(面向对象编程)是通过封装把不同的功能分散到了不同的对象中去,把类细分,降低了代码的复杂度,变得更加易用和重用,
但是在分散代码的时候,增加了代码的重复性,有时候完全是相同的代码,但是面向对象的设计方式就是将同样的代码放到不同的类中去。
而面向对象的设计使得类和类之间无法联系,也无法将这些代码统一起来。造成了代码的冗余。
然而,如果把这段代码写在一个独立类中的独立的方法中,然后在两个类中调用,那么这两个类就产生了耦合,
并且这段代码的改变会影响两个类。
为了使得这个类可以更加的灵活,也就是在运行的时候可以动态的切入到类的指定方法中。这个指定的位置就是面向切面的编程。
AOP(面向切面编程)针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,
以获得逻辑过程中各部分之间低耦合性的隔离效果。
通过对这些行为的分离,将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。
只有等到需要的时候再切入到对象中去,从而改变原有的行为。否则原对象不会发生变化。
AOP和OOP相比,AOP更像一种“动”的语言,而OOP则是“静”的语言
OOP针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。
AOP是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。
AOP的用处范围:
主要功能:日志记录,性能统计,安全控制,事务处理,异常处理等等
主要意图:将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,
现在使用代码来实现这些操作:
假设这样的一个情形:
现在有超市(SuperMarket)和银行(Bank)两个实体类,在这两个类里面有消费、存款、取款等涉及到金钱的操作,所以我们可以把它们封装起来,得到一个Money的接口,这个接口里面有消费这个方法。
而现在的支付方式又有很多种,如现金、银行卡、在线支付等类型。所以我们之前定义的接口就应该分成不同消费方式的消费方法。
那么我们想要在消费的时候记录金额,打印出消费信息来,应该如何做?
1、我们可以在超市(SuperMarket)和银行(Bank)中添加打印记录的方法。但是打印记录并不是一个对象属性,显得不合理。并且代码冗余。
2、在消费方法执行的时候打印出消费的记录。可是这样的话如果有那天不需要打印了,则需要更改源码。并且这种情况下造成了SuperMarket和Bank中都有相同的代码块,代码冗余
3、在Money接口中封装打印记录的方法,使SuperMarket和Bank去继承它们。然而这种情况下如果我修改了Money接口中的方法,那么就会使得这两个类的源码都要进行修改。这岂不是很乱。
4、使用代理模式,在原有的基础上添加打印日志的方法,这样不影响源码的运行,也可以方便的修改。那么如果我想记录我是在哪里消费的,则就需要两个代理对象。当我可以消费的场所更多的时候,我就相应的要增加更多的代理方法。太麻烦。
那么是否可以按照代理模式的方式,把这串代码弄得动态化呢?这就是面向切面了。下面我们用代码来实现这个操作
我们先分析我们要做的步骤:
1-有一个Money的接口,有两个场所SuperMarket和Bank
2-有要打印的方法,并且分成 前,后两部分
Money.java
package com.facetoaop.interfaces; /**
* 文件名称: com.facetoaop.interfaces.Money.java<br/>
* 初始作者: xyou<br/>
* 创建日期: 2018-2-3<br/>
* 功能说明:消费方式 <br/>
* =================================================<br/>
* 修改记录:<br/>
* 修改作者 日期 修改内容<br/>
* ================================================<br/>
* Copyright (c) 2010-2011 .All rights reserved.<br/>
*/
public interface Money { /**
* 方法描述: [使用现金]<br/>
* 初始作者: Administrator<br/>
* 创建日期: 2018-2-3-上午11:00:17<br/>
* 开始版本: 2.0.0<br/>
* =================================================<br/>
* 修改记录:<br/>
* 修改作者 日期 修改内容<br/>
* ================================================<br/>
*
* @param money
* 使用的金额
* @return
* int 消费金额
*/
int useCase(int money); /**
* 方法描述: [使用银行卡]<br/>
* 初始作者: Administrator<br/>
* 创建日期: 2018-2-3-上午11:01:05<br/>
* 开始版本: 2.0.0<br/>
* =================================================<br/>
* 修改记录:<br/>
* 修改作者 日期 修改内容<br/>
* ================================================<br/>
*
* @param money
* @return
* int
*/
int useBankCard(int money); }
Bank.java和SuperMarket.java
package com.facetoaop.entity; import com.facetoaop.interfaces.Money; /**
* 文件名称: com.facetoaop.entity.Bank.java<br/>
* 初始作者: Administrator<br/>
* 创建日期: 2018-2-3<br/>
* 功能说明: 银行类 <br/>
* =================================================<br/>
* 修改记录:<br/>
* 修改作者 日期 修改内容<br/>
* ================================================<br/>
* Copyright (c) 2010-2011 .All rights reserved.<br/>
*/
public class Bank implements Money { /**
* 用户的姓名
*/
private String name; public Bank() { } public String getName() { return name;
} public void setName(String name) { this.name = name;
} @Override
public int useBankCard(int money) { /**
* 这句输出语句只是为了看的,实际项目中应该没有这句
*/
System.out.println("Bank useBankCard");
return money;
} @Override
public int useCase(int money) { /**
* 这句输出语句只是为了看的,实际项目中应该没有这句
*/
System.out.println("Bank useCase");
return money;
} }
package com.facetoaop.entity; import com.facetoaop.interfaces.Money; /**
* 文件名称: com.facetoaop.entity.SuperMar.java<br/>
* 初始作者: xyou<br/>
* 创建日期: 2018-2-3<br/>
* 功能说明: 超市类 <br/>
* =================================================<br/>
* 修改记录:<br/>
* 修改作者 日期 修改内容<br/>
* ================================================<br/>
* Copyright (c) 2010-2011 .All rights reserved.<br/>
*/
public class SuperMarket implements Money { @Override
public int useBankCard(int money) { /**
* 只是为了校验
*/
System.out.println("SuperMarket useBankCard");
/**
* 假设使用银行卡有打折优惠,9折
*/
money=(int) (money*0.9);
return money;
} @Override
public int useCase(int money) { /**
* 只是为了校验
*/
System.out.println("SuperMarket useCase");
return money;
} }
BeforMoneyAspect.java
package com.facetoaop.aspect; import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; /**
* 文件名称: com.facetoaop.aspect.BeforMoenyAspect.java<br/>
* 初始作者: xyou<br/>
* 创建日期: 2018-2-3<br/>
* 功能说明:消费之前的记录 <br/>
*
* =================================================<br/>
* 修改记录:<br/>
* 修改作者 日期 修改内容<br/>
*
*
* ================================================<br/>
* Copyright (c) 2010-2011 .All rights reserved.<br/>
*/
public class BeforMoenyAspect implements MethodBeforeAdvice { @Override
public void before(Method method, Object[] args, Object obj) throws Throwable {
//method:目标中被执行的方法
//args:方法中传入的参数
//obj:调用目标方法中的那个对象
System.out.println("\n------------开始进行消费-------------------\n");
System.out.println("您在:"+obj.getClass().getSimpleName());
System.out.println("您正准备以:"+method.getName()+"的方式花费");
/**
* 因为知道只有一个参数,所以调用args[0]
*/
System.out.println("您消费的金额为:"+Integer.parseInt(args[0].toString()));
} }
AfterMoneyAspect.java
package com.facetoaop.aspect; import java.lang.reflect.Method; import org.springframework.aop.AfterReturningAdvice; /**
* 文件名称: com.facetoaop.aspect.AfterMoney.java<br/>
* 初始作者: Administrator<br/>
* 创建日期: 2018-2-3<br/>
* 功能说明: 使用钱之后的记录打印 <br/>
* =================================================<br/>
* 修改记录:<br/>
* 修改作者 日期 修改内容<br/>
* ================================================<br/>
* Copyright (c) 2010-2011 .All rights reserved.<br/>
*/
public class AfterMoneyAspect implements AfterReturningAdvice { @Override
public void afterReturning(Object result, Method method, Object[] args, Object obj) throws Throwable { // result:切入点方法的返回值
// method:被调用的目标对象
// args:传入方法的参数
// obj:调用方法的目标对象
System.out.println("刚刚消费的场所为:" + obj.getClass().getSimpleName());
System.out.println("消费的方式为:" + method.getName());
System.out.println("消费金额为:" + result);
System.out.println("\n----------------账单结束----------------------\n");
} }
AroundMoneyAspect.java
package com.facetoaop.aspect; import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation; /**
* 文件名称: com.facetoaop.aspect.AroundMoneyAspect.java<br/>
* 初始作者: xyou<br/>
* 创建日期: 2018-2-3<br/>
* 功能说明: 环绕通知 <br/>
* =================================================<br/>
* 修改记录:<br/>
* 修改作者 日期 修改内容<br/>
* ================================================<br/>
* Copyright (c) 2010-2011 .All rights reserved.<br/>
*/
public class AroundMoneyAspect implements MethodInterceptor { @Override
public Object invoke(MethodInvocation invoke) throws Throwable { // invoke.getArguments():获取调用的方法的参数列表
// invoke.getThis():获取调用方法的对象
// invoke.getMethod():获取当前被调用的方法对象
System.out.println("您在" + invoke.getThis().getClass().getSimpleName() + "中准备消费");
System.out.println("您准备用" + invoke.getMethod().getName() + "的方式消费");
/**
* 因为只有一个参数,可以直接使用
*/
int money = Integer.parseInt(invoke.getArguments()[0].toString());
System.out.println("准备消费的金额为:" + money);
// 调用了被代理类中的目标方法
Object obj = invoke.proceed();
/**
* 假设,如果一个人一次消费满10000就减少100元
*/
if (money >= 10000) {
return money - 100;
}
System.out.println("您在" + invoke.getThis().getClass().getSimpleName() + "中已经消费");
System.out.println("您用" + invoke.getMethod().getName() + "的方式消费"); System.out.println("已经消费的金额为:" + money);
return money;
} }
现在,testForAop.xml中初始值是:
<?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:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:util="http://www.springframework.org/schema/util"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
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/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd"> </beans>
我们要把刚刚的文件都配置进去:
先配置银行和超市对象
<!-- Bank和SuperMarket对象 -->
<bean id="bank" class="com.facetoaop.entity.Bank"></bean>
<bean id="market" class="com.facetoaop.entity.SuperMarket"></bean>
配置切入点
<!-- 配置切入点 -->
<!-- 只需要在执行消费的时候打印。通过正则表达式匹配为了可以适配更多的方法 -->
<!-- 我们应该匹配现金消费和银行卡消费 ,通过相同的前缀匹配-->
<bean id="pointCut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value=".*use.*"></property>
</bean>
配置前置声明,后置声明,环绕声明
<!-- 添加前置声明 -->
<bean id="beforMoneyAspect" class="com.facetoaop.aspect.BeforMoenyAspect"></bean>
<!-- 声明前置的切入点 -->
<bean id="beforMoneyAdvice" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<!-- 全小写的是参数名,不要和驼峰命名的pointCut实例弄混淆 -->
<property name="pointcut" ref="pointCut"></property>
<!-- 应该是使用哪个前置提示 -->
<property name="advice" ref="beforMoneyAspect"></property>
</bean> <!-- 后置声明 -->
<bean id="afterMoneyAspect" class="com.facetoaop.aspect.AfterMoneyAspect"></bean>
<!-- 环绕声明 -->
<bean id="aroundMoneyAspect" class="com.facetoaop.aspect.AroundMoneyAspect"></bean> <!-- 对环绕和后置声明进行配置 -->
<bean id="afterMoneyAdvice" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="pointcut" ref="pointCut"></property>
<property name="advice" ref="afterMoneyAspect"></property>
</bean> <bean id="aroundMoneyAdvice" class="org.springframework.aop.support.DefaultPointcutAdvisor"> <property name="pointcut" ref="pointCut"></property> <property name="advice" ref="aroundMoneyAspect"></property>
</bean>
生成代理对象,所有通知都添加
<!-- 生成代理对象,JDK中方法动态生成 -->
<!-- 生超市的代理对象 -->
<bean id="marketProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 需要被代理的目标对象 -->
<property name="target">
<ref bean="market" />
</property> <!-- 使用哪种切入 -->
<property name="interceptorNames">
<list>
<value>aroundMoneyAdvice</value>
<value>afterMoneyAdvice</value>
<value>beforMoneyAspect</value>
</list>
</property>
</bean> <!-- 生银行的代理对象 -->
<bean id="bankProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 需要被代理的目标对象 -->
<property name="target">
<ref bean="bank" />
</property> <!-- 使用哪种切入 -->
<property name="interceptorNames">
<list>
<value>aroundMoneyAdvice</value>
<value>afterMoneyAdvice</value>
<value>beforMoneyAspect</value>
</list>
</property>
</bean>
最后配置测试文件,查看结果
TestForMonery.java
package com.facetoaop.test; import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; import com.facetoaop.interfaces.Money; /**
* 文件名称: com.facetoaop.test.TestUseMoeny.java<br/>
* 初始作者: xyou<br/>
* 创建日期: 2018-2-3<br/>
* 功能说明:测试方法 <br/>
* =================================================<br/>
* 修改记录:<br/>
* 修改作者 日期 修改内容<br/>
* ================================================<br/>
* Copyright (c) 2010-2011 .All rights reserved.<br/>
*/
public class TestUseMoeny { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("testForAop.xml");
System.out.println(ac); // 通过向上造型为Money,调用的是代理对象,毕竟原来对象也没有打印记录的方法啊
Money marketMoney = (Money) ac.getBean("marketProxy");
Money bankMoney = (Money) ac.getBean("bankProxy"); // 调用现金消费方式
System.out.println("========去银行用银行卡消费1000元==========");
bankMoney.useBankCard(10000);
System.out.println("========去银行用银行卡消费结束==========\n\n\n"); System.out.println("========去超市用银行卡消费10000元==========");
// 如果满一万则减少一百,只有超市有优惠
marketMoney.useBankCard(10000);
System.out.println("========去超市用银行卡消费结束==========\n\n\n"); System.out.println("========去银行用现金消费1000元==========");
bankMoney.useCase(1000);
System.out.println("========去银行用现金消费结束==========\n\n\n"); System.out.println("========去超市用现金消费10000元==========");
marketMoney.useCase(10000);
System.out.println("========去超市用现金消费结束==========\n\n\n");
}
}
运行结果:
========去银行用银行卡消费1000元==========
您在Bank中准备消费
您准备用useBankCard的方式消费
准备消费的金额为:10000 ------------开始进行消费------------------- 您在:Bank
您正准备以:useBankCard的方式花费
您消费的金额为:10000
Bank useBankCard
刚刚消费的场所为:Bank
消费的方式为:useBankCard
消费金额为:10000 ----------------账单结束---------------------- 满10000,账单上少打印100元
========去银行用银行卡消费结束==========
========去超市用银行卡消费10000元==========
您在SuperMarket中准备消费
您准备用useBankCard的方式消费
准备消费的金额为:10000 ------------开始进行消费------------------- 您在:SuperMarket
您正准备以:useBankCard的方式花费
您消费的金额为:10000
SuperMarket useBankCard
刚刚消费的场所为:SuperMarket
消费的方式为:useBankCard
消费金额为:9000 ----------------账单结束---------------------- 满10000,账单上少打印100元
========去超市用银行卡消费结束==========
========去银行用现金消费1000元==========
您在Bank中准备消费
您准备用useCase的方式消费
准备消费的金额为:1000 ------------开始进行消费------------------- 您在:Bank
您正准备以:useCase的方式花费
您消费的金额为:1000
Bank useCase
刚刚消费的场所为:Bank
消费的方式为:useCase
消费金额为:1000 ----------------账单结束---------------------- 您在Bank中已经消费
您用useCase的方式消费
已经消费的金额为:1000
========去银行用现金消费结束==========
========去超市用现金消费1000元==========
您在SuperMarket中准备消费
您准备用useCase的方式消费
准备消费的金额为:10000 ------------开始进行消费------------------- 您在:SuperMarket
您正准备以:useCase的方式花费
您消费的金额为:10000
SuperMarket useCase
刚刚消费的场所为:SuperMarket
消费的方式为:useCase
消费金额为:10000 ----------------账单结束---------------------- 满10000,账单上少打印100元
========去超市用现金消费结束==========
理论参考:Segmentfault,百度百科,ImportNew
[AOP] 之让人一脸蒙哔的面向切面编程的更多相关文章
- Spring AOP——Spring 中面向切面编程
前面两篇文章记录了 Spring IOC 的相关知识,本文记录 Spring 中的另一特性 AOP 相关知识. 部分参考资料: <Spring实战(第4版)> <轻量级 JavaEE ...
- 面向切面编程(Aop)
AOP中的概念 AOP(Aspect Orient Programming),也就是面向切面编程.可以这样理解,面向对象编程(OOP)是从静态角度考虑程序结构,面向切面编程(AOP)是从动态角度考虑程 ...
- 吴裕雄--天生自然JAVA SPRING框架开发学习笔记:Spring AOP(面向切面编程)
面向切面编程(AOP)和面向对象编程(OOP)类似,也是一种编程模式.Spring AOP 是基于 AOP 编程模式的一个框架,它的使用有效减少了系统间的重复代码,达到了模块间的松耦合目的. AOP ...
- Spring学习笔记--面向切面编程(AOP)
什么是AOP AOP(Aspect Oriented Programming),意为面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的 ...
- AOP——面向切面编程
目录 什么是AOP AOP的作用和优势 作用: 优势: AOP相关术语 AOP的实现方式 使用动态代理的方式 使用XML的方式 使用注解的方式 什么是AOP AOP:全称是Aspect Oriente ...
- 依赖注入(DI)有助于应用对象之间的解耦,而面向切面编程(AOP)有助于横切关注点与所影响的对象之间的解耦(转good)
依赖注入(DI)有助于应用对象之间的解耦,而面向切面编程(AOP)有助于横切关注点与所影响的对象之间的解耦.所谓横切关注点,即影响应用多处的功能,这些功能各个应用模块都需要,但又不是其主要关注点,常见 ...
- 转:面向切面编程AOP的理解
AOP AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善.OOP引入 ...
- 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之十 || AOP面向切面编程浅解析:简单日志记录 + 服务切面缓存
代码已上传Github+Gitee,文末有地址 上回<从壹开始前后端分离[ .NET Core2.0 Api + Vue 2.0 + AOP + 分布式]框架之九 || 依赖注入IoC学习 + ...
- AOP面向切面编程C#实例
原创: eleven 原文:https://mp.weixin.qq.com/s/8klfhCkagOxlF1R0qfZsgg [前言] AOP(Aspect-Oriented Programming ...
随机推荐
- vue组件,axios ,路由
组件(Component)自定义封装的功能. 把一个功能相关的[HTML.css和javascript]代码封装在一起组成一个整体的代码块封装模式,我们称之为“组件”. 组件就是一个html网页中的功 ...
- Django中模型层中ORM的单表操作
ORM概念: MVC或者MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库,这极大的减轻了开发人员 ...
- Spring boot @Autowired注解在非Controller中注入为null
参考链接:https://blog.csdn.net/qq_35056292/article/details/78430777
- rsync未授权访问漏洞利用
漏洞描述:rsync是Linux系统下的数据镜像备份工具,使用快速增量备份工具Remote Sync可以远程同步,支持本地复制,或者与其他ssh,rsync主机同步.也就是说如果你可以连接目标IP的r ...
- svn: 提交终止
今天我遇到了svn 的问题 svn: 提交终止: “/var/www/modelfinance/modules/incomereport/views/purchase” 处于冲突状态 冲突状态搞的我 ...
- Http简单解析过程
1.域名解析:浏览器先搜索自身的DNS缓存->搜索操作系统自身的DNS缓存(浏览器没有找到缓存或缓存已经失效)->读取本地host文件(操作系统DNS也没找到)->浏览器发起DNS的 ...
- SQL - 数据查询
数据查询是数据库的核心操作.SQL 提供了 select 语句进行数据查询,该语句的一般格式为: select [ ALL | distinct ] <目标列表达式> [ ,<目 ...
- 集成学习二: Boosting
目录 集成学习二: Boosting 引言 Adaboost Adaboost 算法 前向分步算法 前向分步算法 Boosting Tree 回归树 提升回归树 Gradient Boosting 参 ...
- 【LeetCode】二分查找
给一个升序数组,找到目标值在数组中的起始和结束位置,时间复杂度为 O(log n). e.g. 给定数组 [5, 7, 7, 8, 8, 10] 和目标值 8,返回 [3, 4].若目标值不在数组中, ...
- vue组件通信&&v兄弟组件通信eventbus遇到的问题(多次触发、第一次不触发)
组件通讯包括:父子组件间的通信和兄弟组件间的通信.在组件化系统构建中,组件间通信必不可少的 (vuex以后再说). 父组件--> 子组件 1. 属性设置 父组件关键代码如下: <templ ...