Spring框架之AOP开发

一. SpringAOP开发
AOP的概念:    

    AOP

 

(面向切面编程)

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP:面向切面编程。AOP出现是OOP的延伸,是OOP的扩展。解决OOP中的一些问题。
AOP取代传统的纵向继承体系,采用横向抽取机制。
1.1 AOP的底层实现原理
代理机制
Spring实现AOP使用两种代理机制:
JDK动态代理 :基于接口实现
Cglib动态代理 :基于子类实现
1.JDK动态代理 :基于接口实现(模拟其代理的方式)
需要导入一个jar包:Spring-core-...
第一步:写一个接口并且里面有一个方法
public interface UserDao {
public void save();
}
第二步:写一个实现类来实现这个接口
public class UserDaoImpl implements UserDao {
public void save() {
System.out.println("保存执行了...");
}
}
第三步:编写一个工具类
public class ProxyFactory {
public static Object getProxy(final Class clazz){
Object proxy = Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new InvocationHandler() {
public Object invoke(Object proxy, Method method,Object[] args) throws Throwable {
System.out.println("开启事务");
//调用目标对象的真是方法
method.invoke(clazz.newInstance());
System.out.println("提交事务");
return null;
}
});
return proxy;
}
}
第四步:添加测试类,进行测试
public class test01 {
@Test
public void testdemo(){
UserDao ud=(UserDao) ProxyFactory.getProxy(UserDaoImpl.class);
//UserDaoImpl ud=new UserDaoImpl();
ud.save();
}
}
2.Cglib动态代理 :基于子类实现
唯一不同的是:不用定义一个接口了,直接用该被代理的类接收就可以了
UserDaoImpl ud=(UserDaoImpl ) ProxyFactory.getProxy(UserDaoImpl.class);
1.2 SpringAOP的开发
1.2.1 AOP开发中的术语

1、AOP术语

    1)连接点(Joinpoint)
    程序执行的某个特定位置:如类开始初始化前、类初始化后、类某个方法调用前、调用后、方法抛出异常后。一个类或一段程序代码拥有一些具有边界性质的特定点,这些点中的特定点就称为“连接点”。Spring仅支持方法的连接点,即仅能在方法调用前、方法调用后、方法抛出异常时以及方法调用前后这些程序执行点织入增强。连接点由两个信息确定:第一是用方法表示的程序执行点;第二是用相对点表示的方位。
    2)切点(Pointcut)
    每个程序类都拥有多个连接点,如一个拥有两个方法的类,这两个方法都是连接点,即连接点是程序类中客观存在的事物。AOP通过“切点”定位特定的连接点。连接点相当于数据库中的记录,而切点相当于查询条件。切点和连接点不是一对一的关系,一个切点可以匹配多个连接点。在Spring中,切点通过org.springframework.aop.Pointcut接口进行描述,它使用类和方法作为连接点的查询条件,Spring AOP的规则解析引擎负责切点所设定的查询条件,找到对应的连接点。其实确切地说,不能称之为查询连接点,因为连接点是方法执行前、执行后等包括方位信息的具体程序执行点,而切点只定位到某个方法上,所以如果希望定位到具体连接点上,还需要提供方位信息。
    3)增强(Advice)
    增强是织入到目标类连接点上的一段程序代码,在Spring中,增强除用于描述一段程序代码外,还拥有另一个和连接点相关的信息,这便是执行点的方位。结合执行点方位信息和切点信息,我们就可以找到特定的连接点。
    4)目标对象(Target)
    增强逻辑的织入目标类。如果没有AOP,目标业务类需要自己实现所有逻辑,而在AOP的帮助下,目标业务类只实现那些非横切逻辑的程序逻辑,而性能监视和事务管理等这些横切逻辑则可以使用AOP动态织入到特定的连接点上。
    5)引介(Introduction)
    引介是一种特殊的增强,它为类添加一些属性和方法。这样,即使一个业务类原本没有实现某个接口,通过AOP的引介功能,我们可以动态地为该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。    
    6)织入(Weaving)
    织入是将增强添加对目标类具体连接点上的过程。AOP像一台织布机,将目标类、增强或引介通过AOP这台织布机天衣无缝地编织到一起。根据不同的实现技术,AOP有三种织入的方式:
    a、编译期织入,这要求使用特殊的Java编译器。
    b、类装载期织入,这要求使用特殊的类装载器。
    c、动态代理织入,在运行期为目标类添加增强生成子类的方式。
    Spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
 
    7)代理(Proxy)
    一个类被AOP织入增强后,就产出了一个结果类,它是融合了原类和增强逻辑的代理类。根据不同的代理方式,代理类既可能是和原类具有相同接口的类,也可能就是原类的子类,所以我们可以采用调用原类相同的方式调用代理类。
 
    8)切面(Aspect)
    切面由切点和增强(引介)组成,它既包括了横切逻辑的定义,也包括了连接点的定义,Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。


