您现在的位置是:首页 > 文章详情

初识 Spring(07)---(AOP)

日期:2018-08-07点击:304

AOP

参照《Spring思维导图,让Spring不再难懂(aop篇)》

AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。

面向对象的特点是继承、多态和封装。而封装就要求将功能分散到不同的对象中去,这在软件设计中往往称为职责分配。实际上也就是说,让不同的类设计不同的方法。这样代码就分散到一个个的类中去了。这样做的好处是降低了代码的复杂程度,使类可重用。      

但是人们也发现,在分散代码的同时,也增加了代码的重复性。比如说,我们在两个类中,可能都需要在每个方法中做日志。按面向对象的设计方法,我们就必须在两个类的方法中都加入日志的内容。也许他们是完全相同的,但就是因为面向对象的设计让类与类之间无法联系,而不能将这些重复的代码统一起来。    

也许有人会说,那好办啊,我们可以将这段代码写在一个独立的类独立的方法里,然后再在这两个类中调用。但是,这样一来,这两个类跟我们上面提到的独立的类就有耦合了,它的改变会影响这两个类。那么,有没有什么办法,能让我们在需要的时候,随意地加入代码呢?

这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。      

一般而言,我们管切入到指定类指定方法的代码片段称为切面,而切入到哪些类、哪些方法则叫切入点。有了AOP,我们就可以把几个类共有的代码,抽取到一个切片中,等到需要时再切入对象中去,从而改变其原有的行为。这样看来,AOP其实只是OOP的补充而已。OOP从横向上区分出一个个的类来,而AOP则从纵向上向对象中加入特定的代码。有了AOP,OOP变得立体了。如果加上时间维度,AOP使OOP由原来的二维变为三维了,由平面变成立体了。从技术上来说,AOP基本上是通过代理机制实现的。      AOP在编程历史上可以说是里程碑式的,对OOP编程是一种十分有益的补充。

OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,

在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

而AOP技术则允许你定义从左到右的关系,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。

描述AOP常用的一些术语有通知(Adivce)、切点(Pointcut)、连接点(Join point)、切面(Aspect)、引入(Introduction)、织入(Weaving)、通知(Advice)等。

aop使用场景

aop框架种类

  • AspectJ

  • JBoss AOP

  • Spring AOP

使用aop可以做的事情有很多。

  • 性能监控,在方法调用前后记录调用时间,方法执行太长或超时报警。

  • 缓存代理,缓存某方法的返回值,下次执行该方法时,直接从缓存里获取。

  • 软件破解,使用AOP修改软件的验证类的判断逻辑。

  • 记录日志,在方法执行前后记录系统日志。

  • 工作流系统,工作流系统需要将业务代码和流程引擎代码混合在一起执行,那么我们可以使用AOP将其分离,并动态挂接业务。

  • 权限验证,方法执行前验证是否有权限执行当前方法,没有则抛出没有权限执行异常,由业务代码捕捉。

观察一下传统编码方式与使用aop的区别

两种动态代理方式

1:JDK 通过proxy对象和InvocationHandler对象
2:CGlib 通过操纵二进制字节码的方式产生动态代理

Spring默认采取的动态代理机制实现AOP,当动态代理不可用时(代理类无接口)会使用CGlib机制。

Spring提供了两种方式来生成代理对象: JDKProxy和Cglib,具体使用哪种方式生成由AopProxyFactory根据AdvisedSupport对象的配置来决定。默认的策略是如果目标类是接口,则使用JDK动态代理技术,否则使用Cglib来生成代理。

JDK动态代理

  • JDK动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler。InvocationHandler是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编制在一起。

  • Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象。

CGLib动态代理

  • CGLib全称为Code Generation Library,是一个强大的高性能,高质量的代码生成类库,可以在运行期扩展Java类与实现Java接口,CGLib封装了asm,可以再运行期动态生成新的class。和JDK动态代理相比较:JDK创建代理有一个限制,就是只能为接口创建代理实例,而对于没有通过接口定义业务方法的类,则可以通过CGLib创建动态代理。

构建项目

文件目录

applicationContext.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: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/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd"> <context:component-scan base-package="com.neuedu"></context:component-scan> <!-- 自动生成代理类 --> <aop:aspectj-autoproxy> </aop:aspectj-autoproxy> </beans> 

LoggingAspect.java

