Spring的AOP入门(AOP概述、底层实现原理、Cglib、注解)

AOP概述

AOP (面向切面编程
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

AOP面向切面编程,AOP是OOP的扩展和延伸,解决OOP开发遇到的问题

Spring底层的AOP实现原理

  • 动态代理
    • JDK动态代理:只能对实现了接口的类产生代理
    • Cglib动态代理(类似于javassist第三方代理技术):对没有实现接口的类产生代理对象,生成子类对象
package com.spring.aop.demo1;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JdkProxy implements InvocationHandler{
	
	private UserDao userDao;
	public JdkProxy(UserDao userDao) {
		this.userDao = userDao;
	}
	
	/*
	 * 产生UserDao代理的方法
	 */
	public UserDao createProxy() {
		
		UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(), 
				userDao.getClass().getInterfaces(), this);
		return userDaoProxy;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		//判断方法名是不是save
		if ("save".equals(method.getName())) {
			//增强
			System.out.println("===权限校验===");
			return method.invoke(userDao, args);
		}
		return method.invoke(userDao, args);
	}
	
}

Cglib动态代理

package com.spring.aop.demo2;

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

/*
 * Cglib代理
 */

public class CglibProxy implements MethodInterceptor{
	
	private CustomerDao customerDao;
	
	public CglibProxy(CustomerDao customerDao) {
		this.customerDao = customerDao;
	}
	
	/*
	 * 使用cglib产生代理的方法
	 */
	public CustomerDao createProxy() {
		//1。创建cglib的核心类
		Enhancer enhancer = new Enhancer();
		//2.设置父类:
		enhancer.setSuperclass(customerDao.getClass());
		//3.设置回调,类似于(InvocationHandler对象)
		enhancer.setCallback(this);
		CustomerDao proxy = (CustomerDao) enhancer.create();
		return proxy;
	}

	@Override
	public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
		//判断方法是都为save:
		if ("save".equals(method.getName())) {
			//增强
			System.out.println("======权限校验======");
			return methodProxy.invokeSuper(proxy, args);

		}
		return methodProxy.invokeSuper(proxy, args);
	}
	
}

Spring的AOP的开发(AspectJ的XML的方式)

Spring的AOP简介
  • AOP思想最早是由AOP联盟组织提出的。Spring使用这种思想最好的框架
    • Spring的AOP有自己的实现方式(非常繁琐)。AspectJ是一个AOP的框架,Spring引入AspectJ作为自身AOP的开发
    • Spring两套AOP开发方式
      • Spring传统方式(弃用)
      • Spring基于AspectJ的AOP的开发(使用)
AOP开发中的相关术语
  • Joinpoint:连接点,可以被拦截到的点
  • Pointcut:切入点,真正被拦截到的点
  • Advice:通知、增强。方法层面增强
  • introduction:引介。类层面的增强

Target:被增强的对象

  • 对UserDao增强,UserDao称为是目标
    Weaving:织入,将通知应用(Advice)到目标(Target)过程
  • 将权限校验的方法的代码应用到UserDao的save方法上的过程
    Proxy:代理对象
    Aspect:切面,多个通知和多个切入点组合!!!

创建一个web项目,引入jar包

  • 引入基本开发包
  • 引入aop开发的相关包
    • com.springsource.org.aopalliance-1.0.0.jar
    • spring-aspects-4.2.4.RELEASE.jar
    • com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
    • spring-aop-4.2.4.RELEASE.jar

