Spring AOP(实验三)

什么是AOP?
将分散在各个方法中重复的代码提取出来,然后在程序编译或运行时,再将这些提取出来的代码应用到需要执行的地方
AOP术语
点击此处学习AOP术语
在切面覆盖下的类里面,任何对象方法的调用都被叫做连接点
切面 只是一个类,但是这个类只有在Spring容器中被配置才能被识别为切面
切入点就是 需要处理的连接点
通知增强处理就是 切入点需要执行的程序代码,就是切面类中的某个方法
目标对象就是 执行了切面类中方法的一个类。
代理是 在“动态的AOP”方法中,被动态创建的虚拟对象。
织入也就是 生成代理对象的过程

下面来了解一下spring 中两种代理模式。

JDK动态代理:

流程图:
在这里插入图片描述
项目架构:
在这里插入图片描述
代码:

package com.csdn2.AOP;

public interface UserDao {

	public void add();
	public void delete();
}

package com.csdn2.AOP;

public class UserDaoImplement implements UserDao{

	@Override
	public void add() {
		// TODO Auto-generated method stub
		System.out.println("com.csdn2.AOP.UserDaoImplement:添加方法");
	}

	@Override
	public void delete() {
		// TODO Auto-generated method stub
		System.out.println("com.csdn2.AOP.UserDaoImplement:删除方法");
	}
	
}

package com.csdn2.AOP;

public class Aspect {
	public void check() {
		System.out.println("com.csdn2.AOP.Aspect:模拟检查操作......");
	}
	public void log() {
		System.out.println("com.csdn2.AOP.Aspect:模拟记录操作......");
	}
}

package com.csdn2.AOP;

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

public class JdkProxy implements InvocationHandler{

	//声明目标对象
	UserDao userDao;
	public Object createProxy(UserDao userDao) {
		this.userDao=userDao;
		//1、类加载器
		ClassLoader classLoader=JdkProxy.class.getClassLoader();
		//2、被代理对象所有接口
		Class[] clazz=userDao.getClass().getInterfaces();
		//3、使用代理类,进行增强,返回的是代理后的对象
		return Proxy.newProxyInstance(classLoader, clazz, this); 
	}
	//所有动态代理类的方法调用,都会交由invoke()方法去处理
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// TODO Auto-generated method stub
		//1、声明切面
		Aspect aspect=new Aspect();
		//2、前增强
		aspect.check();
		//3、再在目标类上调用方法,并传入参数
		Object obj=method.invoke(userDao, args);
		//3、后增强
		aspect.log();
		return obj;
	}

}
package com.csdn2.AOP;

public class JdkTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//创建代理对象
		JdkProxy jdkProxy=new JdkProxy();
		//创建目标对象
		UserDao userDao=new UserDaoImplement();
		//从代理对象中获取增强后的目标对象
		UserDao userDao2=(UserDao)jdkProxy.createProxy(userDao);
		//执行方法
		userDao2.add();
		System.out.println();
		userDao2.delete();
	}

}

运行结果
在这里插入图片描述

CDGLIB代理:

Code Generation Library 一个高效能的开源的代码生成包,采用字节码技术,对目标类生成一个子类,并对子类进行增强。spring的核心包中(spring-core)中以及集成了CGLIB所需要的包,开发中不需要进行另外的导包操作,并且基于CGLIB不需要像 JDK代理方式那样一定要求:使用动态代理的对象必须实现了一个或者几个接口。
流程图:
在这里插入图片描述
项目结构:
在这里插入图片描述
代码:

package com.csdn2.AOP.CGLIB;

public class UserDao {
	public void add() {
		System.out.println("执行添加方法");
	}
	public void delete() {
		System.out.println("执行删除方法");
	}
}

package com.csdn2.AOP.CGLIB;

public class aspect {
	public void log() {
		System.out.println("正在执行切面中的日志记录.....");
	}
	public void check() {
		System.out.println("正在执行切面中的检查方法......");
	}
}

package com.csdn2.AOP.CGLIB;

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

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

public class CGLIBproxy implements MethodInterceptor{

