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 切入点表达式

扫描二维码关注公众号,回复: 5079187 查看本文章

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
今日推荐