AOP是面向方面的编程,在实际开发中,AOP都会工作在业务层,因为业务层要调用数据层,而业务层也要完成所有辅助性的业务层操作。
范例:定义业务层操作接口:
package com.Spring.Service; import com.Spring.Vo.Member; public interface IMemberService { public Boolean insert(Member vo); }
实现操作接口:
package com.Spring.Service.Impl; import org.springframework.stereotype.Service; import com.Spring.Service.IMemberService; import com.Spring.Vo.Member; @Service public class IMemberServiceImpl implements IMemberService { @Override public Boolean insert(Member vo) { System.out.println("数据层调用 Member="+vo); return false; } }
此时的业务层只关兴核心的业务功能,核心的功能就是调用了业务层代码。
随后所有的辅助性操作功能,都通过spring容器,动态配置。
范例:为项目配置Annotation的扫描支持:
base-package:表示扫描com.spring开头的包以下的文件,配合@Service注解,这个包以下的相关类会自动配置,代替一般的bean配置。
可以复习:http://www.cnblogs.com/alsf/p/7968072.html
<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"> <context:annotation-config/> <context:component-scan base-package="com.Spring"/> </beans>
测试代码:
package com.Spring.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.Spring.Service.IMemberService; import com.Spring.Service.Impl.MemberServiceImpl; import com.Spring.Vo.Member; public class TestMemberService { public static void main(String args[]) { ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml"); IMemberService ser=ctx.getBean("memberServiceImpl",MemberServiceImpl.class); Member vo=new Member(); vo.setMid("hello"); vo.setName("你好"); System.out.println(ser.insert(vo)); } }
输出结果:
但是这种操作并不严格,还需要准备相应的辅助性功能,那么这些辅助性的功能,可以单独定义在一个切面处理的类之中。
范例:定义切面处理类。
这里的切面类也用@Component做annotation配置,applicationcontext.xml文件里使用的时候直接使用serviceAspect就行了。
package com.Spring.aop; import org.springframework.stereotype.Component; @Component public class ServiceAspect { public void serviceBefore() { System.out.println("AOP切面执行日志记录操作"); } public void serviceAfter() { System.out.println("AOP切面执行事务处理操作"); } }
默认情况下spring不会开启切面。引入AOP命名空间。
范例:修改applicationContext.xml文件
勾选上:
代码中多了这些:
下面在XML文件中配置文件组织切面。
范例:定义切面引用
<aop:config> <!-- 定义程序的切入点,这里exucution表示对哪个包下面的类进行切入 --> <aop:pointcut expression="execution(* com.Spring..*.*(..)))" id="pointcut"/> <!-- 这里ref的对象是通过annotation配置@Component出来的, --> <!-- 定义面向方面的处理类 --> <aop:aspect ref="serviceAspect">
<!--aop:before和aop:xx 里面需要pointcut-ref,表示依赖哪个切入点。--> <aop:before method="serviceBefore" pointcut-ref="pointcut"/> <aop:after method="serviceAfter" pointcut-ref="pointcut"/> </aop:aspect> </aop:config>
重新执行最开始的Test类,发现报错:
Bean named 'memberServiceImpl' must be of type [com.Spring.Service.Impl.MemberServiceImpl], but was actually of type [com.sun.proxy.$Proxy8]
网上查询得知:
使用了jdk的自动动态代理,需要在<aop:aspectj-autoproxy中添加proxy-target-class="true"
<aop:config>
<!-- 定义程序的切入点 -->
<aop:pointcut expression="execution(* com.Spring..*.*(..)))" id="pointcut"/>
<!-- 这里ref的对象是通过annotation配置@Component出来的, -->
<!-- 定义面向方面的处理类 -->
<aop:aspect ref="serviceAspect">
<aop:before method="serviceBefore" pointcut-ref="pointcut"/>
<aop:after method="serviceAfter" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
<aop:aspectj-autoproxy proxy-target-class="true"/>
执行后结果:
此时的业务层里面还没有出现任何的与业务层的核心操作有关的代码。而所有的辅助功能都会以切面的形式出现在项目的执行过程之中,而
这些切面就是最早的代理类。
步骤:
1,首先需要定义一个切面aop:pointcut ,里面包含这个切面所覆盖的位置,由表达式expression决定,然后还需要一个id值。
2,然后定义一个面向方面的处理类;aop:aspect,里面需要指定这个类的实例是哪个,上面用ref和annotation配置组合确定了。
3,分别指定由aop:aspect指定的处理类的aop:before,aop,after等方法,里面需要指定这个方法关联哪个切面,通过pointcut-ref指定切面,然后就会对该切面所
覆盖指定的类的对象,进行切面处理了,比如每次相关类实例对象调用方法的时候,aop:befer等方法也会被调用。
分析结果:
这里切面和我们的Service类关联起来的地方就是定义切面处的 expression="execution(* com.Spring..*.*(..)))" 。
只要符合这个表达式的类,这里就是这个包下面的类,的对象实例执行,在执行前和执行后都会调用切面类里面的方法,具体调用哪个方法
,有aop:before,或者aop:xxx 绝对。
整个配置过程中,其基本的操作意义基本一看明白,但是最重要的是切入点的设置。
execution(* com.Spring..*.*(..)))
这种语法就是AspectJ定义的切入点操作语法。此语法结构如下:
excution(修饰符匹配? 返回值类型 操作类型的匹配? 名称匹配(参数匹配) 抛出异常匹配)
这里的?表示一个或0个。
以上语法详细解释如下:
1,修饰符:public private ,只能出现0次或1次,(本次没有出现,默认public)
2,返回值:execution(* com.Spring..*.*(..)))。如果使用的是*,表示返回任意类型。
3,名称匹配:execution(* com.Spring..*.*(..))),表示的是具体要使用的此切面的程序类,如果写的是com.Spring表示这个包中的,
而如果出现“..”,则表示在任意子包下,随后的*.*表示任意类的任意方法,第一个*表示类名称,第二个表示方法名称。
4,方法参数:execution(* com.Spring..*.*(..))),使用“..”表示使用任意多个参数,如果使用的是“*”,表示匹配任意一个参数。
比如execution(* com.Spring..*.*(..)))就表示:在com.spring这个包下,任意返回值的任意类的,任意方法中,任意参数的地方,都要执行这个切面的拦截。
一旦拦截成功,就触发相应的处理方法,让我们的切面执行。