	public Object createProxy(Object object) {
		Enhancer enhancer=new Enhancer();
		enhancer.setSuperclass(object.getClass());
		enhancer.setCallback(this);
		return enhancer.create();
	}
	@Override
	public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
		// TODO Auto-generated method stub
		aspect aspect=new aspect();
		aspect.check();
		Object object=arg3.invokeSuper(arg0,arg2);
		aspect.log();
		return object;
	}

}

package com.csdn2.AOP.CGLIB;

public class test {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		CGLIBproxy cgliBproxy=new CGLIBproxy();
		UserDao userDao=new UserDao();
		UserDao userDao2=(UserDao)cgliBproxy.createProxy(userDao);
		userDao2.add();
		System.out.println();
		userDao2.delete();
	}

}

运行结果:
在这里插入图片描述
目标类中的方法别成功调用并增强了,这种没有实现接口的调用方式,就是CGLIB方法。

基于代理类的AOP代理

在Spring中 提供了ProxyFactoryBean创建AOP代理,
ProxyFactoryBean的常用属性:

属性名称描述
target代理的目标对象
proxyInterface代理所需要的实现的接口,可以是多个接口,可以使用一下格式赋值< list >< value >< /value >…< /list >
interceptorNames需要植入的目标对象的Advice
proxyTargertClass是否对类进行代理(而不是对接口进行代理)。设置为true时,使用cglib代理。
singleton返回的代理对象是否单例,默认是单例
optimize当设置为true时 ,强制使用cglib代理,对于singleton的代理,我们推荐使用cglib代理,对于其他类型的代理,最好使用JDK代理,原因是cglib创建代理时速度慢,而创建出的代理对象运行效率较高,而使用JDK代理的表现正好相反。

流程图:
![在这里插入图片描述](https://img-在这里插入图片描述blog.csdnimg.cn/20201212162154623.png)

引包
spring-aop-X.X.X 在Spring包中可以找到。
aopalliance-1.0.jar下载地址,截止到博主写这篇文章开始,这个jar包的最新版还是1.0版本的,5年未更新过了

项目结构:
在这里插入图片描述
代码:

package com.csdn2.AOP.Factorybean;

public interface UserDao {

	public void add();
	public void delete();
}

package com.csdn2.AOP.Factorybean;

public class UserDaoImplement implements UserDao{

	@Override
	public void add() {
		// TODO Auto-generated method stub
		System.out.println("com.csdn2.AOP.Factorybean.UserDaoImplement:添加方法");
	}

	@Override
	public void delete() {
		// TODO Auto-generated method stub
		System.out.println("com.csdn2.AOP.Factorybean.UserDaoImplement:删除方法");
	}
	
}

package com.csdn2.AOP.Factorybean;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class aspect implements MethodInterceptor {

	@Override
	public Object invoke(MethodInvocation arg0) throws Throwable {
		// TODO Auto-generated method stub
		check();
		Object object=arg0.proceed();
		log();
		return null;
	}
	public void check() {
		System.out.println("执行切面中的检查方法.....");
	}
	public void log() {
		System.out.println("执行切面中的日志记录方法.....");
	}
}

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 目标类 -->
	<bean id="userDao" class="com.csdn2.AOP.Factorybean.UserDaoImplement"></bean>
	<!-- 切面类 -->
	<bean id="aspect" class="com.csdn2.AOP.Factorybean.aspect"></bean>
	<!-- 使用spring代理工厂定义一个代理对象 -->
	<bean id="userDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
		<!-- 指定代理实现的接口 -->
		<property name="proxyInterfaces" value="com.csdn2.AOP.Factorybean.UserDao"></property>  
         <!-- 指定目标对象 -->
       <property name="target" ref="userDao"></property>  
         <!-- 指定切面,植入环绕通知 -->
       <property name="interceptorNames" value="aspect"></property>
       <!-- 指定代理方法,true:CGLIB,false:JDK -->
        <property name="proxyTargetClass" value="true"></property>
    </bean>  
</beans>

package com.csdn2.AOP.Factorybean;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class test {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
//		String xmlPath="com/casdn2/AOP/Factorybean/ac.xml";
		ApplicationContext applicationContext=
				new ClassPathXmlApplicationContext("ac.xml");
		UserDao userDao=(UserDao)applicationContext.getBean("userDaoProxy");
		userDao.add();
		System.out.println();
		userDao.delete();
	}

}

