Spring—AOP

目录

一. 什么是AOP

二.Spring底层的AOP实现原理

2.1 JDK动态代理

2.2 Cglib产生动态代理

三.Spring的AOP开发

3.1AspectJ的XML方式

3.1.1 AOP开发中的术语

3.1.2 AOP开发入门案例

3.1.3 AOP中的通知类型

3.1.4 切入点表达式

3.2 SpringAOP 注解开发

3.2.1 注解开发入门案例

3.2.2 其它通知类型注解案例

3.2.3 AOP的切入点注解

3.2.4 切面类最终代码展示


一. 什么是AOP

二.Spring底层的AOP实现原理

2.1 JDK动态代理

有以下接口和实现类

public interface UserDao {
	public void delete();
	public void save();
	public void update();
	public void select();
}
public class UserDaoImpl implements UserDao {

	@Override
	public void save() {
		System.out.println("dao中保存用户的方法执行了。。。");
	}

	@Override
	public void delete() {
		System.out.println("dao中delete用户的方法执行了。。。");
	}

	@Override
	public void update() {
		System.out.println("dao中更新用户的方法执行了。。。");
	}

	@Override
	public void select() {
		System.out.println("dao中查询用户的方法执行了。。。");
	}

}

需求:需要在调用该类的 save 方法时进行权限校验

解决方案:使用jdk动态代理对UserDao产生代理

/**
 * 使用JDK动态代理对UserDao产生代理
 */
public class JdkProxy implements InvocationHandler{

	//将被增强的对象传到代理当中
	private UserDao userDao;
	public JdkProxy(UserDao userDao){
		this.userDao = userDao;
	}
	/**
	 * 产生UserDao代理对象的方法
	 * @return
	 */
	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);
	}
}

2.2 Cglib产生动态代理

public class CarDao {
	public void save() {
		System.out.println("dao中保存汽车的方法执行了。。。");
	}

	public void delete() {
		System.out.println("dao中删除汽车的方法执行了。。。");
	}

	public void update() {
		System.out.println("dao中更新汽车的方法执行了。。。");
	}

	public void select() {
		System.out.println("dao中查询汽车的方法执行了。。。");
	}
}
/**
 * Cglib动态代理
 *
 */
public class CglibCarDao implements MethodInterceptor {
	private CarDao carDao;
	public CglibCarDao(CarDao carDao){
		this.carDao = carDao;
	}
	/**
	 * 使用Cglib产生代理的方法
	 */
	public CarDao createProxy(){
		//创建Cglib核心类
		Enhancer enhancer = new Enhancer();
		//设置父类(cglib采用继承的方式产生的代理对象)
		enhancer.setSuperclass(carDao.getClass());
		//设置回调:类似于InvocationHandler对象,callback相当于invoke
		enhancer.setCallback(this);
		//创建代理对象
		CarDao proxy =  (CarDao) enhancer.create();
		return proxy;
		
	}
	@Override
	public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
		//判断方法是否为save,是的话对save方法进行增强
		if("save".equals(method.getName())){
			//增强
			System.out.println("增强了CarDao中的保存方法。。。");
		}
		//若果不是save则执行父类的原方法
		return methodProxy.invokeSuper(proxy,args );
	}
}

三.Spring的AOP开发

3.1AspectJ的XML方式

