AOP面向切面编程
一、AOP概念简介
AOP是面向切面编程的简称,通俗的说就是在不改变源码的基础上实现对功能的增加
二、AOP底层原理
AOP底层使用了动态代理,而动态代理有两种情况,分别是有接口的情况和没有接口的情况
①有接口的情况:
通过创建接口实现类的代理对象实现:
②没有接口的情况:
通过创建该类的子类的代理对象实现:
③有接口的动态代理代码实现:g
1.接口:
public interface UserDao {
public void add();
}
2.实现类:
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("add...");
}
}
3.实现增强:
public class UserDaoImplProxy implements InvocationHandler {
//内置一个属性,该属性为要被增强的类
private Object object;
public UserDaoImplProxy(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//增强逻辑
System.out.println("前置增强");
//原来逻辑
method.invoke(object, args);
//增强逻辑
System.out.println("后置增强");
return null;
}
}
4.创建接口实现类的代理对象:
public void test(){
Class[] classes = {UserDao.class};
UserDao userDao = new UserDaoImpl();
//使用Proxy类来实现
UserDao userDao1 = (UserDao) Proxy.newProxyInstance(ProxyTest.class.getClassLoader(), classes, new UserDaoImplProxy(userDao));
userDao.add();
System.out.println(".......");
userDao1.add();
}
创建接口实现类的代理对象,使用的Proxy类中的newProxyInstance方法,其中有三个参数:
第一个为类的加载器,该值为你在创建代理对象时所在的类的类的加载器;
第二个为一个Class类的数组,该数组的值为你要实现动态代理的实现类的接口的类型类;
第三个为一个InvocationHandler接口的实现类的实例,你有创建一个类来实现该接口,在在接口中有一个invoke方法要实现,在该方法中就可以加入增强你的逻辑;
三、AOP常用术语:
public class Person {
private String name;
private Integer id;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}
(1)接入点:意思是可以实现增强的方法,如其中的getName、getId等方法;
(2)切点:意思是实现了增强的方法,如getNamePlus,对应的切点为getName;
(3)通知:意思是用来增强的代码,如getNamePlus中除去getName的部分都是通知;
(4)切面:是指一个动作,即将增强代码加入到其中的过程称为切面;
四、AOP的注解操作
①准备工作:
第一步导入依赖:
第二步,熟悉切入点表达式
格式如下:Execution([权限修饰符] [返回值类型] [全类名] [方法名] [参数列表])
一般格式为Execution(*空格com.lazy.bean.Person.say(…))
例一:对指定包内的指定类的指定方法进行增强
Execution(* com.lazy.bean.Person.say(..))
例二:对指定包内的指定类所有方法进行增强
Execution(* com.lazy.bean.Person.*(..))
例三:对指定包内的所有类的所有方法进行增强
Execution(* com.lazy.bean.*.*(..))
②操作流程
第一步,创建类
public class UserDaoImpl implements UserDao {
public void add(){
System.out.println("add...");
}
}
第二步,创建增强类
public class UserDaoPlus {
//前置通知
public void before(){
System.out.println("before...");
}
}
第三步,进行通知配置(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 http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.lazy.aop2"></context:component-scan>
使用注解创建对象:
@Component
public class UserDaoImpl implements UserDao {
使用@Aspect注解标识增强对象:
@Component
@Aspect
public class UserDaoPlus {
开启代理对象:
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
第四步,将实现对被增强方法的不同通知:
//前置通知
@Before(value = "execution(* com.lazy.aop2.bean.UserDaoImpl.add(..))")
public void before(){
System.out.println("before...");
}
补充实现完全注解开发:
@Configuration
@ComponentScan(basePackages = "com.lazy.aop2.bean")
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AOPConfig {
}
③四种通知介绍即操作
通知中有四种类型分别是:
(1)前置通知,即在要增强方法前的代码;
//前置通知
@Before(value = "execution(* com.lazy.aop2.bean.UserDaoImpl.add(..))")
public void before(){
System.out.println("before...");
}
(2)后置通知,在要增强方法后的代码(return语句执行后);
//后置通知
@AfterReturning(value = "execution(* com.lazy.aop2.bean.UserDaoImpl.add(..))")
public void afterReturn(){
System.out.println("afterReturn....");
}
(3)环绕通知,在要增强的方法前后执行;
//环绕通知
@Around(value = "execution(* com.lazy.aop2.bean.UserDaoImpl.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("around...");
proceedingJoinPoint.proceed();
System.out.println("around...");
}
(4)异常通知,在要增强的方法发生异常之后执行;
//异常通知
@AfterThrowing(value = "execution(* com.lazy.aop2.bean.UserDaoImpl.add(..))")
public void afterThrowing(){
System.out.println("afterThrowing");
}
(5)最终通知,不论方法是否异常都将执行;
//最终通知
@After(value = "execution(* com.lazy.aop2.bean.UserDaoImpl.add(..))")
public void after(){
System.out.println("after...");
}
执行结果如下:
发生异常时如下:
④抽取切入点表达式
@Pointcut(value = "execution(* com.lazy.aop2.bean.UserDaoImpl.add(..))")
public void symbol(){};
//前置通知
@Before(value = "symbol()")
public void before(){
System.out.println("before...");
}
⑤多个增强类作用同一个类的方法时的顺序控制:
可以在各个增强类前加上@Order注解,在注解中加入数值,数值越小表示优先级越高
@Order(1)
public class UserDaoPlus {
五、AOP配置文件操作:
<!-- 创建对象 -->
<bean id="userDaoImpl" class="com.lazy.aop3.bean.UserDaoImpl"></bean>
<bean id="userDaoImplPlus" class="com.lazy.aop3.bean.UserDaoPlus"></bean>
<!-- 标识增强类 -->
<aop:config>
<!-- 创建切入点 -->
<aop:pointcut id="p" expression="execution(* com.lazy.aop3.bean.UserDaoImpl.add(..))"/>
<!-- 配置切面 -->
<aop:aspect ref="userDaoImplPlus">
<!-- 配置通知 -->
<aop:before method="before" pointcut-ref="p"></aop:before>
</aop:aspect>
</aop:config>