package com.neuedu.aspect; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Component @Aspect public class LoggingAspect { @Before("execution(public void com.neuedu.service.UserService.save())") //执行这个注解之后,下面方法就会在一些方法执行之前执行 public void before(){ System.out.print("方法执行之前..."); } } 

UserService.java

package com.neuedu.service; import org.springframework.stereotype.Service; @Service public class UserService { public void save() { System.out.println("调用Dao层保存用户信息"); } } 

Test.java

package com.neuedu.aspect; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.neuedu.service.UserService; public class Test { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); UserService us = ac.getBean(UserService.class); us.save(); } } 

输出:

修改代码:

UserService.java

package com.neuedu.service; import org.springframework.stereotype.Service; @Service public class UserService { public void save() { System.out.println("调用Dao层保存用户信息"); } public void delete(String username){ //添加代码 System.out.println("删除" + username); //添加代码 } } 

LoggingAspect.java

package com.neuedu.aspect; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Component @Aspect public class LoggingAspect { @Before("execution(public void com.neuedu.service.UserService.*(..))") //修改代码 //执行这个注解之后,下面方法就会在一些方法执行之前执行 public void before(){ System.out.print("方法执行之前..."); } } 

Test.java

package com.neuedu.aspect; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.neuedu.service.UserService; public class Test { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); UserService us = ac.getBean(UserService.class); //us.save(); us.delete("zhang"); //修改代码 } } 

输出:

修改代码:新建 GoodsServiec.java

package com.neuedu.service; import org.springframework.stereotype.Service; @Service public class GoodsService { public void save() { System.out.println("保存商品信息..."); } } 

Test.java

package com.neuedu.aspect; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.neuedu.service.GoodsService; import com.neuedu.service.UserService; public class Test { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); UserService us = ac.getBean(UserService.class); us.save(); //us.delete("zhang"); GoodsService gs = ac.getBean(GoodsService.class); //新增代码 gs.save(); //新增代码 } } 

输出:

@Aspect    //整个叫做切面类
public class LoggingAspect {
    @Before("execution(public void com.neuedu.service.UserService.*(..))")  //切入点语法 ,确定切入点位置,即所有类的所有方法(before 通知)
@Before("execution(* com.neuedu.service..*(..))")    //终版,扫描该包下的所有子包和所有类的所有方法
    public void before(){      //这个方法叫做切入点 
        System.out.print("方法执行之前...");
    }

修改代码:

LoggingAspect.java

package com.neuedu.aspect; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Component @Aspect //切面类 public class LoggingAspect { @Before("execution(public void com.neuedu.service.*.*(..))") //执行这个注解之后,下面方法就会在一些方法执行之前执行 public void before(){ System.out.print("方法执行之前..."); } @After("execution(public void com.neuedu.service.*.*(..))") //新增代码 public void after() { System.out.println("执行方法之后..."); } @AfterReturning("execution(public void com.neuedu.service.*.*(..))") //新增代码 public void afterReturning() { System.out.println("只有方法正确执行之后才会执行..."); } @AfterThrowing("execution(public void com.neuedu.service.*.*(..))") //新增代码 public void afterThrow() { System.out.println("当方法抛出异常时调用的方法"); } } 

UserServiceImpl.java

package com.neuedu.service; import org.springframework.stereotype.Service; @Service public class UserServiceImpl implements UserService{ public void save() { System.out.println("调用Dao层保存用户信息"); } public void delete(String username){ System.out.println("删除" + username); } @Override public int div(int a, int b) { //新增代码 int c = a / b; return c; } } 

UserService.java

package com.neuedu.service; import org.springframework.stereotype.Service; public interface UserService { public void save() ; public void delete(String username); public int div(int a,int b); //新增代码 } 

Test.java

package com.neuedu.aspect; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.neuedu.service.GoodsService; import com.neuedu.service.UserService; public class Test { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); UserService us = ac.getBean(UserService.class); //us.save(); //us.delete("zhang"); GoodsService gs = ac.getBean(GoodsService.class); gs.save(); us.div(2, 1); //新增代码 } } 

输出:

@Before("execution(public void com.neuedu.service.*.*(..))")
    //执行这个注解之后,下面方法就会在一些方法执行之前执行


    @After("execution(public void com.neuedu.service.*.*(..))")

 //执行这个注解之后,下面方法就会在一些方法执行之后执行

    @AfterReturning("execution(public void com.neuedu.service.*.*(..))")

 //执行这个注解之后,下面方法只有方法正确执行之后才会执行

    @AfterThrowing("execution(public void com.neuedu.service.*.*(..))")

 //执行这个注解之后,下面方法只有当方法抛出异常时才会执行
 

原文链接:https://yq.aliyun.com/articles/623905
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章