1.AOP简介
AOP为Aspect Oriented Programming的缩写,意为面向切面编程,他是通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
通俗描述:不通过修改源代码方式,在主干功能里面添加新功能
2.AOP的一些术语和简单实用
-
连接点
类里面那些方法能被增强 -
通知点
实际被增强的方法 -
增强
实际增强的逻辑部分 -
切面
把通知应用到切入点的过程这样听起来肯定是有点含糊不清的,下面我们举个栗子来说明一下,首先我们先引入这些jar包。并且这里我们主要阐述全注解开发形式,xml形式不做过多阐述!
然后我们新建4个类一次是User类里面有一个eat方法作为通知点,然后UserProxy类作为他的代理来对他增强,以及一个懒得写xml文件的config配置类和一个测试类
package cheerful.aop;
import org.springframework.stereotype.Component;
/**
* @author WangZhiFu
* @create 2020-09-16 23:33
* @Description:
*/
@Component("user")
public class User {
public void eat(){
System.out.println("I am eating...");
}
}
package cheerful.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* @author WangZhiFu
* @create 2020-09-16 23:50
* @Description:
*/
@Component //生成bean
@Aspect //生成代理对象
public class UserProxy {
//把插入点抽取出来
//切入点表达式:execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]) )
@Pointcut(value = "execution(* cheerful.aop.User.eat())")
public void pointcut() { }
//给这个插入点加上通知,@Before代表前置通知
@Before("pointcut()")
public void before(){
System.out.println("before");
}
//最终通知
@After("pointcut()")
public void after(){
System.out.println("after");
}
//后置通知
@AfterReturning("pointcut()")
public void AfterReturning(){
System.out.println("AfterReturning");
}
//异常通知
@AfterThrowing("pointcut()")
public void AfterThrowing(){
System.out.println("AfterThrowing");
}
//环绕通知
@Around("pointcut()")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
System.out.println("around1");
//proceedingJoinPoint.proceed();执行被加强的方法!
proceedingJoinPoint.proceed();
System.out.println("around2");
}
}
package cheerful.aop;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
/**
* @author WangZhiFu
* @create 2020-09-17 0:02
* @Description:
*/
@Configuration
@ComponentScan(basePackages = {"cheerful.aop"})
@EnableAspectJAutoProxy(proxyTargetClass = true)//开启 Aspect生成代理对象
public class Config {
}
public class Test {
@org.junit.Test
public void test1(){
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
User user = context.getBean("user",User.class);
user.eat();
}
}
通过运行结果我们可以狠清晰的发现我们成功的对User类的add方法进行了加强!这就想当于aop 的helloworld,aop实际执行的就是这样对类里面已有的方法进行增强。好比我们加强一个登录功能,就可以在登录功能基础上加上一些逻辑功能或者说权限判断值类的增强。从而实现不改变其源代码增加新的功能。
以上均为个人理解欢迎指正,如果后面我有新理解我会回到这儿来增加哈哈哈哈。
3.AOP原理和JDK动态代理
1.AOP底层原理
第一种 有接口情况,使用 JDK 动态代理 :一般有接口的时候会创建接口实现类代理对象,增强类的方法
例如用Class UserDaoImpl implements UserDao();
第二种 没有接口情况,使用 CGLIB 动态代理 :创建子类的代理对象,增强类的方法
例如
Class Person extend User{
public void test(){
super.eat();
//增强逻辑~~~~
}
}
2.JDK动态代理
使用 JDK 动态代理,使用 Proxy 类里面的方法创建代理对象
Proxy是java.lang里面的一个类,调用 newProxyInstance 方法创建代理对象,然后我们贴上源码看看他的参数
方法有三个参数:
第一参数,类加载器
第二参数,增强方法所在的类,这个类实现的接口,支持多个接口所以是个列表
第三参数,实现这个接口 InvocationHandler,创建代理对象,写增强的部分也就是这个部分就相当于上面的注解@Before,@After等等
然后我们现在就来写一段试试吧~
这里我贴了一个接口3个类,分别是UserDao接口和他的实现类UserDaoImpl以及实现增强的JDKProxy类和用于获取UserDao接口的一个方便使用的类。这里可能有点绕,我尽量把注释写清楚叭…
package cheerful.aop;
/**
* @author WangZhiFu
* @create 2020-09-11 22:45
* @Description:
*/
public interface UserDao {
int add(int a,int b);//我们以add方法作为要增强的方法为例子吧
}
package cheerful.aop;
/**
* @author WangZhiFu
* @create 2020-09-14 23:48
* @Description:
*/
public class UserDaoImpl implements UserDao{
@Override
public int add(int a, int b) {
return a+b;
System.out.println("add方法已经执行");
}
}
package cheerful.aop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author WangZhiFu
* @create 2020-09-15 00:50
* @Description:
*/
public class JDKProxy {
public static void main(String[] args) {
Class[] interfaces={UserDao.class};//增强方法所在的类或接口
UserDaoImpl us=new UserDaoImpl();//这个us用于当有参构造传到下面new UserDaoProxy(us)来创建代理对象
UserDao res = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(),interfaces,new UserDaoProxy(us));
int add = res.add(1, 2);
System.out.println("result:"+add);
}
}
class UserDaoProxy implements InvocationHandler{
//创建代理对象,即把谁(有参构造)传递过来
private Object object;
public UserDaoProxy(Object object){
this.object=object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
//这里等于@Before的功能
System.out.println("执行的方法"+method.getName()+" 传递的参数"+ Arrays.toString(args));
//执行被增强的方法,这里写增强的逻辑,后面的args就是执行被增强方法的参数(即上面res.add里面的1,2)
Object result = method.invoke(object, args);
//这里等于@After的功能
System.out.println("执行方法之后"+object);
return result;
}
}
这样就实现了通过JDK动态代理的方式对方法进行了前后增强!
初学Spring5若有什么不对的地方还请大家指正.