代理
通过代理类,为原始类增加额外的功能。
好处:利于原始类(目标类)的维护。
静态代理
为每一个原始类,编写一个代理类,二者实现相同接口
public class UserServiceProxy implements UserService {
UserService userService=new UserServiceImpl();
@Override
public void login() {
System.out.println("============before===========");
userService.login();;
System.out.println("============after===========");
}
}
缺点:1)静态类文件太多,不利于管理。2)额外功能扩展性差。
AOP
面向切面编程:本质就是spring的动态代理开发,通过代理类增加额外功能。
以切面为基本单位的程序开发,通过切面间的彼此协同,相互调用,完成程序的构建。
切面=切入点+额外功能。
spring动态代理底层
动态代理:动态创建代理类。使用了动态字节码技术。
底层有两种实现方式JDK 和CGLIB
spring 默认使用jdk方式
springboot 默认使用cglib方式。
也可以自己设置。
<!--基于注解AOP-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!--传统AOP-->
<aop:config proxy-target-class="true"/>
JDK
通过接口(实现与原始类相同的接口)才能创建代理类。
public class Main {
public static void main(String[] args) {
final UserService userService=new UserServiceImpl();
InvocationHandler invocationHandler=new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("=======================logging========================");
return method.invoke(userService,args);
}
};
UserService userServiceProxy=(UserService) Proxy.newProxyInstance(Main.class.getClassLoader(), userService.getClass().getInterfaces(),invocationHandler);
userServiceProxy.login();
}
}
CGLIB
通过继承原始类创建的代理类
public static void main(String[] args) {
UserServiceImpl userServiceImpl=new UserServiceImpl();
Enhancer enhancer=new Enhancer();
enhancer.setClassLoader(Main.class.getClassLoader());
enhancer.setSuperclass(userServiceImpl.getClass());
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("================before================");
method.invoke(userServiceImpl,objects);
System.out.println("================end================");
return null;
}
});
UserServiceImpl userService=(UserServiceImpl) enhancer.create();
userService.login();
}
类加载的作用
1)把字节码文件加载到JVM中
2)创建类的Class对象。
如何获取类加载器
每一个.class文件 自动分配与之对应的ClassLoader
动态字节码技术 直接在jvm中生成代理对象字节码。
需要ClassLoader创建代理类的class对象,因为动态字节码没有对应.class文件,所以JVM不为其分配ClassLoader ,所以需要借用一个。
spring工厂如何加工原始对象
实现BeanPostProcessor
public class BeanPostProcessorImpl implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return Proxy.newProxyInstance(BeanPostProcessorImpl.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("================logging before method=================");
Object o= method.invoke(bean,args);
return o;
}
});
}
}
<bean id="BeanPostProcessorImpl" class="proxy.BeanPostProcessorImpl"></bean>
Spring 动态代理(Spring AOP)
实现MethodBeforeAdvice
在原始方法前增加额外功能
public class Before implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("在原始方法前增强");
}
}
配置文件
<bean id="userService" class="proxy.UserServiceImpl"></bean>
<bean id="before" class="proxy.Before"></bean>
<aop:config>
<aop:pointcut id="pc" expression="execution(* *(..))"/>
<aop:advisor advice-ref="before" pointcut-ref="pc"/>
</aop:config>
方法拦截器(实现MethodInterceptor)
在原始方法前、后、前后、异常是添加额外功能
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("在原始方法前添加额外功能");
try{
methodInvocation.proceed();
}catch (Exception e){
System.out.println("在原始方法异常时添加额外功能");
}
System.out.println("在原始方法后添加额外功能");
return null;
}
}
配置方法同上
基于注解的AOP(只是使用了aspectJ ,实质还是Spring AOP)
@Aspect
public class BaseAspectJ {
@Pointcut("execution(* *(..))")
public void pointcut(){}
@Around(value="pointcut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("===================loging==============");
return proceedingJoinPoint.proceed();
}
}
<bean id="baseAspectJ" class="proxy.BaseAspectJ"></bean>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
spring aop 和 aspectJ 对比
1、Spring aop只增强bean对象,aspectJ可增强任何java对象。
public class UserServiceImpl implements UserService , ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void login() {
System.out.println("登录");
((UserService)this.applicationContext.getBean("userService")).subLogin();
//直接调用this.subLogin(), subLogin()不增强。 因为this不是代理对象,需要getBean获取代理。
}
public void subLogin(){
System.out.println("子登录");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext=applicationContext;
}
}
如果你正在维护一个现有的应用(该应用并没有使用Spring框架),AspectJ就将是一个自然的选择了
2、织入时机
1)Aspectj是静态织入:即编译期间织入。编译出来的class文件,字节码就已经被织入了
2)Spring Aop采用的动态织入,即编译后织入或运行时织入
编译后织入:指织入过程只在第一次调用时执行。
运行时织入指根据代码动态运行的中间状态来决定如何操作,每次调用Target的时候都执行。
Spring只支持运行时织入。如果你有多个团队分别开发多个使用Spring编写的模块(导致生成多个jar文件,例如每个模块一个jar文件),并且其中一个团队想要在整个项目中的所有Spring bean(例如,包括已经被其他团队打包了的jar文件)上应用日志通知(在这里日志只是用于加入横切关注点的举例),那么通过配置该团队自己的Spring配置文件就可以轻松做到这一点。之所以可以这样做,就是因为Spring使用的是运行时织入。