执行结果:
在这里插入图片描述

AspectJ开发:

新版本 的Spring框架,建议使用AspectJ来开发AOP。
使用AspectJ实现AOP有两种方式:一种的基于XML的声明式AspectJ、另一种是基于注解的声明式AspectJ。

基于XML的声明式AspectJ:
切面、切入点、通知都通过XML文件进行定义。

aop:config元素及其子元素:

在这里插入图片描述
图片转载源地址

< aop:aspect >元素的属性及其描述:

属性名称描述
id用于定义该切面的唯一标识
ref用于引用普通的Spring Bean

< aop:pointcut >元素的属性及其描述:

属性名称描述
id用于指定切入点的唯一标识
expression用于指定切入点关联的切入点表达式

通知的常用属性及其描述:

属性名称描述
pointcut该属性用于指定一个切入点,Spring将在匹配该表达式的连接点时织入该通知
pointcut-ref该属性用于指定一个已经存在的切入点名称,如配置该代码中
myPointCut通常pointcut和pointcut-ref两个属性只需要使用其中之一
method该属性指定一个方法名,指定将切面Bean中的该方法转换为增强
throwing该属性只对 元素有效,它用与指定一个形象名,异常通知方法可以通过该形参访问目标方法所抛出的异常
returning该属性只对元素有效,它用于指定一个形参名,后置通知方法可以该形参访问目标方法的返回值

流程图:
在这里插入图片描述

项目结构:
在这里插入图片描述

代码

package com.csdn2.AOP.AspectJ.Xml;

public interface UserDao {

	public void add();
	public void delete();
}

package com.csdn2.AOP.AspectJ.Xml;

public class UserDaoImplement implements UserDao{

	@Override
	public void add() {
		// TODO Auto-generated method stub
		System.out.println("com.csdn2.AOP.AspectJ.Xml.UserDaoImplement:添加方法");
	}

	@Override
	public void delete() {
		// TODO Auto-generated method stub
		System.out.println("com.csdn2.AOP.AspectJ.Xml.UserDaoImplement:删除方法");
	}
	
}

package com.csdn2.AOP.AspectJ.Xml;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

public class aspect {
	public void myBefore(JoinPoint joinPoint) {
		System.out.print("前置通知:模拟执行权限检查......");
		System.out.print(",目标类是:"+joinPoint.getClass());
		System.out.println(",被植入增强处理的目标方法为:"+joinPoint.getSignature().getName());
	}
	public void myAfterReturning(JoinPoint joinPoint) {
		System.out.print("后置通知:模拟记录日志....");
		System.out.println("被植入增强处理的目标方法为:"+joinPoint.getSignature().getName());
	}
	public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
		System.out.println("环绕开始:执行目标方法之前,模拟开启事务.....");
		Object object=proceedingJoinPoint.proceed();
		System.out.println("环绕结束:执行目标方法之后,模拟关闭事务.....");
		return object;
	}
	public void myAfterThrowing(JoinPoint joinPoint ,Throwable e) {
		System.out.println("异常处理:"+"出错了"+e.getMessage());
	}
	public void myAfter() {
		System.out.println("最终通知:模拟方法结束后的释放资源");
	}
}

package com.csdn2.AOP.AspectJ.Xml;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class test {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ApplicationContext applicationContext=
				new ClassPathXmlApplicationContext("com/csdn2/AOP/AspectJ/Xml/ac.xml");
		UserDao userDao=
				(UserDao)applicationContext.getBean("userDao");
		userDao.add();
	}

}

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 目标类 -->
    <bean id="userDao" class="com.csdn2.AOP.AspectJ.Xml.UserDaoImplement"></bean>
<!-- 切面 -->
	<bean id="aspect" class="com.csdn2.AOP.AspectJ.Xml.aspect"></bean>