applicationContext.xml配置文件(引入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"> <!-- bean definitions here -->

</beans>
编写目标类并完成配置
<!-- 配置目标对象 :被增强的对象-->
	<bean id="productDao" class="com.spring.aop.demo1.ProductDao"/>
编写测试类
  • Spring整合单元测试
package com.spring.aop.demo1;

import javax.annotation.Resource;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/*
 * AOP的入门
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Test {
	
	@Resource(name="productDao")
	private ProductDao productDao;
	
	@org.junit.Test
	public void demo1() {
		productDao.save();
		productDao.update();
		productDao.delete();
		productDao.find();
	}
}
编写一个切面类
  • 编写切面
package com.spring.aop.demo1;

/*
 * 切面类
 */
public class MyAspectXML {
	
	public void checkPri() {
		System.out.println("权限校验==============");
	}
	
}
  • 将切面类交给Spring管理
<!-- 将切面类交给Spring管理 -->
	<bean id="myAspect" class="com.spring.aop.demo1.MyAspectXML" />
通过AOP配置实现
<!-- 通过AOP配置完成对目标类产生代理 -->
	<aop:config>
		<!-- 表达式配置哪些类的哪些方法需要进行增强 -->
		<aop:pointcut expression="execution(* com.spring.aop.demo1.ProductDaoImpl.save(..))" id="pointcut1"/>
		
		<!-- 配置切面 -->
		<aop:aspect ref="myAspect" >
			<aop:before method="checkPri" pointcut-ref="pointcut1" />
		</aop:aspect>
	</aop:config>

Spring中的通知类型

前置通知:在目标方法执行之前进行操作
  • 前置通知:可以获得切入点信息
<!-- 配置切面 -->
		<aop:aspect ref="myAspect" >
			<aop:before method="checkPri" pointcut-ref="pointcut1" />
		</aop:aspect>
后置通知:在目标方法执行之后进行操作
  • 后置通知:获得方法的返回值
<!-- 配置切面 -->
		<aop:aspect ref="myAspect" >
			<!-- 后置通知====== -->
			<aop:after-returning method="writeLog" pointcut-ref="pointcut2" returning="result" />
		</aop:aspect>
环绕通知:在目标方法执行之前和之后进行操作
  • 环绕通知可以阻止目标方法的执行
<!-- 配置切面 -->
		<aop:aspect ref="myAspect" >
			<!-- 环绕通知 ======-->
			<aop:around method="around" pointcut-ref="pointcut3" />
		</aop:aspect>
异常抛出通知:在程序出现异常的时候,进行的操作
<!-- 配置切面 -->
		<aop:aspect ref="myAspect" >
			<!-- 异常抛出通知 ======-->
			<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="ex"/>
		</aop:aspect>
最终通知:无论代码是否有异常,总是会执行(相当于try catch里面的finally)
<!-- 配置切面 -->
		<aop:aspect ref="myAspect" >
			<!-- 最终通知====== -->
			<aop:after method="after" pointcut-ref="pointcut4" />
		</aop:aspect>
引介通知(不用会)

Spring的切入点表达式写法:

切入点表达式语法
  • 基于execution的函数完成的
  • 语法
    * 【访问修饰符】方法返回值 包名.类名.方法名【参数】
    * public void com.java.Spring.Demo()

Spring的AOP的基于Aspect注解开发

创建一个web项目,引入jar包,引入配置文件
  • 编写目标类
<!-- 配置目标类 -->
	<bean id="orderDao" class="com.spring.aop.demo1.OrderDao" />
  • 编写切面类并配置
package com.spring.aop.demo1;

/*
 * 切面类:注解的切面
 */
public class MyAspectAnno {
	
	public void before() { 
		System.out.println("前置增强==============");
	}
	
}

<!-- 配置切面类 =========-->
	<bean id="myAspect" class="com.spring.aop.demo1.MyAspectAnno" />
使用注解的AOP对象目标类进行增强
  • 在配置文件中打开注解的AOP开发
<!-- 在配置文件中开启注解的AOP开发 -->
	<aop:aspectj-autoproxy/>
  • 在切面类上使用注解
/*
 * 切面类:注解的切面
 */
@Aspect
public class MyAspectAnno {
	
	@Before(value="execution(* com.spring.aop.demo1.OrderDao.save(..))")
	public void before() {
		System.out.println("前置增强==============");
	}
	
}
编写测试类
package com.spring.aop.demo1;

import javax.annotation.Resource;

import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/*
 * Spring的AOP注解开发
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Test {

	@Resource(name="orderDao")
	private OrderDao orderDao;
	
	@org.junit.Test
	public void demo1() {
		orderDao.save();
		orderDao.update();
		orderDao.delete();
		orderDao.find();
	}
	
}

Spring的AOP注解的切入点的注解

@Pointcut(value="execution(* com.spring.aop.demo1.OrderDao.save(..))")
	private void pointCut() {}

猜你喜欢

转载自blog.csdn.net/Woo_home/article/details/89786153