Spring总结3—AOP

来源:互联网 发布:淘宝客点击要钱吗 编辑:程序博客网 时间:2024/06/05 14:07

一、AOP概念及原理

1、AOP概念:
定义:将程序中的交叉业务逻辑提取出来,称之为切面。将这些切面动态织入到目标对象,然后生成一个代理对象的过程。
AOP(Aspect-Oriented Programming,面向切面编程)是一种程序设计思想,该思想的主要目标是将系统分为两部分:一部分封装了系统中各组件的通用功能(罗辑或者责任),形成一个切面,该切面被称为应用系统的”横切关注点”,此部分包含了与系统核心商业逻辑关系不大的部分;系统的其余部分,即核心商业逻辑部分,被称为系统的”核心关注点”,此部分包含了系统中业务处理的主要流程。其中,”横切关注点”经常出现在”核心关注点”的周围,但无论出现在何处,它们的基本功能是相似的,比如权限认证、日志、事务处理等。


2、面向切面编程思维的好处
一方面可以使我们在系统开发过程中的责任分工更为明确(比如,可以让高级工程师负责”横切关注点”的开发,初级工程师负责”核心关注点”的开发,配置工程师负责将以上两部分搭建成一个完整的应用系统);另一方面,清晰的系统结构将使我们以后的系统维护工作变得更为轻松。
总述:可以动态的添加和删除在切面上的逻辑而不影响原来的执行代码。


3、工作原理—动态代理

动态代理原理:动态代理原理其实就是反射+多态+聚合的实现。JDK 5引入的动态代理机制,允许开发人员在运行时刻动态的创建出代理类及其对象。在运行时刻,可以动态创建出一个实现了多个接口的代理类。每个代理类的对象都会关联一个表示内部处理逻辑的InvocationHandler接 口的实现。当使用者调用了代理对象所代理的接口中的方法的时候,这个调用的信息会被传递给InvocationHandler的invoke方法。在 invoke方法的参数中可以获取到代理对象、方法对应的Method对象和调用的实际参数。invoke方法的返回值被返回给使用者。这种做法实际上相 当于对方法调用进行了拦截。
具体实现案例:
创建代理器,让其实现InvocationHandler接口

public class LogInterceptor implements InvocationHandler{    //log日志要添加一个目标    private Object target;    public Object getTarget() {        return target;    }    public void setTarget(Object target) {        this.target = target;    }    public void beforeMethod(Method method){        System.out.println(method.getName()+"start!");    }    public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {        beforeMethod(method);        method.invoke(target, args);        return null;    }}

测试该代理类:关键是Proxy.newProxyInstance()里面有3个参数,分别是被代理目标对象的ClassLoader,被代理目标对象实现的接口,InvocationHandler类型的代理器。

public void testProxy(){    //代理的目标对象(面向抽象编程:UserDAOPImpl()是抽象类UserDAP的实现类)    UserDAO userDAO = new UserDAOImpl();    //代理器(实现了InvocationHandler接口)    LogInterceptor log = new LogInterceptor();    //在代理器中注入代理对象    log.setTarget(userDAO);    //执行代码任务,转换成目标对象    UserDAO userDAOProxy = (UserDAO)Proxy.newProxyInstance(userDAO.getClass().getClassLoader(),userDAO.getClass().getInterfaces(), (InvocationHandler) log);    System.out.println(userDAOProxy.getClass());    //调用目标对象的方法,(保存用户)    userDAOProxy.save(new User());}

AOP采用动态代理的过程:

  1. 将切面使用动态代理的方式动态织入到目标对象(被代理类),形成一个代理对象;
  2. 目标对象如果没有实现代理接口,那么Spring会采用CGLib生成代理对象,该代理对象是目标对象的子类;
  3. 目标对象是final类的并且也没有实现代理接口的,就不能运用AOP

二、AOP基础知识

a)JoinPoint(连接点)
连接点,指切面可以织入到目标对象的位置(方法,属性等).例如类中的一个方法。它是一个抽象的概念,在实现AOP时,并不需要去定义一个Join point。
b)PointCut(切入点)
切入点,指通知应用到哪些类的哪些方法或属性之上的规则。本质上是一个捕获连接点的结构(或称连接点的集合)。在AOP中,可以定义一个Point cut,来捕获相关方法(通知中的逻辑)的调用。
c)Aspect(切面)
Point cut和Advice的组合,它类似于OOP中定义的一个类,但它代表的更多的是对象间横向的关系。
d)Advice(通知)
Point cut的执行代码,它是执行”切面”的具体逻辑。
e)Target(目标对象)
指需要织入切面的对象。包含Join point的对象,它是被Advice的类,也是商务逻辑对象。这个对象永远是一个被代理的对象。
f)Weave(织入)
织入,指将通知插入到目标对象。将Aspec模块与核心商务逻辑(即目标对象)进行关联的过程,比如,将事务处理模块和银行柜员机程序通过配置结合起来,决定什么情况下事务处理模块被通知调用。
g)Proxy(AOP代理)
代理对象,指切面织入目标对象之后形成的对象。由AOP框架创建的对象,它是真正执行Advice的实体。

