文章目录
AOP
1.1AOP的作用及优势
作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强
优势:减少代码重复,提高开发效率,并且便于维护
1.2AOP的底层实现
实际上,AOP的底层是通过Spring提供的动态代理技术实现的,在运行期间,Spring通过动态代理技术动态生成代理对象,代理对象方法执行时进行功能增强的介入,在去调用目标对象方法,从而完成功能的增强
1.3动态代理技术
常用的动态代理技术
- JDK:基于接口的动态代理技术
1.定义一个接口及实现类(实现了相同的接口)
package com.blb.com.jdk;
public interface Tar {
public void say();
}
package com.blb.com.jdk;
public class TarImpl implements Tar {
@Override
public void say() {
System.out.println("hollow word");
}
}
2.增强方法
package com.blb.com.jdk;
public class Add {
public void before(){
System.out.println("代理前置增强");
}
public void afer()
{
System.out.println("代理后置增强");
}
}
3.实现代理
package com.blb.com.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Text {
public static void main(String[] args) {
final Tar tar=new TarImpl();//目标对象
final Add add=new Add();//增强对象
//返回值为动态生成的代理对象
Tar o = (Tar) Proxy.newProxyInstance(
tar.getClass().getClassLoader(),//目标对象的类加载器
tar.getClass().getInterfaces(),//目标对象相同的接口字节码对象数组
new InvocationHandler() {
//调用代理对象的任何方法,实质上都是执行invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
add.before();//前置增强
method.invoke(tar, args);//目标方法
add.afer();//后置增强
return null;
}
}
);
o.say();
}
}
- cglib:基于父类发动态代理技术(继承关系)
1.原实现类
package com.blb.com.cglib;
public class Tar {
public void say() {
System.out.println("hollow word");
}
}
2.增强类
package com.blb.com.cglib;
public class Add {
public void before(){
System.out.println("代理前置增强");
}
public void afer()
{
System.out.println("代理后置增强");
}
}
3.实现代理
package com.blb.com.cglib;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class Text {
public static void main(final String[] args) {
final Tar tar=new Tar();
final Add add=new Add();//增强对象
//1.创建增强器
Enhancer enhancer =new Enhancer();
//2.设置父类
enhancer.setSuperclass(Tar.class);
//3.设置回调
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
add.before();//执行前置
Object invoke = method.invoke(tar, args);//执行目标
add.afer();//执行后置
return invoke;
}
});
Tar o =(Tar) enhancer.create();//创建代理对象
o.say();
}
}
1.4AOP相关概念
Spring的AOP实现底层就是对上面的动态代理的代码实现的封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。
AOP的常用术语
Target(目标对象):代理的目标对象
Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
Joinpoint(连接点):所谓的连接点是指哪些被拦截到的点。在Spring中,这些点指的是方法,因为spring只支持方法类型的连接点
Pointcut(切入点):所谓的切入点是指我们要对哪些Jointpoint进行拦截的定义
Advice(通知/增强):所谓通知是指拦截到Jointpoint之后所要做的事情就是通知
Aspect(切面):是切入点和通知的结合
Weaving(织入):是指吧增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,二AspectJ采用编译器织入和类装载期织入
1.5springAOP开发明确的事项
1.5.1需要编写的内容
- 编写核心业务代码(目标类的目标方法)
- 编写切面类,切面类中有通知(增强功能方法)
- 在配置文件中,配置织入关系,即将哪些通知与哪些连接点进行结合
1.5.2AOP技术实现的内容
Spring框架监控切入点方法的执行,一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将=通知的对应功能织入,完成完整的代码逻辑运行
1.5.3AOP底层使用哪种代理方式
在spring中,框架会根据目标类是否实现了接口来决定采用哪种动态代理方式
1.6知识要点
- aop:面向切面编程
- aop底层实现:基于jdk动态代理和基于cglib的动态代理
- aop是重点概念:
Pointcut(切入点):被增强的方法
Advice(通知/增强):封装增强业务逻辑的方法
Aspect(切面):切点+通知
Weaving(织入):将切点与通知结合的过程 - 开发明确事项:
谁是切点(切点表达式配置)
谁是通知(切面类中的增强方法)
将切点和通知进行织入配置
2.基于xml的AOP的开发
2.1快速入门
- 导入AOP相关坐标
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
- 创建目标接口和目标类(内部有切点)
- 创建切面类(内部有增强方法)
- 将目标类和切面的创建权交给spring
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd ">
<!--目标对象 -->
<bean id="target" class="comm.blb.Aop.TargetImpl"></bean>
<!-- 切面对象-->
<bean id="myAspect" class="comm.blb.Aop.MyAspect"></bean>
<!-- 配置织入:告诉是sping框架 哪些方法(切点)需要进行增强(前置、后置)-->
<aop:config>
<!--声明切面 -->
<aop:aspect ref="myAspect">
<!--切面:切点+通知 -->
<aop:before method="before" pointcut="execution(public void comm.blb.Aop.TargetImpl.say())"/>
<aop:after method="af" pointcut="execution(public void comm.blb.Aop.TargetImpl.say())"></aop:after>
</aop:aspect>
</aop:config>
</beans>
- 在applicationContext.xml中配置织入关系
- 测试代码
2.2xml配置AOP详解
1切点表达式的写法
表达式语法:
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
- 访问修饰符可以省略
- 返回值类型、 包名.类名.方法名可以使用星号*代表任意
- 包名与类名一个点.代表当前包下的类,两个点…表示当前包及其子包下的类
- 参数列表可以使用两个点…表示任意个数,任意类型的参数列表
2通知类型
通知的配置语法:
<aop:通知类型 method="切面类中的方法名" pointcut="切点表达式"/><?aop:通知类型>
名称 | 标签 | 说明 |
---|---|---|
前置通知 | before | 用于配置前置通知,指定增强方法在切入点之前执行 |
后置通知 | after-returning | 用于配置后置通知,指定增强方法在切入点之后执行 |
环绕通知 | around | 用于配置环绕通知,指定增强方法在切入点之前和之后执行 |
异常抛出通知 | throwing | 用于配置异常抛出通知,指定增强方法在出现异常时执行 |
最终通知 | after | 用于配置最终通知,无论增强方式 是否有异常都会通知 |
3切点表达式抽取
当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用pointcut-ref属性代替pointcut属性来引用抽取后的切点表达式
<aop:config>
<!--声明切面 -->
<aop:aspect ref="myAspect">
<!--切面:切点+通知 -->
<aop:pointcut id="point" expression="execution(public void comm.blb.Aop.TargetImpl.say())"/>
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="af" pointcut-ref="point"></aop:after>
</aop:aspect>
</aop:config>
3.基于注解的AOP开发
3.1快速入门
基于注解的Aop开发步骤
- 创建目标接口和目标类(内部有切点)
- 创建切面类(内部有增强方法)
- 将目标类和切面的创建权交给spring
- 在切面类中使用注解配置织入关系
- 在配置文件中开启组件扫描和AOP的自动代理
- 测试
目标类
package com.blb.anno;
import comm.blb.Aop.Target;
import org.springframework.stereotype.Component;
@Component("target")
public class TargetImpl implements Target {
public void say() {
System.out.println("Target...");
}
}
接口
package com.blb.anno;
public interface Target {
public void say();
}
切面
package com.blb.anno;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component("myAspect")
@Aspect//当前
public class MyAspect {
@Before("execution(public void com.blb.anno.TargetImpl.say())")
public void before(){
System.out.println("前置增强方法");
}
}
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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.blb"></context:component-scan>
<!-- aop自动代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
测试类
package com.blb.text;
import comm.blb.Aop.Target;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applica.xml")
public class annoText {
@Autowired
private Target target;
@Test
public void text1(){
target.say();
}
}