<!-- aop编程 -->
	<aop:config>
		<aop:aspect ref="aspect">
			<aop:pointcut id="mypointcut" expression="execution(* com.csdn2.AOP.AspectJ.Xml.*.*(..))" />
				<aop:before method="myBefore" pointcut-ref="mypointcut"/>
				<aop:after-returning method="myAfterReturning" pointcut-ref="mypointcut" returning="returnVal"/>
				<aop:around method="myAround" pointcut-ref="mypointcut"/>
				<aop:after-throwing method="myAfterThrowing" pointcut-ref="mypointcut" throwing="e"/>
				<aop:after method="myAfter" pointcut-ref="mypointcut"/>
		</aop:aspect>
	</aop:config>	
</beans>

运行结果
在这里插入图片描述

基于注解的声明式AspectJ*:

虽然与基于代理类的AOP实现相比,基于XML的声明式AspectJ要便捷很多了,但是任然需要在XML文件中配置大量的代码信息。为此AspectJ为AOP提供了一套注解,用以取代Spring配置文件中为实现AOP功能所配置的臃肿代码。

流程图同上

项目结构:
在这里插入图片描述
代码:

package com.csdn2.AOP.AspectJ.annotation;

public interface UserDao {

	public void add();
	public void delete();
}

package com.csdn2.AOP.AspectJ.annotation;

import org.springframework.stereotype.Repository;

@Repository("userDao")
public class UserDaoImplement implements UserDao{

	@Override
	public void add() {
		// TODO Auto-generated method stub
		System.out.println("com.csdn2.AOP.AspectJ:添加方法");
	}

	@Override
	public void delete() {
		// TODO Auto-generated method stub
		System.out.println("com.csdn2.AOP.AspectJ.Xml:删除方法");
	}
	
}

package com.csdn2.AOP.AspectJ.annotation;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class aspect {
	@Pointcut("execution(* com.csdn2.AOP.AspectJ.annotation.*.*(..))")
	private void myPointCut() {}//使用一个返回值为void ,方法体为空 的方法来命名切入点
	@Before("myPointCut()")
	public void myBefore(JoinPoint joinPoint) {
		System.out.print("前置通知:模拟执行权限检查......");
		System.out.print(",目标类是:"+joinPoint.getClass());
		System.out.println(",被植入增强处理的目标方法为:"+joinPoint.getSignature().getName());
	}
	@AfterReturning("myPointCut()")
	public void myAfterReturning(JoinPoint joinPoint) {
		System.out.print("后置通知:模拟记录日志....");
		System.out.println("被植入增强处理的目标方法为:"+joinPoint.getSignature().getName());
	}
	@Around("myPointCut()")
	public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
		System.out.println("环绕开始:执行目标方法之前,模拟开启事务.....");
		Object object=proceedingJoinPoint.proceed();
		System.out.println("环绕结束:执行目标方法之后,模拟关闭事务.....");
		return object;
	}
	@AfterThrowing(value="myPointCut()" ,throwing="e")
	public void myAfterThrowing(JoinPoint joinPoint ,Throwable e) {
		System.out.println("异常处理:"+"出错了"+e.getMessage());
	}
	@After("myPointCut()")
	public void myAfter() {
		System.out.println("最终通知:模拟方法结束后的释放资源");
	}
}

<?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"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
	<!-- 指定需要扫描的包,使注解生效 -->
	<context:component-scan base-package="com.csdn2.AOP.AspectJ.annotation"/>
	<!-- 启动基于注解的声明式AspectJ支持 -->
	<aop:aspectj-autoproxy />
</beans>
package com.csdn2.AOP.AspectJ.annotation;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class test {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ApplicationContext applicationContext=
				new ClassPathXmlApplicationContext("com/csdn2/AOP/AspectJ/annotation/ac.xml");
		UserDao userDao=
				(UserDao)applicationContext.getBean("userDao");
		userDao.add();
	}

}

运行结果:
目标方法前后通知的执行顺序发生了变化:
在这里插入图片描述

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值