SpringFramework的AOP

1、什么是AOP

Aspect Oriented Programming, 面向切面编程
面向切面编程,一句话解释:对哪些类的哪些方法,在什么时候,做哪些增强。

2、AOP的作用

方法的增强。
假如有一个代码,里面有10000个方法,现如今需要修改,例:每个方法里面加一个事务。那需要手写10000次吗?肯定不现实。

public class UserServiceImpl implements UserService{
    @Override
    public void create() {
        System.out.println("创建一个用户");
    }

    @Override
    public void delete() {
        System.out.println("删除一个用户");
    }
}

3、办法

3.1 手动写(非代理)

显然不现实

public class UserServiceImpl2 implements UserService{
    private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @Override
    public void create() {
        System.out.println("创建一个用户");
        System.out.println(format.format(new Date()));
    }

    @Override
    public void delete() {
        System.out.println("删除一个用户");
        System.out.println(format.format(new Date()));
    }
}

3.2 代理

什么叫代理?
简单讲就是一个中间人。

public class UserServiceProxyImpl implements UserService {
    private UserService userService;
    private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public UserServiceProxyImpl(UserService userService) {
        this.userService = userService;
    }

    @Override
    public void create() {
        userService.create();
        System.out.println(format.format(new Date()));
    }

    @Override
    public void delete() {
        userService.delete();
        System.out.println(format.format(new Date()));
    }
 }
 //UserServiceProxyImpl 就相当于是中间人,Client里面就可以使用这个代理

3.3动态代理

动态(运行时在内存的操作),简单点说就是,它会帮你把以前的方法写好,自己只需要敲需要新添加的方法。

3.3.1 JDK 动态代理
/**
*Proxy.newProxyInstance 创建一个新的代理实例
*ClassLoader loader,
*Class<?>[] interfaces, 所需要代理的接口
*InvocationHandler
*
*/
public class DtproxyImpl {
    private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public static UserService getUserService (final UserService userService){
       return (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),
               userService.getClass().getInterfaces(),
               new InvocationHandler() {
                   @Override
                   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              		//刚开始会有疑问。为什么这里method可以直接用invoke这个方法,其实这个地方已经封装好了。
              		//原方法
                       Object res = method.invoke(proxy, args);
					//需要加的方法
                       System.out.println(format.format(new Date())+method.getName());

                       return res;
                   }
               });

    }
}

原理:它会生成一个新的类

public final class $Proxy0 extends Proxy implements UserService

