AOP
概念
面向切面编程:对业务的各个部分进行隔离,减低耦合度,提高程序的可重用性,同时提高效率。
解析
以登录功能为例:
如果增加功能,不通过修改源代码的方式,降低耦合度。
底层原理
AOP底层使用的是 动态代理
-
有两种情况
-
有接口的情况
使用JDK的动态代理
-
没有接口的情况
使用CGLIB的动态代理
-
代理
还是按照登录为例子
public interface Dao
{
void login(){}
}
如果我们需要在登录中添加新的逻辑,使用AOP的话就是创建一个这个接口的实现对象的代理对象。
如果没有接口,例如
public class User{
public void login(){}
}
如果想要在上面添加一些逻辑,增强login方法,可以继承User类,在子类中调用父类的方法,并且添加额外的逻辑。
而此时的动态代理(CGLIB),那么创建当前类的子类的代理对象。
注意,动态代理增强的都是接口中的方法,并不适用于普通方法。
public class JdkProxy {
public static void main(String[] args) {
Class[] interfaces = {UserDao.class};
UserDao us = new UserDaoImpl();
UserDao userDao =(UserDao) Proxy.newProxyInstance(JdkProxy.class.getClassLoader(), interfaces,
new UserDaoProxy(us));
userDao.update();
}
static class UserDaoProxy implements InvocationHandler {
private Object object;
public UserDaoProxy(Object userDao) {
this.object = userDao;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法之前执行" + method.getName());
Object invoke = method.invoke(object, args);
System.out.println("方法之后执行" + object);
return invoke;
}
}
}
AOP术语
-
连接点:类中的哪些方法可以(enable)被增强, 这些方法就被称为连接点。
-
切入点:实际真正被增强的方法称为切入点。
-
通知(增强):实际增强的逻辑部分称为通知。具有多种类型
- 前置通知:前面有
- 后置通知:后面有
- 环绕通知:前后都有
- 异常通知:异常才用
- 最终通知:类似于 finally
-
切面:把通知应用到切入点的过程就称为切面。
AspectJ
我们通常使用AspectJ来实现 AOP 的操作。
在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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
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="service,dao,aop">
</context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
被代理类
@Component
public class User {
public void add() {
System.out.println("添加");
}
}
代理类
@Component
@Aspect
public class UserProxy {
//抽取公共切入点
//..代表参数列表,。之类是包名.Class.Method(..) 的格式
@Pointcut(value = "execution(* aop.User.add(..))")
public void point() {
}
@Before("point()")
public void before() {
System.out.println("增强之前");
}
@Before("point()")
public void after() {
System.out.println("之后");
}
}
测试
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//尽管我们使用的是注解,我们依然可以靠这样获取对象。
User user = context.getBean("user", User.class);
user.add();
}
}
如果有多个增强类,可以设置这些增强类的优先级。
添加注解 order 里面的值越小,优先级越高。
完全注解开发
首先需要一个配置类。
@Configuration
@ComponentScan(basePackages = {"com.tifa.aop"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigAop {
}
测试
public class Test {
public static void main(String[] args) {
//注意对象变了。
ApplicationContext context = new AnnotationConfigApplicationContext(ConfigAop.class);
var user = context.getBean( User.class);
user.add("你");
}
}