AOP介绍
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP(面向对象编程)的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,的一种是函数式编程衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
经典应用:事务管理、日志、安全检查、性能监视、缓存等…
Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码
AspectJ是一个基于Java语言的AOP框架,Spring2.0开始,Spring AOP引入对Aspect的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入
实现原理 :
AOP底层将采用代理机制进行实现。
接口 + 实现类 :Spring采用 Jdk 的动态代理Proxy。
实现类:Spring 采用 Cglib字节码增强。
术语
1.target:目标类,需要被代理的类。例如:UserService
2.Joinpoint(连接点):所谓连接点是指那些可能被拦截到的方法。例如:所有的方法
3.PointCut 切入点:已经被增强的连接点。例如:addUser()
4.advice 通知/增强,增强代码。例如:after、before
5. Weaving(织入):是指把增强advice应用到目标对象target来创建新的代理对象proxy的过程.
6.proxy 代理类
7. Aspect(切面): 是切入点pointcut和通知advice的结合
一个线是一个特殊的面。
一个切入点和一个通知,组成成一个特殊的面。
Spring - AOP 全自动
需求 : UserService UserServiceImpl 其中有方法add() , 在执行add()方法前开启事务,执行方法后提交事务
1.导包 :
4+1+aop.jar+aspects.jar+aspectj提供的2个jar包
2.编写对应的接口 :
package com.zzx.service;
public interface UserService {
void add();
}
3.编写对应的接口实现类 :
package com.zzx.service.impl;
import com.zzx.dao.UserDao;
import com.zzx.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service("userService")
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("马赛克执行了");
//userDao.add();
}
}
4.编写切面 :
package com.zzx.aspect;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
* @auther ZhengZiXuan
* @date 2021/3/2 14:54
* @desc SpringAOP的切面
*/
public class MyTXAspect implements MethodInterceptor {
/**
* 将重写的invoke方法,理解为上午的jdk/cglib中的拦截方法
* 参数: methodInvocation 目标类的目标方法
*/
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("开启事务");
//让方法执行
Object result = methodInvocation.proceed();
System.out.println("提交事务");
return result;
}
}
5.配置application_aop.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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
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-2.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 目标类 -->
<bean id="userService" class="com.zzx.service.impl.UserServiceImpl"> </bean>
<!-- 切面类 -->
<bean id="aspect" class="com.zzx.aspect.MyTXAspect"></bean>
<!-- 织入 -->
<aop:config>
<!--切入点:将切面切入的位置-->
<aop:pointcut id="myP" expression="execution(* com.zzx.service.impl.UserServiceImpl.*(..))"></aop:pointcut>
<aop:advisor advice-ref="aspect" pointcut-ref="myP"></aop:advisor>
</aop:config>
</beans>
6.编写测试类,测试代码 :
@Test
public void m1(){
String path = "application_aop.xml";
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext(path);
UserService userService =(UserService) app.getBean("userService");
userService.add();
}
7.运行的效果图如下 :
AspectJ – AOP
介绍 :
AspectJ是一个基于Java语言的AOP框架
Spring2.0以后新增了对AspectJ切点表达式支持
@AspectJ 是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面
新版本Spring框架,建议使用AspectJ方式来开发AOP
主要用途 : 自定义开发
AspectJ的通知方式
before:前置通知(应用:各种校验)在方法执行前执行,如果通知抛出异常,阻止方法运行
afterReturning:后置通知(应用:常规数据处理)方法正常返回后执行,如果方法中抛出异常,通知无法执行.必须在方法执行后才执行,所以可以获得方法的返回值。
around:环绕通知(应用:十分强大,可以做任何事情),方法执行前后分别执行,可以阻止方法的执行,必须手动执行目标方法 .
afterThrowing:抛出异常通知(应用:包装异常信息),方法抛出异常后执行,如果方法没有抛出异常,无法执行 .
after:最终通知(应用:清理现场),方法执行完毕后执行,无论方法中是否出现异常 .
演示 :
1.编写对应的接口 :
package com.zzx.service;
public interface UserService {
void add();
}
2.编写对应的接口的实现类 :
package com.zzx.service.impl;
import com.zzx.dao.UserDao;
import com.zzx.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service("userService")
//@Service
public class UserServiceImpl implements UserService {
@Autowired
@Qualifier("userDaoImpl")
private UserDao userDao;
@Override
public void add() {
System.out.println("马赛克执行了");
//userDao.add();
}
}
3.编写切面 :
package com.zzx.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* @auther ZhengZiXuan
* @date 2021/3/2 16:02
* @desc AspectJ的切面
* 特点:
* 1.不再实现接口
* 2.不要求重写指定的方法
* 3.根据自身需求,自定义所需的方法
*/
public class MyAspect {
//前置通知
public void MyBefore(JoinPoint joinPoint){
System.out.println("前置通知 --> 权限校验" +joinPoint.getSignature().getName());
}
//后置通知
// @param joinPoint
public void myAfter(JoinPoint joinPoint,Object o){
System.out.println("后置通知 --> 日志记录" +joinPoint.getSignature().getName() +"返回值"+o);
}
//环绕通知
public void myAround(ProceedingJoinPoint joinPoint){
try {
System.out.println("环绕通知 --> 前 --> 开启事务");
joinPoint.proceed();
System.out.println("环绕通知 --> 后 --> 提交事务");
}catch (Throwable throwable){
throwable.printStackTrace();
}
}
}
4.配置application_aopj.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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
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-2.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 注解扫描 -->
<context:component-scan base-package="com.zzx"/>
<!-- 目标类 -->
<bean id="userService" class="com.zzx.service.impl.UserServiceImpl"> </bean>
<!-- 切面类 -->
<bean id="aspect" class="com.zzx.aspect.MyAspect"></bean>
<!-- 织入 -->
<aop:config>
<!--关联切面-->
<aop:aspect ref="aspect">
<!-- 定义切入点 -->
<aop:pointcut id="Mys" expression="execution(* com.zzx.service.impl.*.*(..))"></aop:pointcut>
<!-- 定义增强方法 前置通知MyBefore是切面类中的方法名 -->
<aop:before method="MyBefore" pointcut-ref="Mys"></aop:before>
<aop:after-returning method="myAfter" pointcut-ref="Mys" returning="o"></aop:after-returning>
<aop:around method="myAround" pointcut-ref="Mys"></aop:around>
</aop:aspect>
</aop:config>
</beans>
5.编写测试类,测试代码 :
@Test
public void m2(){
String path = "application_aopj.xml";
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext(path);
UserService userService =(UserService) app.getBean("userService");
userService.add();
}
6.运行的效果图如下 :
注解实现AOP
1.编写对应的接口 :
package com.zzx.service;
public interface UserService {
void add();
}
2.编写对应的接口的实现类 :
package com.zzx.service.impl;
import com.zzx.dao.UserDao;
import com.zzx.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service("userService")
//@Service
public class UserServiceImpl implements UserService {
@Autowired
@Qualifier("userDaoImpl")
private UserDao userDao;
@Override
public void add() {
System.out.println("马赛克执行了");
//userDao.add();
}
}
3.编写切面 :
package com.zhiyou.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* @author ZhengZiXuan
* @date 2021/3/2
* @desc
*/
@Component // 1 声明该类交给Spring托管
@Aspect // 2 声明该类是切面类
public class MyAspectAnno {
/**
* 公共的切入点表达式
*/
@Pointcut("execution(* com.zhiyou.service.impl.*.*(..))")
public void pointcut() { }
/**
* 前置通知
* JoinPoint 目标方法
* @Before 声明前置通知的注解
*/
@Before("pointcut()")
public void myBefore(JoinPoint joinPoint) {
// 获得目标方法的名字
System.out.println("前置通知 - 权限校验 - "+joinPoint.getSignature().getName() );
}
/**
* 后置通知
* @param joinPoint 目标方法
* joinPoint.getTarget() 得到目标对象
*/
// @After("execution(* com.zhiyou.service.impl.*.*(..))")
public void myAfter(JoinPoint joinPoint){
System.out.println("后置通知 - 日志记录 - "+joinPoint.getTarget() );
}
/**
* 后置返回通知
* joinPoint 目标方法
* ret 目标方法执行后,返回的返回值
*/
@AfterReturning(value="execution(* com.zhiyou.service.impl.*.*(..))",returning = "ret")
public void myAfterReturn(JoinPoint joinPoint,Object ret) {
System.out.println("后置返回通知 - 返回值 - "+ret );
}
// 环绕通知
// @Around("execution(* com.zhiyou.service.impl.*.*(..))")
public void myAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知 - 前 - 开启事务" );
joinPoint.proceed();
System.out.println("环绕通知 - 后 - 提交事务" );
}
}
4.配置application_aopj.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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描组件,使注解生效 -->
<context:component-scan base-package="com.zhiyou" />
<!-- 开启AspectJ注解 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
5.编写测试类,测试代码 :
@Test
public void test02() {
// 1 获得容器
String path = "application-aopanno.xml";
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext(path);
// 2 从容器得到对象
UserService userService = (UserService) app.getBean("userServiceImpl");
// 3 使用对象
Integer i = userService.add();
System.out.println(i );
}