AOP
-
什么是AOP
- 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
- AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码
- 经典应用:性能监视,事务管理,安全检查,缓存,日志等
- Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期间通过代理方式向目标类织入增强代码
- AspectJ是一个基于Java语言的AOP框架,从Spring2.0开始,Spring AOP引入对AspectJ的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,子啊编译时提供横向代码的织入
-
AOP实现原理
- AOP底层将采用代理机制进行实现。
- 接口+实现类:Spring 采用JDK的动态代理Proxy
- 实现类:Spring采用cglib字节码增强
-
AOP术语
- target:目标类,需要被代理的类:如UserService
- Joinpoint(连接点):所有连接点是指那些可能被拦截的方法。例如:所有的方法
- PoinCut 切入点:已经被增强的连接点。例如:addUser()
- advice 通知/增强:增强代码。after、before
- Weaving(织入):是指把增强advice应用到目标对象target来创建新的代理对象proxy的过程
- proxy代理类
- Aspect(切面):是切入点pointcut和通知advice的结合
- 一个线是一个特殊的面
- 一个切入点和一个通知,组成一个特殊的面
-
手动代理
-
JDK动态代理
-
JDK动态代理对“装饰着”设计模式简化。使用前提:必须有接口
- 目标类:接口+实现类
- 切面类:用于存通知MyAspect
- 工厂类:编写工厂生成代理
- 测试
-
目标类
public interface UserService { void addUser(); void updateUser(); void deleteUser(); } 复制代码
-
切面类
public class MyAspect { public void before(){ System.out.println("鸡头"); } public void after(){ System.out.println("凤尾"); } } 复制代码
-
工厂
public class MyBeanFactory { public static UserService createService() { // 1,目标类 final UserService userService = new UserServiceImpl(); // 2,切面类 final MyAspect myAspect = new MyAspect(); /** * 3 代理类:将目标类和切面类结合==》切面 * Proxy.newProxyInstance * 参数1:loader,类加载器,动态代理类运行时创建,任何类都需要加载器将其加载到内存 * 一般情况:当前类.class.getClassLoader(); * 目标类实例.getClass().get... * 参数2:interfaces 代理类需要实现的所有接口 * 方式1:目标实例.getClass().getInterfaces();注意:只能获得自己接口,不能获得父类元素接口 * 方式2:new Class[]{UserService.class} * 参数3:InvocationHandler 处理类,接口,必须进行实现类,一般采用匿名内部类 * 提供invoke方法,代理类的每一个方法执行时,都将调用一次invoke * 参数1:Oject proxy:代理对象 * 参数2:Method method:代理对象当前执行的方法的描述(反射) * 执行方法名:method.getName() * 执行方法:method.invoke(对象,实际参数) * 参数3:Object[] args:方法实际参数 */ UserService proxService = (UserService) Proxy.newProxyInstance( MyBeanFactory.class.getClassLoader(), userService.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 将目标类和切面类结合 //前执行 myAspect.before(); //执行目标方法 Object obj = method.invoke(userService,args); //后方法 myAspect.after(); return obj; } }); return proxService; } } 复制代码
-
测试
@Test public void demo(){ UserService userService = MyBeanFactory.createService(); userService.addUser(); userService.updateUser(); userService.deleteUser(); } 运行结果: 鸡头 addUser 凤尾 鸡头 updateUser 凤尾 鸡头 deleteUser 凤尾 复制代码
-
-
CGLIB字节码增强
-
没有接口,只有实现类
-
采用字节码增强框架cglib,在运行时,创建目标类的子类,从而对目标类进行增强
-
导入jar包:
- 手动方式(连接)
- 核心:cglib.jar
- 依赖:asm.jar
- spring-core.jar:已经整合了这两个内容
- 手动方式(连接)
-
实现
public class MyBeanFactory { public static UserServiceImpl createService() { // 1,目标类 final UserServiceImpl userService = new UserServiceImpl(); // 2,切面类 final MyAspect myAspect = new MyAspect(); /** * 3.代理类,采用chlib,底层创建目标类的子类 */ // 3.1核心类 Enhancer enhancer = new Enhancer(); // 3.2 确定父类 enhancer.setSuperclass(userService.getClass()); /** 3.3设置回调函数 等效 invocationHandler * intercept()等效jdk invoke() * 参数1,2,3与invoke一样 * 参数4:方法的代理 */ enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { //前方法 myAspect.before(); // 执行目标类的方法 Object obj = method.invoke(userService, objects); // 执行代理类的父类,执行目标类(目标类和代理类 父子关系) methodProxy.invokeSuper(o,objects); //后方法 myAspect.after(); return obj; } }); // 3.4创建代理 UserServiceImpl proxService = (UserServiceImpl) enhancer.create(); return proxService; } } 复制代码
-
-
Spring AOP增强类型
- Spring按照通知在Advice在目标类方法的连接点位置,可以分为5类
- 前置通知:在目标方法执行前实施增强
- 后置通知:在目标方法执行后实施增强
- 环绕通知:在目标方法执行前后实施增强
- 异常抛出通知:在方法抛出异常后实施增强
- 引介通知:在目标类中添加一些新的方法和属性
- Spring按照通知在Advice在目标类方法的连接点位置,可以分为5类
-
//环绕通知,必须手动执行目标方法
try{
//前置通知
//执行目标方法
//后置通知
} catch(){
//抛出异常通知
}
复制代码
-
spring代理:半自动
-
让spring去创建代理对象,从spring容器中手动获取代理对象
-
导入jar包:
- 核心:4+1
- AOP:AOP联盟(规范)、spring-aop.jar
-
目标类
public interface UserService { void addUser(); void updateUser(); void deleteUser(); } 复制代码
-
切面类
/** * 切面类中确定通知,需要实现不同的接口,接口就是规范,从而确定方法名称。 * MethodInterceptor 环绕通知 */ public class MyAspect implements MethodInterceptor { @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { System.out.println("前"); //手动执行目标方法 Object obj = methodInvocation.proceed(); System.out.println("后"); return obj; } } 复制代码
-
xml配置
<!--创建目标类--> <bean id="userServiceId" class="com.adolph.AOP.jdk.UserServiceImpl"></bean> <!--创建切面类--> <bean id="myAspectId" class="com.adolph.AOP.jdk.MyAspect"></bean> <!--创建代理类 * 使用工厂bean Factory Bean ,底层调用getObject() 放回特殊bean ProxyFactoryBean 用于创建代理工厂bean,生成特殊代理对象 interfaces:确定接口们 通过<array>可以设置多个值 只有一个值时:value="" target:确定目标类 interceptorNames:通知切面类的名称,类型String[],如果设置一个值value="" optimize:强制使用cglib 底层机制 如果目标类有接口,采用jdk动态代理 如果没有接口,采用cglib字节码增强 如果声明optimize = true,无论是否有接口,都采用cglib --> <bean id="proxyServiceId" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="interfaces" value="com.adolph.AOP.jdk.UserService"></property> <property name="target" ref="userServiceId"></property> <property name="interceptorNames" value="myAspectId"></property> <property name="optimize" value="true"></property> 复制代码
-
测试
@Test public void demo(){ String xmlPath = "com/adolph/AOP/jdk/beans.xml"; ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); UserServiceImpl userService = applicationContext.getBean("proxyServiceId",UserServiceImpl.class); userService.addUser(); userService.updateUser(); userService.deleteUser(); } 复制代码
-
-
spring aop编程:全自动【掌握】
-
从spring容器中获得目标类,如果配置了aop,spring将自动生成代理
-
要确定目标类,aspectj 切入点表达式,导jar包
-
xml
<!--创建目标类--> <bean id="userService" class="com.adolph.AOP.jdk.UserServiceImpl"></bean> <!--创建切面类--> <bean id="myAspect" class="com.adolph.AOP.jdk.MyAspect"></bean> <!--aop编程 使用<aop:config>进行配置 proxy-target-class="true":使用cglib代理 <aop:pointcut>切入点,从目标对象获得具体方法 <aop:advisor> 特殊的切面,只有一个通知和一个切入点 advice-ref:通知引用 pointcut-ref:切入点引用 切入点表达式: execution(* com.adolph.AOP.jdk.*.*(..)) 选择方法 返回值任意 包 类名任意 方法名任意 参数任意 --> <aop:config proxy-target-class="true"> <aop:pointcut id="myPointCut" expression="execution(* com.adolph.AOP.jdk.*.*(..))"/> <aop:advisor advice-ref="myAspect" pointcut-ref="myPointCut"/> </aop:config> 复制代码
-
测试
@Test public void demo(){ String xmlPath = "com/adolph/AOP/jdk/beans.xml"; ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); //返回的时目标类,但是获得的时代理类 UserServiceImpl userService = applicationContext.getBean("userService",UserServiceImpl.class); userService.addUser(); userService.updateUser(); userService.deleteUser(); } 复制代码
-