3.1.1 AOP开发中的术语

  • Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为 spring 只
  • 支持方法类型的连接点.
  • Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义.
  • Advice(通知/增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知.通知分为前置通知,后置
  • 通知,异常通知,最终通知,环绕通知(切面要完成的功能)
  • Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类
  • 动态地添加一些方法或 Field.
  • Target(目标对象):代理的目标对象
  • Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程.
  • spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装在期织入
  • Proxy(代理):一个类被 AOP 织入增强后,就产生一个结果代理类
  • Aspect(切面): 是切入点和通知(引介)的结合

3.1.2 AOP开发入门案例

①引入jar包

  • spring 的传统 AOP 的开发的包

spring-aop-4.2.4.RELEASE.jar
com.springsource.org.aopalliance-1.0.0.jar

  • aspectJ 的开发包:

com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
spring-aspects-4.2.4.RELEASE.jar

②配置xml文件

引入在线约束:

<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>

若无法上网则配置本地约束

③编写目标类并配置xml

public interface ProductDao {

	public void delete();
	public void save();
	public void update();
	public void select();
}
public class ProductDaoImpl implements ProductDao{

	@Override
	public void delete() {
		System.out.println("删除商品。。。");
	}

	@Override
	public void save() {
		System.out.println("保存商品。。。");
	}

	@Override
	public void update() {
		System.out.println("更新商品。。。");
	}

	@Override
	public void select() {
		System.out.println("查询商品。。。");
	}
	
}
<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 id="productDao" class="spring.dao.impl.ProductDaoImpl"></bean>          
</beans>

④Spring整合Junit

引入 spring-test.jar,编写测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Test01 {
	
	@Resource(name="productDao")
	private ProductDao productDao;

	@Test
	public void test01(){ 
		productDao.save();
		productDao.delete();
		productDao.update();
		productDao.select();
	}
}

⑤编写一个切面类并交给Spring管理

public class MyAspectXml {
    // 前置增强
    public void before(){
        System.out.println("前置增强===========");
    }
}
    <!-- 配置切面类 -->
    <bean id="myAspectXml" class="spring.test.MyAspectXml"></bean>

 ⑥配置AOP

    <!-- 进行 aop 的配置 -->
	<aop:config>
		<!-- 配置切入点表达式:哪些类的哪些方法需要进行增强 -->
		<aop:pointcut expression="execution(* spring.dao.impl.ProductDaoImpl.save(..))" id="pointcut1"/>
		<!-- 配置切面 -->
		<aop:aspect ref="myAspectXml">
			<aop:before method="before" pointcut-ref="pointcut1"/>
		</aop:aspect>
	</aop:config>

3.1.3 AOP中的通知类型

  • 前置通知 :在目标方法执行之前执行,可以获得切入点信息
  • 后置通知 :在目标方法执行之后执行
  • 环绕通知 :在目标方法执行前和执行后执行
  • 异常抛出通知:在目标方法执行出现 异常的时候 执行
  • 最终通知 :无论目标方法是否出现异常 最终通知都会 执行.

编写切面类

public class MyAspectXml {
    // 前置通知
    public void before(JoinPoint jp){
        System.out.println("前置增强==========="+jp);
    }
    //后置通知
    public void afterReturning(Object result){
    	System.out.println("后置通知。。。。。。。。。。。"+result);
    }
    //环绕通知
    public void around(ProceedingJoinPoint pjp) throws Throwable{
    	System.out.println("环绕前通知。。。");
    	Object obj = pjp.proceed();
    	System.out.println("环绕后通知。。。"+obj);
    }
    //异常抛出通知
    public void afterThrowing(Throwable ex){
    	System.out.println("异常抛出通知。。。"+ex.getMessage());
    }
    //最终通知 相当于 finally 代码块中的内容
    public void after(){
    	System.out.println("最终通知。。。");
    }

配置切面、切入点

<!-- 配置切面类 -->
<bean id="myAspectXml" class="spring.test.MyAspectXml"></bean>
<!-- 进行 aop 的配置 -->
<aop:config>
	<!-- 配置切入点表达式:哪些类的哪些方法需要进行增强 -->
	<aop:pointcut expression="execution(* spring.dao.impl.ProductDaoImpl.save(..))" id="pointcut1"/>
	<aop:pointcut expression="execution(* spring.dao.impl.ProductDaoImpl.delete(..))" id="pointcut2"/>
	<aop:pointcut expression="execution(* spring.dao.impl.ProductDaoImpl.update(..))" id="pointcut3"/>
	<aop:pointcut expression="execution(* spring.dao.impl.ProductDaoImpl.select(..))" id="pointcut4"/>
	<aop:pointcut expression="execution(* spring.dao.impl.ProductDaoImpl.testAfter(..))" id="pointcut5"/>
	<!-- 配置切面 -->
	<aop:aspect ref="myAspectXml">
		<!-- 前置通知 -->
		<aop:before method="before" pointcut-ref="pointcut1"/>
		<!-- 后置通知 -->
	 	<aop:after-returning method="afterReturning" pointcut-ref="pointcut2" returning="result"/>
	 	<!-- 环绕通知 -->
		<aop:around method="around" pointcut-ref="pointcut3"/>
		<!-- 异常抛出通知 -->
		<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="ex"/>
		<!-- 最终通知 -->
		<aop:after method="after" pointcut-ref="pointcut5"/>
	</aop:aspect>
</aop:config>

运行结果

3.1.4 切入点表达式

  • execution(表达式)
    • [方法访问修饰符] 方法返回值 包名.类名.方法名(方法的参数)
      • 其中访问权限修饰符可以省略,方法返回值可以写具体类型或者 * 表示任意返回值类型
    • * *.*.*Dao.save(..)
      • 指向所有Dao类中的save()
    • * spring.dao.UserDao+.save(..)
      • 指向当前类UserDao及其子类中的save()
    •  * spring.dao.UserDao+.*(..)
      • 指向当前类及其子类下的所有方法
    • * spring.dao..*.*(..)
      • 指向当前包及子包下的所有类的所有方法
    • public * spring.dao.*.*(..)
      • 指向当前包下的所有类的所有方法

3.2 SpringAOP 注解开发

3.2.1 注解开发入门案例

① 创建项目,引入jar包

② 配置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"
	xmlns:tx="http://www.springframework.org/schema/tx"
	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.xsd
	http://www.springframework.org/schema/aop
	http://www.springframework.org/schema/aop/spring-aop.xsd
	http://www.springframework.org/schema/tx 
	http://www.springframework.org/schema/tx/spring-tx.xsd">
	
</beans>

③ 目标类的编写及配置

public class OrderDao {

	public void save(){
		System.out.println("保存订单...");
	}
	public void update(){
		System.out.println("修改订单...");
	}
	public String delete(){
		System.out.println("删除订单...");
		return "赵冠希";
	}
	public void find(){
		System.out.println("查询订单...");
//		int d = 1/0;
	}
	<!-- 配置目标类================ -->
	<bean id="orderDao" class="spring.demo1.OrderDao"></bean>

 ④ 编写切面类添加注解并配置

/**
 * 切面类:注解的切面类
 */
@Aspect
public class MyAspectAnno {

	@Before(value="execution(* spring.demo1.OrderDao.save(..))")
	public void before(){
		System.out.println("前置增强===========");
	}
	 
}
	<!-- 配置切面类================ -->
	<bean id="myAspect" class="spring.demo1.MyAspectAnno"></bean>

⑤在配置文件中打开注解的AOP开发

	<!-- 在配置文件中开启注解的AOP的开发============ -->
	<aop:aspectj-autoproxy/>

⑥编写测试类

/**
 * Spring的AOP的注解开发
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringDemo1 {
	@Resource(name="orderDao")
	private OrderDao orderDao;
	
	@Test
	public void demo1(){
		orderDao.save();
		orderDao.update();
		orderDao.delete();
		orderDao.find();
	}
}

3.2.2 其它通知类型注解案例

在上述案例的基础上使用其它通知类型注解只需修改切面类即可

@Before  :前置通知

	@Before(value="execution(* spring.demo1.OrderDao.save(..))")
	public void before(){
		System.out.println("前置增强===========");
	}

@AfterReturning     :后置通知

	@AfterReturning(value="execution(* spring.demo1.OrderDao.delete(..))",returning="result")
	public void afterReturning(Object result){
		System.out.println("后置增强==========="+result);
	}

@Around       :环绕通知

	// 环绕通知:
	@Around(value="execution(* spring.demo1.OrderDao.update(..))")
	public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
		System.out.println("环绕前增强==========");
		Object obj  = joinPoint.proceed();
		System.out.println("环绕后增强==========");
		return obj;
	}

@AfterThrowing      :异常抛出通知

	// 异常抛出通知:
	@AfterThrowing(value="execution(* spring.demo1.OrderDao.find(..))",throwing="e")
	public void afterThrowing(Throwable e){
		System.out.println("异常抛出增强========="+e.getMessage());
	}

@After           :最终通知

    // 最终通知
    @After(value="execution(* spring.demo1.OrderDao.find(..))")
    public void after(){
        System.out.println("最终增强============");
    }

3.2.3 AOP的切入点注解

为了方便对切入点进行管理我们使用切入点注解

	// 切入点注解:
	@Pointcut(value="execution(* spring.demo1.OrderDao.find(..))")
	private void pointcut1(){}
	@Pointcut(value="execution(* spring.demo1.OrderDao.save(..))")
	private void pointcut2(){}
	@Pointcut(value="execution(* spring.demo1.OrderDao.update(..))")
	private void pointcut3(){}
	@Pointcut(value="execution(* spring.demo1.OrderDao.delete(..))")
	private void pointcut4(){}

3.2.4 切面类最终代码展示

/**
 * 切面类:注解的切面类
 */
@Aspect
public class MyAspectAnno {

	@Before(value="MyAspectAnno.pointcut2()")
	public void before(){
		System.out.println("前置增强===========");
	}
	
	// 后置通知:
	@AfterReturning(value="MyAspectAnno.pointcut4()",returning="result")
	public void afterReturning(Object result){
		System.out.println("后置增强==========="+result);
	}
	
	// 环绕通知:
	@Around(value="MyAspectAnno.pointcut3()")
	public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
		System.out.println("环绕前增强==========");
		Object obj  = joinPoint.proceed();
		System.out.println("环绕后增强==========");
		return obj;
	}
	
	// 异常抛出通知:
	@AfterThrowing(value="MyAspectAnno.pointcut1()",throwing="e")
	public void afterThrowing(Throwable e){
		System.out.println("异常抛出增强========="+e.getMessage());
	}
	
	// 最终通知
	@After(value="MyAspectAnno.pointcut1()")
	public void after(){
		System.out.println("最终增强============");
	}
	
	// 切入点注解:
	@Pointcut(value="execution(* spring.demo1.OrderDao.find(..))")
	private void pointcut1(){}
	@Pointcut(value="execution(* spring.demo1.OrderDao.save(..))")
	private void pointcut2(){}
	@Pointcut(value="execution(* spring.demo1.OrderDao.update(..))")
	private void pointcut3(){}
	@Pointcut(value="execution(* spring.demo1.OrderDao.delete(..))")
	private void pointcut4(){}
}

猜你喜欢

转载自blog.csdn.net/qq_30162219/article/details/86645446