JavaEE开发框架-Spring-AOC
AOP概述
AOP(Aspect Oriented Programming),面向切面编程,通过预编译的方式在运行期通过动态代理实现一种技术。
AOP是OOP(面向对象)延续,利用AOP可以实现业务和切面的逻辑分离,降低耦合度,程序的重用性提高
基本概念扫盲:
Aspect(切面):对横切性关注点(前置后置与异常情况下所做的事)的一种抽象
Jionpoint(连接点):要拦截的方法
Pointcut(切点):是连接点的集合
Advice(通知):在连接点前后或异常情况来做的事情,前置通知,后置通知,返回通知,异常通知,环绕通知
Target(目标对象):代理的目标对象
Weave(织入):切面应用到目标对象并且导致proxy对象创建的过程叫织入
Spring
第一步:引入Spring的aop的包
AOP环境配置
IDEA导包
配置文件内容(AplicationContext.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
<!-- 开启以注解形式切面的驱动 -->
<aop:aspectj-autoproxy/>
</beans>
定义切面
- 创建一个类,在上加入@Aspect注解
- 在任意方法上加上切点注解@PointCut并配好execution属性(确定拦截目标)
- 定义前置通知并关联切点注解的方法
package com.aspect;
import com.model.User;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
//切面类加上注解@Aspect
@Aspect
public class PermAspect {
/*
* 第一个*:拦截方法的返回值,*代表任意
* 第一个..子包下
* 第二个*:所有的类
* 第三个*:所有的方法
* 第二个..所有的参数
* */
@Pointcut("execution(* com.service..*.*(..))")
public void anyMethod(){
}
/*
*Before前置通知
*anyMethod():指定前置通知的切点
*args(参数)
* */
@Before("anyMethod()&&args(user)")
/*或者写成以下形式
* @Before(value = "anyMethod()&&args(param)",argNames = "param")
* */
public void preAdvice(User user){
System.out.println(user);
System.out.println("执行前置通知");
}
}
在配置文件中配置切面bean与被切的目标bean
<!--依赖注入-->
<bean id="userDao" class="com.daoimpl.UserDaoImpl"></bean>
<bean id="userDaoService" class="com.serviceimpl.UserDaoServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
<!--定义切面bean-->
<bean id="permApect" class="com.aspect.PermAspect"></bean>
连接点:
package com.serviceimpl;
import com.dao.UserDao;
import com.model.User;
import com.service.UserDaoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
public class UserDaoServiceImpl implements UserDaoService {
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
private UserDao userDao ;
@Override
public void save() {
userDao.save();
}
/*连接点->要拦截的方法*/
@Override
public void saveUser(User user) {
userDao.saveUser(user);
}
}
测试
public static void main(String[] args) {
User user = new User("三金",15) ;
//创建spring容器
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
//从容器中获得bean
UserDaoService service = (UserDaoService) context.getBean("userDaoService");
//调用方法
service.saveUser(user);
}
结果
前置通知
在上面已经讲过,这里不再赘述
后置通知
定义一个void方法,在方法上加**@After**注解,在业务方法执行之后来执行,其余使用方法和前置拦截一样
返回通知
在返回通知里面可以对业务方法的返回值做最后的统一加工
切面代码:
/**
* pointcut:指定返回通知的切点
* returning:指定返回值和返回通知的方法的参数名称要一致
*
* @param user
*/
@AfterReturning(pointcut = "anyMethod()",returning = "user")
public void returnAdvice(User user){
user.setName("四金");
System.out.println("执行返回通知");
System.out.println(user);
}
环绕通知
最常用的一种通知,该类型通知涵盖以上所有通知的功能
package com.aspect;
import com.model.User;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
//切面类加上注解@Aspect
@Aspect
public class PermAspect {
/*
* 第一个*:拦截方法的返回值,*代表任意
* 第一个..子包下
* 第二个*:所有的类
* 第三个*:所有的方法
* 第二个..所有的参数
* */
@Pointcut("execution(* com.service..*.*(..))")
public void anyMethod(){
}
@Around("anyMethod()")
public Object aroundAdvice(ProceedingJoinPoint jp){
//获得拦截的业务方法的参数
Object[] args = jp.getArgs();
System.out.println("环绕通知-前"+"-------传递的参数:"+args[0]);
Object obj = null;
User user = null;
try {
//proceed执行业务方法,也可以使用无参数,返回值就是业务方法的返回值
obj = jp.proceed(args);
user = (User) obj;
} catch (Throwable e) {
//例外通知处理逻辑可在catch中处理
e.printStackTrace();
}
//后置通知
user.setName("五金");
System.out.println("环绕通知-后"+"-------返回值:"+obj);
return obj;
}
}
AOP配置文件中定义切面
<aop:config>
<!--
切点配置:
expression:切点的表达式
id:唯一标识
-->
<aop:pointcut expression="execution(* com.rl.spring.service..*.*(..))" id="mycut"/>
<!-- 切面配置
ref:要关联的切面类,在切面类(不用加注解)中定义切面方法
-->
<aop:aspect ref="permApect">
<!-- 前置通知
method:切面类中方法
pointcut-ref:切点
-->
<aop:before method="preAdvice" pointcut-ref="mycut"/>
<!-- 后置通知 -->
<aop:after method="afterAdvice" pointcut-ref="mycut"/>
<!-- 返回通知
returning:业务方法返回的值,也是切面方法的参数,一般设为为Object
-->
<aop:after-returning method="returnAdvice" pointcut-ref="mycut" returning="returnval"/>
<!--
例外外通
throwing:抛出的异常的变量名知
-->
<aop:after-throwing method="exceptionAdvice" pointcut-ref="mycut" throwing="ex"/>
<!--
环绕通知
-->
<aop:around method="aroundAdvice" pointcut-ref="mycut"/>
</aop:aspect>
</aop:config>
切面类permApect:
JoinPoint类可以方便我们操作参数信息
public class PermAspect {
public void preAdvice(JoinPoint jp){
Object[] args = jp.getArgs();
if(args.length > 0)
System.out.println("执行前置通知-----------业务参数:"+args[0]);
}
public void afterAdvice(JoinPoint jp){
Object[] args = jp.getArgs();
if(args.length > 0)
System.out.println("执行后置通知-----------业务参数:"+args[0]);
}
public void returnAdvice(JoinPoint jp, Object returnval){
if(returnval instanceof User){
User user = (User) returnval;
user.setUsername("六金");
}
System.out.println("返回通知----------返回的值"+returnval);
}
public void exceptionAdvice(JoinPoint jp, Exception ex){
System.out.println("例外通知------------");
ex.printStackTrace();
}
public Object aroundAdvice(ProceedingJoinPoint jp){
Object[] objs = jp.getArgs();
if(objs.length>0)
System.out.println("执行环绕通知前-----------业务参数:"+objs[0]);
Object obj = null;
try {
obj = jp.proceed();
System.out.println("执行环绕通知后-----------业务参数:"+objs[0]);
} catch (Throwable e) {
e.printStackTrace();
}
return obj;
}
}