参考文章
https://blog.csdn.net/tenghu8888/article/details/122444743
https://cloud.tencent.com/developer/article/1497770
JDK和CGLIB动态代理:
jdk动态代理实现
@Test
public void test(){
IPerson target = new ManPerson();
IPerson proxy = (IPerson)Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new PersonInvocationHandler(target));
proxy.eat();
proxy.sleep();
}
// 代理对象
class PersonInvocationHandler implements InvocationHandler{
private Object target;
public PersonInvocationHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
System.out.println("start");
Object result = method.invoke(target, args);
System.out.println("end");
return result;
}
}
// 目标对象
class ManPerson implements IPerson{
@Override
public void eat(){
System.out.println("吃饭中......");
}
@Override
public void sleep(){
System.out.println("睡觉中......");
}
}
// 目标对象接口
interface IPerson{
void eat();
void sleep();
}
cglib动态代理
@Test
public void test(){
Person proxy = (Person)Enhancer.create(Person.class, new PersonMethodInterceptor());
proxy.eat();
proxy.sleep();
}
// 代理对象
class PersonMethodInterceptor implements MethodInterceptor{
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable{
System.out.println("start");
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("end");
return result;
}
}
// 目标对象
public class Person{
public void eat(){
System.out.println("吃饭中......");
}
public void sleep(){
System.out.println("睡觉中......");
}
}
JDK和CGLIB动态代理区别:
1、JDK动态代理是实现了被代理对象的接口,Cglib是继承了被代理对象
2、JDK动态代理只能对实现了接口的类生成代理,而不能针对类
3、Cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法进行增强,但是因为采用的是继承,所以该类或方法最好不要声明为final,对于final类或方法,是无法继承的。
Spring如何选择是用JDK还是cglib?
1、当bean实现接口时,会用JDK代理模式
2、当bean没有实现接口,会用cglib实现
3、可以强制使用cglib,@EnableAspectJAutoProxy(proxyTargetClass = true)
AOP使用举例:
@Aspect //切面类
@Component
public class ControllerHandler {
//切入点,切入点是匹配连接点的拦截规则
//@within指向类@executionexecution(* com.threebody.*.*.biz.impl.*BoImpl.*(..)) 指向类中的方法
@Pointcut("@within(com.threebody.one.Controller) || " +
"@within(com.threebody.two.Controller))")
public void controller() { }
/**
* 处理数据
* @param point
*/
@Around("controller()")
//JoinPoint连接点,Spring只支持方法类型的连接点,所以在Spring AOP中的一个连接点表示一个方法的执行
public Object controller(ProceedingJoinPoint point) throws Throwable {
Object result = point.proceed();
return result;
}
}
AOP源码总结:
1.在spring容器refresh时
2.会调用invokeBeanFactoryPostProcessors方法,这个方法会获取实现了BeanFactoryPostProcessors的类,对BeanFactory做增强处理,其中有一个类叫ConfigurationClassPostProcessor
3.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry方法会去调用ConfigurationClassParser.parse方法
4.ConfigurationClassParser.parse方法–》processConfigurationClass(ConfigurationClass configClass)方法–》doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)方法,此方法会去解析启动类中的@PropertySource、@ComponentScan、 @ImportResource、 @Bean、 @Import注解,将这些注解标志的类,注册到beanFactory中
5.ConfigurationClassParser解析@EnableAspectJAutoProxy下的@Import(AspectJAutoProxyRegistrar.class)注解:
AspectJAutoProxyRegistra(自动代理导入器)会去注册一个AnnotationAwareAspectJAutoProxyCreator(自动代理注册器)
6.在bean进行初始化initializeBean时,AnnotationAwareAspectJAutoProxyCreator(自动代理注册器)作为一个BeanPostProcessor,在postProcessAfterInitialization()中时执行了AbstractAutoProxyCreator.wrapIfNecessary();
7.获取当前系统中所有的切面类封装为Advisor,筛选出当前需被代理bean的Advisor集合,根据Advisor集合生成代理类
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
wrapIfNecessary方法
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 判断当前bean是否在TargetSource缓存中存在,如果存在,则直接返回当前bean。这里进行如此判断的
// 原因是在上文中,我们讲解了如何通过自己声明的TargetSource进行目标bean的封装,在封装之后其实
// 就已经对封装之后的bean进行了代理,并且添加到了targetSourcedBeans缓存中。因而这里判断得到
// 当前缓存中已经存在当前bean,则说明该bean已经被代理过,这样就可以直接返回当前bean。
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
// 这里advisedBeans缓存了已经进行了代理的bean,如果缓存中存在,则可以直接返回
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
// 这里isInfrastructureClass()用于判断当前bean是否为Spring系统自带的bean,自带的bean是
// 不用进行代理的;shouldSkip()则用于判断当前bean是否应该被略过
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
// 对当前bean进行缓存
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// 获取当前bean的Advisor集合
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
// 对当前bean的代理状态进行缓存
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 根据获取到的Advisor集合为当前bean生成代理对象
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors,
new SingletonTargetSource(bean));
// 缓存生成的代理bean的类型,并且返回生成的代理bean
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
List<Advisor> candidateAdvisors = findCandidateAdvisors();
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
findEligibleAdvisors方法
@5
@Bean
public Advisor txAdviceAdvisor(DataSourceTransactionManager transactionManager) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
return new DefaultPointcutAdvisor(pointcut, txAdvice(transactionManager));
}
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 获取当前系统中所有实现了Advisor的类,比如上边@5我们通过@Bean注入的DefaultPointcutAdvisor
//以及获取系统中所有的引入@Aspect注解的类
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 对获取到的所有Advisor进行判断,看其切面定义是否可以应用到当前bean,从而得到最终需要应用的Advisor
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors,
beanClass, beanName);
// 提供的hook方法,用于对目标Advisor进行扩展
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
// 对需要代理的Advisor按照一定的规则进行排序
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
@Override
protected List<Advisor> findCandidateAdvisors() {
// 获取当前系统中所有实现了Advisor的类,比如上边@5我们通过@Bean注入DefaultPointcutAdvisor
List<Advisor> advisors = super.findCandidateAdvisors();
//以及获取系统中所有的引入@Aspect注解的类
if (this.aspectJAdvisorsBuilder != null) {
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
return advisors;
}
Spring AOP相关概念:
横切关注点:一些具有横切多个不同软件模块的行为,通过传统的软件开发方法不能够有效地实现模块化一类特殊关注点。横切关注点可以对某些方法进行拦截,拦截后对原方法进行增强处理。
切面(Aspect):切面就是对横切关注点的抽象,这个关注点可以横切多个对象,在代码中用一个类来表示。
连接点(JoinPoint):连接点是在程序执行过程中某个特定的点,比如某个方法调用的时候或处理异常的时候,由于Spring只支持方法类型的连接点,所以在Spring AOP中的一个连接点表示一个方法的执行。
切入点(Pointcut):切入点是匹配连接点的拦截规则,在满足这个切入点的连接点上运行通知。切入点的表达式如何与连接点相匹配是AOP的核心,Spring默认使用AspectJ切入点语法。
通知(Advice):在切面上拦截到某个特定的连接点之后执行的操作
目标对象(Target Object):目标对象,被一个或多个切面所通知的对象,及业务中需要进行增强的业务对象。
织入(Weaving):织入是把切面作用到目标对象,然后产生一个代理对象的过程。
引入(Introduction):引入是用来在运行时给一个类声明额外的方法或属性,即不需为实现一个接口,就能使用接口中的方法。
SpringAOP有以下通知类型:
前置通知[Before advice]:在连接点前面执行,前置通知不会影响连接点的执行,除非此处抛出异常。
正常返回通知[After returning advice]:在连接点正常执行完成后执行,如果连接点抛出异常,则不会执行。
异常返回通知[After throwing advice]:在连接点抛出异常后执行。
返回通知[After (finally) advice]:在连接点执行完成后执行,不管是正常执行完成,还是抛出异常,都会执行返回通知中的内容。
环绕通知[Around advice]:环绕通知围绕在连接点前后,比如一个方法调用的前后。这是最强大的通知类型,能在方法调用前后自定义一些操作。环绕通知还需要负责决定是继续处理join point(调用ProceedingJoinPoint的proceed方法)还是中断执行。