1. AOP
1.1 什么是AOP呢?
-
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
-
AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
-
AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
1.2 Spring底层的AOP的实现原理
- 动态代理
- JDK动态代理 : 只能对实现了接口的类产生代理
- Cglib动态代理(类似与javassit 第三方代理技术)对没有实现接口的类产生代理对象,生成子类对象。
1.2.1 JDK 的动态代理
- 1.编写UserDao类
package jdk.daili;
public interface UserDao {
public void save();
public void update();
public void find();
public void delete();
}
- 2.编写UserDaoImpl类
package jdk.daili;
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("保存用户信息");
}
@Override
public void update() {
System.out.println("更新用户信息");
}
@Override
public void find() {
System.out.println("查询用户信息");
}
@Override
public void delete() {
System.out.println("删除用户信息");
}
}
- 编写代理类
package jdk.daili;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//使用JDK的动态代理对UserDao产生代理
public class jdkProxy implements InvocationHandler {
// 实现InvocationHandler
// 将被增强的对象传递到代理中
private UserDao userDao;
public jdkProxy(UserDao userDao) {
this.userDao = userDao;
}
// 产生UserDao代理的方法
public UserDao createProxy() {
UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(
userDao.getClass().getClassLoader(),userDao.getClass().getInterfaces(), this);
return userDaoProxy;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 判断方法名是否是需要增强的
if ("save".equals(method.getName())) {
// 增强该方法
System.out.println("权限校验============");
return method.invoke(userDao, args);
}
return method.invoke(userDao, args);
}
}
- 编写测试类
package jdk.daili;
import org.junit.Test;
public class JDKTest {
@Test
public void demo1() {
UserDao userDao = new UserDaoImpl();
// 创建代理
UserDao proxy = new jdkProxy(userDao).createProxy();
proxy.save();
proxy.update();
proxy.delete();
proxy.find();
}
}
1.2.2 Cglib动态代理
- 编写CustomerDao类
package cglib.daili;
public class CustomerDao {
public void save() {
System.out.println("保存客户。。。");
}
public void find() {
System.out.println("查询客户。。。");
}
public void update() {
System.out.println("更新客户。。。");
}
public void delete() {
System.out.println("删除客户。。。");
}
}
- 编写代理类
package cglib.daili;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class CglibProxy implements MethodInterceptor {
private CustomerDao customerDao;
public CglibProxy(CustomerDao customerDao) {
this.customerDao = customerDao;
}
public CustomerDao createProxy() {
// 1.创建cglib的核心类对象
Enhancer enhancer = new Enhancer();
// 设置父类
enhancer.setSuperclass(customerDao.getClass());
// 设置回调(类似于InvocationHander对象)
enhancer.setCallback(this);
// 创建代理对象
CustomerDao proxy = (CustomerDao) enhancer.create();
return proxy;
}
@Override
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
// 1.判断方法是否是需要增强
if ("save".equals(method.getName())) {
// 增强方法
System.out.println("权限校验一下。。。");
return methodProxy.invokeSuper(proxy, args);
}
return methodProxy.invokeSuper(proxy, args);
}
}
- 编写测试类
package cglib.daili;
import org.junit.Test;
public class TestDemo {
@Test
public void demo1() {
CustomerDao customerDao = new CustomerDao();
CustomerDao proxy = new CglibProxy(customerDao).createProxy();
proxy.save();
proxy.update();
proxy.find();
proxy.delete();
}
}
1.3 Spring的AOP的开发入门
- AOP 思想最早是由AOP联盟组织提出的,Spring使用这种思想最好的框架
- Spring的AOP有自己实现的方式(非常繁琐)AspectJ是一个AOP框架,Spring引入AspectJ作为自身AOP的开发
- Spring有两套AOP的开发方式
- Spring的传统方式(已经弃用)
- Spring基于AspectJ的AOP的开发(在用)
- 相关术语
1.3.1 spring AOP 的入门
- 1.引入配置文件
<?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"
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">
<!--配置目标对象,被增强的对象 -->
<bean id="productDao" class="springAop.ProductDaoImpl"></bean>
</beans>
- 2.编写目标类
package springAop;
public class ProductDaoImpl implements ProductDao {
@Override
public void save() {
System.out.println("保存商品。。。");
}
@Override
public void find() {
System.out.println("查询商品。。。");
}
@Override
public void update() {
System.out.println("更新商品");
}
@Override
public void delete() {
System.out.println("删除商品");
}
}
- 3.编写测试类
package springAop;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestDemo {
@Resource(name = "productDao")
private ProductDao productDao;
@Test
public void demo1() {
productDao.save();
productDao.update();
productDao.delete();
productDao.find();
}
}
2.增强方法
- 1.编写切面类
package springAop;
public class MyAspectXML {
public void checkPri() {
System.out.println("权限校验。。。");
}
}
- 2.配置切面类
<!--将切面类交给Spring管理 -->
<bean id="MyAspect" class="springAop.MyAspectXML"></bean>
<!--通过AOP的配置完成对目标类产生代理 -->
<aop:config>
<!-- 表达式配置哪些类的哪些方法需要增强-->
<aop:pointcut
expression="execution(* springAop.ProductDaoImpl.save(..))"
id="pointcut1" />
<!-- 配置切面-->
<aop:aspect ref="myAspect">
<!-- 前置通知-->
<aop:before method="checkPri" pointcut-ref="pointcut1" />
</aop:aspect>
</aop:config>
Spring的通知类型
- 1.前置通知:在目标方法执行之前进行操作
- 可以获得切入点信息
public class MyAspectXML {
public void checkPri(JoinPoint joinPoint) {
System.out.println("权限校验。。。"+joinPoint);
}
---------------------------
<aop:before method="checkPri" pointcut-ref="pointcut1" />
- 2.后置通知:在目标方法执行之后进行操作
- 后置通知
public void writeLog() {
System.out.println("日志记录一下。。");
--------------------------
<aop:pointcut expression="execution(* springAop.ProductDaoImpl.delete(..))"
id="pointcut2" />
<!--后置通知 -->
<aop:after-returning method="writeLog" pointcut-ref="pointcut2" />
- 3.环绕通知:在目标方法执行之前和之后进行操作
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前通知----------");
Object obj = joinPoint.proceed();
System.out.println("环绕后通知----------");
return obj;
}
-------------
<aop:around method="around" pointcut-ref="pointcut3" />
- 4.异常跑出通知:在程序出现异常时,进行的操作
- 5.最终通知: 无论代码是否有异常,总会执行的
- 6.引介通知(了解)
Spring切入点表达式写法
- 语法
- 【访问修饰符】方法返回值 包名.类名.方法名(参数)
public void com.spring.CustomerDao(..)
* 代表所有,上面可以改写称
**.*.*Dao.save(..)