springAOP面向切面思想
spring 三大思想
IOC 控制反转
DI 依赖注入
AOP( Aspect Oriented Programming) 面向切面编程 弥补面向对象的不足
是通过预编译方式和运行期动态代理实现程
序功能的统一维护的一种技术。
AOP 是 OOP 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,利用AOP可以对业务逻辑的各
个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
这样做的好处是:
- 在程序运行期间,在不修改源码的情况下对方法进行功能增强
- 逻辑清晰,开发核心业务的时候,不必关注增强业务的代码
- 减少重复代码,提高开发效率,便于后期维护
AOP相关概念
Target(目标对象):代理的目标对象
Proxy (代理):一个类被 AOP 织入增强后,就产生一个结果代理类
Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方 法类型的连接点
Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义
Advice(通知/ 增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知 分类:前置通知、后置通知、异常通知、最终通知、环绕通知
Aspect(切面):是切入点和通知(引介)的结合
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采 用编译期织入和类装载期织入
OOP 面向对象编程 对象 类
先通过代码进行简单的了解一下aop
创建一个maven项目
实现:通过controller层调用service层然后调用dao层的方法
创建一个maven项目,导包(注意:把junit包的版本改为4.12以上),
创建一个包的层级,然后运行看看是否能走的通。
在resources下创建applicationContext.xml配置文件
比如创建一个bean类在pojo包
package com.lanou.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component(value = "user")
public class User {
@Value(value = "123")
private int id;
@Value("张三丰")
private String username;
@Value("123456")
private String password;
//setter getter 有参 无参 toString方法
}
比如dao层
接口
import com.lanou.pojo.User;
import java.util.List;
public interface UserDao {
List<User> findAll();
User findUserById(int id);
int updateUser(User user);
int deleteUser(int id);
}
实现类
import com.lanou.dao.UserDao;
import com.lanou.pojo.User;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository(value = "userDao")
public class UserDaoImpl implements UserDao {
@Override
public List<User> findAll() {
System.out.println("---------dao findAll 执行了--------");
return null;
}
@Override
public User findUserById(int id) {
return null;
}
@Override
public int updateUser(User user) {
return 0;
}
@Override
public int deleteUser(int id) {
return 0;
}
}
比如service层
import com.lanou.pojo.User;
import java.util.List;
public interface UserService {
List<User> findAll();
User findUserById(int id);
int updateUser(User user);
int deleteUser(int id);
}
service实现类
import com.lanou.dao.UserDao;
import com.lanou.pojo.User;
import com.lanou.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import java.util.List;
@Service(value = "userService")
public class UserServiceImpl implements UserService {
@Autowired
@Qualifier(value = "userDao")
private UserDao userDao;
@Override
public List<User> findAll() {
return userDao.findAll();
}
@Override
public User findUserById(int id) {
return null;
}
@Override
public int updateUser(User user) {
return 0;
}
@Override
public int deleteUser(int id) {
return 0;
}
}
比如:控制层
import com.lanou.pojo.User;
import com.lanou.service.Impl.UserServiceImpl;
import com.lanou.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import java.util.List;
@Controller(value = "userController")
public class UserController {
@Autowired
@Qualifier(value = "userService")
private UserService userService;
public void findAll(){
System.out.println("开始执行当前方法之前");
List<User> userList = userService.findAll();
System.out.println("方法执行结束");
}
}
测试类中:
import com.lanou.controller.UserController;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestSpringJUnit {
@Autowired
@Qualifier(value = "userController")
private UserController userController;
@Test
public void findAll() {
userController.findAll();
}
}
走的通
基于xml文件的切面编程
使用aop需要导入的jar包
spring-aop
spring-aspects
aopalliance
aspectjweaver
在maven会自动导入依赖的jar包
所以只需要在maven库中找到spring-aspects,aopalliance这两个jar包就行了
<!--aop 面向切面编程需要的jar包-->
<!--aop-->
<!--aspects-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.14.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
在utils包中定义一个切面类
/*
日志切面类
*/
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
//声明当前类是一个切面类
public class LogAdvice {
/*
切面方法
前置通知方法
*/
public void before(){
System.out.println("--前置通知----");
}
/*
切面方法
后置通知方法 不管是否异常都返回
*/
public void after(){
System.out.println("--后置通知----");
}
/*
后置通知 如果异常不返回值
*/
public void afterReturning(){
System.out.println("---后置通知--无异常-");
}
/*
后置通知 异常返回
*/
public void afterException(){
System.out.println("--后置通知--异常通知--");
}
/*
环绕通知
*/
public Object around(ProceedingJoinPoint point) throws Throwable {
System.out.println("环绕通知--前置通知");
Object object = point.proceed();//继续调用目标方法
System.out.println("环绕通知--后置通知");
return object;
}
}
在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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:annotation-config></context:annotation-config>
<context:component-scan base-package="com.lanou"></context:component-scan>
<!--定义切面信息-->
<bean id="logAdvice" class="com.lanou.utils.LogAdvice2"></bean>
<!--定义切入点-->
<aop:config>
<!--
切入点
id : 名称
expression : 表达式 找到包、类、方法
execution(* com.lanou.service.*.*(..))
第一个* 方法的返回值类型
com.lanou.service 包名
第二个* 类名
第三个* 方法名
(..) 任意参数
-->
<aop:pointcut id="pc" expression="execution(* com.lanou.service.*.*(..))"/>
<!--定义关联的切面-->
<aop:aspect ref="logAdvice">
<aop:before method="before" pointcut-ref="pc"></aop:before>
<aop:after method="after" pointcut-ref="pc"></aop:after>
<aop:after-returning method="afterReturning" pointcut-ref="pc"></aop:after-returning>
<aop:after-throwing method="afterException" pointcut-ref="pc"></aop:after-throwing>
<aop:around method="around" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
</beans>
在测试类中运行测试方法可以看到,在核心逻辑执行前执行前置通知,环绕通知。oop是感觉是纵向的,但是aop就像是横向的给你切开,加上你想要执行的增强业务(前置通知属于增强业务),在执行核心业务(dao findAll核心业务)之前执行前置业务
基于注解的AOP
在xml文件中开启注解
(注意:开启注解的时候把xml配置的给注释喽,因为我测试的时候,感觉xml配置的优先级高于注解的优先级)
<!--开启aop注解-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
在utils包中LogAdvice加上注解
/*
日志切面类
*/
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component(value = "logAdvice")
//声明当前类是一个切面类
@Aspect
public class LogAdvice {
/*
如果表达式相同就不用再配置别的,
可以这样加上@Pointcut("execution(* com.lanou.service.*.*(..))")
注解让每次环绕的时候加入"LogAdvice.pc()"就不用每次都配置了
*/
@Pointcut("execution(* com.lanou.service.*.*(..))")
public void pc(){
}
/*
切面方法
前置通知方法
每次都要跟上一个表达式有没有一个方法,如果表达式相同就不用再配置的其他的
*/
@Before("LogAdvice.pc()")
public void before(){
System.out.println("--前置通知----");
}
/*
切面方法
后置通知方法 不管是否异常都返回
*/
@After("execution(* com.lanou.service.*.*(..))")
public void after(){
System.out.println("--后置通知----");
}
/*
后置通知 如果异常不返回值
*/
@AfterReturning("execution(* com.lanou.service.*.*(..))")
public void afterReturning(){
System.out.println("---后置通知--无异常-");
}
/*
后置通知 异常返回
*/
@AfterThrowing("execution(* com.lanou.service.*.*(..))")
public void afterException(){
System.out.println("--后置通知--异常通知--");
}
/*
环绕通知
*/
@Around("execution(* com.lanou.service.*.*(..))")
public Object around(ProceedingJoinPoint point) throws Throwable {
System.out.println("环绕通知--前置通知");
Object object = point.proceed();//继续调用目标方法
System.out.println("环绕通知--后置通知");
return object;
}
}
调用测试方法:跟在xml中的配置是一个效果