AOP(概念)
1.面向切面编程
把业务逻辑各个部分进行分离,降低耦合度,提高重用性
2.通俗表述:不修改源代码,在主干功能添加新功能
3.用登录例子来说明aop
AOP(底层原理)
1.底层使用动态代理
(1)动态代理有两种情况
第一种 有接口情况,使用JDK动态代理
1.创建接口实现类的代理对象
2.在代理对象的方法里增加一些新功能
第二种 没有接口情况,使用CGLIB动态代理
1.创建当前类的子类的代理对象
2.子类继承当前类并重写父类的方法,
super调用父类方法并增加新功能
AOP(JDK动态代理)
1.使用JDK动态代理,使用Proxy类里的方法创建代理对象
(1)调用newProxyInstance方法返回接口代理类的实例
方法有三个参数
第一个参数:类加载器
第二个参数:增强方法所在的类实现的接口,支持多个接口
第三个参数:是一个接口InvocationHandler,需要一个类来
实现这个接口InvocationHandler,创建代理对象,invoke方法里写增强的功能
2.JDK动态代理代码
(1)创建接口,定义方法
(2)创建接口的实现类,实现方法
3.使用Proxy类创建接口代理对象
public class JDKProxy {
public static void main(String[] args) {
//创建接口实现类的代理对象
Class[] interfaces = {UserDao.class};
UserDaoImpl userDao = new UserDaoImpl();
//用接口等于实现类创建代理对象
UserDao dao =(UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
int result = dao.add(1, 2);
System.out.println("result"+result);
}
}
//创建代理对象代码
class UserDaoProxy implements InvocationHandler {
//把需要增强的方法所在的类的对象传递过来(被代理对象)
//通过有参构造传递
private Object obj;
public UserDaoProxy(Object obj) {
this.obj = obj;
}
//增强的逻辑代码
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//方法之前的增加处理
System.out.println("方法之前执行"+method.getName()+":传递的参数.."+ Arrays.toString(args));
//被增加的方法的执行
Object res = method.invoke(obj, args);
//方法之后的增加处理
System.out.println("方法之后输出"+obj);
return res;
}
}
AOP(术语)
1.连接点
类里面的哪些方法可以被增强(增加功能),这些方法都被称为连接点
2.切入点
实际被增强的方法叫做切入点
3.通知(增强)
(1)实际增强的逻辑部分称为通知
(2)有多种类型
*前置通知
*后置通知
*环绕通知
*异常通知
*最终通知
4.切面
把通知应用到切入点的过程称为切面
AOP操作(准备)
1.Spring框架一般都是基于AspectJ实现AOP操作
(1)AspectJ是一个独立的AOP框架,通常和Spring一起使用
2.基于AspectJ实现AOP操作
(1)基于xml配置文件
(2)基于注解方式(实际中使用)
3.项目引入AOP依赖
4.切入点表达式
(1)作用是对哪个方法进行增强
(2)语法(权限修饰符是public/private):
execution([权限修饰符][返回类型][类的全路径][方法名称]([参数列表]))
例一:对com.xlj.dao.BookDao类里面的add方法进行增强
//*表示任一,返回类型可以省略,..表示参数列表
execution(* com.xlj.dao.BookDao.add(..))
例二:对com.xlj.dao.BookDao类里面的所有方法(*表示)进行增强
execution(* com.xlj.dao.BookDao.*(..))
例二:对com.xlj.dao.包里的所有类(*表示),类里面的所有方法(*表示)进行增强
execution(* com.xlj.dao.*.*(..))
AOP操作(基于注解方式进行aop操作)
1.创建类,定义方法
2.创建增强类(里面写增强的部分)
(1)在里面写不同的方法表示不同的通知类型
3.进行通知的配置
(1)写配置文件开启注解扫描(配置类也可以实现)
引入context的名称空间
<context:component-scan base-package="com.xlj.spring5.aopanno"></context:component-scan>
(2)使用注解创建被增强类和增强类的对象
(3)在增强类上加注解@Aspect
(4)在spring配置文件中开启生成代理对象
引入aop的名称空间
<!-- 开启AspectJ生成代理对象 ,有@Aspect的注解的类就生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
4.配置不同类型的通知
(1)在增强类的方法上添加它所表示的通知类型注解,并使用切入点表达式
@Component
@Aspect
public class UserProxy {
//前置通知(value=“切入点表达式”)value可以省略
@Before("execution(* com.xlj.spring5.aopanno.User.add(..))")
public void before() {
System.out.println("before....");
}
//最终通知,无论是否发生异常都会执行
@After("execution(* com.xlj.spring5.aopanno.User.add(..))")
public void after() {
System.out.println("after....");
}
//后置通知,有返回结果才执行,有异常不执行
@AfterReturning("execution(* com.xlj.spring5.aopanno.User.add(..))")
public void afterReturning() {
System.out.println("afterReturning....");
}
//异常通知,在增强的方法出现异常时才会执行
@AfterThrowing("execution(* com.xlj.spring5.aopanno.User.add(..))")
public void afterThrowing() {
System.out.println("afterThrowing....");
}
//环绕通知
@Around("execution(* com.xlj.spring5.aopanno.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint)throws Throwable {
System.out.println("环绕之前...");
//执行被增强的方法
proceedingJoinPoint.proceed();
System.out.println("环绕之后....");
}
}
5.相同的切入点的抽取(就是重复写的execution)
(1)//相同切入点的抽取
@Pointcut(value = "execution(* com.xlj.spring5.aopanno.User.add(..))")
public void pointdemo() {
}
(2)通知注解的表达式改为value=“方法名()”
例如@Before(value = "pointdemo()")
6.多个增强类对同一个方法增强,设置增强类的优先级
(1)在增强类上添加注解@Order(数字类型值),数字越小优先级越高
@Order(1)
7.完全注解开发
(1)写配置类
//表示作为配置类
@Configuration
//开启注解扫描
@ComponentScan(basePackages = {"com.xlj"})
//开启Aspect生成代理对象
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigAop {
}
(2)编写测试方法(和加载bean不太一样)
//加载配置类
ApplicationContext context =
new AnnotationConfigApplicationContext(ConfigAop.class);
AOP操作(基于xml配置文件进行aop操作)
1.创建增强类和被增强类以及各自方法
2.在xml配置文件创建两个类的对象
3.在xml配置文件中配置切入点
<!-- 配置aop增强-->
<aop:config>
<!-- 配置切入点-->
<aop:pointcut id="p" expression="execution(* com.xlj.spring5.aopxml.Book.buy(..))"/>
<!-- 配置切面-->
<aop:aspect ref="bookProxy">
<!--配置把通知应用到切入点,method="before"通知,pointcut-ref="p"切入点-->
<aop:before method="before" pointcut-ref="p"></aop:before>
</aop:aspect>
</aop:config>