目录
什么是AOP
AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,AOP解决个层的公共问题,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能无关,这种散布在各处的无关的代码被称为横切(cross cutting),它导致了大量代码的重复,而不利于各个模块的重用。AOP技术利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
AOP核心概念
1、横切关注点
对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点
2、切面(aspect)
类是对物体特征的抽象,切面就是对横切关注点的抽象
3、连接点(joinpoint)
被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器
4、切入点(pointcut)
对连接点进行拦截的定义
5、通知(advice)
所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类
6、目标对象
代理的目标对象
7、织入(weave)
将切面应用到目标对象并导致代理对象创建的过程
8、引入(introduction)
在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段
代理模式
在AOP编程是会用到一些代理的设计模式。代理就是用另一个类去管理你要使用的类,就像房屋中介一样,你租房子不再需要联系房东,房东把房子交给中介,中介打理一切,包括装修、卫生、发广告和加价。代理模式把类交给代理类,类所需要的日志、异常处理,对象声明等于业务无关的就都可以交给代理类去做,使用的时候直接找代理去要,其他事就都不用操心。
静态代理
还是我步枪的例子
步枪接口
public interface Gun {
void name();
}
中正式实现类
public class Zhongzheng implements Gun{
public void name(){
System.out.print("中正式");
}
}
代理类
public class Proxy {
private Gun gun;
public void useGun() {
System.out.println("前增强");
gun.name();
System.out.println("后增强");
}
public void setGun(Gun gun) {
this.gun = gun;
}
}
使用方式
Proxy proxy=new Proxy();
proxy.setGun(new Zhongzheng());
proxy.useGun();
一个很简单的静态代理就完成了,感觉作用不大,都已经明确指定类和方法,没个类都得写相应的代理,所以使用动态代理
jdk方式
jdk自带的,可以实现动态代理,通过编写代理类的生成类在内存中动态生成代理类
public class GunProxy implements InvocationHandler{
Object obj;
public Object getProxy(){
//参数:类加载器,类对象数组,InvocationHandler
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),new Class[]{obj.getClass().getInterfaces()[0]},this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.print("前缀");
Object invoke = method.invoke(this.obj, args);//调用目标对象的方法
System.out.println("后缀");
return invoke;
}
public void setGun(Object obj){
this.obj=obj;
}
}
根据接口创建类,只能是通过接口,必须有接口
GunProxy gp=new GunProxy();
gp.setGun(new Zhongzheng());
Gun zz=(Gun)gp.getProxy();
zz.name();
Cglib方式
需要导入Spring框架的相关包
public class GunCgProxy implements MethodInterceptor{
Object obj;
public Object getProxy(){
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(this.obj.getClass());//设置父类的Class对象
enhancer.setCallback(this);
return enhancer.create();
}
public void setGun(Object obj){
this.obj=obj;
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.print("前缀");
Object invoke = method.invoke(this.obj, args);//调用目标对象的方法
System.out.println("后缀");
return invoke;
}
}
这种方式是依靠继承实现代理,所以被代理的类必须是可继承的类,与接口无关,使用时候可选择用接口也可选择用类。
GunCgProxy gp=new GunCgProxy();
gp.setGun(new Zhongzheng());
Gun zz=(Gun)gp.getProxy();
zz.name();
输出结果都是“前缀中正式后缀”
AOP实现动态代理
默认JDK的动态代理,分别编写增强类和目标类,用AOP的方式让增强类去增强目标类
增强类
public class CodeAdvice implements MethodBeforeAdvice,AfterReturningAdvice {
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("后增强");
}
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("前增强");
}
}
编程式增强
手动new工厂对象
ProxyFactory proxyFactory=new ProxyFactory();//代理工厂 生产代理对象
proxyFactory.addAdvice(new CodeAdvice());//获取代理对象
proxyFactory.setTarget(new Zhongzheng());
Gun gun=(Gun)proxyFactory.getProxy();
gun.name();
声明式增强
在xml中实例,由于增强类继承那俩接口,所以Spring能认得出,可以直接用配置实例化,否则需要用aop标签
<bean id="m91" class="service.Gunimp.M91"></bean>
<bean id="advice" class="proxy.CodeAdvice"></bean>
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="m91"></property>
<property name="interceptorNames" value="advice"></property>
</bean>
aop标签
<aop:config>
<aop:pointcut id="pointcut" expression="execution(public void name())"></aop:pointcut>
<aop:aspect ref="advice">
<aop:before method="beforefire" pointcut-ref="pointcut"></aop:before>
<aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="o"></aop:after-returning>
</aop:aspect>
</aop:config>
需要注意的是这两种方式会冲突,选一种用。
注解式增强
@Component:实现Bean组件的定义
@Repository:用于标注DAO类
@Service:用于标注业务类
@Controller:用于标注控制器
这几个注解就是实例化的作用,仅长得不一样,标错类对程序无影响。
增强类,aop配置
@Component
@Aspect
public class MyGun {
@Pointcut("execution(* service.Gunimp.*.*(..))")
void pointcut(){}
@Before("pointcut()")
public void beforefire(JoinPoint jp) {
System.out.print("前增强--");
}
@AfterReturning(pointcut="pointcut()",returning = "result")
public void afterfire(JoinPoint jp,Object result){
System.out.print("--后增强");
}
}
目标类
@Service
public class Zhongzheng implements Gun{
public void name(){
System.out.print("中正式");
}
}
配置xml
<context:component-scan base-package="service"/>
<context:component-scan base-package="config"/>
<aop:aspectj-autoproxy/>
测试
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
Gun g=app.getBean(Gun.class);
g.name();
多种增强
环绕增强
增强类
public class AroundAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.print("环绕增强(");
Object proceed = invocation.proceed();
System.out.println(")环绕增强");
return proceed;
}
}
异常抛出增强
增强类
public class ExceptionAdvice implements ThrowsAdvice {
public void myadvice(JoinPoint jp, RuntimeException e){
System.out.println(jp.getSignature().getName()+"方法抛出了异常:"+e.getClass().getSimpleName());
}
}
最终增强
增强类
public class AfterAdvice implements org.springframework.aop.AfterAdvice {
public void after(JoinPoint jp){
System.out.println("最终增强---,方法名为:"+jp.getSignature().getName());
}
}
上述三种注解分别是@Around @AfterThrowing @After 里边放你的@Pointcut下指定的方法,异常抛出多个异常属性。