Julywhj 2019-10-28
面向切面:AOP
在不修改源代码的基础上,对方法进行增强。AOP的底层原理就是代理技术(第一种:jdk的动态代理(编写程序必须要有接口)。第二种:cglib代理技术(生成类的子类)。如果编写的程序有借口,则spring框架会自动使用jdk的动态代理技术增强,)。
Joinpoint(连接点) 所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
Pointcut(切入点) -- 所谓切入点是指我们要对哪些Joinpoint进行拦截的定义
Advice(通知/增强)-- 所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
Target(目标对象)-- 代理的目标对象
Weaving(织入)-- 是指把增强应用到目标对象来创建新的代理对象的过程
Proxy(代理)-- 一个类被AOP织入增强后,就产生一个结果代理类
Aspect(切面)-- 是切入点和通知的结合,以后自己来编写和配置的
创建一个普通的Maven项目工程引入坐标
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.12</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <!-- AOP联盟 --> <dependency> <groupId>aopalliance</groupId> <artifactId>aopalliance</artifactId> <version>1.0</version> </dependency> <!-- Spring Aspects --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.0.2.RELEASE</version> </dependency> <!-- aspectj --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.3</version> </dependency> </dependencies>
创建Spring的配置文件,引入具体的AOP的schema约束
<?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" 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"> </beans>
package cn.tx.demo2; public class UserServiceImpl implements UserService { ? @Override public void save() { System.out.println("业务层:保存用户..."); } ? }
<bean id="userService" class="cn.tx.demo2.UserServiceImpl"/>
package cn.tx.demo2; ? /** * 自定义切面类 = 切入点(表达式) + 通知(增强的代码) */ public class MyXmlAspect { ? /** * 通知 */ public void log(){ // 发送手机短信 // 发送邮件/记录日志/事务管理 ? System.out.println("增强的方法执行了..."); } ? }
在配置文件中定义切面类
1 <bean id="myXmlAspect" class="cn.tx.demo2.MyXmlAspect"/>
在配置文件中完成aop的配置
<!--配置AOP的增强--> <aop:config> <!--配置切面 = 切入点 + 通知组成--> <aop:aspect ref="myXmlAspect"> <!--前置通知:UserServiceImpl的save方法执行前,会增强--> <aop:before method="log" pointcut="execution(public void cn.tx.demo2.UserServiceImpl.save())" /> </aop:aspect> </aop:config>
package cn.tx.test; ? import cn.tx.demo2.UserService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext_demo2.xml") public class Demo2 { ? @Autowired private UserService userService; ? /** * 测试 */ @Test public void run1(){ userService.save(); } ? }
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
修饰符可以省略不写,不是必须要出现的。
返回值类型是不能省略不写的,根据你的方法来编写返回值。可以使用 * 代替。
包名例如:com.tx.demo3.BookDaoImpl
类名也可以使用 * 号代替,也有类似的写法:*DaoImpl
方法也可以使用 * 号代替
参数如果是一个参数可以使用 * 号代替,如果想代表任意参数使用 ..
<?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" 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"> <!--bean管理--> <bean id="userService" class="cn.tx.demo1.UserServiceImpl" /> <!--=================编写AOP配置文件====================--> <!--先配置切面类--> <bean id="myXmlAspect" class="cn.tx.demo1.MyXmlAspect" /> <!--配置AOP的增强--> <aop:config> <!--正在配置切面,引入真正切面对象--> <aop:aspect ref="myXmlAspect"> <!--配置的是前置通知:目标对象方法执行前,先增强。method="切面类中通知的方法" pointcut="切入点的表达式"--> <!-- 切入点的表达式 execution() 写法是固定的 public 可以省略不写的 方法返回值 void int String * 通用的写法 包名 * 通用的写法 类名 * 推荐的写法 *ServiceImpl 例如:UserServiceImpl DeptServiceImpl 方法名称 * 推荐写法:save* 方法参数列表 .. == Object... obj Object类型的可变参数 <aop:before method="log" pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" /> --> <!-- 通知类型 前置通知:目标对象方法执行前,先增强。 <aop:before method="log" pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" /> 最终通知:目标对象方法执行成功或者失败,都会增强。finally <aop:after method="log" pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" /> 后置通知:目标对象方法执行成功,才会增强。 <aop:after-returning method="log" pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" /> 异常通知:目标对象方法执行失败,才会增强。 <aop:after-throwing method="log" pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" /> <aop:before method="begin" pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" /> <aop:after-returning method="commit" pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" /> <aop:after-throwing method="rollback" pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" /> <aop:after method="close" pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" /> 环绕通知:自己决定增强的位置。使用了环绕通知,目标对象的方法默认没有执行的,需要自己手动执行目标对象方法。 --> <aop:around method="logAroud" pointcut="execution(* cn.tx.*.*ServiceImpl.save*(..))" /> </aop:aspect> </aop:config> </beans>
Spring的AOP技术-注解方式
package cn.tx.demo3; ? import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; ? @Component // 把该类交给IOC去管理 @Aspect // 声明是切面类 == <aop:aspect ref="myXmlAspect"> public class MyAnnoAspect { ? /** * 通知的方法 */ // @Before(value = "切入点的表达式") @Before(value = "execution(public * cn.tx.demo3.OrderServiceImpl.save(..))") public void log(){ System.out.println("增强了..."); } ? } ?
配置文件中开启自动代理
<aop:aspectj-autoproxy/>
测试方法
package cn.tx.test; ? import cn.tx.demo2.UserService; import cn.tx.demo3.OrderService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext_demo3.xml") public class Demo3 { ? @Autowired private OrderService orderService; ? /** * 测试 */ @Test public void run1(){ orderService.save(); } ? }
通知类型注解
@Before -- 前置通知 ? @AfterReturing -- 后置通知 ? @Around -- 环绕通知(目标对象方法默认不执行的,需要手动执行) ? @After -- 最终通知 ? @AfterThrowing -- 异常抛出通知
纯注解的方式
package cn.tx.demo3; ? import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; ? @Configuration // 配置类 @ComponentScan(value = "cn.tx.demo3") // 扫描包 @EnableAspectJAutoProxy // 开启自动代理 == <aop:aspectj-autoproxy /> public class SpringConfig { }