1.2.2各种术语关系图解
Spring框架之AOP开发

1.2.3AOP执行原理

Spring框架之AOP开发
2.1 Spring使用AspectJ的方式(XML方式)

第一步:导入jar包(10个)
springaop开发包
    spring-aop-4.2.4.RELEASE.jar
    spring-aspects-4.2.4.RELEASE.jar
aspectj的开发包
    com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
引入aop联盟的包
    com.springsource.org.aopalliance-1.0.0.jar
spring整合aspectJ的包(红色的四个)
Spring框架之AOP开发

第二步:在src下面创建一个xml文件,然后引入Spring的配置文件

引入了aop的约束
<?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">
</beans>

第三步:编写类,可以实现一个接口(JDK动态代理),也可以不用接口(Cglib动态代理)
//目标类
public class CustomerServiceImpl implements CustomerService {
public void addCustomer() {
System.out.println("调用了保存客户dao方法");
}
 
public void updateCustomer() {
System.out.println("调用了修改客户dao方法");
}
}
第四步:将类交给Spring
 <!-- 目录类 -->
 <bean id="customerService" class="cn.itcast.service.impl.CustomerServiceImpl"></bean>
第五步:编写切面类
public class MyAspect {
//前置增强
public void privilege(){
System.out.println("用户权限校验执行了。。。");
}
//后置增强
public void writeLog(String aa){
System.out.println("写入日志执行了。。。"+aa);
}
//环绕增强
public void around(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("方法执行前的时间:"+System.nanoTime());
joinPoint.proceed();//调用目标方法
System.out.println("方法执行后的时间:"+System.nanoTime());
}
//异常增强
public void afterThrowing(Exception e){
System.out.println("异常增强执行了"+e.getMessage());
}
//最终增强
public void after(){
System.out.println("最终增强执行了");
}
}
第六步:配置目标类和切面类,让其联系起来,让某个方法可以增强

<!-- 声明aop的配置  使用<aop:config> </aop:config>-->
<aop:config>
     <!-- 切入点:真正被增强的方法 -->使用  <aop:pointcut/>
     <aop:pointcut expression="execution(* com.itheima.service.CustomerService.add*(..))" id="pointcut1"/>
     <aop:pointcut expression="execution(* com.itheima.service.CustomerService.update*(..))" id="pointcut2"/>
     <!-- 配置AOP的切面 -->使用  <aop:aspect/>
     <aop:aspect ref="myAspect">
     <!-- 配置前置通知 -->使用<aop:before/>
     <aop:before method="privilege" pointcut-ref="pointcut1"/>
     </aop:aspect>
