Part 1
什么叫面向切面?
AOP 即 Aspect Oriented Program 面向切面编程
首先,在面向切面编程的思想里面,把功能分为核心业务功能,和周边功能。
所谓的核心业务,比如登陆,增加数据,删除数据都叫核心业务
所谓的周边功能,比如性能统计,日志,事务管理等等
周边功能在Spring的面向切面编程AOP思想里,即被定义为切面
在面向切面编程AOP的思想里面,核心业务功能和切面功能分别独立进行开发
然后把切面功能和核心业务功能"编织"在一起,这就叫AOP
Part 2
思路
1. 功能分两大类,辅助功能和核心业务功能
2. 辅助功能和核心业务功能彼此独立进行开发
3. 比如登陆功能,即便是没有性能统计和日志输出,也可以正常运行
4. 如果有需要,就把"日志输出" 功能和 "登陆" 功能编织在一起,这样登陆的时候,就可以看到日志输出了
5. 辅助功能,又叫做切面,这种能够选择性的,低耦合的把切面和核心业务功能结合在一起的编程思想,就叫做切面编程
![v2-1257a70d83e9b618e3b78d8bafc454f4_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/7e8489e2f858c5868ac608d847166ed2.jpeg)
Part 3
准备业务接口,和实现类,模拟实现一个对一个用户的CURD。
业务接口
package cn.vaefun.service;
public interface UserService {
public void add(); //添加用户
public void delete(); //删除用户
public void update(); //更新用户
public void search(); //查找用户
}
实现类
package cn.vaefun.dao;
import cn.vaefun.service.UserService;
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("添加用户");
}
public void delete() {
System.out.println("删除用户");
}
public void update() {
System.out.println("更新用户");
}
public void search() {
System.out.println("查找用户");
}
}
Part 6
准备一个自定义的切面类,定义before()和after()。
package cn.vaefun.aspect;
public class LoggerAspect {
public void before(){
System.out.println("方法执行前");
}
public void after(){
System.out.println("方法执行后");
}
}
Part 5
在src下准备配置文件
<?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:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd"
>
<bean name="userServiceImpl" class="cn.vaefun.dao.UserServiceImpl"></bean>
<bean id="loggerAspect" class="cn.vaefun.aspect.LoggerAspect"></bean>
<aop:config>
<aop:aspect ref="loggerAspect"><!-- 为aspect切面类注入依赖位置 -->
<aop:pointcut id="point"
expression="execution(* cn.vaefun.dao.UserServiceImpl.*(..))" />
<!-- 切面的切入点,cn.vaefun.dao.UserServiceImpl里方法执行的时候 -->
<aop:before method="before" pointcut-ref="point"/><!-- 切入点方法执行前 -->
<aop:after method="after" pointcut-ref="point"/><!-- 切入点方法执行后 -->
</aop:aspect>
</aop:config>
</beans>
Part 6
准备测试类,需要注意的是在获取userServiceImpl对象的时候,只能是其实现的接口的对象。这里牵扯到一个知识点:
Spring AOP实现方式有两种
1:使用JDK动态代理,如果被代理的目标实现了至少一个接口,则会使用JDK动态代理,所有该目标类型实现的接口都将被代理。
2:通过CGLIB来为目标对象创建代理,若该目标对象没有实现任何接口,则创建一个CGLIB代理,创建的代理类是目标类的子类。
我的UserServiceImpl实现了UserService接口,如果使用UserServiceImpl接收对象,会报一个错误:com.sun.proxy.$Proxy cannot be cast to *****。
报错的原因,是不能用接口的实现类(UserServiceImpl)来转换Proxy的实现类,它们是同级,应该用共同的接口(UserService)来转换。
除了使用UserService之外,还有另一种解决方案:强制使用CGLIB创建代理,需要引用jar包并修改spring配置文件。此方法日后再深究。
package cn.vaefun.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
//import cn.vaefun.dao.UserServiceImpl;
import cn.vaefun.service.UserService;
public class TestSpring {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userServiceImpl");
userService.add();
userService.search();
}
}
Part 7
运行测试,得到以下结果:
![v2-a2524faf96a314cd757920d91ab0b600_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/9f38510a3894ba3e4a7ec7371f15b57c.jpeg)
cn.vaefun.dao.UserServiceImpl的每一个方法执行前,都会执行cn.vaefun.aspect.LoggerAspect中的before(),cn.vaefun.dao.UserServiceImpl的每一个方法执行后,都会执行cn.vaefun.aspect.LoggerAspect中的after()。