三、AOP配置—Annotation

1、xml文件中首先引入
xmlns:aop=”http://www.springframework.org/schema/aop”以及其对应的xsi:schemaLocation。
2、再添加aop注解配置

<!--aop面向切面编程注解必须添加上下面的语句--><aop:aspectj-autoproxy></aop:aspectj-autoproxy>下:

此时就可以解析对应的Annotation了。
配置文件代码如下:

<?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-2.5.xsd    http://www.springframework.org/schema/context        http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-2.5.xsd">    <!--注解annotation-->    <context:annotation-config />    <!--Component:在类之上添加@Component注解,意味着是一个成员,默认名字是类名首字母小写,可以指定名字@Component("")    对于需注入的成员,添加@Resource(name=""),即可注入成功    @Service @Controller @Repository 与@Component相同意义。-->    <context:component-scan base-package="com.cdd"/>    <!--aop面向切面编程注解必须添加上下面的语句-->    <aop:aspectj-autoproxy></aop:aspectj-autoproxy></beans>

3、建立我们的拦截类
4、用@Aspect注解这个类
拦截类就是我们要作为切面的那个类,即穿插到我们的业务逻辑中的类。比如监听或是打印日志
5、建立处理方法
拦截类中建立处理方法,具体方法的实现。
6、用@Before、@After、@AfterThrowing(“”)、@Around来注解对应的方法,规定该处理方法在切入点执行的位置
7、写明白切入点(execution …….)
对于每个方法的切入点都要进行规定。例如注释在某方法上@Before ,即表示:在织入点(execution)方法之前执行该方法

@Before("execution(public void com.cdd.dao.impl.UserDAOImpl.save(com.cdd.model.User))")//织入点到具体某个类的具有某个返回值的方法之前。

若织入点是某个包下的任意子包的具有任意返回值的任意方法 之前都执行该方法,织入点命名如下

@Before("execution(public * com.cdd..*.*(..))")public void before(){    System.out.println("method before!");}

8、让spring对我们的拦截器类进行管理@Component
完整代码如下:

@Aspect    //2.声明是一个切面@Component //1.首先声明该类是个成员public class LogInterceptor_AOP {    //a).@Before 在织入点方法(execution)之前执行该方法。    //@Before("execution(public void com.cdd.dao.impl.UserDAOImpl.save(com.cdd.model.User))")//3.织入点    //b)若在某包下的任意子包下的具有任意返回值的任意方法 之前都执行该方法,织入点命名如下    @Before("execution(public * com.cdd..*.*(..))")    public void before(){        System.out.println("method before!");    }    //AfterReturning()是指织入点方法执行正常且完成后执行该方法    @AfterReturning("execution(public * com.cdd..*.*(..))")    public void afterReturn(){        System.out.println("after returning!");    }    //若每个方法的织入点相同,则可以将织入点定义为PointCut    @Pointcut("execution(public * com.cdd..*.*(..))")    public void myPointCut(){}    //@Around 环绕通知。环绕通知在一个方法执行之前和之后执行。它使得通知有机会 在一个方法执行之前和执行之后运行    //必须有ProceedingJoinPoint参数。执行pjp.proceed()方法    @Around("myPointCut()")    public void aroundMethod(ProceedingJoinPoint pjp){        System.out.println("pjp around before");        try {            pjp.proceed();        } catch (Throwable e) {            // TODO Auto-generated catch block            e.printStackTrace();        }        System.out.println("pjp around after");    }    //@AfterThrowing("")抛出异常通知在一个方法抛出异常后执行    //@After 不论一个方法是如何结束的,最终通知都会运行。通常用它来释放资源}

四、AOP配置—xml

1、把interceptor对象初始化
建立拦截类

public class LogInterceptor_XML {    public void before(){        System.out.println("method before~!");    }}

2、xml配置,设置切面、切入点以及切入位置

<?xml version="1.0" encoding="UTF-8"?><beans ...此处省略,与注解相同>    <context:annotation-config />    <context:component-scan base-package="com.cdd" />    <bean id="logInterceptor" class="com.cdd.aop.LogInterceptor_XML">    </bean>    <!--AOP xml配置-->    <aop:config>        <!--全局的PointCut -->        <aop:pointcut expression="execution(public * com.cdd.service..*.add(..))" id="servicePointCut"/>        <aop:aspect id="logAspect" ref="logInterceptor">            <!--有个before方法,其属性method:执行切面的哪个方法  织入点引用全局的,也可引用局部定义的,也可以直接指定pointcut-->            <!--<aop:pointcut expression="execution(public * com.cdd.service..*.add(..))" id="logPointCut"/>-->            <aop:before method="before" pointcut-ref="servicePointCut"/>        </aop:aspect>    </aop:config></beans>
0 0
原创粉丝点击