</aop:config>
第七步:编写测试类。
public class TestAOP {
@Test
public void test1(){
ApplicationContext ac =new ClassPathXmlApplicationContext("applicationContext.xml");
CustomerService cs = (CustomerService) ac.getBean("customerService");
cs.updateCustomer();
}
}
说明:关于标签的使用以及相关解释
<aop:config>
作用:
用于声明开始aop的配置
<aop:aspect>
作用:
用于配置切面。
属性:
id给切面提供一个唯一标识。
ref引用配置好的通知类beanid
<aop:pointcut>
作用:
用于配置切入点表达式
属性:
expression用于定义切入点表达式。
id用于给切入点表达式提供一个唯一标识。
<aop:before>
作用:
用于配置前置通知
属性:
method指定通知中方法的名称。
pointct定义切入点表达式
pointcut-ref指定切入点表达式的引用
<aop:after-returning>
作用:用于配置后置通知
属性:
method指定通知中方法的名称。
pointct定义切入点表达式
pointcut-ref指定切入点表达式的引用
<aop:after-throwing>
作用:用于配置异常通知
属性:
method指定通知中方法的名称。
pointct定义切入点表达式
pointcut-ref指定切入点表达式的引用
<aop:after
作用:用于配置最终通知
属性:
method指定通知中方法的名称。
pointct定义切入点表达式
pointcut-ref指定切入点表达式的引用
<aop:around>
作用:用于配置环绕通知
属性:
method指定通知中方法的名称。
pointct定义切入点表达式
pointcut-ref指定切入点表达式的引用
2.1 Spring的AOP的注解和xml配置使用方式
第一步:导入jar包
           Spring框架之AOP开发
比只用xml多导入一个jar包
第二步:在src下面创建一个xml文件,然后引入Spring的配置文件,这一次只配置一个开启包扫描和开启aop注解
<!-- 开启包扫描 -->
<context:component-scan base-package="cn.itcast"/>
<!-- 开启aop注解 -->
<aop:aspectj-autoproxy/>

第三步:编写类,可以实现一个接口(JDK动态代理)
@Component("userdao")--->相当与<bean id="userdao" class="cn.itcast.UserDao.impl.UserDaoImpl"/>
public class UserDaoImpl implements DaoUser {
// 保存用户
public void save() {
System.out.println("保存用户成功");
}
第四步:在自己定义的切面类上配置注解
@Component-->相当与<bean id="myAspect" class="cn.itcast.utils.MyAspect"></bean>
@Aspect----> 相当与//<aop:config>
public class MyAspect {
@Before("execution(* cn.itcast.UserDao.*.save(..))")-->相当于<aop:pointcut expression="execution(* cn.itcast.UserDao.*.save(..))" id="point1/>
public void privilege(){
System.out.println("用户权限校验执行了。。。");
}
其它的方法,跟这个一样。知识标签不一致而已
@AfterReturning(value="execution(* cn.itcast.service.*.updateCustomer(..))",returning="aa")
public void writeLog(String aa){
System.out.println("写入日志执行了。。。"+aa);
}
//环绕增强
@Around("pointcut1()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("方法执行前的时间:"+System.nanoTime());
joinPoint.proceed();//调用目标方法
System.out.println("方法执行后的时间:"+System.nanoTime());
}
//异常增强
@AfterThrowing(value="pointcut1()",throwing="e")
public void afterThrowing(Exception e){
System.out.println("异常增强执行了"+e.getMessage());
}
//最终增强
@After("pointcut1()")
public void after(){
System.out.println("最终增强执行了");
}
@Pointcut("execution(* cn.itcast.service.*.deleteCustomer(..))")
public void pointcut1(){ // id="pointcut1"
}
}
第五步:编写测试类。
public class TestSpring {
@Test
public void test01(){
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
DaoUser du= (DaoUser) ac.getBean("userdao");
du.save();
}
}
2.2 Spring的AOP纯注解配置方式配合(整合Junit
1. 所谓的纯注解就是讲xml中的两个开启包扫描和aop注解都配置在一个类上,这个类需要自己定义。
@Configuration
@ComponentScan("cn.itcast")--->相当于 //<context:component-scan base-package="cn.itcast"/>
@EnableAspectJAutoProxy--->相当于 //<aop:aspectj- autoproxy/>
public class SpringConfiguration {
}
2.在测试类上引进上来
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=SpringConfiguration.class)
public class TestAOP {
@Autowired
private CustomerService cs;
@Test
public void test1(){
cs.save();
}
}