AOP(底层原理)
1.1 AOP底层是使用动态代理进行实现
(1)有两种情况动态代理
1、有接口情况 使用JDK动态代理
创建接口实现类代理对象 增强类的方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cIxxxJEB-1617801756181)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20210307153604401.png)]
2、 没有接口情况 使用cglib动态代理
创建子类的代理对象增强类的方法
``[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-csXYa9GC-1617801756183)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20210307153751803.png)]
AOP底层原理(JDK动态代理)
1.使用JDK动态代理,使用Proxy里面的方法创建代理对象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gEKGQFUS-1617801756185)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20210307154138849.png)]
(1)调用newProxyInstance方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LeDxumoV-1617801756187)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20210307154248773.png)]
方法里有三个参数:
第一个参数:类加载器
第二个参数:增加方法所在的类,这个类实现的接口,可以写多个接口
第三个参数:InvocationHandler :实现InvocationHandler 接口,创建代理的对象,写增强的方法【重点JVM会自动回调】
2、JDK动态代理代码
(1)创建接口,定义方法
public interface UserDao {
public int add(int a,int b);
public String update(String id);
}
(2)创建接口实现类,实现方法
public class UserDaoImpl implements UserDao{
@Override
public int add(int a, int b) {
return a+b;
}
@Override
public String update(String id) {
return id;
}
}
(3)使用Proxy类创建接口代理对象
package com.yc;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
/**
* @program: SpringAOP_demo
* @description:
* @author: HillCheung
* @create: 2021-03-07 15:52
*/
public class JDKProxy {
public static void main(String[] args) {
//创捷接口实现类代理对象
Class[] interfaces ={UserDao.class};
// Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
//
//
// //匿名内部类
// @Override
// public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// return null;
// }
// });
UserDaoImpl userDao =new UserDaoImpl();
//代理对象
UserDao dao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
System.out.println(dao.add(1, 1));
}
}
class UserDaoProxy implements InvocationHandler{
//1.把创建的是谁的对象,把谁传递过来
//有参数构造传递
private Object obj;
public UserDaoProxy(Object obj){
this.obj=obj;
}
//增强的逻辑
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//方法之前
System.out.println("方法之前执行....."+method.getName()+":传递的参数"+ Arrays.toString(args));
//被增强的方法执行
Object res = method.invoke(obj, args);
//方法之后
System.out.println("方法之后执行...."+obj);
return res;
}
}
AOP(术语)
1.连接点
类里面的哪些方法可以被增强,这些方法可以称为连接点
2.切入点
实际被增强的方法,被称为切入点
3.通知(增强)
(1)实际增强的逻辑部分称为通知
(2)通知有多种类型
1.前置通知
2.后置通知
3.环绕通知
4.异常通知
5.最终通知(finally)
4.切面
是动作
(1)把通知应用到切入点的过程叫做切面
AOP操作(准备)
Spring框架中一般基于AspectJ实现AOP操作
(1)什么是AspectJ
AspectJ不是Spring的组成部分,独立AOP框架,一般把AspectJ和Spring框架进行使用,进行AOP操作
2.基于AspectJ使用AOP操作
基于 xml配置文件进行配置
基于注解方式进行配置(使用)
3. 在项目工程中引用AOP相关依赖
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6nDI9r2D-1617801756189)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20210307163309160.png)]
4.切入点表达式
(1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强
(2)语法结构:
execution([权限修饰符] [返回类型] [类全路径] [方法名称] ([参数列表]))
举例1: 对com.yc.dao.BookDao类里面的add方法
execution(* com.yc.dao.BookDao.add(…))
举例2: 对com.yc.dao.BookDao类里面的所有方法
execution(* com.yc.dao.BookDao.*(…))
举例3: 对com.yc.dao包里面的所有类,类里面所有方法进行增强
execution(* com.yc.dao..(…))
AOP操作(AspectJ注解)
1.创建类,在类里面定义方法
public class User {
public void add(){
System.out.println("add.......");
}
}
2.创建增强类,编写你增强的逻辑
(1)在增强类里面,创建方法,让不同方法代表不同通知类型
//增强的类
public class UserProxy {
//前置通知java
public void before(){
System.out.println("before......");
}
}
3.进行配置的通知
(1)在spring配置文件中开启注解扫描
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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="com.yc.annoactionaop"></context:component-scan>
<!-- 开启Aspect生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
(2)使用注解创建User和UserProxy对象
在User和UserProxy类上面加入@compent注解
(3)在增强类上面添加@Aspect
//增强的类
@Component
@Aspect //生成代理对象
public class UserProxy {
//前置通知
public void before(){
System.out.println("before......");
}
}
(4)在Spring配置文件中开启生成代理对象
(1)在增强类的里面,在作为通知方法上面通知类型注解,使用切入点表达式
//增强的类
@Component
@Aspect //生成代理对象
public class UserProxy {
//
@Before(value = "execution(* com.yc.annoactionaop.User.add(..))")
//前置通知
public void before(){
System.out.println("before......");
}
}
AOP源码分析(重点)
1)寻找 aop:aspectj-autoproxy/ 注解对应的解析器
但凡注解都有对应的解析器,以用来解析该注解的行为。全局搜索之后可发现
public class AopNamespaceHandler extends NamespaceHandlerSupport {
/**
* Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the
* '{@code config}', '{@code spring-configured}', '{@code aspectj-autoproxy}'
* and '{@code scoped-proxy}' tags.
*/
@Override
public void init() {
// In 2.0 XSD as well as in 2.5+ XSDs
registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
//重点
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
// Only in 2.0 XSD: moved to context namespace in 2.5+
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}
}
2)了解AspectJAutoProxyBeanDefinitionParser对应的行为
class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 1.注册proxy creator
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
extendBeanDefinition(element, parserContext);
return null;
}
...
// registerAspectJAnnotationAutoProxyCreatorIfNecessary()
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {
// 注册行为主要内容
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
registerComponentIfNecessary(beanDefinition, parserContext);
}
// registerAspectJAnnotationAutoProxyCreatorIfNecessary()
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
// 主要就是为了注册AnnotationAwareAspectJAutoProxyCreator类
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
// 注册类相关代码
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
// 类似于我们在使用BeanFactory.getBean()时候的操作,生成一个RootBeanDefinition,然后放入map中
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
总结:通过以上的代码分析,可知,AspectJAutoProxyBeanDefinitionParser主要的功能就是将*AnnotationAwareAspectJAutoProxyCreator注册到Spring容器中,把bean交给Spring去托管。*****
3)分析AnnotationAwareAspectJAutoProxyCreator主要行为
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F64waX8P-1617801756190)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20210307193228939.png)]
通过查看AnnotationAwareAspectJAutoProxyCreator的类层次结构,可知,其实现了BeanPostProcessor接口,实现类为AbstractAutoProxyCreator
4)AbstractAutoProxyCreator主要方法
``
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}
/**
* Create a proxy with the configured interceptors if the bean is
* identified as one to proxy by the subclass.
* @see #getAdvicesAndAdvisorsForBean
*/
// 主要看这个方法,在bean初始化之后对生产出的bean进行包装
@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;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
// 意思就是如果该类有advice(通知 增强)则创建proxy
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 1.通过方法名也能简单猜测到,这个方法就是把bean包装为proxy的主要方法,
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
// 2.返回该proxy代替原来的bean
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
总结:
**1)通过AspectJAutoProxyBeanDefinitionParser类将AnnotationAwareAspectJAutoProxyCreator注册到Spring容器中 **
2)AnnotationAwareAspectJAutoProxyCreator类的postProcessAfterInitialization()方法将所有有advice的bean重新包装成proxy
4.创建proxy过程分析
以下是AbstractAutoProxyCreator.wrapIfNecessary(Object bean, String beanName, Object cacheKey)中的createProxy()代码片段分析
/**
* Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
* @param bean the raw bean instance
* @param beanName the name of the bean
* @param cacheKey the cache key for metadata access
* @return a proxy wrapping the bean, or the raw bean instance as-is
*/
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//重点createProxy
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
createProxy源码分析
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
// 1.创建proxyFactory,proxy的生产主要就是在proxyFactory做的
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
// 2.将当前bean适合的advice,重新封装下,封装为Advisor类,然后添加到ProxyFactory中
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
// 3.调用getProxy获取bean对应的proxy
return proxyFactory.getProxy(getProxyClassLoader());
}
1)创建何种类型的Proxy?JDKProxy还是CGLIBProxy?
public Object getProxy(@Nullable ClassLoader classLoader) {
//点击去看源码
return createAopProxy().getProxy(classLoader);
}
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
//点击去看源码
return getAopProxyFactory().createAopProxy(this);
}
// createAopProxy()
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// 1.config.isOptimize()是否使用优化的代理策略,目前使用与CGLIB
// config.isProxyTargetClass() 是否目标类本身被代理而不是目标类的接口
// hasNoUserSuppliedProxyInterfaces()是否存在代理接口
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
// 2.如果目标类是接口或者是代理类,则直接使用JDKproxy
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// 3.其他情况则使用CGLIBproxy
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
(2)CGLIB
class ObjenesisCglibAopProxy extends CglibAopProxy {
private static final Log logger = LogFactory.getLog(ObjenesisCglibAopProxy.class);
private static final SpringObjenesis objenesis = new SpringObjenesis();
/**
* Create a new ObjenesisCglibAopProxy for the given AOP configuration.
* @param config the AOP configuration as AdvisedSupport object
*/
public ObjenesisCglibAopProxy(AdvisedSupport config) {
super(config);
}
//创建代理类和实例(核心代码)
@Override
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
Class<?> proxyClass = enhancer.createClass();
Object proxyInstance = null;
if (objenesis.isWorthTrying()) {
try {
proxyInstance = objenesis.newInstance(proxyClass, enhancer.getUseCache());
}
catch (Throwable ex) {
logger.debug("Unable to instantiate proxy using Objenesis, " +
"falling back to regular proxy construction", ex);
}
}
if (proxyInstance == null) {
// Regular instantiation via default constructor...
try {
Constructor<?> ctor = (this.constructorArgs != null ?
proxyClass.getDeclaredConstructor(this.constructorArgTypes) :
proxyClass.getDeclaredConstructor());
ReflectionUtils.makeAccessible(ctor);
proxyInstance = (this.constructorArgs != null ?
ctor.newInstance(this.constructorArgs) : ctor.newInstance());
}
catch (Throwable ex) {
throw new AopConfigException("Unable to instantiate proxy using Objenesis, " +
"and regular proxy instantiation via default constructor fails as well", ex);
}
}
((Factory) proxyInstance).setCallbacks(callbacks);
return proxyInstance;
}
}
(2)JDK动态代理
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable// JdkDynamicAopProxy类结构,由此可知,其实现了InvocationHandler,则必定有invoke方法,来被调用,也就是用户调用bean相关方法时,此invoke()被真正调用
// getProxy()
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
// JDK proxy 动态代理的标准用法
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
(3) invoke()方法
以上的代码模式可以很明确的看出来,使用了JDK动态代理模式,真正的方法执行在invoke()方法里,下面我们来看下该方法,来看下bean方法如何被代理执行的
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Class<?> targetClass = null;
Object target = null;
try {
// 1.以下的几个判断,主要是为了判断method是否为equals、hashCode等Object的方法
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return hashCode();
}
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations on ProxyConfig with the proxy config...
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// May be null. Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
}
// 2.获取当前bean被拦截方法链表
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// 3.如果为空,则直接调用target的method
if (chain.isEmpty()) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
// 4.不为空,则逐一调用chain中的每一个拦截方法的proceed
else {
// We need to create a method invocation...
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}
...
return retVal;
}
...
}
4)拦截方法真正被执行调用invocation.proceed()
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
总结4:依次遍历拦截器链的每个元素,然后调用其实现,将真正调用工作委托给各个增强器
总结:
纵观以上过程可知:实际就是为bean创建一个proxy,JDKproxy或者CGLIBproxy,然后在调用bean的方法时,会通过proxy来调用bean方法
重点过程可分为:
1)通过AspectJAutoProxyBeanDefinitionParser类将AnnotationAwareAspectJAutoProxyCreator注册到Spring容器中
2)AnnotationAwareAspectJAutoProxyCreator类的postProcessAfterInitialization()方法将所有有advice的bean重新包装成proxy
3)调用bean方法时通过proxy来调用,proxy依次调用增强器的相关方法,来实现方法切入