目录
一:基本概念
AOP, Aspect Oriented Programming, 面向切面编程,一种编程范式,指导开发者如何组织程序结构, 是对面向对象编程OOP的升华。OOP(Object Oriented Programming)是纵向对一个事物的抽象, 一个对象包括静态的属性信息, 包括动态的方法信息等。而AOP是横向的对不同事物的抽象, 属性与属性、方法与方法、对象与对象都可以组成一个切面,而用这种思维去设计编程的方式叫做面向切面编程
作用:在不惊动原始设计的基础上为其进行功能增强
Spring理念:无入侵式/无侵入式
二:案例:模拟AOP的基础代码
其实在之前学习BeanPostProcessor时, 在BeanPostProcessor的after方法中使用动态代理对Bean进行了增强, 实际存储到单例池singleObjects中的不是当前目标对象本身, 而是当前目标对象的代理对象Proxy, 这样在调用目标对象方法时, 实际调用的是代理对象Proxy的同名方法, 起到了目标方法前后都进行增强的功能,对该方式进行一下优化,将增强的方法提取出去到一个增强类中,且只对com.tangyuan.service.impl包下的任何类的任何方法进行增强
//自定义增强类
public class My Advice{
public void beforeAdvice() {
System.out.println("beforeAdvice...") ;
}
public void afterAdvice() {
System.out.println("afterAdvice...") ;
}
}
1.创建service类
public interface UserService {
void show1();
void show2();
}
2.实现service的实现类
public class UserServiceImpl implements UserService {
@Override
public void show1() {
System.out.println(show1.....);
}
@Override
public void show2() {
System.out.println(show2.....);
}
}
ps:以上目标对象,目标类已经准备完成
3.创建增强类,内部提供增强方法
public class MyAdvice{
//前置增强方法
public void beforeAdvice(){
System.out.println(前置的增强.....);
}
public void afterAdvice(){
System.out.println(后置的增强.....);
}
}
目的:在执行show1()方法的同时,也要将before和after的方法执行
4.将UserServiceImpl类和MyAdvic类配置到xml文件Spring容器中
<bean id="userService" class="com.tangyuan.service.impl.UserServiceImpl"></bean>
<bean id="myAdvice" class="com.tangyuan.advice.MyAdvice"></bean>
5.BeanProxy对象创建
public class MockAopBeanPostProcessor implements BeanPostProcessor,ApplicationContextAware{
//成员变量
private ApplicationContext applicationContext;
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
//目的:对UserServiceImpl中的show1和show2方法进行增强,增强方法存在于MyAdvice类中
//问题:1.筛选Service,impl包下的所有的类的所有方法都可以进行增强----解决方案if-else
//2.MyAdvice怎么获取到?解决方案:从Spring容器中获得MyAdvice
if(bean.getClass().getPackage().getName().equals("com.tangyuan.service.impl")){
//生成当前bean的Proxy对象
Object beanProxy=Proxy.newProxyInstance(
bean.getClass().getClassLoader(),
bean.getClass().getInterfaces(),
(Object proxy,Method method,Object[] args) -> {
MyAdvice myAdvice=applicationContext.getBean(MyAdvice.class);
//执行增强对象的before方法
myAdvice.beforeAdvice();
//执行目标对象的目标方法
Object result = method.invoke(bean, args);
//执行增强对象的after法
myAdvice.afterAdvice();
return result;
}
);
return beanProxy;
}
return bean;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext=applicationContext;
}
}
6.将 MockAopBeanPostProcessor类配置到xml文件Spring容器中
7.测试
ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
IUserService userService = applicationContext.getBean(UserService.class);
userService.show1();
三:AOP相关概念
切入点范围小,连接点范围大,切入点一定在连接点中
四:AOP入门案例思路分析
目的:在接口执行前输出当前系统时间
开发模式:xml or 注解
思路分析:
1.导入坐标(pom.xml)
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
</dependencies>
spring-context坐标依赖spring-aop坐标:
2.制作连接点方法(原始操作,Dao接口与实现类)
public interface BookDao {
public void save();
public void update();
}
import com.tangyuan.dao.BookDao;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;
@Repository
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println(System.currentTimeMillis());
System.out.println("book dao save ...");
}
public void update(){
System.out.println("book dao update ...");
}
3.制作共性功能(通知类与通知)
创建一个类,用来存储共性功能
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
//通知类必须配置成Spring管理的bean
@Component
//设置当前类为切面类类
@Aspect
public class MyAdvice {
//4.定义切入点
//设置切入点,要求配置在方法上方
@Pointcut("execution(void com.tangyuan.dao.BookDao.update())")
private void pt(){}
//5.绑定切入点与通知关系(切面)
//设置在切入点pt()的前面运行当前操作(前置通知)
@Before("pt()")
public void method(){
System.out.println(System.currentTimeMillis());
}
}
4.定义切入点
切入点定义依托一个不具有实际意义的方法进行,即无参数,无返回值,方法体无实际逻辑
5.绑定切入点与通知关系(切面),并指定通知添加到原始接点的具体执行位置
6.定义通知类受Spring容器管理,并定义当前类为切面类
7.在配置类中进行相关属性的配置,开启Spring对AOP注解驱动支持
五:AOP工作流程
1.Spring容器启动
2.读取所有切面配置中的切入点