Spring的笔试相关题目:https://blog.csdn.net/a745233700/article/details/80959716
参考文章:https://blog.csdn.net/zh15732621679/article/details/80819824
说到Spring,想必一定会提到面向切面编程(AOP),学生日常使用很少涉及,可能很快就忘记了,现在让我再回复一下这方面的知识。
定义:
官方说明:通过预编译方
式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个
热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑
的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高
了开发的效率。
简单来说,就是当我们完成一个操作的时候,我们会在完成这个操作中间,固定一个操作,例如在访问一个页面时,固定先认证权限,或者在各种操作时用日志记录。
还是不懂可以看下图形象的例子,看看一般方法到切面,一步步过渡
1.权限认证:一般对每一个接口都会做活动的有效性校验(是否开始、是否结束等等)、以及这个接口是不是需要用户登录。
1)一般方法
2):使用接口,类似于
上面图二的修改虽然不用每次都copy代码了,但是,每个接口总得要调用这个方法吧。于是就有了切面的概念,我将方法注入到接口调用的某个地方(切点),这样接口只需要关心具体的业务,而不需要关注其他非该接口关注的逻辑或处理。
相关名词
-
Aspect(切面):Aspect声明类似于java中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
-
Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。
-
Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
-
Advice(增强):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
-
Target(目标对象):织入 Advice 的目标对象.。
-
Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程
SSM框架AOP的实现
- 静态代理
静态代理的代表为AspectJ;
定义:静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强,他会在编译阶段将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。
实现
1):定义一个抽象接口
public interface IBookService{
public void hello();
}
2):被代理类实现IBookService接口
public class BookService implements IBookService{
public void hello(){
//相关操作
System.out.println("我是service");
}
}
3):创建一个代理类,实现共同的接口IBookService
public class Proxy implements IBookService {
//依赖被代理的对象
IBookService bookService = new BookService();
//重写方法
public void hello() {
System.out.println("方法之前...");
//调用被代理对象的接口
bookService.hello();
}
}
优点:
符合设计原则:开放封闭原则(OCP),没有修改原来的代码,而是通过代理扩展
符合单一职责原则
缺点:
代理对象和被代理对象之前是一种强耦合,代理对象必须知道被代理对象具体的变量或方法,从而进行调用。一旦被代理对象多起来,那就需要创建多个代理,同样不好维护。
- 动态代理
2.1):使用JDK提供的动态代理优化
- 特点:DK提供的动态代理有一个特点是基于接口的,也就是被代理对象必须是实现接口的,否则JDK的动态代理是无法实现代理的。使用JDK的动态代理需要创建接口,让被代理对象实现接口。
public class Proxy implements InvocationHandler {
Object targetObject;
//3.创建一个方法来生成对象,参数是要代理的对象,getInstance返回的对象是代理对象
public Object getInstance(Object o){
//3.1创建LogProxy对象
//3.2设置这个代理对象
this.targetObject = o;
//3.3通过Proxy的方法创建代理对象,第一个参数是要代理对象的classLoader
//第二个参数是要代理对象实现的所有接口,第三个参数是实现类的InvocationHandler对象
//此时的result就是一个代理对象,代理的是o
Object result = java.lang.reflect.Proxy.newProxyInstance(o.getClass().getClassLoader(),o.getClass().getInterfaces(),this);
return result;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法之前...");
Object invoke = method.invoke(targetObject, args);
return invoke;
}
}
一个测试类
public class TestJDK {
public static void main(String[] args) {
IBookService instance = (IBookService) new Proxy().getInstance(new BookService());
instance.hello();
}
}
总结:
- JDK提供的动态代理,被代理的对象必须要有接口,这样就有一些局限性,当需要被代理的对象没有接口时就不能使用这种方式,然而也没有必要为了使用JDK动态代理而抽象出一些不必要的接口。
- JDK提供的动态代理使用步骤如下:①被代理类实现接口 ②代理类实现InvocationHandler接口 ③通过Proxy.newProxyInstance创建代理对象 ④重写InvocationHandler接口的invoker方法,实现在不修改原来代码的前提下动态扩展
2.2):cglib动态代理优化
解决JDK提供的代理方式要求被代理对象必须实现接口的这个缺点,cglib提供的动态代理方式不要求被代理对象实现接口
- 添加maven代理
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>
- 代理类
public class Proxy implements MethodInterceptor
Enhancer enchancer = new Enhancer();//字节码增强器
public Object getInstance(Object o){
enchancer.setSuperclass(User.class);//设置被代理类为父类
enchancer.setCallback(new UserInterceptor());//设置回调
return enchancer.create();//创建代理实例
}
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("方法之前...");
Object object = methodProxy.invokeSuper(o,objects);
return object;
}
}
测试类
public class TestCglib {
public static void main(String[] args) {
BookService instance = (BookService) new Proxy().getInstance(new BookService());
instance.hello();
}
}
总结:
- 被代理对象不需要实现接口
- 因为是继承关系,因此final修饰的方法是无法增强的,这种代理方式也是有限制的