所以从这里可以看出它必须有接口,才能进行动态代理,因为Proxy0 必须继承Proxy ,而java是单继承的。

    public final void create() throws  {
        try {
            super.h.invoke(this, m5, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
   	static {
    try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m6 = Class.forName("day5.noproxy.UserService").getMethod("updata");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m4 = Class.forName("day5.noproxy.UserService").getMethod("delete");
            m3 = Class.forName("day5.noproxy.UserService").getMethod("get");
            m5 = Class.forName("day5.noproxy.UserService").getMethod("create");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }

这里可以看出,JDK代理它用了反射。再回调了invoke方法,这个时候参数也变了。
extends Proxy 所以这里是this,所以前面参数有用,不是没用到。

假如:这里不实现接口会有什么影响?
那就要new 一个新的对象去点这些方法,若是实现接口只要换个名字。

3.3.2 cglib动态代理

/**
 * Enhancer.create()
 * 参数
 * Class type,
 * Callback callback,Ctrl进去是空的接口,看它的子类
 *
 */
public class CgClibProxyImpl {
    private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public static UserService getUserService(UserService userService){
        return (UserService) Enhancer.create(userService.getClass(),
                new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                Object invoke = methodProxy.invoke(userService, objects);
                System.out.println(format.format(new Date())+"zzzz");
                return invoke;
            }
        });

    }
}

同样的道理,这个地方也是封装好了的。

public class UserServiceImpl$$EnhancerByCGLIB$$5ced208f extends UserServiceImpl implements Factory 

它继承了UserService的实现类,不是实现的接口,所以它的优势很明显,cglib可以增强无接口实现的方法。

    public final void get() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            var10000.intercept(this, CGLIB$get$0$Method, CGLIB$emptyArgs, CGLIB$get$0$Proxy);
        } else {
            super.get();
        }
    }
    //-------------------------------------------------------------
        public int getIndex(Signature var1) {
        String var10000 = var1.toString();
        switch(var10000.hashCode()) {
        case -1949372272:
            if (var10000.equals("updata()V")) {
                return 10;
            }
            break;
        case -1870561232:
            if (var10000.equals("CGLIB$findMethodProxy(Lorg/springframework/cglib/core/Signature;)Lorg/springframework/cglib/proxy/MethodProxy;")) {
                return 17;
            }
            break;
        case -1860420502:
            if (var10000.equals("CGLIB$clone$7()Ljava/lang/Object;")) {
                return 26;
            }
            break;

这底层实现原理代码可以看出来:

public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
1、它同样回调了这个方法(intercept),所以代码里面看起来没用到的Object o底层也用到了。
2、它不是用的反射,他是直接得到的。

3.3.3 两种动态代理的区别

1、JDK动态代理只能代理接口实现的方法,而cglib不是。
2、JDK底层用的是反射,而cglib是直接去取的。

4、SpringAOP

4.1 基于接口(xml,接口)

是因为他的"什么时候"是要实现接口,所以叫基于接口。
代理需要什么?
1、谁被代理了,被代理实例。
2、被代理的接口
3、新添加的方法(代理通知)
xml里面写的bean:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--被代理bean-->
    <bean id="userService" class="day5.noproxy.UserServiceImpl"/>
    <!--  代理通知bean  -->
    <bean id="logAdvice" class="day5.springAop.LogAdvice"/>
    <bean id="logAdvice2" class="day5.springAop.LogAdvice2"/>
    <!--  代理工厂bean  -->
    <bean id="userServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--被代理接口-->
        <property name="proxyInterfaces">
            <list>
                <value>day5.noproxy.UserService</value>
            </list>
        </property>
        <!--被代理bean-->
        <property name="target" ref="userService"/>
        <!--通知-->
        <property name="interceptorNames">
            <list>
                <value>logAdvice</value>
                <value>logAdvice2</value>
            </list>
        </property>

    </bean>


</beans>

这里是使用的xml和接口bean,是对整个被代理bean里面的方法进行了方法的增强,那是否能够只对其中一个或几个方法来进行增强呢?当然可以。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--被代理bean-->
    <bean id="userService" class="day5.noproxy.UserServiceImpl"/>
    <!--  代理通知bean  -->
    <bean id="logAdvice" class="day5.springAop.advic.LogAdvice"/>
    <bean id="logAdvice2" class="day5.springAop.advic.LogAdvice2"/>
    
    <bean id="advior" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
        <property name="advice" ref="logAdvice"/>
        <property name="mappedNames" value="get,updata"/>
    </bean>

    <!--  代理工厂bean  -->
    <bean id="userServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--被代理接口-->
        <property name="proxyInterfaces">
            <list>
                <value>day5.noproxy.UserService</value>
            </list>
        </property>
        <!--被代理bean-->
        <property name="target" ref="userService"/>
        <!--通知-->
        <property name="interceptorNames">
            <list>
<!--                <value>logAdvice</value>-->
<!--                <value>logAdvice2</value>-->
                <value>advior</value>
            </list>
        </property>
    </bean>
</beans>

前面Client下使用需要getBean 接口 , 那是否能不用getBean接口,直接getBean被代理类呢?当然可以

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--被代理bean-->
    <bean id="userService" class="day5.noproxy.UserServiceImpl"/>
    <!--  代理通知bean  -->
    <bean id="logAdvice" class="day5.springAop.advic.LogAdvice"/>
    <bean id="logAdvice2" class="day5.springAop.advic.LogAdvice2"/>


    <bean id="advior" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
        <property name="advice" ref="logAdvice2"/>
        <property name="mappedNames" value="get,delete"/>
    </bean>
    <!--  代理工厂bean  -->
    <bean id="userServiceProxy" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <property name="beanNames" value="*Service"/>
        <!--通知-->
        <property name="interceptorNames">
            <list>
<!--                <value>logAdvice</value>-->
<!--                <value>logAdvice2</value>-->
                <value>advior</value>
            </list>
        </property>
    </bean>
</beans>

代理工厂代码太多了,能够优化呢?也可以

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--被代理bean-->
    <bean id="userService" class="day5.noproxy.UserServiceImpl"/>
    <!--  代理通知bean  -->
    <bean id="logAdvice" class="day5.springAop.advic.LogAdvice"/>
    <bean id="logAdvice2" class="day5.springAop.advic.LogAdvice2"/>
    <bean id="advior" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
        <property name="advice" ref="logAdvice"/>
        <property name="mappedNames" value="get,delete"/>
    </bean>
    <!--  代理工厂bean  -->
    <bean id="userServiceProxy" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
    
</beans>

4.2基于标签

为什么说是基于标签呢?
因为他需要在xml文件里面用标签来实现动态代理。

<?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: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/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--被代理bean-->
    <bean id="userService" class="day5.noproxy.UserServiceImpl"/>
    <!--  代理通知bean  -->
    <bean id="logAdvice" class="day6.LabelAOP.LogAdvice"/>

    <aop:config>
        <aop:aspect ref="logAdvice">
            <aop:pointcut id="pointcut" expression="
            execution(* day5.noproxy.UserServiceImpl.create(..))"/>
            <aop:before method="logDate" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>
</beans>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值